一:fork 函数
一个运行中的进程可以调用fork 函数来产生一个新的进程,函数原型及头文件定义如下:
#include <unistd.h>
pid_t fork(void);
Returns: 0 in child, process ID of child in parent, 1 on error
下面有几个细节性问题来说明:
1:普通的函数被调用时会有一次返回, fork 函数会有 两次 返回。在进程中返回0 ,在父进程中返回子进程的 PID 。
2:子进程和父进程都会执行 fork 后面的指令,即 fork 以前的指令,子进程不会执行。
3:历史上,子进程是父进程的拷贝。目前的 fork 实现不会完全赋值父进程的数据、堆、栈,因为 fork 通常后面会跟着 exec 函数。而是利用 copy-on-write(COW) 技术来实现,这些区域为子进程和父进程所共享,由内核来改变保护模式,从而实现数据的只读。当有进程试图修改这些区域的数据时,内核仅仅做一个这个要修改区域的赋值。
4:子进程提前与父进程执行或者是相反,这要取决的系统的调度策略。
Fork下的文件共享
利用fork 调用时,子进程会复制父进程所有的文件描述符。在父进程和子进程共同写入一个文件时,如果没有同步机制,比如 wait 。那么结果是两者的写入数据混合在一块。
对于调用fork 后的文件描述符操作,有以下两点
1:父进程等待子进程结束。父进程不对描述符进程操作。当子进程结束时,任何子进程读写的共享文件,都会更新文件偏移。
2:如果,父进程关闭了,一个共享的文件描述符,则在子进程中,同样被关闭。
以下为一个进程间操作文件的例子:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> int main(int argc, char * argv[]) { char * buffer = "Hello Linux.I am Li Yachao./n"; char read_buffer[128]={0}; int fd = 0; int s = 0; int file_size = 0; off_t currpos = 0; /*file offset*/ pid_t pid = 0; fd = creat("./test.txt",O_RDWR | O_CREAT| O_TRUNC ); if(fd <0) { printf(" create error/n"); exit(1); } s = write(fd,buffer,strlen(buffer)); if(s != (strlen(buffer)) ) { printf("write error/n"); } printf("Create success,content:%s",buffer); /*Crete a file */ fd = open("./test.txt",O_RDONLY); file_size = lseek(fd, 0, SEEK_END);/*display the file size*/ printf("File size is %d/n",file_size); pid = fork(); if(pid<0) { printf("Fork error/n"); exit(1); } if(pid == 0)/*@1*/ { currpos = lseek(fd, 7, SEEK_SET);/*@2*/ /*set the read lseek value*/ printf("In the child set the lseek is %d/n",(int)currpos); } else if(pid >0)/*@3*/ { wait(10);/*wait for the child process to set lseek, or parent read null from file.*/ s = read(fd,read_buffer, (file_size - currpos) ); printf("Parent read %d byttes,content:%s",s,read_buffer); close(fd); } return 0; }
在 @1中,在子进程中设置fd的lseek值为7。
在@3中,在父进程读取fd的值。
在 @2中设lseek的第二个参数为7,结果如下:
Create success,content:Hello Linux.I am Li Yachao.
File size is 28
In the child set the lseek is 7
Parent read 21 byttes,content:inux.I am Li Yachao.
在 @2中设lseek的第二个参数为0,结果如下:
Create success,content:Hello Linux.I am Li Yachao.
File size is 28
In the child set the lseek is 0
Parent read 28 byttes,content:Hello Linux.I am Li Yachao.
从以上可以看出,和进程间操作变量不同,在子进程中操作父进程的文件描述,会影响到父进程的操作。
总结:
在fork 下,子进程和父进程共享同样的文件描述符,任何进程对于文件描述符的操作都会影响到其他进程对于文件描述符的操作。
二:vfork 函数
Vfork函数的调用和返回值和 fork 函数的相同,不同的是, vfork 函数目的为生成一个新的进程,新的进程用 exec 来执行一个新的程序。
Vfork函数有以下特点:
1:在调用 vfork 后,子进程仅仅调用 exec 。
2:在子进程调用 exit 或者是 exec 之前,子进程运行在父进程的地址空间。
3:子进程优先执行,直到调用 exit 或者是调用 exec 时,父进程才执行。
