Linux信号下

简介: Linux信号下
1. pause
#include <unistd.h>
    int pause(void);

调用pause函数的进程将一直堵塞到(即放弃cpu)有信号递达将其唤醒。

  • 返回值:
  • 如果信号的默认动作是终止进程,则进程终止。
  • 如果信号的默认动作是忽略,继承基于处于挂起状态,pause不返回。
  • 如果信号处理动作是捕捉,则信号处理函数调用结束,pause返回-1, errno设置为EINTR。
  • pause接受到的信号被屏蔽,puase就将会永远不被唤醒。
  • 实现sleep函数功能:alarm() + pause()
2.sigsuspend
#include <signal.h>
    int sigsuspend(const sigset_t *mask); // mask是临时的,在函数执行时有效
  • 特点。这是一个原子操作,将设置mask指的屏蔽字 + 使得进程休眠(pause) 变成原子操作。可用来替代pause具有的时许竞争隐患。比如用alarm() + pause() 实现的sleep函数功能。
  • 返回。和pause一样,无成功返回值。如果返回调用进程,返回-1,并且errno=EINTR。
alarm(secs);
    pause();
  • 原因:
    如果在调用alarm之后,调用进程失去了cpu控制权,但是在cpu控制权回到这个进程之前alarm的计时时候到达,当cpu控制权回归调用进程时先处理SIGALRM信号,再调用pause函数,这就导致pause再也接受不到信号,因此这个进程就永远堵塞。
  • 解决方案:使得sigsuspend(sus_mask) = alarm() + pause() 成为一个原子操作。
  • 先使用sigprocmask设置信号屏蔽集合,使得cpu在sigsuspend之前不处理。
  • 比如同样发生上述调用进程失去cpu控制权的情况,cpu控制权返回时,并不会先处理SIGALRM信号
  • 因此到sigsuspend函数时,在这个函数的sus_mask中解除对SIGALRM的屏蔽,使得 信号处理函数的执行后直接返回,唤醒cpu(这是因为pause的堵塞导致)。
  • 关键点:调用进程的堵塞原因
  • 调用进程失去cpu的控制权
  • 调用pause导致的cpu挂起
3.全局变量异步I/O

尽量避免使用全局变量。类似于多线程中的多个线程对同一个变量进行操作,很容易造成异常。

4.不可/可重入函数
  • 定义可重入函数,函数内部不能含有全局变量或者static变量,以及malloc和free。
  • 信号捕捉函数应该设计为可重入函数。
  • 信号处理函数可以调用的可重入函数,参考 man 7 signal
  • 其他大多是不可重入的,因为:
  • 使用静态数据结构
  • 调用malloc/free
  • 是标准I/O函数
5.SIGCHLD

这是子进程向父进程发送的信号,那么可否利用这个信号来回收子进程?

  • 要求
  • 在父进程中注册对信号SIGCHLD的捕捉函数。
  • 循环产生十个子进程
  • 十个子进程在父进程前终止
  • 能顺利回收十个子进程
  • 信号处理函数
void exe_sigchld(int singo) {
        pid_t pid;
        int statloc;
        // 这里的while很关键
        while(pid = waitpid(0, &statloc, WNOHANG) > 0) {
            if(WIFEXITED(statloc))  
                printf("child process pid=%d, exited  status:%d.\n", pid, WEXITSTATUS(statloc));
            else if(WIFSIGNALED(statloc))
                printf("child process pid=%d, signaled stauts:%d.\n", pid, WTERMSIG(statloc)); 
        }
    }

上面都while处理很关键,不是if而是while,是因为:

  • 对于多个子进程同时发送终止信号SINCHLD,父进程只调用一次信号处理函数。
  • 如果是if那么进入一次信号处理函数,只能处理一个终止的子进程,那么对于同时终止的其他进程就无法处理,剩下的就只能成为僵死进程。
  • 选用while就可以一次进入信号处理函数,但是可以处理多个终止的子进程。
6.信号传参

信号不能携带大量参数,实在有特殊需求时也可以。有相关的函数:

  • 发送信号传参
#include <signal.h>
    int sigqueue(pid_t pid, int sig, const union sigval value);
        
    union sigval {
        int   sival_int;
        void *sival_ptr;
    };

类似于kill,但是多了一个发送参数,可以作为数据发送。联合体sigval在跨进程传递数据时候不要使用指针,因此各个进程之间的虚拟地址不同,指针传参是为同一个进程准备的,跨进程传参使用的是int类型。

  • 捕捉函数传参
#include <signal.h>
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    struct sigaction {
        void     (*sa_handler)(int);        
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);
    };

使用的结构体sigaction的第二项:sa_sigaction,此时sa_flags = SA_SIGINFO。

7.中断系统调用

系统调用分为二类:慢速系统调用和其他系统调用

  • 慢速系统调用:可能会使进程永远堵塞的一类。如果在堵塞期间收到一个信号,该系统调用就会被中断,那么就不再被执行。也可以设定系统调用是否重启。这类函数诸如:read、write、pause、wait。
  • 其他系统调用:getpid(), getppid(),fork()...

慢速系统调用被信号中断时的行为,和pause类似:

  • 想中断pause,信号不能屏蔽
  • 信号的处理方式必须是捕捉(默认和忽略都不可)
  • 中断后返回-1,设置errno为EINTR

可修改sa_flags参数来设置被信号中断后系统调用是否重启。

  • SA_RESTART:重启这个慢速调用函数比如:
  • read被一个信号中断,处理完信号,read应该继续工作,所以需要重启。
  • pause被信号中断就不需要重启,因为不影响。
  • SA_NODEFER:不屏蔽待捕捉信号
相关文章
|
6月前
|
Ubuntu Linux
【Linux】详解信号产生的方式
【Linux】详解信号产生的方式
|
6月前
|
Unix Linux
【Linux】详解信号的分类&&如何自定义信号的作用
【Linux】详解信号的分类&&如何自定义信号的作用
|
3月前
|
Linux 调度
Linux0.11 信号(十二)(下)
Linux0.11 信号(十二)
25 1
|
3月前
|
存储 Linux 调度
|
3月前
|
存储 Unix Linux
Linux0.11 信号(十二)(上)
Linux0.11 信号(十二)
31 0
|
4月前
|
安全 小程序 Linux
Linux中信号是什么?Ctrl + c后到底为什么会中断程序?
信号在进程的学习中是一个非常好用的存在,它是软件层次上对中断机制的一种模拟,是异步通信方式,同时也可以用来检测用户空间到底发生了什么情况,然后系统知道后就可以做出相应的对策。
120 6
|
4月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
206 2
|
4月前
|
存储 NoSQL Unix
【Linux】进程信号(下)
【Linux】进程信号(下)
37 0
|
4月前
|
安全 Linux Shell
【Linux】进程信号(上)
【Linux】进程信号(上)
44 0
|
5月前
|
Linux Shell
蓝易云 - 【Linux-Day8- 进程替换和信号】
这两个概念在Linux系统编程和shell脚本编写中都非常重要,理解它们可以帮助你更好地理解和控制Linux系统的行为。
40 9