进程信号

进程信号:
    信号概念:信号就是一个软件中断,通知进程发生了某件事情(打断当前阻塞操作,选择一个合适的时机去处理信号)
    
    功能:通知事件的发生
            
    能够识别信号
    
    信号有不同种类:
        查看种类:(共有62种)
            命令:kill -l
            
            1号—31号(非实时信号):linux继承unix的信号(非可靠信号,表示信号可能会丢失)
                当有多个信号一起来的时候,只会处理一个信号
            
            34号—64号(实时信号):可靠信号,表示信号不会丢失
    
    信号有生命周期:
        
        信号的产生——>信号在进程中的注册——>信号在进程中的注销——>信号的处理(消亡)
            
        信号的产生:
            硬件: ctrl + c; ctrl + |; ctrl + z(中断一个前台进程)
            软件: kill命令; kill(); raise(); abort(); alarm();
            
            接口函数:
                int kill(int pid, int sig)
                    给指定的 pid 发送指定的信号 sig
                int raise(int sig)
                    给调用进程发送 sig 信号
                void abort(void)
                    给调用进程发送 SIGABRT 信号
                unsigned int alarm(unsigned int seconds)
                    seconds 秒后给调用进程发送一个 SIGALRM 信号
                    若 seconds = 0, 则取消上一个定时器
                    alarm(1);//表示取消上一个定时器,在设定 seconds = 1 的定时器
                    返回值: 若第一次使用 alarm 则返回 0; 若不是则返回上一个定时器剩余的时间
        
        信号在进程中的注册:修改未决信号集合对应位图,添加sigqueue结点
            sigset_t 信号集合(位图)
            pcb->struct sigpending->sigset_t signal
            未决信号:注册了但是没有被处理的信号
            未决:是一个信号从产生到信号处理之间所处的状态
            
            sigset_t结构体的认识:unsigned long int_val[_SIGSET_NWORDS];
            是一个128位的位图,用于对信号是否到来做标记
            
            sigqueue是一个链表,信号到来后会组织一个对应信号的节点,添加到链表中
            
            非可靠信号:判断如果位图已经标记,将什么都不做(每一个信号只添加一个节点,
                        信号同时到来时,有可能丢失事件)
            可靠信信号:不管位图是否标记,都会为每个到来的信号组织一个节点,添加到链表中,标记位图
    
                可靠/非可靠信号:最大区别:是否位每个到来的信号添加isgqueue节点
            
        在进程中注销信号:删除信号的sigqueue节点,修改位图
            可靠信号的注销:删除结点,判断链表中是否还有相同节点,如果没有在位图置0,否则位图不变
            
            非可靠信号的注销:删除节点,修改位图位0
            
        信号的处理:
            1.默认处理方式
                SIG_DFL
            2.忽略处理方式
                SIG_IGN
            3.自定义处理方式
                signal()
                
                    typedef void (*sighandler_t)(int);
                    sighandler_t signal(int signum, sighandler_t handler);
                        signum:信号编号    handler:信号的处理方式
            
                sigaction()
                    使用act 动作替换signum信号的处理方式,将原来的处理方式放到oldact中
                    
                    int sigaction(int signum, const struct sigaction* act, struct sigaction*oldact);
                        signum:信号编号        act:新的信号动作    oldact:保存旧的信号动作
                    
                    int sigemptyset(sigset_t *set);———>清空信号集合
            
            自定义处理信号的捕捉流程:        
                什么时候才会处理信号的事件:
                    
                main()经过系统调用/中断/异常来完成 “ 内核功能从内核态返回用户态的到时候”  判断是否有信号待处理,
                如果有,并且这个信号处理方式是忽略/默认方式,则直接在内核态完成。
                如果这个信号处理方式是自定义处理方式则返回一个sig_return,执行完毕后返回内核态,没有处理信号,则返回主控流程
                
            进程从用户态运行切换到内核态运行:系统调用,中断,异常
        
            信号的阻塞:阻止信号被递达
                递达:一个动作——表示信号处理
                阻止信号表示在进程pcb 的 block 信号集合中标记信号并且暂时不处理,不代表信号不会被传递,
                直到被解除阻塞(从 block 集合中解除)
                
                信号集合 sigset_t -> long int_val[16],使用位图作为信号的位图    
                    
                对信号阻塞集合进行操作的接口:
                    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
                    
                        how:
                            SIG_BLOK    blocked = blocked | set  //添加
                            SIG_UNBLOCK    blocked = blocked & ~set //移除
                            SIG_SETMASK    blocked = set              //直接设置
                        set: 要阻塞/解除阻塞的信号集合
                        oldset:被用于保存修改前,blocked集合原有数据
            
                    int sigemptyset(sigset_t *set);———>清空信号集合
                
                    int sigfillset(sigset_t *set);———>将所有信号添加到set的集合中
                    
                    int sigaddset(sigset_t *set, int signum);———>将指定信号,添加到指定集合中
                    
                    int sigpending(sigset_t *set);———>获取未决信号集合
                    
                    int sigismember(const sigset_t *set, int signum);———>判断是否在信号集合中
            
                    int sigdelset(sigset_t *set, int signum);———>从集合中删除信号
            
            哪些信号是不能被阻塞的:
                带有unblockable表示不能被阻塞
                所有信号中:SIGKILL(强杀), SIGSTOP()信号无法被阻塞,无法被自定义处理,无法被忽略。
                可靠信号不会丢失,非可靠信号可能会丢失
            
    竞态条件:程序运行时序的竞争执行(体会竞态条件/理解函数的可重入与不可重入)
        
        函数的可重入/不可重入:
            一个函数在不同的执行流下是否可以被重复调用,并且不会出现问题,有可能出问题,这个函数
            就是不可重入函数;否则是可重入函数
    
        原子操作:操作不可被打断
        
        函数的可重入/不可重入的关键点在:
            是否对全局数据进行了非原子操作
            
        当用户自己设计接口时,如果有可能在多个执行流中被调用,那么就要考虑函数中对全局数据操作的保护
        在多个执行流中调用相同的函数,也要考虑这个函数是否可能重入
        比如:malloc/free
 
        关键字:volatile 用于修饰一个变量——保持变量内存可见性
            每次对变量进行操作都要从内存中重新获取数据
            功能:防止编译器过度优化代码
            -O2
            C语言中常用的关键字:
                变量类型关键字(解释内存中数据的特性):
                    static ->1.正对变量(作用域,证明周期),2.正对函数
                    const->
                    volatile->
        
        SIGCHILD信号:
            子进程退出时操作系统给父进程发送的信号。(默认处理方式是忽略,导致父进程没有处理子进程的退出,导致僵尸进程)
            避免僵尸进程:
                1.死等: wait waitpid
                2.修改信号处理方式:在信号的回调函数中调用wait/waitpid 处理子进程推出事件
            
            因为SIGCHILD 信号是一个非可靠信号,在大量子进程同时退出的情况下,可能会丢失信号,因此在一次回调时,
            把能处理的僵尸子进程全部处理。
                sigcb()

posted on 2019-08-25 23:23 The_Ocean 阅读(...) 评论(...) 编辑 收藏

导航