• 辅助理解递归:明确这个函数是做什么的,然后在这个函数里有需要做这一步,比如按扩展先序创建二叉树,函数作用为:“创建输入节点的子树,先左子树,后右子树”。然后创建左右子树的时候是递归调用。
  • 管道和进程间通信,即 pipe() + fork() 的形式,注意关掉无用的文件文件描述符,否则会造成文件描述符的浪费,并且 fork () 后关闭比较麻烦。先 pipe (),之后先把能立刻用完 **** 之后就不在用的符立刻用完,然后将他们关掉,再 fork ()。因为子进程会继承父进程的文件描述符。例:MIT6.S081 lab 的第三个求 2-35 内的素数
#include <stidio.h>
void is_primes(int fd_father[2]){
    int fd_child[2];// 该进程的子进程的管道读写符
    pipe(fd_child);
    // 打印一定是素数的数(第一个写进来的数)
    int p = 0;
    read(fd_father[0], &p, sizeof(p));
    printf("prime %d\n", p);
    // 如果 n 是 p 的整数倍,那他一定不是素数,丢弃;否则,可能是素数,写给子进程。
    int n;
    int left = 0; // 剩下几个数字没有判断。
    while(read(fd_father[0], &n, sizeof(p)) != 0) {
        if(n % p != 0) {
            write(fd_child[1], &n, sizeof(n));
            left++;
        }
    }
    close(fd_father[0]);
    close(fd_child[1]);
    if(left == 0){ // 说明所有数都判断完了,为了避免最后一层还会执行后面的 fork, 避免出现多个 0.
        exit(0);
    }
    // 上面同理,先把自己进程的读端和子进程的写端用完,关掉,再开子进程。
    
    if(fork() == 0){ // 该进程的子进程
        is_primes(fd_child); // 子进程读取父进程进程写进来数字,并判断是否为素数:可能是素数的,写给孙进程然后递归调用,一定不是素数的,丢弃。
    }
    wait(0);
}
int main(int argc, char** atgv) {
    int fd_child[2]; // 子进程的管道读写端
    pipe(fd_child);
    // 先把数字写进子进程管道,并关掉写端。存存储器,系统总线,主存,磁盘。
操作系统管理硬件。进程(处理器,主存,i/o设备),虚拟内存(主存,i/o设备),文件(i/o设备)。
并发运行:一个进程的指令和另一个进程的指令交错执行。这种交错执行的机制叫上下文切换,进程间切换由内核管理。
上下文:操作系统保持跟踪进程运行所需的所有状态信息。
内核:操作系统代码常驻主存的一部分,不是独立的进程。是系统全部进程所用代码和数据结构的集合。
栈位于用户虚拟地址空间顶部,编译器用它来实现函数调用,和堆一样,用户栈在程序执行期间可以动态地扩展和收缩 。 调用函数时,栈会增长;从一个函数返回时,栈会收缩 。
内核虚拟内存:程序无法读写,或调用内核代码定义的函数,需要调用内核来执行操作。
<img title="" src="file:///home/yuanye/图片/2022-10-06%2017-25-26%20的屏幕截图.png" alt="2022-10-06 17-25-26 的屏幕截图.png" width="309" data-align="center">
PC:程序计数器(寄存器),ALU(算数/逻辑单元)
    for(int i = 2; i <= 35; i++){
        write(fd_child[1], &i, sizeof(i));
    }
    close(fd_child[1]);
    
    // 创建子进程,只需要子进程的读端。
    if(fork() == 0) {  
        is_primes(fd_child); // 子进程读取父进程进程写进来数字,并判断是否为素数:可能是素数的,写给孙进程然后递归调用,一定不是素数的,丢弃。
    } 
    wait(0);
    exit(0);
}
阅读次数