创建进程—fork函数
函数原型:pid_t fork(void);
返回值:子进程返回0;父进程返回子进程pid,失败返回-1(失败的原因:1内存不够;2系统进程数量太多);
注意:fork之前,父进程独立执行,fork之后,父子两个执行流分别执行。但是父子进程谁先执行是由系统调度决定。
写实拷贝:父进程创建子进程后,子进程室父进程的副本;父子代码共享,父子不写入时,数据也是共享。当任意一方试图写入时,便以写实拷贝的方式各自创建一份副本。
代码:
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 pid_t pid = fork();
7 if(pid>0){
8 sleep(3);
9 printf("I am father:%d\n",getpid());
10 }else if(pid==0){
11 printf("I am child:%d\n",getpid());
12 sleep(1);
13 }else{
14 perror("fork");
15 }
16 return 0;
17 }
vfork函数:也是创建进程,用法跟fork相似;
不同的是:
1、vfork一个子进程,父子进程共享地址空间,而fork的子进程具有独立的地址空间。
2、vfork保证子进程先运行,直到调用exec或者exit之后父进程才运行。
代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 int flag = 100;//设定一个全局变量100
5 int main()
6 {
7 pid_t pid = vfork();
8 if(pid>0){
9 sleep(2);
10 flag = 10;//父进程把flag变为10;
11 printf("father:%d;flag = %d\n",getpid(),flag);
12 exit(0);
13 }else if(pid==0){
14 flag = 0; //子进程把flag变为0; printf("child:%d,father_id=%d,flag=%d\n",getpid(),getppid(),flag);
16 sleep(5);
17 }else{
18 perror("fork");
19 }
20 return 0;
21 }
结果:
我们看到子进程改变了父进程的的值,因为子进程实在父进程的地址空间运行。)
进程等待—wait和waitpid
进程等待必要性:
1、子进程退出,如果父进程不管不顾,就可能造成僵尸进程的问题,造成内存泄露。
2、父进程需要知道子进程的退出状态,完成的任务结果如何。
3、父进程通过进程等待的方式,回收子进程资源,获取子进程退出状态。
函数原型:pid_t wait(int *status)
返回值:成功返回被等待进程的pid;失败返回-1;
参数:输出型参数,获取子进程的退出状态,不关心可以设置为NULL;
status&0x7f==0:表示代码执行完了,正常退出;
status&0x7f!=0:表示异常退出,具体值表示使该进程退出的信号的编码。
status>>8 & 0xff的值表示子进程的退出码。
调用wait函数会发生什么?
1. 如果有子进程在运行,父进程进入阻塞状态(可以理解为什么事情都没有做,一直在检测子进程是否在运行)
2.如果父进程在调用wait前子进程已经终止,wait可立即获得子进程的终止状态(退出码,退出信息),子进程的终止状态是体现在status参数上的,另外wait还会返回所终止的子进程的标识符。
3.如果当前没有子进程,则会出错返回-1。
4.如果有一个子进程终止,那么wait便返回。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 int ret=fork();
9 if(ret>0){
10 int status=0;
11 int wait_id = wait(&status);
12 if(wait_id>0){
13 printf("child has success dead!,id=%d\n",wait_id);
14 }else{
15 printf("child has false dead!,%d\n",wait_id);
16 }
17 }else if(ret==0){
18 printf("I am child,id=%d\n",getpid());
19 sleep(5);
20 exit(0);
21 }else{
22 perror("fork");
23 }
24 return 0;
25 }
waitpid函数:也是创建进程,用法跟wait相似;
函数原型:pid_t waitpid(pid_t pid,int *status,int options)
pid:检测子进程的pid
status:子进程的终止状态,如果不是空指针,则终止进程的终止信息就存放在它所指向的单元内。不关心终止状态可以将status制成NULL。
options:指定参数,默认情况下waitpid与wait做的事情都是一样的,为阻塞式监测子进程终止状态;当options=WNOHANG时,此时为非阻塞方式,就是监测时如果子进程没有终止,调用者可以做其他事情,另外还有两个参数分别为WCONTINUED和WUNTRACED,这两个参数是跟作业控制有关的。
结果:
非阻塞等待:
结果:
WNOHANHG:若子进程没有结果,waipid返回0,不予等待,若正常结束,则返回该子进程的pid。
WIFEXITED(status):表示若为正常终止子进程返回的状态,则为真(查看进程是否是正常退出),这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值;
WEXITSTATUS(status):若WIFEXITED非零,提取子进程的退出码(查看进程退出码)。
进程终止—_exit()和exit()
查看进程退出码: echo $?
#include<unistd.h>
void _exit(int status)
参数:status定义了程序的终止状态,父进程通过wait获取该值。
#include<unistd.h>
void exit(int statsu)
_exit()—系统调用 exit()— 库函数
exit()做的三件事:
1,执行用户通过atexit或者on_exit定义的清理函数
2,关闭所有打开流,所有的缓存数据均被写入。
3,调用_exit;
思考题:vfork创建的子进程, 直接return为什么会出现崩溃?
结束子进程的调用是exit()而不是return,如果你在vfork中return了,那么,这就意味main()函数return了,注意因为函数栈父子进程共享,所以整个程序的栈就出现问题了。