linux--进程信号

进程信号      **


     信号:事件通知---软中断
     信号基本认识:每个信号都对应了一个事件
           linux信号的种类:kill -l 查看信号种类--62种信号
             1 ~31 是非可靠信号(非实时信号)
             34~64 是可靠信号(实时信号)
     信号的生命周期
          (1)信号的产生
                诞生一个信号(硬件/软件)
            硬件产生:
                ctrl+c  ctrl+z 20  ctrl+|
            软件产生:
                #include <signal.h>
                #include <abort.h>
               int kill(pid_t pid,int sig)
               pid:指定信号要发送给哪个进程的进程pid
               sig:要发送的信号
               
               void abort()
               //给调用进程发送SIGABORT信号
               
               int raise(int sig)
               //给调用进程发送指定信号

               unsigned int alarm(unsgned int seconds)
               //定时器:在seconds秒后给调用进程发送SIGALARM信号
               
               大部分信号都会导致进程退出
               SIGCHLD :子进程退出后系统给父进程发送的信号
                        而且不会引起进程退出
               信号的到来会打断当前进程的阻塞状态
            core dumped: 核心转储--退出时保存程序的运行信息
                  默认是关闭状态
                  core文件命名方式 core.pid  pid:进程pid
                  ulimit -a        查看核心转储文件大小
                  ulimit -c  1024  设置核心转储文件大小为1024
                  core文件的使用:
                        gdb ./loop -->  core-file ./core.pid -->bt查看调用栈信息
              先加载运行程序->加载程序运行信息->开始调试:bt查看调用栈信息
          (2)信号的注册
                 将信号添加到pcb中
                 PCB->struct sigpending -->  sigset_t(信号集合:位图)
                 信号到来之后都会添加到未决信号集合中
                 操作系统给一个进程发送信号,实际上就是向这个进程pcb的
             信号pending 集合中添加信号(修改位图),因为位图只能标记
             信号是否存在,不能标记信号的个数    
             pcb中有一个sigqueue链表,信号到来就会组织一个节点添加到这个链表中    
              
              可靠信号到来:修改位图,每个信号都组织节点添加到链表
              非可靠信号到来:修改位图,若位图为1,则什么都不做
                                否则,添加一个节点,修改位图
          (3)信号的注销
                从pcb中将信号移除
                可靠信号:因为节点可能有多个,删除节点后判断是否还有相同节点
                           判断是否修改位图
                非可靠信号:因为节点只有一个,因此删除节点后,位图直接修改为0
          (4)信号的处理
               (1)默认:  因为每个信号都对应了一个事件,这些事件在操作系统中都有
               既定义完毕的处理方式
               (2)忽略:  什么都不干
               (3)自定义: 用户定义处理方式(函数)--修改内核中对信号的处理方式
                  sighandler_t signal(int signum,sighandler_t handler);
                  void sigcb(int signo);
                  //signum: 信号编号
                  //handler: 处理方式
                  //SIG_DFL: 默认处理方式
                  //SIG_IGN: 忽略处理方式
                 信号的捕捉流程:自定义信号
                     信号的捕捉处理是在从程序运行内核态到用户态切换之前完成
                     一个进程如何从用户态切换到内核态(系统调用接口,程序异常,中断)
                     信号的处理方式选择一个合适的时机去处理--从内核态切换到用户态之
                 前看看有没有信号需要被处理:如果有信号处理,并且是自定义处理方式,
                 调用do_signal处理信号,返回用户态执行我们自定义的信号处理函数,用户
                 自定义信号的处理函数执行完毕后,调用sig_return返回内核态,再次查看
                 是否有信号待处理,如果没有了,则返回用户态回到程序主控流程,执行程序
                  sigaction(int signo,struct sigaction* act,struct sigaction* old)
                      struct sigaction:  sa_handler    sa_sigaction    sa_mask 
                                         sa_flags=0     SA_AIGINFO
                  void (*sa_sigaction)(int ,siginfo_t*,void*);
                  sa_sigaction: 有三个参数--不仅有触发回调信号值,还有信号的相关参数/附加信息
                  void (*sa_handler)(int);---只有一个参数,就是触发回调的信号值
                  如果给某个进程发送信号时,顺带一些其他信息,这时候只能使用sa_sigaction才能处理
                  
                  
     信号的阻塞
           能收到信号,但是暂时不处理--阻止信号递达
                信号的递达:  动作--信号处理
                信号的未决: 状态--从产生到递达之间的状态
                未决信号: 还没有被处理的信号
            sigprocmask(houw,set,old);
                  SIG_BLOCK   mask=mask|set
                  SIG_UNBLOCK  mask&(~set)
                  SIG_SETMASK  mask=set
                sigemptyset   清空信号集合    
                sigfillset    向集合中填充所有信号
                sigaddset     向集合中添加指定信号   
                sigdelset     从集合中删除指定信号
                sigismember   判断指定信号是否在集合中   mask&(1<<num)
                sigpending    获取进程当前未决信号
                
           信号进行阻塞:实际上就是在 blocked 位图中标记哪些信号到来之后暂不处理
              有两个信号不会被阻塞,也不会被自定义: SIGKILL 9    SIGSTOP 19(ctrl+z)
            进程杀不死:
                 (1)没有被阻塞,但是也杀不死的可能僵尸进程    
                 (2)信号被阻塞(可用 kill -9 杀死)    
            如何阻塞信号/解除信号:--sigmptyset sigfillset sigaddset sigprocmask
            如何处理未决信号:--sigpending sigismember
                先阻塞所有信号->getchar()->解除阻塞--信号的阻塞:可靠/非可靠信号    
            
            mysleep实现:信号会打断当前进程的阻塞操作    
                alarm+pause---pause使进程阻塞,alarm 保证n秒后发送信号打断阻塞
              sigsuspend(mask)--临时使用mask替换阻塞集合,阻塞mask中的信号--陷入休眠--唤醒
            后还原阻塞集合
                 先阻塞SIGALARM防止定时器与休眠之间SIGALARM被处理,使用sigsuspend对SIGALARM
            解除阻塞,并且陷入休眠(原子操作)             
                
     竞态条件
          竞态条件:多个执行时序之间出现的资源竞争关系
     可重入函数/不可重入函数:
             能否在多个运行时序重复调用,而不造成数据二义/程序异常 
          能否重入的关键:是否对非全局数据进行原子操作
          原子性操作:操作不可被打断
          
          当用户进行函数接口及调用时,就要考虑接口是否可重入
          
          malloc free  --不可重入--内部有不受保护的全局链表操作
          
      关键字: volatie---保持内存可见性--防止编译器对代码进行过度优化
        若要进行代码优化,就需要用户对程序有更多的可控--需要明白哪些变量需要修饰
             gcc -o2 test.c -o test  代码优化
             volatile int i=0;//防止 i 被过度优化( i 在使用代码优化的时候,使用频率少的
             时候就会把 i 放在寄存器中,当我们在代码中更改了 i 的值,只会改变内存中i的值
             就不会改变寄存器中 i 的值 )
     SIGHLD信号:17信号
         一个进程退出后,操作系统通知父进程子进程已经退出(操作系统通过SIGCHLD信号
         通知父进程)
       问题来了:SIGCHLD信号的默认处理方式就是忽略处理;
            因为父进程不知道SIGCHLD什么时候来(不知道子进程什么时候退出),因此创建子进程
       之后就要进行等待,但是这样造成了父进程的浪费(什么都没做,就是死等子进程);
       如果父进程对SIGCHLD信号进行了自定义处理方式,并且在自定义方式中包含进程等待;
       这个时候就不需要空等待
           因为SIGCHLD信号是一个非可靠信号,只注册一次,有可能丢失(大量子进程同时退出)
       意味着sigcd只会被回调一次,然而waitpid调用一次,只能处理一个进程退出;因此我
       们尽可能的在一次回调中将退出的进程处理掉 while(waitpid(-1,NULL,WNOHANG)>0){}
       只要返回值大于0就表示有子进程退出,那就一直处理,直到没有子进程退出(返回0)
       或者出错(返回-1),退出while循环        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值