8、linux c 信号机制

一、信号概述

1. 信号概念

信号是一种在软件层次上对中断机制的模拟,是一种异步通信方式。信号的产生和处理都由操作系统内核完成,用于在进程之间传递信息或通知某些事件的发生。

2. 信号的产生

信号可以通过以下方式产生:

  1. 按键产生:例如用户按下Ctrl-C产生SIGINT信号,按下Ctrl-\产生SIGQUIT信号。

  2. 系统调用函数产生:例如killraise等函数。

  3. 硬件异常:例如非法指令、除以零等。

  4. 命令行产生:例如使用kill命令向进程发送信号。

  5. 软件条件:例如访问非法内存、管道写入端关闭等。

3. 信号处理方式

信号可以有以下几种处理方式:

  1. 缺省方式:执行系统默认的动作。

  2. 忽略信号:对信号不做任何处理。

  3. 捕捉信号:通过自定义函数处理信号。

二、常用信号

信号名含义默认操作
SIGHUP用户终端关闭时产生,发给与终端关联的会话内的所有进程终止
SIGINT用户键入Ctrl-C时产生,发送给当前终端的所有前台进程终止
SIGQUIT用户键入Ctrl-\时产生,与SIGINT类似终止
SIGILL进程企图执行非法指令时产生终止
SIGSEGV非法访问内存时产生,如野指针、缓冲区溢出终止
SIGPIPE进程往没有读端的管道中写入时产生,代表“管道断裂”终止
SIGKILL用来结束进程,不能被捕捉和忽略终止
SIGSTOP暂停进程,不能被捕捉和忽略暂停进程
SIGTSTP用户键入Ctrl-Z时产生,用于暂停进程暂停进程
SIGCONT让暂停的进程继续运行继续运行
SIGALRM定时器时间到时产生终止
SIGUSR1保留给用户程序使用的信号终止
SIGUSR2保留给用户程序使用的信号终止
SIGCHLD子进程状态改变时发给父进程忽略

三、信号相关函数

1. kill函数

函数介绍
#include <sys/types.h>
#include <signal.h>
​
int kill(pid_t pid, int sig);
  • 参数

    • pid:目标进程的进程ID。

      • > 0:发送信号给指定进程。

      • = 0:发送信号给与调用进程处于同一进程组的所有进程。

      • < -1:发送信号给该进程组的所有进程。

      • = -1:发送信号给所有有权限发送信号的进程。

    • sig:要发送的信号编号。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:向指定进程发送信号。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
int main() {pid_t pid = 1234; // 假设目标进程ID为1234if (kill(pid, SIGINT) < 0) {perror("kill");return -1;}printf("信号已发送到进程 %d\n", pid);return 0;
}

2. raise函数

函数介绍
#include <signal.h>
​
int raise(int sig);
  • 参数

    • sig:要发送的信号编号。

  • 返回值:成功时返回0,失败时返回非0值。

  • 作用:向调用进程自身发送信号。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle); // 设置SIGINT的处理函数raise(SIGINT); // 向自身发送SIGINT信号while (1) {sleep(1);}return 0;
}

3. alarm函数

函数介绍
#include <unistd.h>
​
unsigned int alarm(unsigned int seconds);
  • 参数

    • seconds:定时时间,单位为秒。如果seconds为0,则清除之前设置的定时器。

  • 返回值:返回上次设置的定时器剩余时间(秒)。如果之前没有设置定时器,则返回0。

  • 作用:设置一个定时器,定时结束后向进程发送SIGALRM信号。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("定时器信号捕获到\n");
}
​
int main() {signal(SIGALRM, handle); // 设置SIGALRM的处理函数alarm(5); // 设置5秒后发送SIGALRM信号while (1) {sleep(1);}return 0;
}

4. ualarm函数

函数介绍
#include <unistd.h>
​
useconds_t ualarm(useconds_t usecs, useconds_t interval);
  • 参数

    • usecs:第一次定时的时间,单位为微秒。

    • interval:触发后的间隔时间,单位为微秒。如果interval不为0,则定时器会周期性触发。

  • 返回值:返回上次设置的定时器剩余时间(微秒)。如果之前没有设置定时器,则返回0。

  • 作用:设置一个微秒级的定时器,定时结束后向进程发送SIGALRM信号,并可以设置定时器的间隔时间。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("定时器信号捕获到\n");
}
​
int main() {signal(SIGALRM, handle); // 设置SIGALRM的处理函数ualarm(5000000, 1000000); // 5秒后发送SIGALRM信号,之后每隔1秒发送一次while (1) {sleep(1);}return 0;
}

5. setitimer函数

函数介绍
#include <sys/time.h>
​
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
  • 参数

    • which:定时器类型。

      • ITIMER_REAL:以实际时间计时,发送SIGALRM信号。

      • ITIMER_VIRTUAL:以用户态运行时间计时,发送SIGVTALRM信号。

      • ITIMER_PROF:以用户态和内核态运行时间计时,发送SIGPROF信号。

    • new_value:指向新的定时器值的结构体。

    • old_value:指向存储旧的定时器值的结构体(可选)。

  • 结构体itimerval

    • struct timeval it_interval:定时器的间隔时间。

    • struct timeval it_value:定时器的初始时间。

  • 结构体timeval

    • time_t tv_sec:秒。

    • suseconds_t tv_usec:微秒。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:设置一个间隔定时器。

示例代码
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
​
void handle(int sig) {printf("定时器信号捕获到\n");
}
​
int main() {struct itimerval timer;timer.it_interval.tv_sec = 1;timer.it_interval.tv_usec = 0;timer.it_value.tv_sec = 5;timer.it_value.tv_usec = 0;
​signal(SIGALRM, handle); // 设置SIGALRM的处理函数setitimer(ITIMER_REAL, &timer, NULL); // 设置定时器while (1) {sleep(1);}return 0;
}

6. signal函数

函数介绍
#include <signal.h>
​
typedef void (*sighandler_t)(int);
​
sighandler_t signal(int signum, sighandler_t handler);
  • 参数

    • signum:要设置的信号编号。

    • handler:信号处理函数。

      • SIG_DFL:恢复为默认处理方式。

      • SIG_IGN:忽略信号。

      • 其他:自定义处理函数。

  • 返回值:成功时返回之前的信号处理函数,失败时返回SIG_ERR

  • 作用:设置信号处理方式。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle); // 设置SIGINT的处理函数while (1) {sleep(1);}return 0;
}

7. sigaction函数

函数介绍
#include <signal.h>
​
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • 参数

    • signum:要设置的信号编号。

    • act:指向新的信号处理方式的结构体。

    • oldact:指向存储旧的信号处理方式的结构体(可选)。

  • 结构体sigaction

    • void (*sa_handler)(int):信号处理函数。

    • void (*sa_sigaction)(int, siginfo_t *, void *):支持传递额外信息的信号处理函数。

    • sigset_t sa_mask:在信号处理函数执行期间需要屏蔽的信号集。

    • int sa_flags:信号处理标志。

      • SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数。

      • SA_RESTART:使被信号打断的系统调用自动重新发起。

      • SA_RESETHAND:信号处理之后重新设置为默认的处理方式。

      • SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。

    • void (*sa_restorer)(void):已废弃。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:设置信号处理方式,功能比signal更强大。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {struct sigaction act;act.sa_handler = handle;sigemptyset(&act.sa_mask);act.sa_flags = 0;
​sigaction(SIGINT, &act, NULL); // 设置SIGINT的处理方式while (1) {sleep(1);}return 0;
}

四、信号集操作函数

1. sigemptyset函数

函数介绍
#include <signal.h>
​
int sigemptyset(sigset_t *set);
  • 参数

    • set:指向信号集的指针。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:清空信号集。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;sigemptyset(&set); // 清空信号集return 0;
}

2. sigfillset函数

函数介绍
#include <signal.h>
​
int sigfillset(sigset_t *set);
  • 参数

    • set:指向信号集的指针。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:将信号集中的所有信号都设置为1。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;sigfillset(&set); // 填充信号集return 0;
}

3. sigaddset函数

函数介绍
#include <signal.h>
​
int sigaddset(sigset_t *set, int signum);
  • 参数

    • set:指向信号集的指针。

    • signum:要添加到信号集中的信号编号。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:将指定信号添加到信号集中。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;sigemptyset(&set);sigaddset(&set, SIGINT); // 添加SIGINT到信号集return 0;
}

4. sigdelset函数

函数介绍
#include <signal.h>
​
int sigdelset(sigset_t *set, int signum);
  • 参数

    • set:指向信号集的指针。

    • signum:要从信号集中移除的信号编号。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:从信号集中移除指定的信号。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;sigfillset(&set);sigdelset(&set, SIGINT); // 从信号集中移除SIGINTreturn 0;
}

5. sigismember函数

函数介绍
#include <signal.h>
​
int sigismember(const sigset_t *set, int signum);
  • 参数

    • set:指向信号集的指针。

    • signum:要检查的信号编号。

  • 返回值

    • 如果信号在信号集中,返回1。

    • 如果信号不在信号集中,返回0。

    • 如果出错,返回-1。

  • 作用:检查指定信号是否在信号集中。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;sigemptyset(&set);sigaddset(&set, SIGINT);
​if (sigismember(&set, SIGINT)) {printf("SIGINT在信号集中。\n");} else {printf("SIGINT不在信号集中。\n");}
​return 0;
}

五、信号的阻塞与挂起

1. sigprocmask函数

函数介绍
#include <signal.h>
​
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
  • 参数

    • how:指定如何修改当前信号屏蔽字。

      • SIG_BLOCK:将set中的信号添加到当前信号屏蔽字中。

      • SIG_UNBLOCK:从当前信号屏蔽字中移除set中的信号。

      • SIG_SETMASK:将当前信号屏蔽字设置为set中的信号。

    • set:指向新的信号屏蔽集的指针。

    • oset:指向存储旧的信号屏蔽集的指针(可选)。

  • 返回值:成功时返回0,失败时返回-1。

  • 作用:设置或获取进程的信号屏蔽字。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {sigset_t set;
​sigemptyset(&set);sigaddset(&set, SIGINT);
​signal(SIGINT, handle);
​sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT信号sleep(5); // 在这5秒内,SIGINT信号会被阻塞
​sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除SIGINT信号的阻塞
​while (1) {sleep(1);}return 0;
}

2. pause函数

函数介绍
#include <unistd.h>
​
int pause(void);
  • 返回值:总是返回-1,并设置errnoEINTR

  • 作用:使进程挂起,直到接收到信号为止。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle);
​printf("进程已挂起。按Ctrl-C继续。\n");pause(); // 挂起进程printf("进程已恢复。\n");return 0;
}

3. sigsuspend函数

函数介绍
#include <signal.h>
​
int sigsuspend(const sigset_t *sigmask);
  • 参数

    • sigmask:指向信号屏蔽集的指针。

  • 返回值:总是返回-1,并设置errnoEINTR

  • 作用:将进程的信号屏蔽字设置为sigmask,然后挂起进程,直到接收到信号为止。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {sigset_t set;
​sigemptyset(&set);sigaddset(&set, SIGINT);
​signal(SIGINT, handle);
​printf("进程已挂起。按Ctrl-C继续。\n");sigsuspend(&set); // 挂起进程printf("进程已恢复。\n");return 0;
}

六、使用SIGCHLD信号回收子进程

1. SIGCHLD信号

SIGCHLD信号在以下情况下产生:

  1. 子进程终止。

  2. 子进程接收到SIGSTOP信号而暂停。

  3. 子进程从暂停状态被唤醒。

父进程可以通过捕捉SIGCHLD信号来回收子进程,避免产生僵尸进程。

示例代码
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
​
void handle(int sig) {wait(NULL); // 回收子进程printf("子进程已终止。信号 %d 收到。\n", sig);
}
​
int main() {pid_t pid;
​struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGCHLD, &act, NULL); // 设置SIGCHLD的处理函数
​pid = fork();if (pid > 0) {// 父进程while (1) {printf("父进程正在运行。PID: %d\n", getpid());sleep(1);}} else if (pid == 0) {// 子进程printf("子进程正在运行。PID: %d\n", getpid());sleep(5);exit(0);}
​return 0;
}

七、定时器的实现

1. alarm函数

alarm函数用于设置一个定时器,当定时时间到达时,系统会向进程发送SIGALRM信号。

函数原型

#include <unistd.h>
​
unsigned int alarm(unsigned int seconds);

参数

  • seconds:定时时间,单位为秒。如果seconds为0,则清除之前设置的定时器。

返回值

  • 返回上次设置的定时器剩余时间(秒)。如果之前没有设置定时器,则返回0。

示例代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle_sigalrm(int sig) {printf("定时器信号捕获到\n");alarm(1); // 重新设置定时器,实现周期性触发
}
​
int main() {signal(SIGALRM, handle_sigalrm);
​alarm(1); // 设置1秒后触发定时器
​printf("等待定时器信号...\n");while (1) {pause();}
​return 0;
}

2. ualarm函数

ualarm函数用于设置一个微秒级的定时器,当定时时间到达时,系统会向进程发送SIGALRM信号。它可以设置一次性定时器或周期性定时器。

函数原型

#include <unistd.h>
​
useconds_t ualarm(useconds_t usecs, useconds_t interval);

参数

  • usecs:首次触发的时间,单位为微秒。

  • interval:触发后的间隔时间,单位为微秒。如果interval不为0,则定时器会周期性触发。

返回值

  • 返回上次设置的定时器剩余时间(微秒)。如果之前没有设置定时器,则返回0。

示例代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle_sigalrm(int sig) {printf("定时器信号捕获到\n");
}
​
int main() {signal(SIGALRM, handle_sigalrm);
​ualarm(5000000, 1000000); // 5秒后触发,之后每隔1秒触发一次
​printf("等待定时器信号...\n");while (1) {pause();}
​return 0;
}

3. setitimer函数

setitimer函数用于设置一个间隔定时器,可以指定定时器的类型,并在定时时间到达时发送相应的信号。

函数原型

#include <sys/time.h>
​
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

参数

  • which:定时器类型,可选值为:

    • ITIMER_REAL:基于实际时间的定时器,触发时发送SIGALRM信号。

    • ITIMER_VIRTUAL:基于用户态运行时间的定时器,触发时发送SIGVTALRM信号。

    • ITIMER_PROF:基于用户态和内核态运行时间的定时器,触发时发送SIGPROF信号。

  • new_value:指向新的定时器设置的指针。

  • old_value:指向存储旧的定时器设置的指针(可选)。

结构体itimerval

struct itimerval {struct timeval it_interval; // 定时器的间隔时间struct timeval it_value;    // 定时器的初始时间
};

结构体timeval

struct timeval {long tv_sec;         // 秒long tv_usec;        // 微秒
};

返回值

  • 成功时返回0,失败时返回-1。

示例代码

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
​
void handle_sigalrm(int sig) {printf("定时器信号捕获到\n");struct itimerval timer;timer.it_interval.tv_sec = 1;timer.it_interval.tv_usec = 0;timer.it_value.tv_sec = 1;timer.it_value.tv_usec = 0;setitimer(ITIMER_REAL, &timer, NULL); // 重新设置定时器
}
​
int main() {struct itimerval timer;
​signal(SIGALRM, handle_sigalrm);
​timer.it_interval.tv_sec = 1;timer.it_interval.tv_usec = 0;timer.it_value.tv_sec = 1;timer.it_value.tv_usec = 0;setitimer(ITIMER_REAL, &timer, NULL);
​printf("等待定时器信号...\n");while (1) {pause();}
​return 0;
}

八、信号的捕捉与处理

1. 信号捕捉过程

信号捕捉的步骤如下:

  1. 定义信号处理函数,该函数在信号到达时被调用。

  2. 使用signalsigaction函数将信号与处理函数关联起来。

2. signal函数

signal函数用于设置信号的处理方式。

函数原型

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

参数

  • signum:要设置处理方式的信号编号。

  • handler:信号处理函数,或者SIG_DFL(恢复默认处理)、SIG_IGN(忽略信号)。

返回值

  • 返回之前的信号处理函数,如果设置失败则返回SIG_ERR

示例代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle_sigint(int sig) {printf("SIGINT信号捕获到\n");
}
​
int main() {signal(SIGINT, handle_sigint);
​printf("进程正在运行。按Ctrl+C发送SIGINT。\n");while (1) {sleep(1);}
​return 0;
}

3. sigaction函数

sigaction函数提供了更灵活的信号处理设置,推荐使用。

函数原型

#include <signal.h>
​
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数

  • signum:要设置处理方式的信号编号。

  • act:指向新的信号处理动作的指针。

  • oldact:指向存储旧的信号处理动作的指针(可选)。

结构体sigaction

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_flags标志

  • SA_SIGINFO:使用sa_sigaction而不是sa_handler

  • SA_RESTART:被信号中断的系统调用会自动重新发起。

  • SA_NODEFER:在信号处理函数执行期间,不对该信号进行屏蔽。

示例代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle_sigint(int sig) {printf("SIGINT信号捕获到\n");
}
​
int main() {struct sigaction act;
​act.sa_handler = handle_sigint;sigemptyset(&act.sa_mask);act.sa_flags = 0;
​sigaction(SIGINT, &act, NULL);
​printf("进程正在运行。按Ctrl+C发送SIGINT。\n");while (1) {sleep(1);}
​return 0;
}

九、信号的阻塞与挂起

1. 信号的阻塞

信号的阻塞是指阻止信号被处理,但信号仍然可以产生。阻塞信号不会被立即处理,而是等到解除阻塞后才处理。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {sigset_t set;
​sigemptyset(&set);sigaddset(&set, SIGINT);
​signal(SIGINT, handle);
​sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT信号sleep(5); // 在这5秒内,SIGINT信号会被阻塞
​sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除SIGINT信号的阻塞
​while (1) {sleep(1);}return 0;
}

2. pause函数

pause函数使进程挂起,直到接收到信号为止。如果信号的处理动作是终止进程,则pause函数不会返回;如果信号的处理动作是忽略,则进程继续挂起;如果信号的处理动作是捕捉,则调用完信号处理函数后,pause函数返回-1。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle);
​printf("进程已挂起。按Ctrl-C继续。\n");pause(); // 挂起进程
​printf("进程已恢复。\n");return 0;
}

3. sigsuspend函数

sigsuspend函数将进程的信号屏蔽字设置为指定的信号集,然后挂起进程,直到接收到信号为止。它通常用于在信号处理函数中临时更改信号屏蔽字。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {sigset_t set;
​sigemptyset(&set);sigaddset(&set, SIGINT);
​signal(SIGINT, handle);
​printf("进程已挂起。按Ctrl-C继续。\n");sigsuspend(&set); // 挂起进程
​printf("进程已恢复。\n");return 0;
}

十、信号集操作

1. 信号集操作函数

信号集操作函数用于管理信号集,可以清空、填充、添加、删除和检查信号集中的信号。

示例代码
#include <stdio.h>
#include <signal.h>
​
int main() {sigset_t set;
​sigemptyset(&set); // 清空信号集sigaddset(&set, SIGINT); // 添加SIGINT到信号集sigaddset(&set, SIGQUIT); // 添加SIGQUIT到信号集
​if (sigismember(&set, SIGINT)) {printf("SIGINT在信号集中。\n");} else {printf("SIGINT不在信号集中。\n");}
​if (sigismember(&set, SIGQUIT)) {printf("SIGQUIT在信号集中。\n");} else {printf("SIGQUIT不在信号集中。\n");}
​sigdelset(&set, SIGINT); // 从信号集中删除SIGINT
​if (sigismember(&set, SIGINT)) {printf("SIGINT在信号集中。\n");} else {printf("SIGINT不在信号集中。\n");}
​return 0;
}

十一、信号的忽略与默认处理

1. 忽略信号

可以使用signal函数将信号处理方式设置为SIG_IGN,从而忽略指定的信号。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
int main() {signal(SIGINT, SIG_IGN); // 忽略SIGINT信号
​printf("SIGINT信号被忽略。按Ctrl+C测试。\n");while (1) {sleep(1);}
​return 0;
}

2. 恢复默认处理

可以使用signal函数将信号处理方式设置为SIG_DFL,从而恢复系统的默认处理方式。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle); // 设置SIGINT的处理函数sleep(5);
​signal(SIGINT, SIG_DFL); // 恢复SIGINT的默认处理方式
​printf("SIGINT信号恢复为默认处理。按Ctrl+C测试。\n");while (1) {sleep(1);}
​return 0;
}

十二、信号在进程间通信中的应用

1. 使用kill命令发送信号

可以在命令行使用kill命令向指定进程发送信号。

示例代码
# 向进程ID为1234的进程发送SIGINT信号
kill -SIGINT 1234

2. 使用raise函数发送信号给自己

可以使用raise函数向自身发送信号。

示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
​
void handle(int sig) {printf("捕获到信号 %d\n", sig);
}
​
int main() {signal(SIGINT, handle); // 设置SIGINT的处理函数raise(SIGINT); // 向自身发送SIGINT信号while (1) {sleep(1);}return 0;
}

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

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

相关文章

CSP-J 2019 入门级 第一轮(初赛) 完善程序(2)

【题目】 CSP-J 2019 入门级 第一轮&#xff08;初赛&#xff09; 完善程序&#xff08;2&#xff09; &#xff08;计数排序&#xff09;计数排序是一个广泛使用的排序方法。下面的程序使用双关键字计数排序&#xff0c;将n对10000 以内的整数&#xff0c;从小到大排序。 例如…

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档&#xff08;带图片&#xff09;预览&#xff0c;并导出 预览安装插件示例代码项目目录结构截图实际效果截图 动态渲染 .docx 文档&#xff08;带图片&#xff09;&#xff0c;预览、导出安装插件docx 模板文件内容完整代码…

养老更安心!智绅科技“智慧”养老系统,智在何处?

在老龄化趋势不断加剧的当下&#xff0c;养老问题成为全社会关注的焦点。 人们对于养老服务的需求日益增长&#xff0c;不仅期望能够得到基本的生活照料&#xff0c;更渴望在安全、舒适、便捷的环境中安享晚年。 智绅科技的“智慧”养老系统应运而生&#xff0c;凭借其独特的…

MySQL 当中的锁

MySQL 当中的锁 文章目录 MySQL 当中的锁MySQL 中有哪些主要类型的锁&#xff1f;请简要说明MySQL 的全局锁有什么用&#xff1f;MySQL 的表级锁有哪些&#xff1f;作用是什么&#xff1f;元数据锁&#xff08;MetaData Lock&#xff0c;MDL&#xff09;意向锁&#xff08;Inte…

vue前端代码作业——待办事项

美化样式示意图&#xff1a; 后端IDEA代码示意图&#xff1a; 代码解释&#xff1a; 1. isAllChecked 计算属性的作用 isAllChecked 用于实现 “全选 / 全不选” 功能&#xff0c;它是一个 双向绑定 的计算属性&#xff08;因为 v-model 需要同时支持读取和设置值&#xff09…

Oracle数据库数据编程SQL<3.1 PL/SQL 匿名块 及 流程控制中的条件判断、循环、异常处理和随机函数应用>

PL/SQL部分 在SQL的基础上增加了一些过程化的控制语句。 过程化控制语句包括&#xff1a;类型定义、判断、循环、游标、异常处理&#xff08;例外处理&#xff09; 目录 PL/SQL匿名块 一、匿名块基本结构 1、匿名块由三个部分组成&#xff1a; 2、注意事项&#xff1a; …

DeepSeek详解:探索下一代语言模型

文章目录 前言一、什么是DeepSeek二、DeepSeek核心技术2.1 Transformer架构2.1.1 自注意力机制 (Self-Attention Mechanism)(a) 核心思想(b) 计算过程(c) 代码实现 2.1.2 多头注意力 (Multi-Head Attention)(a) 核心思想(b) 工作原理(c) 数学描述(d) 代码实现 2.1.3 位置编码 (…

Git Reset 命令详解与实用示例

文章目录 Git Reset 命令详解与实用示例git reset 主要选项git reset 示例1. 撤销最近一次提交&#xff08;但保留更改&#xff09;2. 撤销最近一次提交&#xff0c;并清除暂存区3. 彻底撤销提交&#xff0c;并丢弃所有更改4. 回退到特定的提交5. 取消暂存的文件 git reset 与 …

前端知识点---事件监听器里面的e.target跟this的区别,e.target在事件委托中的好处

文章目录 ✅ 相同点✅ 不同点✅ 总结区别e.target与事件委托之间的关系 在事件监听器中&#xff0c;e.target 和 this 有时是一样的&#xff0c;但它们并不完全相同。 ✅ 相同点 当事件直接绑定到元素时&#xff1a; e.target 和 this 通常指向相同的元素&#xff0c;即事件绑…

Elasticsearch 完全指南

1. Elasticsearch基础知识 1.1 什么是Elasticsearch Elasticsearch是一个基于Lucene的分布式、RESTful风格的搜索和数据分析引擎。它是一个开源的、高扩展的、分布式的全文搜索引擎,可以近乎实时地存储、检索数据。 Elasticsearch不仅仅是一个全文搜索引擎,它还可以用于以…

Python 3 与 MySQL 数据库连接:mysql-connector 模块详解

Python 3 与 MySQL 数据库连接&#xff1a;mysql-connector 模块详解 概述 在Python 3中&#xff0c;与MySQL数据库进行交互是一个常见的需求。mysql-connector是一个流行的Python模块&#xff0c;它提供了与MySQL数据库连接和交互的接口。本文将详细介绍mysql-connector模块…

SQL:CASE WHEN使用详解

文章目录 1. 数据转换与映射2. 动态条件筛选3. 多条件分组统计4. 数据排名与分级5. 处理空值与默认值6. 动态排序 CASE WHEN 语句在 SQL 中是一个非常强大且灵活的工具&#xff0c;除了常规的条件判断外&#xff0c;还有很多巧妙的用法&#xff0c;以下为你详细总结&#xff1a…

【字符设备驱动开发–IMX6ULL】(二)Linux 设备号

【字符设备驱动开发–IMX6ULL】&#xff08;二&#xff09;Linux 设备号 文章目录 【字符设备驱动开发–IMX6ULL】&#xff08;二&#xff09;Linux 设备号1 设备号的组成2.设备号的分配 1 设备号的组成 为了方便管理&#xff0c;Linux 中每个设备都有一个设备号&#xff0c;设…

【字符设备驱动开发–IMX6ULL】(一)简介

【字符设备驱动开发–IMX6ULL】&#xff08;一&#xff09;简介 一、Linux驱动与裸机开发区别 1.裸机驱动开发回顾 ​ 1、底层&#xff0c;跟寄存器打交道&#xff0c;有些MCU提供了库。 spi.c&#xff1a;主机驱动&#xff08;换成任何一个设备之后只需要调用此文件里面的…

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割&#xff0c;结合DeepSORT算法完成目标的实时跟踪&#xff0c;并利用GPU加速技术提升处理速度。系统支持模块化设计&#xff0c;可导入其他权重文件以适应不同场景需求&#xff0c;同时提供自定义配置选项&#xff0c;如显示标签和保存…

蓝桥杯嵌入式学习笔记

用博客来记录一下参加蓝桥杯嵌入式第十六届省赛的学习经历 工具环境准备cubemx配置外部高速时钟使能设置串口时钟配置项目配置 keil配置烧录方式注意代码规范头文件配置 模块ledcubemx配置keil代码实现点亮一只灯实现具体操作的灯&#xff0c;以及点亮还是熄灭 按键cubemx配置k…

ARCGIS PRO SDK VB2022 图层要素类类型判断

arcgis pro 常见要素类类型有以下几种&#xff1a; FeatureLayer ——要素图层&#xff08;矢量数据&#xff09; RasterLayer ——栅格图层 MapImageLayer ——地图图像图层 VectorTileLayer ——矢量切片图层 SceneLayer …

【hadoop】远程调试环境

根据上一节&#xff0c;我们已经安装完成hadoop伪分布式环境 hadoop集群环境配置_jdk1.8 441-CSDN博客 还没安装的小伙伴可以看看这个帖子 这一节我们要实现使用vscode进行远程连接&#xff0c;并且完成java配置与测试 目录 vscode 配置远程 安装java插件 新建java项目 …

Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现

此次官方发布的 Spring AI Alibaba OpenManus 实现&#xff0c;包含完整的多智能体任务规划、思考与执行流程&#xff0c;可以让开发者体验 Java 版本的多智能体效果。它能够根据用户的问题进行分析&#xff0c;操作浏览器&#xff0c;执行代码等来完成复杂任务等。 项目源码及…

【Linux网络与网络编程】02.初识Socket编程

1. 数据传输的目的 前一篇文章中我们讲解了网络传输的流程&#xff0c;那么网络传输的目的是什么呢&#xff1f;难道我们只是将数据从一台主机传输到另一台主机吗&#xff1f; 当然不是的&#xff01;因为数据是给人用的。比如&#xff1a;聊天是人在聊天&#xff0c;下载是人…