fork创建子进程
#include <unistd.h> pid_t fork(void); 功能:创建一个子进程 返回值:创建失败返回-1 创建成功会返回两次 父进程:返回子进程的pid 子进程:返回0 注意:总进程数或者实际拥有pid的进程数量超过了系统的限制,该函数失败
注意:子进程创建出来后,父子进程会同时各自运行代码,因此可以通过分支判断返回值,来让父子进程执行不同的程序代码
#include <stdio.h> #include <unistd.h> int main(int argc,const char* argv[]) { printf("我是进程%u\n",getpid()); pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(0 == pid) { printf("我是子进程%u,我的父进程是%u\n",getpid(),getppid()); pause(); } else { printf("我是父进程%u,我的子进程是%u\n",getpid(),pid); pause(); } }
父子进程谁先运行:
-
通过fork系统调用创建出来的子进程与它父进程会各自往下运行,但是其先后顺序不确定,可以通过睡眠等系统调用确定让哪个进程先执行
子进程是父进程的副本:
-
由fork创建的子进程会获得拷贝出父进程的data段、bss段、heap段、stack段、I/O流缓冲区。
#include <stdlib.h> int main(int argc,const char* argv[]) { int num = 0; int* p = malloc(4); if(fork()) { sleep(1); // 父进程 num = 1000; *p = 1000; } else { // 子进程 num = 2000; *p = 2000; } // 各自的num都没有被其他进程改变,证明父子进程的num不是同一个,是子进程拷贝了父进程的数据 // 虽然父子进程中的num 和p的地址是相同的,但是每个进程都拿到4g的虚拟内存,但是映射的物理内存是不一样的,所以虚拟地址相同没有参考价值 printf("pid=%u : num=%d *p=%d &num=%p p=%p\n", getpid(),num,*p,&num,p); sleep(2); printf("pid=%u : num=%d *p=%d &num=%p p=%p\n", getpid(),num,*p,&num,p); }
#include <stdio.h> #include <unistd.h> int main(int argc,const char* argv[]) { // *会残留在输出缓冲区,被拷贝给子进程 // 子进程创建后会继续代码,有可能也会创建子进程 printf("*"); for(int i=0; i<3; i++) { fork(); } }
子进程会共享父进程的代码段、文件描述符fd:
-
通过fork创建的子进程会共享父进程的代码段,fork之前的代码只有父进程执行,fork之后的代码父子进程都有机会执行,主要受到逻辑的控制进入不同的分支
-
不同的程序之间,文件描述符是不能共享的
-
但是由fork创建的父子进程之间,是把父进程内核中的文件描述符的表格拷贝给了子进程,此时两者共享父进程的已打开的文件描述符
fork子进程会继承父进程的信号处理方式:
-
通过fork创建子进程会继承父进程的信号处理方式,是因为子进程共享了父进程的代码段
#include <signal.h> void sigint(int num) { printf("我是进程%u,获得了%d信号\n",getpid(),num); } int main(int argc,const char* argv[]) { signal(SIGINT,sigint); if(fork()) { printf("我是父进程%u\n",getpid()); for(;;); } else { printf("我是子进程%u\n",getpid()); for(;;); } }
练习1:实现出孤儿进程与僵尸进程,根据ppid和ps命令查看
#include <stdio.h> #include <unistd.h> int main(int argc,const char* argv[]) { pid_t pid = fork(); if(pid) { for(;;) { printf("我是父进程%u,我的子进程是%u\n",getpid(),pid); sleep(1); } } else { printf("我是子进程%u,\n",getpid()); sleep(3); printf("我私了!\n"); } /* 孤儿进程 if(fork()) { printf("我是父进程%u\n"); sleep(3); printf("我是父进程我要死了\n"); } else { for(;;) { printf("我是子进程%u,我的父进程是%u\n", getpid(),getppid()); sleep(1); } } */ }
练习2:给主进程创建出4个子进程,再给每个子进程创建2个子进程
#include <stdio.h> #include <unistd.h> int main(int argc,const char* argv[]) { printf("我是主进程%u\n",getpid()); for(int i=0; i<4; i++) { if(0 == fork()) { printf("我是进程%u,我的父进程%u\n",getpid(),getppid()); for(int i=0; i<2; i++) { if(0 == fork()) { printf("我是孙子进程%u,我的父进程%u\n", getpid(),getppid()); pause(); } } pause(); } } pause(); }