fork系统调用
1.fork系统调用
pid_t fork(void);
该函数每次调用都返回两次,在父进程中返回的是子进程的PID,在子进程中返回0。失败返回-1,并设置errno。(因为父进程可能需要根据返回值来记录子进程的id,而子进程只需要根据返回值0来判断是否创建成功)
fork函数复制当前进程,在内核进程表中创建一个新的表项。
子进程代码和父进程完全相同,同时它还会复制父进程的数据(堆数据、栈数据和静态数据)。数据的复制采用的是写时复制,即只有在任一进程(父进程或者子进程)对数据执行了写操作时,复制才会发生。
此外创建子进程后,父进程打开的文件描述符(在fork之前打开的文件)在子进程中也是打开的,并且共享文件读写偏移量,且文件描述符的引用计数加1。
2.问题
问题1:如果在fork之前进行了char *p = malloc(1024)操作,则子进程也继承这部分内存空间,因为两个进程有各自的虚拟空间,所以两个进程的p的地址一样,但是进行读写操作时会对应到不同的物理地址。
如果在fork之前进行了char *p = malloc(1024)操作,并且执行了strcpy(p,"hello");,所以如果两个进程读这部分物理内存数据时,此时物理内存只有一份数据,而如果一个进程有写操作时,会复制一份到当前进程的物理内存(写时复制)。
问题2:子进程的执行位置就是fork()之后的位置,这是因为子进程继承了父进程的PC程序计数器。
问题3:在fork之前执行fp = fopen("abc.dat", "w");则父子进程共享文件描述符和文件偏移量,如果子进程写入数据,父进程要读取的话需要调用lseek函数。而如果是在fork之后打开文件,则此时父子进程不共享文件描述符。
问题4:下面的例子很简单,第一个fork之后,变为两个进程,之后这两个进程都执行自己的fork,因此最后变为4个进程。
//[1]给出如下C程序,在linux下使用gcc编译,给出输出结果。 int main() { pid_t pid1, pid2; pid1 = fork(); pid2 = fork(); printf("pid1:%d, pid2:%d\n", pid1, pid2); return 0; } 第一个fork之后: F C1 第二个fork之后: F C2 C1 CC1 所以最后有4个进程, 100,200 100,0 0,300 0,0 数字代表当前进程两次fork的返回值 //[2]给出如下C程序,在linux下使用gcc编译,给出输出结果。 int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("-\n"); } return 0; } 这个和上面的是一样的,最后也是有4个进程。会输出6个“-”。 第一个fork之后: F('-') C1('-') 第二个fork之后: F('-') C2('-') C1('-') CC1('-')