进程退出
wait() 和 waitpid()
vfork() 和 exec系列函数
信号
进程退出的方式:
正常退出:
1.main()执行了return
2.执行了 exit() _exit() _Exit()函数
3.最后一个线程结束
非正常退出:
1.收到了信号
2.最后一个线程 被取消
exit() 和 _Exit _exit()的区别
1._Exit() _exit() 本质是一样的
2.exit()函数在退出前允许调用其他的函数做一些收尾工作,_exit()函数要求立即退出进程
3.exit()退出前会调用atexit()注册的函数
4.return 其实也会调用atexit()注册的函数
-----------------------------------------------------------
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void at()
5 {
6 printf("at is called\n");
7 }
8 int main()
9 {
10 atexit(at);//注册了退出前调用的函数at
11 printf("begin\n");
12 //exit(0);//在这里退出,退出之前调用at()
13 _Exit(0);//立即退出,不调用at()
14 printf("end\n");
15 return 0;//效果等同于exit()
16 }
-----------------------------------------------------------
wait() 和 waitpid() 可以让父进程等待子进程的结束,效果:父进程阻塞,子进程运行,直到子进程结束
wait()等待方式比较单一,waitpid()等待方式有更多的选择。
pid_t wait(int* status)
等待任意一个子进程结束,status会存储结束子进程的信息(退出码,是否正常退出),返回值是结束子进程的PID,会导致父进程一直阻塞,直到有一个子进程结束为止(包括僵尸子进程)
宏函数:WIFEXITED(status)可以判断是否正常退出,如果正常退出返回真,而WEXITSTATUS(status) 取退出码(exit(值))
pid_t waitpid(pid_t pid,int* status,int option)
参数: pid 指定等待的子进程
> 0 指定等待子进程的 进程ID=参数
== -1 等待任意子进程的结束
== 0 等待本进程组的所有子进程
< -1 等待指定进程组的所有子进程
option 可以指定是否阻塞方式等待,WNOHANG就相当于非阻塞方式等待
返回:阻塞方式:返回结束子进程的PID,失败返回-1
WNOHANG方式:返回结束子进程的PID,如果没有子进程结束,返回0,失败返回-1
vfork() 函数格式和fork() 完全一样,但vfork()不会复制任何的内存空间,而是直接使用父进程的内存空间运行代码,因此,vfork()创建的子进程可以确保先于父进程运行
子进程把父进程的内存空间还给父进程的条件:
1.子进程运行结束
2.子进程调用exec系列函数,从而获得全新的内存空间。
只要父进程拿到了内存空间,父子进程就可能同时运行
直接调用vfork()实际意义不大,必须和exec系列函数结合使用才有实际意义,vfork()只能创建新的子进程,exec系列函数负责提供全新的代码和数据
经验:
为了安全起见,无论fork()还是vfork()创建子进程,最后一句都写一句exit()。
信号
常见信号:
ctrl+C -> 中断死循环(信号2)
段错误 -> 信号
总线错误 -> 信号
整数被0除 -> 信号
kill -9 -> 发信号9
1.信号
中断 - 终止当前正在执行的代码,转而执行其它的代码
信号就是在Unix/Linux应用广泛一种中断方式
中断分为 软件中断和硬件中断,信号属于 软件中断的一种
2.信号是什么?
信号本质上就是一个整数,系统用信号实现中断,信号都有一个宏名称,都以SIG开头,比如:SIGINT就是信号2,POSIX规范中没有定义整数的规范,而是用了宏名称做规范,因此使用宏名称代表信号有更好的通用性
查看信号的命令: kill -l
3.信号的特点:
信号分为可靠和不可靠信号,对于Linux系统来说,1-31的是不可靠信号,主要特点是不支持排队,因此可能发生信号的丢失,非实时信号。 34-64的是可靠信号,主要特点是支持排队,不会造成信号的丢失,是实时信号。
程序中,无法确定信号什么时候会来,信号来不来也无法确定(异步方式)
信号的来源:信号函数和硬件故障
4.信号处理方式
4.1 默认处理,采用系统默认处理方式,80%以上都是中止进程
4.2 忽略处理,忽略信号的到来
4.3 自定义处理函数,执行自己的代码
注:信号9不能被忽略,也不能自定义,只能默认,当前用户只能给自己用户的进程发信号,但root能给所有用户的进程发信号
5.修改信号的处理方式,需要使用信号注册函数:
signal() 或 sigaction()
void(*fa) (int) signal(int signum,void (*fa) (int))
参数: signum 就是信号(整数)
fa有三种可能: SIG_IGN 忽略
SIG_DFL 默认
自定义的函数名(函数指针)
使用步骤:
1.先定义一个函数void fa(int signo)
2.使用signal()完成注册
练习:
测试一下fork(),创建的子进程的信号处理和父进程之间的继承关系
关于子进程对父进程,信号处理方式的继承:
1.fork()创建的子进程,完全继承父进程的信号处理方式,如果父进程默认/忽略/自定义,子进程也会一样的,默认/忽略/自定义
2.vfork() + exec系列函数创建的子进程,父进程默认,子进程也默认,父进程忽略,子进程也忽略,父进程自定义,子进程改为默认
wait() 和 waitpid()
vfork() 和 exec系列函数
信号
进程退出的方式:
正常退出:
1.main()执行了return
2.执行了 exit() _exit() _Exit()函数
3.最后一个线程结束
非正常退出:
1.收到了信号
2.最后一个线程 被取消
exit() 和 _Exit _exit()的区别
1._Exit() _exit() 本质是一样的
2.exit()函数在退出前允许调用其他的函数做一些收尾工作,_exit()函数要求立即退出进程
3.exit()退出前会调用atexit()注册的函数
4.return 其实也会调用atexit()注册的函数
-----------------------------------------------------------
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void at()
5 {
6 printf("at is called\n");
7 }
8 int main()
9 {
10 atexit(at);//注册了退出前调用的函数at
11 printf("begin\n");
12 //exit(0);//在这里退出,退出之前调用at()
13 _Exit(0);//立即退出,不调用at()
14 printf("end\n");
15 return 0;//效果等同于exit()
16 }
-----------------------------------------------------------
wait() 和 waitpid() 可以让父进程等待子进程的结束,效果:父进程阻塞,子进程运行,直到子进程结束
wait()等待方式比较单一,waitpid()等待方式有更多的选择。
pid_t wait(int* status)
等待任意一个子进程结束,status会存储结束子进程的信息(退出码,是否正常退出),返回值是结束子进程的PID,会导致父进程一直阻塞,直到有一个子进程结束为止(包括僵尸子进程)
宏函数:WIFEXITED(status)可以判断是否正常退出,如果正常退出返回真,而WEXITSTATUS(status) 取退出码(exit(值))
pid_t waitpid(pid_t pid,int* status,int option)
参数: pid 指定等待的子进程
> 0 指定等待子进程的 进程ID=参数
== -1 等待任意子进程的结束
== 0 等待本进程组的所有子进程
< -1 等待指定进程组的所有子进程
option 可以指定是否阻塞方式等待,WNOHANG就相当于非阻塞方式等待
返回:阻塞方式:返回结束子进程的PID,失败返回-1
WNOHANG方式:返回结束子进程的PID,如果没有子进程结束,返回0,失败返回-1
vfork() 函数格式和fork() 完全一样,但vfork()不会复制任何的内存空间,而是直接使用父进程的内存空间运行代码,因此,vfork()创建的子进程可以确保先于父进程运行
子进程把父进程的内存空间还给父进程的条件:
1.子进程运行结束
2.子进程调用exec系列函数,从而获得全新的内存空间。
只要父进程拿到了内存空间,父子进程就可能同时运行
直接调用vfork()实际意义不大,必须和exec系列函数结合使用才有实际意义,vfork()只能创建新的子进程,exec系列函数负责提供全新的代码和数据
经验:
为了安全起见,无论fork()还是vfork()创建子进程,最后一句都写一句exit()。
信号
常见信号:
ctrl+C -> 中断死循环(信号2)
段错误 -> 信号
总线错误 -> 信号
整数被0除 -> 信号
kill -9 -> 发信号9
1.信号
中断 - 终止当前正在执行的代码,转而执行其它的代码
信号就是在Unix/Linux应用广泛一种中断方式
中断分为 软件中断和硬件中断,信号属于 软件中断的一种
2.信号是什么?
信号本质上就是一个整数,系统用信号实现中断,信号都有一个宏名称,都以SIG开头,比如:SIGINT就是信号2,POSIX规范中没有定义整数的规范,而是用了宏名称做规范,因此使用宏名称代表信号有更好的通用性
查看信号的命令: kill -l
3.信号的特点:
信号分为可靠和不可靠信号,对于Linux系统来说,1-31的是不可靠信号,主要特点是不支持排队,因此可能发生信号的丢失,非实时信号。 34-64的是可靠信号,主要特点是支持排队,不会造成信号的丢失,是实时信号。
程序中,无法确定信号什么时候会来,信号来不来也无法确定(异步方式)
信号的来源:信号函数和硬件故障
4.信号处理方式
4.1 默认处理,采用系统默认处理方式,80%以上都是中止进程
4.2 忽略处理,忽略信号的到来
4.3 自定义处理函数,执行自己的代码
注:信号9不能被忽略,也不能自定义,只能默认,当前用户只能给自己用户的进程发信号,但root能给所有用户的进程发信号
5.修改信号的处理方式,需要使用信号注册函数:
signal() 或 sigaction()
void(*fa) (int) signal(int signum,void (*fa) (int))
参数: signum 就是信号(整数)
fa有三种可能: SIG_IGN 忽略
SIG_DFL 默认
自定义的函数名(函数指针)
使用步骤:
1.先定义一个函数void fa(int signo)
2.使用signal()完成注册
练习:
测试一下fork(),创建的子进程的信号处理和父进程之间的继承关系
关于子进程对父进程,信号处理方式的继承:
1.fork()创建的子进程,完全继承父进程的信号处理方式,如果父进程默认/忽略/自定义,子进程也会一样的,默认/忽略/自定义
2.vfork() + exec系列函数创建的子进程,父进程默认,子进程也默认,父进程忽略,子进程也忽略,父进程自定义,子进程改为默认