fork学习(一基础学习)

1.fork():复制创建进程,出错返回-1,调用一次返回两次,在父进程中返回子进程的pid,在子进程中返回0.

2.子进程会继承父进程中的数据和程序计数器,且从fork()之后开始执行,fork()之后父子进程就是两个独立的进程,谁先运行由操作系统决定。

3.两个例子:

int main()
{
    int i=0;
    for(;i<2;i++)
    {
        if(fork==0)
        {
            printf("A\n");
        }
        else
        {
            printf("B\n");
        }
    }
    return 0;
}
//打印的结果是三个A三个B

int main()
{
    int i=0;
    for(;i<2;i++)
    {
        if(fork==0)
        {
            printf("A");
        }
        else
        {
            printf("B");
        }
    }
    return 0;
}
//打印的结果是四个A四个B

第一个例子图示:

fork学习(一基础学习)_第1张图片

第二个例子图示:

输出缓冲区:printf()是将数据写入缓冲区    缓冲区刷新的四个条件:(1)\n  (2)fflush  (3)缓冲区满(4)程序已exit结束

fork学习(一基础学习)_第2张图片

4.父子进程的关系中数据和描述符的关系如下:

(1)数据包括全局变量、局部变量和堆区数据:这些数据都不共享,地址一样,只是因为逻辑地址相同而已。

(2)堆区数据:malloc之后仅仅是只是把虚拟的地址空间开辟出来,真正开辟物理空间是在程序使用开辟的空间时。刚开始虚拟地址并没有指向一块有效的地址空间,只有在使用时才会把虚拟地址映射到物理地址上,这个时候才会真正的开辟。在子进程中也是这样。

(3)fork并非直接拷贝所有父进程空间给子进程,fork之后,给子进程拷贝的就只有PCB然后对 PCB中的一些数据做一些改变。刚开始父进程的页表直接拷贝给子进程,并没有改变页表,父子进程共享所有的数据空间。当父子进程有任意一个尝试修改数据时,操作系统就会将要修改的页直接复制出来页号不变帧号改变。这也就是写时拷贝。

(4)fork采用写时拷贝的主要原因是一般fork之后紧跟着就是进程替换,之前的子程序会被用一个新的程序替换。如果每次直接复制则替换之后复制的那份代码相当于没用浪费内存,所以采用写时拷贝。

(5)文件描述符:此处的文件描述符主要指fork之前打开的文件描述符,因为fork之后父子进程是相互独立的,会单独执行并且拥有自己的空间

       ①进程打开一个文件最初始是记录在PCB中的struct file结构体类型的数组中,文件描述符fd就是该数组的一个下标,指向一个struct file结构,在struct file结构中有一个f_pos也就是文件读写偏移量,在该结构中还有一个指针指向struct m_inode(在fork源码剖析中有详细解释)。由于拷贝PCB时直接拷贝属于浅拷贝所以父子进程共享一个偏移量

     ②虽然即使 父子进程虚拟地址空间相同一般映射的空间是不同的,但是文件偏移量在PCB中,PCB中有指针指向页表,页表才会映射,文件偏移量显然不在页表中,所以并不会导致文件文件偏移量的不同

5.

PCB在内核中,虚拟地址空间一共有4G,3G的用户空间,1G的内核空间,1G的内核空间所有地址都是共享的,3G的用户空间是独立的,PCB在操作系统中,操作系统独一份,在我们的内核空间根本没有所谓的虚拟地址,直接使用物理地址。操作系统是为了安全控制才会启用虚拟地址空间

6.所以父子进程的全局,局部,堆区数据都不共享,文件描述符只共享fork之前的。

你可能感兴趣的:(Linux)