C语言-信号

news2024/10/5 16:28:38

信号

一、信号是什么东西

信号是事件发生时通知进程的一种机制,有时也称之为软件中断。

信号的到来会打断了程序执行的正常流程。

大多数情况下,无法预测信号到达的精确时间。

一个(具有合适权限的)进程能够向另一进程发送信号。
信号的这一用法可作为一种同步技术,甚至是进程间通信( IPC )的原始形式。

进程也可以向自身发送信一号。

发往进程的诸多信号,通常都是源于内核,引发内核为进程产生信号的各类事件如下:
1、硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。
硬件异常的例子包括执行一条异常的机器语言指令,诸如,被 0 除,或者引用了无法访问的内存区域。

2、用户键入了能够产生信号的终端特殊字符。比如:中断字符,通常是 Ctrl+C 。

3、发生了软件事件。
例如,针对文件描述符的输出变为有效,调整了终端窗口大小,定时器到期,进程执行的 CPU 时间超限,
或者该进程的某个子进程退出。

查看信号命令:kill -l
在这里插入图片描述

1-31:标准信号
32-64:实时信号

标准信号的简要说明:

  1. SIGHUP
    本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载。
    此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
  2. SIGINT
    程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。
  3. SIGQUIT
    和SIGINT类似, 但由QUIT字符(通常是Ctrl-)让进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
  4. SIGILL
    执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
  5. SIGTRAP
    由断点指令或其它trap指令产生. 由debugger使用。
  6. SIGABRT
    调用abort函数生成的信号。
  7. SIGBUS
    非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
  8. SIGFPE
    在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
  9. SIGKILL
    用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
  10. SIGUSR1
    留给用户使用
  11. SIGSEGV
    试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
  12. SIGUSR2
    留给用户使用
  13. SIGPIPE
    管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
  14. SIGALRM
    时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
  15. SIGTERM
    程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
  16. SIGCHLD
    子进程结束时, 父进程会收到这个信号。
    如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。
  17. SIGCONT
    让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
  18. SIGSTOP
    停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
  19. SIGTSTP
    停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
  20. SIGTTIN
    当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
  21. SIGTTOU
    类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
  22. SIGURG
    有"紧急"数据或out-of-band数据到达socket时产生.
  23. SIGXCPU
    超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。
  24. SIGXFSZ
    当进程企图扩大文件以至于超过文件大小资源限制。
  25. SIGVTALRM
    虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
  26. SIGPROF
    类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
  27. SIGWINCH
    窗口大小改变时发出.
  28. SIGIO
    文件描述符准备就绪, 可以开始进行输入/输出操作.
  29. SIGPWR
    Power failure
  30. SIGSYS
    非法的系统调用。

二、标准信号相关函数

1、改变信号处置:signal()

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t handler);

handler 可以是一个函数指针,也可以指定为如下值:
SIG_DFL 将信号重置为默认值。
SIG_IGN 忽略信号。
返回值:这次改变之前的处理信号的行为。

用程序终止信号SIGINT来举例子:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    void (*oldHandler)(int);
    oldHandler = signal(SIGINT, SIG_IGN);
    if (oldHandler == SIG_ERR) perror("signal");

    for (int i = 0; i < 10; i++) {
        sleep(1);
        printf("*");  //按ctrl+c不管用
        fflush(stdout);
    }

    // oldHandler = signal(SIGINT, oldHandler);
    oldHandler = signal(SIGINT, SIG_DFL);
    if (oldHandler == SIG_ERR) perror("signal");

    for (int i = 0; i < 10; i++) {
        sleep(1);
        printf("=");  //ctrl+c管用了
        fflush(stdout);
    }

    return 0;
}

信号处理简介
   调用信号处理器程序,可能随时会打断主程序流程。
   内核代表进程来调用处理器程序,当处理器返回时,主程序会在处理器打断的位置恢复执行。

另外:信号会打断阻塞,
      所以当我们调用会阻塞的函数的时候,判断错误的时候,应该排除收到信号打断阻塞的情况。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(void) {
    int fd = -1;
    int ret = 0;
    fd = open("/dev/stdin", O_RDONLY);
    if (fd < 0) {
        perror("open stdin is fail");
        exit(-1);
    }
    char buf[1024] = {0};
    while(1){
        ret = read(fd, buf, 1024);  //会阻塞
        if (ret < 0) { 
            //出错,也有假错,比如这里遇到信号,所以这种可那个,健壮的程序,应该考虑到
            if(errno == EINTR){
                continue;
            }
            perror("read error");
            return -1;
        }
        printf("ret:%d-%s", ret, buf);
    }

    close(fd);

    return 0;
}

2、发送信号:kill()
一个进程可以向另一个进程发送信号(必须要有权限)。
#include <signal.h>
#include <sys/types.h>

int kill(pid_t pid, int sig);

//成功返回0, 失败-1

//pid > 0发送为指定进程
//pid = 0发送给当前进程组里所有进程包括自己
//pid < -1 发送给进程组id = pid的绝对值的进程组下的每个进程,效果等同killpg(pid_t pgrp, int sig);
//pid = -1发送给所有进程(必须有权限)除了1号进程
//sig = 0为空信号用来检测进程是否存在

/进程是否存在/
int is_p_alive(pid_t pid) {
int kill_rc = kill(pid, 0); //发送0号信号,测试是否存活
if (kill_rc == ESRCH) { //进程不存在,不可靠,Unix/Linux系统经过一段时间会重复使用进程ID,
return false; //一个所给定进程ID存在并不一定是你想要找的进程,或许它是一个新的进程,
} //你需要的进程早已经死亡消失在内存中!
return true;
}

特权级进程可以向任何进程发送信号,非特权级必须满足如下图条件。

进程的实际用户ID:
标识我们实际上是谁,是当前登录的用户ID.

进程的有效用户ID:
决定的是文件的访问权.
通常有效用户ID就是实际用户ID.

进程的设置-用户-ID,
是一个特殊标志,当该标志设置时,执行该文件时的有效用户ID就是文件的所有者ID。
比如,当一个文件的所有者是root当你以另一个用户登录时,
如果没有设置-用户-ID,执行该文件时的有效ID和实际ID就是登录用户的ID,是无权限操作这个文件的。
但如果设置了设置-用户-ID,实际用户ID是登录用户ID,而有效用户ID是root用户的ID。

看个例子就更清楚了:
默认情况下
zhonghao执行cat,系统创建一个cat进程,进程的属主属组取程序发起者,也就是zhonghao
cat进程访问/etc/shadow,由于进程属主属组是zhonghao,与/etc/shadow的属组属主都不匹配,所以被拒绝访问.

给cat设置SUID之后
zhonghao执行cat.系统创建一个cat进程,进程的属主取cat的属主,属组取程序发起者,就是root
cat进程访问/etc/shadow,由于进程属主是root,与/etc/shadow的属主匹配,所以被允许访问.

3、向自己发送信号:raise()
#include <signal.h>
int raise(int sig);
//成功返回0,失败返回非0

//单线程程序相当于
kill(getpid(), sig);

//多线程相当于
pthread_kill(pthread_self(), sig);

4、向进程组发送信号killpg
#include <signal.h>

int killpg(pid_t pgrp, int sig);

//成功返回0,失败-1

//相当于kill函数中pid<-1的模式
kill(-pgrp, sig);

5、信号描述
每个信号都有一段与之相对应的描述,可以用strsignal() 函数获取。
#include <string.h>
char *strsignal(int sig);

也可以直接打印到错误输出
#include <signal.h>
void psignal(int sig, const char *msg);

看例子:
#include <signal.h>
#include <stdio.h>
#include <string.h>

int main(){
char *tmp = strsignal(SIGINT);
printf(“%s\n”, tmp);

psignal(SIGINT,"sigerr");

}

6、信号集
许多信号相关的系统调用都需要能表示一组不同的信号。
多个信号可使用一个称之为信号集的数据结构来表示,数据类型为 sigset_t。
sigemptyset() 初始化一个未包含任何成员的信号集。
sigfillset() 初始化一个信号集,包含所有信号。
#include <signal.h>

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
//成功返回0,失败-1
必须使用上述2个函数初始化信号集,因为C语言不会对自动变量进行初始化。

添加或移除单个信号
#include <signal.h>

int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);

//成功返回0,失败-1

测试信号 sig 是否为 set的成员
#include <signal.h>

int sigismember(const sigset_t *set, int sig);

//1表示是,0不是

7、信号掩码(阻塞信号传递)
也可叫信号屏蔽。内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其针对该进程传递。
在多线程环境中,每个线程都可以使用pthread_sigmask()函数来独立检查和修改其信号。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//Returns 0 on success, or –1 on error
sigprocmask() 既可以修改信号掩码,又可获取现有的掩码,或两者兼具。

how参数指定了该函数信号掩码带来的变化:
SIG_BLOCK 将set 指向信号集并集到当前信号掩码中。
SIG_UNBLOCK 将set指向的信号集中的信号从信号掩码中移除。
SIG_SETMASK 将set指向的信号集设置为信号掩码。
如果 oldset 参数不为NULL,则其指向sigset_t 结构缓冲区,用于返回之前的信号掩码。
如果只是想获取信号掩码, set 参数设为NULL即可,这时将忽略how 参数。

上面讲的屏蔽实质上是一种阻塞,如果解除了对某个等待信号的阻塞,会立刻将该信号传递给进程。

下面代码时暂时阻止 SIGINT 信号的传递。
sigset_t blockSet, prevMask;

sigemptyset(&blockSet);
sigaddset(&blockSet, SIGINT);

if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask) == -1)
perror(“sigprocmask1”);

if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
perror(“sigprocmask2”);

SIGKILL 和 SIGSTOP 信号是不允许阻塞的,也就是下面的代码会阻塞除这2个信号以外的任何信号。
sigfillset(&blockSet);
if (sigprocmask(SIG_BLOCK, &blockSet, NULL) == -1)
perror(“sigprocmask”);

8、获取等待中的信号集
如果某进程接收了一个正在阻塞的信号,那么会将该信号添加到进程的等待信号集中。
之后如果解除了对该信号的阻塞,就会把该信号传递给此进程(就算在阻塞期间发生了N次,解除时只会传递1次,而实时信号可以排队)。

sigpending() 系统调用返回进程处于等待状态的信号集,并存入set 指向的sigset_t 结构中。
#include <signal.h>
int sigpending(sigset_t *set);
//Returns 0 on success, or –1 on error

写一下例子:
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

int main()
{
sigset_t blockSet, prevMask,pendingset;

sigemptyset(&blockSet);
sigaddset(&blockSet, SIGINT);

if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask) == -1)
    perror("sigprocmask1");

// if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
//     perror("sigprocmask2");

for(int i=0;i<5;i++){
    sleep(1);
    printf("i:%d\n",i);
}

if (sigpending(&pendingset)==-1){
    perror("sigpending");
}

if(sigismember(&pendingset,SIGINT)){
    printf("SIGINT exist  in pendingset! \n");
}else{
    printf("SIGINT not exist  in pendingset! \n");
}

}

9、改变信号处置另一个函数:sigaction()
除signal() 之外,sigaction() 系统调用是设置信号处置的另一选择。
用法复杂一点,但是功能强大,且可移植性强。
#include <signal.h>

int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
//Returns 0 on success, or –1 on error

sig为想要获取或改变的信号编号,除去 SIGKILL和SIGSTOP。
act是指向信号新处置的数据结构,oldact用来返回之前的信号处置结构,都可以设置为 NULL。
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void); //废弃
}
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数。
sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集里的信号屏蔽掉
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用:
◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
◆ SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。

看一个例子:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void show_handler(int sig)
{
printf(“I got signal %d\n”, sig);
int i;
for(i = 0; i < 5; i++)
{
printf(“i = %d\n”, i);
sleep(1);
}
}

int main(void)
{
int i = 0;
struct sigaction act, oldact;
act.sa_handler = show_handler;
sigaddset(&act.sa_mask, SIGQUIT);
sigaddset(&act.sa_mask, SIGTERM);
act.sa_flags = SA_RESETHAND | SA_NODEFER;

sigaction(SIGINT, &act, &oldact);
while(1) 

{
sleep(1);
printf(“sleeping %d\n”, i);
i++;
}
}

三、实时信号
实时信号意在弥补对标准信号的诸多限制。
其优势如下:
1、实时信号的信号范围有所扩大,可应用于自定义目的,
而标准信号中可提供随意使用的只有SIGUSR1和 SIGUSR2。
2、实时信号为队列化管理,同一信号发送多次将会多次传递给进程。
3、发送信号可以伴随数据(一整形或者指针值)
4、不同实时信号同时处于等待状态时,那么率先传递较小编号的信号。
如果排队的是同一类型的信号,那么信号的传递顺序会按照发送来的的顺序传给进程。

发送实时信号,sigqueue 函数
#include <signal.h>

int sigqueue(pid_t pid, int sig, const union sigval value);

union sigval {
int sival_int; /* Integer value for accompanying data */
void sival_ptr; / 很少用到 */
};

//成功返回0,失败-1
一旦触及对排队信号的数量限制(ulimit -i查看和修改),sigqueue()调用会失败,errno置为EAGAIN。
其他kill()、killpg()、raise()调用也能发送实时信号,但是排不排队由具体实现决定,linux下是排队的。

处理实时信号
可以像标准信号一样,使用常规(1个参数)信号处理器来处理实时信号。
也可以用带有3个参数的信号处理器函数来处理实时信号。
一旦采用了第二种方式,第二个参数是一个siginfo_t 结构。
对于一个实时信号而言,会在siginfo_t 结构中设置如下字段:
1.si_signo,其值与传递给信号处理器函数的第一个参数相同。
2.si_code,表示信号来源,由sigqueue()发送的实时信号来说,该值是SI_QUEUE,由用户用kill命令发送的信号,该值是SI_USER。
3.si_value,为进程于 sigqueue() 带过来的额外参数,sigval union。
4.si_pid 和 si_uid,分别为信号发送进程的进程id,实际用户id。

例子
main.c 用来接收信号。可带2个参数:程序休眠时间(为了让信号排队)、信号处理器函数处理间隔时间。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

//demo会用到的一些全局变量
static volatile int handlerSleepTime; //信号处理器的休眠间隔时间
static volatile int sigCnt; //信号处理次数的统计
static volatile int allDone; //信号队列里的信号是不是都处理完的标志

//处理信号的函数,一个参数,三个参数
static void siginfoHandler(int sig, siginfo_t *si,void *ucontext){
//如果来的信号是SIGINT SIGTERM,结束处理
if(sig == SIGINT || sig == SIGTERM){
allDone = 1;
return;
}

sigCnt++;
printf(“到达处理的信号是:%d\n”,sig);
printf(“si_signo:%d----si_code:%d (%s)-----si_pid:%d----si_uid:%d \n”, si->si_signo, si->si_code,
(si->si_code == SI_USER) ? “SI_USER” :
(si->si_code == SI_QUEUE) ? “SI_QUEUE” : “other”,si->si_pid,si->si_uid);

sleep(handlerSleepTime); //休眠是为了,一会我们体现标准信号会丢弃,实时队列管理,不会丢失
}

//执行这个函数的时候,传两个参数过来,第一个参数主程序休眠的间隔时间,信号处理器休眠间隔时间
int main(int argc, char *argv[]) {
//声明要用的局部变量
struct sigaction sa;
int sig;
sigset_t prevMask,blockMask;

printf(“要去接受处理信号的进程的id:%ld\n”,(long)getpid());

handlerSleepTime = atoi(argv[2]);

sa.sa_sigaction = siginfoHandler;
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);

sigfillset(&blockMask);
sigdelset(&blockMask,SIGINT);
sigdelset(&blockMask,SIGTERM);

//所有信息的处置方式都设置为sa
for(sig=1;sig<NSIG;sig++){
sigaction(sig,&sa,NULL);
}

//信号屏蔽
if(sigprocmask(SIG_SETMASK,&blockMask,&prevMask)==-1){
perror(“sigprocmask”);
}

printf(“main 开始休眠…\n”);
sleep(atoi(argv[1]));
printf(“main 休眠结束…\n”);

//接触信号屏蔽
if(sigprocmask(SIG_SETMASK,&prevMask,NULL)==-1){
perror(“sigprocmask”);
}

while(!allDone){
pause(); //等信号到达,直接alldone为1
}

printf(“main 程序终止!\n”);
return 0;
}

send.c 用来发送信号
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
union sigval sv;

 if (sigqueue(atoi(argv[1]), atoi(argv[2]), sv) == -1)
        perror("sigqueue");

return 0;

}

使用掩码来等待信号:sigsuspend()
对信号编程时偶尔会遇到如下情况:
1.临时阻塞一个信号,以防止在处理关键代码时被此信号打断
2.关键代码执行完后,需要暂停执行,等到有信号到达,在放行

sissuspend() 系统调用将以mask 所指向的信号集来替换信号掩码,然后挂起进程的执行,直到其捕获到信号,
并从处理器函数返回,返回后,信号掩码恢复为调用前的值。
#include <signal.h>
int sigsuspend(const sigset_t *mask);
//(Normally) returns –1 with errno set to EINTR

例子:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define MYRTSIG (SIGRTMIN+6)

//自定义的信号处置函数
static void mysig_handler(int s){
printf(" mysig_handler run! ");
fflush(stdout);
}

int main() {
int i=0,j=0;
sigset_t set,oset,saveset;

printf(“我的进程id:%ld\n”,(long)getpid());

signal(MYRTSIG,mysig_handler);
sigemptyset(&set);
sigaddset(&set,MYRTSIG);
sigprocmask(SIG_BLOCK,&set,&saveset);
for(i=0;i<1000;i++){
for(j=0;j<10;j++){
write(1,“*”,1);
sleep(1);
}
write(1,“\n”,1);
sigsuspend(&oset);
}
sigprocmask(SIG_SETMASK,&saveset,NULL);

return 0;
}

以同步方式等待信号
同步方式等待信号会相对简单易控一点。
#include <signal.h>
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
//成功返回信号编号,失败-1
int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout);
//可以指定超时时间,如果调用超时而又没有收到信号返回-1
//并设置errno为EAGAIN
sigwaitinfo() 系统调用挂起进程的执行,直至set指向的信号集中的某一信号到达。
如果有信号处于等待状态,则立马返回。

info 如果不为NULL,则会指向经过初始化处理的 siginfo_t 结构。

sigwaitinfo() 接受信号的顺序和排队特性与信号处理器所捕获的信号相同。
标准信号不排队,实时信号按照低编号优先。
除了不需要编写信号处理器,它的速度也更快一点。

例子
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

// SIGQUIT 用
void handler(int s) { printf(“handler :%d\n”, s); }

int main(int argc, char** argv) {
printf(“PID:%ld\n”,(long)getpid());
signal(SIGQUIT, handler); // ctrl + , 此信号没BLOCK
siginfo_t info;
sigset_t mask;
// sigfillset(&mask);
sigaddset(&mask, SIGRTMIN + 1);
sigaddset(&mask, SIGRTMIN + 2);
sigprocmask(SIG_BLOCK, &mask, NULL); //设置屏蔽

int sig = -1;
while (1) {
//阻塞等待
sig = sigwaitinfo(&mask, &info);
if (sig < 0) {
if (errno == EINTR) {
// 上面 sigquit 将在这里中断
perror(“sigwaitinfo false”);
continue;
}
perror(“sigwaitinfo”);
break;
}
printf(“sig:%d , from pid:%d , code:%d , signo:%d\n”, sig, info.si_pid,
info.si_code, info.si_signo);

// ctrl+c 就结束了
if (SIGINT == sig) {
  break;
}

}
return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1687411.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python查找内容在文件中的第几行(利用了滑动窗口)

def find_multiline_content(file_path, multiline_content):with open(file_path, r) as file:# 文件内容file_lines file.readlines()# 待检测内容multiline_lines multiline_content.strip().split(\n)# 待检测内容总行数num_multiline_lines len(multiline_lines)matchi…

Postgresql源码(130)ExecInterpExpr转换为IR的流程

相关 《Postgresql源码&#xff08;127&#xff09;投影ExecProject的表达式执行分析》 《Postgresql源码&#xff08;128&#xff09;深入分析JIT中的函数内联llvm_inline》 《Postgresql源码&#xff08;129&#xff09;JIT函数中如何使用PG的类型llvmjit_types》 表达式计算…

计算机系统基础 8 循环程序

概要 两种实现方法——分支指令实现和专门的循环语句实现以及有关循环的优化。 分支指令实现 倒计数 …… MOV ECX&#xff0c;循环次数 LOOPA&#xff1a;…… …… DEC ECX JNE LOOPA 正计数 …… MOV ECX&#xff0c;0 LOOPA&#xff1a; …… INC ECX CMP …

Kafka-集群管理者(Controller)选举机制、任期(epoch)机制

Kafka概述 Kafka-集群管理者&#xff08;Controller&#xff09;选举机制 Kafka中的Controller是Kafka集群中的一个特殊角色&#xff0c;负责对整个集群进行管理和协调。Controller的主要职责包括分区分配、副本管理、Leader选举等。当当前的Controller节点失效或需要进行重新…

【CALayer-CALayer的transform属性 Objective-C语言】

一、接下来,我们来说的是这个,transform的属性 1.layer的transform属性, 把最后一份代码command + C、command + V、一份儿,改个名字, Name:04-CALayer的transform属性, 我们把这个代码稍微修改一下, 我们先添加了一个layer,到控制器的view上, 然后呢,这两句话不…

【安装笔记-20240524-Windows-安装测试 7-Zip】

安装笔记-系列文章目录 安装笔记-20240524-Windows-安装测试 7-Zip 文章目录 安装笔记-系列文章目录安装笔记-20240524-Windows-安装测试 7-Zip 前言一、软件介绍名称&#xff1a;7-Zip主页官方介绍7-Zip 主要特征 二、安装步骤测试版本&#xff1a;24.05 (2024-05-14) for Wi…

保安维稳,四信以科技构筑高速公路安全智慧防线

近日&#xff0c;广东梅大高速发生严重塌方事故&#xff0c;造成了严重的人员伤亡和财产损失。这一事件在公众心中敲响了安全的警钟&#xff0c;再次引起了公众对于交通设施运营安全性的重点关注。 国务院安委会办公室和国家防灾减灾救灾委员会办公室等主管机构先后印发紧急通知…

CentOS7 部署单机版 elasticsearch

一、环境准备 1、准备一台系统为CentOS7的服务器 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 2、创建新用户&#xff0c;用于elasticsearch服务 # elastic不允许使用root账号启动服务 [rootlocalhost ~]# useradd elastic [rootlo…

58同城如何降低 80%的机器成本 | OceanBase案例

本文作者&#xff1a;58同城架构师刘春雷 一、背景介绍 58同城作为中国互联网生活服务领域的领军者&#xff0c;其平台规模居国内之首&#xff0c;涵盖了包括车辆交易、房产服务、人才招聘、本地生活服务以及金融等多元化的业务场景。 因其业务的广泛性和多样性&#xff0c;我…

自主创新助力科技强军,麒麟信安闪耀第九届军博会

由中国指挥与控制学会主办的中国指挥控制大会暨第九届北京军博会于5月17日-19日在北京国家会议中心盛大开展&#xff0c;政府、军队、武警、公安、交通、人防、航天、航空、兵器、船舶、电科集团等从事国防军工技术与产业领域的30000多名代表到场参加。 麒麟信安作为国产化方案…

【okhttp】小问题记录合集

can’t create native thread 问题描述 OkHttpClient 每次使用都new创建&#xff0c;造成OOM&#xff0c;提示can’t create native thread… 问题分析 没有将OkHttpClient单例化. 每个client对象都有自己的线程池和连接池&#xff0c;如果为每个请求都创建一个client对象&a…

Java设计模式 _行为型模式_中介者模式

一、中介者模式 1、中介者模式 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为型模式。主要通过一个中介类&#xff0c;该类通常处理不同类之间的通信&#xff0c;并支持松耦合&#xff0c;使代码易于维护。 2、实现思路 &#xff08;1&#xff09;、定义实体…

入门级指纹密码智能锁方案简析以及适用芯片SSD210介绍

上篇我们大概讲了一下门锁的发展历史&#xff0c;近几年家用智能门锁行业中近几年的市场增长变化&#xff0c;举例说明了智能猫眼门锁在类市场份额最大的产品的一些技术参数以及芯片功能框架。 智能猫眼锁核心解决方案以及适用的芯片推荐简介https://blog.csdn.net/Chipsupply…

基于Pytorch的卷积神经网络MNIST手写数字识别

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 手写数字识别是计算机视觉和模式识别领域的一个经典问题。MNIST数据集是一个包含大量手写数字图片的…

军工单位如何做到安全跨网文件交换与导出的

在现代信息化战争中&#xff0c;军工单位在信息安全方面的需求尤为突出。跨网文件交换与导出作为军工单位日常运营的重要环节&#xff0c;面临着网络带宽限制、数据安全风险、合规性要求和传输稳定性等挑战。下面&#xff0c;我们将从以下几个方面探讨军工单位如何实现安全、高…

【UE5.1 多线程 异步】“Async Blueprints Extension”插件使用记录

目录 一、异步生成Actor示例 二、异步计算示例 参考视频 首先需要在商城中下载“Async Blueprints Extension”插件 一、异步生成Actor示例 2. 创建一个线程类&#xff0c;这里要指定父类为“LongAsyncTask”、“InfiniteAsyncTask”、“ShortAsyncTask”中的一个 在线程类…

成品短视频APP源码搭建

在数字化时代&#xff0c;短视频已成为全球范围内的流行趋势&#xff0c;吸引了大量的用户和内容创作者。对于有志于进入短视频领域的企业和个人来说&#xff0c;成品短视频APP源码搭建提供了一条快速、高效的路径。本文将探讨成品短视频APP源码搭建的过程及其优势&#xff0c;…

sheng的学习笔记-docker部署Greenplum

目录 docker安装gp数据库 mac版本 搭建gp数据库 连接数据库 windows版本 搭建gp数据库 连接数据库 docker安装gp数据库 mac版本 搭建gp数据库 打开终端&#xff0c;输入代码&#xff0c;查看版本 ocker search greenplum docker pull projectairws/greenplum docker…

uni-starter创建App项目最全流程(日后还有其他功能会不断更新)

一、创建项目 在HbuilderX中点击创建项目&#xff0c;选择uni-starter模板&#xff0c;选择阿里云、Vue3&#xff0c;填写项目名称后点击创建。如果没有下载过uni-starter会自动下载该插件&#xff0c;如下图&#xff1a; 二、 创建云服务器并关联项目 如果是第一次使用&#…

Linux中gcc/g++的基本使用

目录 gcc/g的使用gcc/g是如何生成可执行文件的预处理编译汇编链接 库.o文件是如何与库链接的&#xff1f; debug版本和release版本 gcc/g的使用 在windows中&#xff0c;我们在VS中编写好了代码之后就可以直接在VS中对源码进行编译等操作后运行 而在Linux下&#xff0c;我们可…