进程创建
- fork
- fork有两个返回值
- 父子进程代码共享,数据独有,私有一份(采用写实拷贝技术)
新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。
int main(){
int x = 1;
if(fork() == 0){
printf("p1: x = %d\n",++x);
}
printf("p2: x = %d\n",--x);
exit(0);
}
程序运行的结果为啥是这个呢?
这道题的关键点是父进程只执行第六行的printf,所以输出的是p2:x = 0
子进程执行了两个printf语句。在fork返回之后,它执行第四行的printf,然后它从if语句中出来,执行第六行的printf。所以会输出
- vfork
- 父子进程共用一块虚拟地址空间,阻塞父进程直到子进程exit退出或程序替换重新开辟自己的空间以及虚拟地址空间页表。
进程终止
- 进程常见退出方法
- 正常退出(结果符合预期/结果不符合预期)
- 从main返回
- 调用exit:库函数,退出进程的时候先刷新缓冲区,然后释放资源
- 调用_exit:系统调用接口,退出时直接释放资源
- 异常退出:常见的程序崩溃
- crtl+c,信号终止
进程等待
- 进程等待:等待子进程的改变(等待子进程的退出)- 获取子进程的返回值,避免僵尸进程
1 //这是一个实现进程等待的demo 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<string.h> 6 #include<errno.h> 7 #include<sys/wait.h> 8 int main(){ 9 int pid = fork(); 10 if(pid<0){ 11 //error是一个全局变量,存储每次系统调用出现错误原因编号 12 //strerror通过错误编号获取字符串错误原因 13 printf("fork error:%s\n",strerror(errno)); 14 //perror直接打印上一个系统的调用错误原因 15 perror("fork perror"); 16 }else if(pid == 0){ 17 sleep(3); 18 exit(111); 19 } 20 //pid_t wait(int* status); 21 //阻塞等待任意一个子进程退出,获取返回值 22 // wait(NULL); 23 // pid_t waitpid(pid_t pid,int* status,int options); 24 // 阻塞等待任意一个子进程或者指定的子进程退出 25 // pid:-1:等待任意一个子进程 pid>0:等待指定子进程 26 // options:WNOHANG:将waitpid设置为非阻塞,0是默认阻塞 27 //返回值:若WNOHANG被指定,没有子进程退出则立即报错返回0;错误:-1; 28 int statu; 29 while( waitpid(-1,&statu,WNOHANG) == 0){ 30 //非阻塞轮询操作 31 printf("drink water\n"); 32 sleep(1); 33 } 34 if((statu&0x7f) == 0){ 35 printf("exit code:%d\n",(statu>>8)&0xff); 36 } 37 if(WIFEXITED(statu)){ 38 printf("exit code:%d\n",WEXITSTATUS(statu)); 39 } 40 while(1){ 41 printf("i am parent\n"); 42 sleep(1); 43 } 44 return 0; 45 }
- 正常退出(结果符合预期/结果不符合预期)
- 父子进程共用一块虚拟地址空间,阻塞父进程直到子进程exit退出或程序替换重新开辟自己的空间以及虚拟地址空间页表。