Day29_wait、waitpid及信号

进程退出
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系列函数创建的子进程,父进程默认,子进程也默认,父进程忽略,子进程也忽略,父进程自定义,子进程改为默认

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值