• Linux C应用编程-4-信号


                                                                                                                                                                                                                                                                                                                    1.产生信号

    1)通过kill函数

    • kill函数可以给一个指定的进程发送指定的信号。

    • raise函数可以给当前进程发送指定的信号(自己给自己发信号)

    1. #include
    2. int kill(pid_t pid, int signo);
    3. int raise(int signo);
    4. /*
    5. 成功返回0,错误返回-1。
    6. */

    2)通过SIGPIPE函数

    1. #include
    2. unsigned int alarm(unsigned int seconds);
    3. /*
    4. 告诉内核在seconds秒之后给当前进程发SIGALRM信号。
    5. */

    注意:信号产生函数不能放在信号处理函数中

    2.阻塞信号

    信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

    1)信号集操作函数

    修改信号集变量

    1. #include
    2. int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
    3. int sigfillset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。
    4. int sigaddset(sigset_t *set, int signo);//添加某种有效信号
    5. int sigdelset(sigset_t *set, int signo);//删除某种有效信号
    6. int sigismember(const sigset_t *set, int signo);//判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。
    7. /*
    8. 成功返回0,出错返回-1
    9. */

    2)修改进程信号屏蔽字

    真正开始屏蔽信号

    1. #include
    2. int sigprocmask(int how, const sigset_t *set, sigset_t *oset);//读取或更改进程的信号屏蔽字
    3. /*
    4. how参数:
    5. SIG_BLOCK   set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
    6. SIG_UNBLOCK set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set
    7. SIG_SETMASK 设置当前信号屏蔽字为set所指向的值,相当于mask=set
    8. 先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字
    9. 若成功则为0,若出错则为-1
    10. */

    3)检测当前进程信号集

    通过sigismember函数可以判断出当前信号集是否包含未决信号

    1. #include
    2. int sigpending(sigset_t *set);//读取当前进程的未决信号集,通过set参数传出
    3. /*
    4. 调用成功则返回0,出错则返回-1。
    5. */

    4)实例

    设置进程对SIGINT信号挂起

    1. #include
    2. #include
    3. //信号需要的头文件
    4. #include
    5. #include
    6. int main(void)
    7. {
    8.    int i = 0;
    9.    //定义信号集
    10.    sigset_t s,p;
    11.    
    12.    //定义信号集
    13.    sigemptyset(&s);
    14.    sigaddset(&s, SIGINT); //在信号集中只将SIGINT置位
    15.    //在进程信号屏蔽字中添加SIGINT,即该进程将会使收到的SIGINT信号pending
    16.    sigprocmask(SIG_BLOCK, &s, NULL);
    17.    
    18.    while (1) {
    19.        //读取当前进程的未决信号集
    20.        sigpending(&p);
    21.        
    22.        for (i = 0; i < 32; i++) {
    23.            //如果进程中有被pending的信号
    24.            if (sigismember(&p, i) == 1) {
    25.                printf("signal:%d\n",i);
    26.           }
    27.       }
    28.        sleep(1);
    29.   }
    30.    return 0;
    31. }

    【学习地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
    【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击1079654574加群领取哦~
      

    3.捕捉信号

    如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。

    1)重定义信号处理函数

    1. #include
    2. int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

    2)挂起进程等待信号

    1. #include
    2. int pause(void);
    3. /*
    4. 如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR,所以pause只有出错的返回值
    5. */

    3)解除信号屏蔽并挂起等待信号

    1. #include
    2. int sigsuspend(const sigset_t *sigmask);
    3. /*
    4. 进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值。
    5. sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。
    6. */

    4)实例1

    使用pause挂起等待

    1. #include
    2. #include
    3. //signal需要的头文件
    4. #include
    5. #include
    6. //改变SIGALRM信号的处理函数
    7. //alarm 触发SIGALRM信号,执行sigalrm_handler函数
    8. void sigalrm_handler(int sig)
    9. {
    10. printf("Get a signal %d\n", sig);
    11. }
    12. int main(void)
    13. {
    14. struct sigaction actnew, actold;
    15. //重新定义信号处理函数
    16. actnew.sa_handler = sigalrm_handler;
    17. actnew.sa_flags = 0;
    18. sigemptyset(&actnew.sa_mask); //信号处理函数执行期间 不屏蔽信号
    19. sigaction(SIGALRM, &actnew, &actold); //actold保存原来的处理动作
    20. while (1) {
    21. alarm(2);
    22. //bug:如果在alarm与pause之间出现更高级的中断或进程任务,在SIGALRM信号出现后还没恢复,则会导致pluse函数一直挂起程序
    23. //使进程挂起直到有信号抵达
    24. //信号抵达后先执行信号处理函数,再往下执行
    25. pause();
    26. printf("have a signal\n");
    27. }
    28. return 0;
    29. }

    5)实例2

    使用sigsuspend挂起等待

    1. #include
    2. #include
    3. //signal需要的头文件
    4. #include
    5. #include
    6. //改变SIGALRM信号的处理函数
    7. //alarm 触发SIGALRM信号,执行sigalrm_handler函数
    8. //sigsuspend保证进程一定可以监听到SIGALRM信号
    9. void signal_handler(int sig)
    10. {
    11.    printf("get a signal %d\n", sig);
    12. }
    13. int main(void)
    14. {
    15.    struct sigaction act_new, act_old;
    16.    sigset_t mask_new, mask_old;
    17.    
    18.    //重新定义信号处理函数
    19.    act_new.sa_handler = signal_handler;
    20.    act_new.sa_flags = 0;
    21.    sigemptyset(&act_new.sa_mask);
    22.    sigaction(SIGALRM, &act_new, &act_old);
    23.    
    24.    //进程先屏蔽SIGALRM信号
    25.    sigemptyset(&mask_new);
    26.    sigaddset(&mask_new, SIGALRM);
    27.    sigprocmask(SIG_BLOCK, &mask_new, &mask_old);
    28.    
    29.    while (1) {
    30.        alarm(2);
    31.        
    32.        //临时设置新的信号屏蔽(打开SIGALRM信号),并挂起进程
    33.        //直到有信号抵达恢复原来的信号屏蔽并往下执行
    34.        sigsuspend(&mask_old);//将挂起pluse()函数和sigprocmask函数合并
    35.        printf("have a signal\n");
    36.   }
    37.    
    38.    return 0;
    39. }

    4.通过SIGCHLD信号处理子进程终结

    • waitwaitpid函数清理僵尸进程,父进程需要阻塞等待子进程结束。

    • 子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,当子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

    1. #include
    2. #include
    3. //signal需要使用的头文件
    4. #include
    5. #include
    6. //进程需要使用的头文件
    7. #include
    8. //#include
    9. #include
    10. #include
    11. //子进程终止时都会给父进程发送SIGCHLD信号
    12. //重定义SIGCHLD信号处理函数,自动调用wait清理子进程,父进程不必阻塞
    13. void signal_handler(int sig)
    14. {
    15.    printf("child exit\n");
    16.    wait(NULL); //回收资源
    17. }
    18. int main(void)
    19. {
    20.    pid_t pid;
    21.    
    22.    if ((pid = fork()) < 0) {
    23.        perror("fork");
    24.        exit(1);
    25.   }
    26.        
    27.    if (pid == 0) {
    28.        //child
    29.        sleep(1);
    30.        printf("I am child\n");
    31.        exit(2);
    32.   } else {
    33.        //parent
    34.        struct sigaction act_new;
    35.        
    36.        //重新定义SIGCHLD信号处理函数
    37.        act_new.sa_handler = signal_handler;
    38.        act_new.sa_flags = 0;
    39.        sigemptyset(&act_new.sa_mask);
    40.        sigaction(SIGCHLD, &act_new, NULL);
    41.        
    42.        while (1) {
    43.            printf("I am parent\n");
    44.            sleep(1);
    45.       }
    46.        
    47.   }
    48.    return 0;
    49. }

  • 相关阅读:
    Git查询某次提交属于哪个分支
    java计算商场折扣 判断体重 判断学生成绩等级 验证邮箱 demo
    Xcode Build Setting之Compiler flags
    炫云云渲染3ds max效果图渲染教程
    ESP8266-Arduino编程实例-Si7021温度传感器驱动
    Java SE 13 新增特性
    使用js中的(offset,page)实现登录效果
    Pytorch(Tensor)-Numpy(ndarrays) API对照表
    市面上主流源表软件全面对比,总有一款适合你!
    AI视频检索丨历史视频标签化,助力重要事件高效溯源
  • 原文地址:https://blog.csdn.net/irainsa/article/details/128021115