8、linux c 信号机制
一、信号概述
1. 信号概念
信号是一种在软件层次上对中断机制的模拟,是一种异步通信方式。信号的产生和处理都由操作系统内核完成,用于在进程之间传递信息或通知某些事件的发生。
2. 信号的产生
信号可以通过以下方式产生:
-
按键产生:例如用户按下
Ctrl-C
产生SIGINT
信号,按下Ctrl-\
产生SIGQUIT
信号。 -
系统调用函数产生:例如
kill
、raise
等函数。 -
硬件异常:例如非法指令、除以零等。
-
命令行产生:例如使用
kill
命令向进程发送信号。 -
软件条件:例如访问非法内存、管道写入端关闭等。
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,并设置
errno
为EINTR
。 -
作用:使进程挂起,直到接收到信号为止。
示例代码
#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,并设置
errno
为EINTR
。 -
作用:将进程的信号屏蔽字设置为
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
信号在以下情况下产生:
-
子进程终止。
-
子进程接收到
SIGSTOP
信号而暂停。 -
子进程从暂停状态被唤醒。
父进程可以通过捕捉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. 信号捕捉过程
信号捕捉的步骤如下:
-
定义信号处理函数,该函数在信号到达时被调用。
-
使用
signal
或sigaction
函数将信号与处理函数关联起来。
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; }
相关文章:
8、linux c 信号机制
一、信号概述 1. 信号概念 信号是一种在软件层次上对中断机制的模拟,是一种异步通信方式。信号的产生和处理都由操作系统内核完成,用于在进程之间传递信息或通知某些事件的发生。 2. 信号的产生 信号可以通过以下方式产生: 按键产生&…...
Set,Map,WakeSet,WakeMap
简介 Set、Map、WeakMap 和 WeakSet 是 ES6 引入的高级数据结构,它们的底层实现和特性与传统的对象和数组有显著差异 强弱引用了解: link Set Set对象 是一种用于存储 唯一值 的可迭代集合,可存储任意类型的值(原始值、对象引用等&…...
NSSCTF(MISC)—[HITCTF 2021]PNG
相应的做题地址:https://www.nssctf.cn/problem/819 import zlib from Crypto.Cipher import AES import base64 def decode(data, key, iv): cipher AES.new(key, AES.MODE_CBC, iv) decryptByts base64.b64decode(data) msg cipher.decrypt(decryptByts) msgs…...
只出现一次的数字
这个题目动了点脑筋,由于它们时无序的,所以我们如果去找的话比较费劲,可能要循环嵌套再嵌套,所以我们先利用库中自带的sort函数进行排序,把这些数从小到大以此排列,然后我们进行判断哪个数出现了一次即可。…...
【编程中的框架】
编码中常用的框架及其使用方法和好处 框架(Framework)是一种为解决特定问题而设计的软件架构,它提供了一组预定义的组件、模式和工具,帮助开发者更高效地构建应用程序。框架通常不仅仅是方法库,它们提供了一种结构化的…...
Python-常用关键字
基础值 1. False - 意义:布尔类型假值(首字母大写) - 用法示例: if condition is False: print("条件为假") 2. True - 意义:布尔类型真值(首字母大写) - 用法示例&…...
【计算机网络】DHCP工作原理
DHCP(动态主机配置协议) Dynamic Host Configuration Protocol 基于UDP协议传输 DHCP分配IP地址的过程 (1)DHCP DISCOVER客户机请求 IP 地址: 当一个 DHCP 客户机启动时,客户机还没有 IP 地址,所以客户机要通过 DHC…...
python 原型链污染学习
复现SU的时候遇到一道python原型链污染的题,借此机会学一下参考: 【原型链污染】Python与Jshttps://blog.abdulrah33m.com/prototype-pollution-in-python/pydash原型链污染 文章目录 基础知识对父类的污染命令执行对子类的污染pydash原型链污染打污染的…...
量子计算:未来计算技术的革命性突破
在当今科技飞速发展的时代,量子计算正逐渐从理论走向实践,成为计算技术领域最具潜力的革命性突破之一。与传统计算机基于二进制的计算方式不同,量子计算利用量子比特(qubit)的叠加和纠缠特性,能够在处理复杂…...
Maven:Java项目构建与依赖管理工具
Maven 是什么 Maven 将项目开发过程和管理过程抽象成一个项目对象模型(POM),本质上是一个项目管理工具。Maven 主要用于Java项目的依赖管理、编译、测试、打包和部署等操作。 Maven的核心设计围绕标准化和自动化,通过一系列约定和…...
内积相似系数——内积度量相似系数
内积与相似系数 内积(Inner Product) 内积(Inner Product),也称为点积(Dot Product)或标量积,两个向量点积的结果是一个标量(通常是实数或复数)。 内积&…...
问题:md文档转换word,html,图片,excel,csv
文章目录 问题:md文档转换word,html,图片,excel,csv,ppt**主要职责****技能要求****发展方向****学习建议****薪资水平** 方案一:AI Markdown内容转换工具打开网站md文档转换wordmd文档转换pdfm…...
GET 和 POST 有什么区别
GET 和 POST 是 HTTP 协议中两种最常见的请求方法,它们在用途、安全性、数据传递方式等方面有显著的区别。以下是它们的主要区别: 1. 用途 • GET: • 用于从服务器获取资源(数据)。 • 是一种无状态的操作…...
AI Agent 人工智能相关公开比赛汇总
参与 AI 相关比赛是提升技术能力、接触前沿算法、积累项目经验的绝佳方式。以下是全球知名的比赛,以及适合不同水平选手的竞赛分类。 1. 全球知名 AI & 计算机竞赛 (1) Kaggle 竞赛(Kaggle Competitions) 简介:全球最知名的…...
Java 多线程编程之 Object.wait 方法(工作原理、高级特性、notify 方法与 notifyAll 方法)
一、wait 方法 1、基本介绍 wait 方法是 Java 中每个对象都拥有的方法,它继承自 Object 类 wait 方法使当前线程进入等待状态,直到其他线程调用该对象的 notify 方法或 notifyAll 方法 wait 方法必须在同步代码块中使用,否则抛出 Interrup…...
python下载m3u8格式视频
一、安装 m3u8库 pip install requests pip install requests m3u8 二、编码实现 import os import re import requests import subprocess# 下载ts文件 def down_ts_file(base_url, m3u8_url, download_dir):# 从m3u8文件中获取所有ts的分片名称信息response requests.get…...
3.30 代码随想录第三十天打卡
准备:01背包理论基础(二维) 1.有n个物品每个物品只有一个 2.完全背包是有n个物品每个物品有无限多个 3.多重背包是有n个物品每种物品个数各不相同 (1)题目描述: (2)解题思路; 1…...
01 相机标定与相机模型介绍
学完本文,您将了解不同相机模型分类、内参意义,及对应的应用代码模型 标定的意义 建模三维世界点投影到二维图像平面的过程。标定输出的是相机模型。 相机模型 相机模型可以解理解为投影模型 +...
鸿蒙学习手册(HarmonyOSNext_API16)_应用开发UI设计:相对布局
概述 RelativeContainer 就像个「智能拼图板」,帮你把界面组件像拼图一样自由组合,不用一层套一层地堆叠。每个组件可以直接「贴」到其他组件旁边或容器边缘,省去多层嵌套的麻烦,让复杂界面更高效。 举个接地气的例子 dz…...
关于为什么使用redis锁,不使用zk锁的原因
实际项目中,redis一直是最为稳定、可靠的部分,你根本不用担心redis本身的问题。至于ap模型的问题,绝大多数分布式锁只是用于避免一些极端情况的,若单一数据会有那么高的并发量你还加锁,那就要考虑这个业务场景设置的合…...
string的基本使用
C基础格式 C语言语法STL。蓝桥杯选用C11的版本。 #include <bits/stdc.h> #include <iostream> using namespace std; int main() {cout<<"Hello World!"<<endl;printf("Hello World!");return 0; } 基本数据类型 #include &l…...
论文阅读笔记——PointVLA: Injecting the 3D World into Vision-Language-Action Models
PointVLA 论文 现有的 VLA 基于 2D 视觉-语言数据表现良好但缺乏 3D 几何先验导致空间推理缺陷。传统方案:1)3D->2D 投影,造成几何信息损失;2)3D 数据集少。PointVLA 保留原有 VLA,提取点云特征…...
MySQL数据库精研之旅第四期:解锁库操作高阶技能
专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、查看所有表 1.1. 语法 二、创建表 2.1. 语法 2.2. 示例 2.3. 表在磁盘上对应的⽂件 三、查看表结构 3.1. 语法 3.2. 示例 四、修改表 4.1. 语法 4.2. 示例 五、删除表 5.1. 语法 5.2.…...
自定义一个C语言字符串取整函数
一、字符串取整的主要思路 1、遍历每个字符; 2、获得0到9的字符对应的整数值; 3、把对应位置的十进制权重相乘; 4、把所有的相乘结果相加; 5、返回相加结果; 二、主要代码 // 主要是把十进制的整数字符转成十进制变量值…...
Ruby 命令行选项
Ruby 命令行选项 概述 Ruby 是一种广泛使用的编程语言,它拥有强大的命令行工具,可以帮助开发者进行各种任务。了解 Ruby 的命令行选项对于提高开发效率至关重要。本文将详细介绍 Ruby 的常用命令行选项,帮助开发者更好地利用 Ruby 的命令行功能。 Ruby 命令行选项概述 R…...
3.29:数据结构-绪论线性表-上
一、时间复杂度 1、ADT 2、定义法计算时间复杂度:统计核心语句的总执行次数 (1)例题1,与2022年的真题对比着写 此题关键在于求和公式的转化,类型为:线性循环嵌套非线性循环 2022年那道题如果考场上实在脑…...
【百日精通 JAVA | SQL篇 | 第一篇】初识数据库
一、数据库是什么? 数据库是一类软件,数据库的作用用于管理系统(这是一款成品软件,内部应用了很多数据结构)。 二、数据库分为两大类 1.关系型数据库 对于数据的要求比较严格 通常是以表格的方式来组织数据的。(和Excel差不多) 典型代表…...
yum repolist all全部禁用了 怎么办
文章目录 步骤思考解决yum仓库全部被禁用的问题步骤思考: 检查仓库状态:运行yum repolist all,查看所有仓库的启用状态。 被禁用的仓库会显示为disabled。 启用所有仓库:可以逐一启用,或者使用命令批量启用。 例如使用yum-config-manager --enable ‘*’,但需要注意是否有…...
gnvm切换node版本号
1. gnvm下载官网 GNVM - Node.js version manager on Windows by Go 2. 安装 2.1 不存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到任意文件夹,并将此文件夹加入到环境变量 Path。 2.2 存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到 Node.js 所在的文件夹。 2.…...
maven高级
1.分模块开发与设计 理解并实现分模块开发 能够使用聚合工程快速构建项目 能够使用继承简化项目配置 能够根据需求配置生成、开发、测试环境,并在各个环境间切换运行 了解Maven的私服 1.1分模块开发:将别人写好的功能或是包直接使用, 引入依赖…...
MyBatis-Plus 多数据源配置与读写分离实战
一、引言 在实际的项目开发中,我们常常会遇到需要操作多个数据库的情况,比如纯粹多库、读写分离、一主多从、混合模式等。本文将详细介绍如何使用 MyBatis-Plus 实现纯粹多库的场景,并探讨读写分离的实现思路。 二、环境准备 开发工具&…...
pip install cryptacular卡住,卡在downloading阶段
笔者安装pip install cryptacular卡在downloading阶段,但不知道为何 Collecting cryptacularCreated temporary directory: /tmp/pip-unpack-qfbl8f08http://10.170.22.41:8082 "GET http://repo.huaweicloud.com/repository/pypi/packages/42/69/34d478310d6…...
Baklib解析企业内容管理与内容中台核心差异
企业内容管理技术架构解析 在企业数字化进程中,企业内容管理系统(ECM)以结构化技术框架为核心,通过文档全生命周期管理与元数据控制实现内容资产的高效治理。其架构通常包含分布式存储引擎、多层级权限体系及标准化工作流模块&am…...
力扣每日一题:2716——最小化字符串长度
2716——最小化字符串长度 题目示例示例 1示例 2示例 3 题解理解 题目 给你一个下标从 0 开始的字符串 s ,重复执行下述操作任意次: 在字符串中选出一个下标i ,并使 c 为字符串下标i处的字符。并在 i 左侧(如果有)和…...
掌握正则表达式:从基础到实用示例
目录 一、简单谈谈正则 二、基础知识学习 (一)正则元字符 1.特殊单字符 2.空白符 3.量词 4.范围备和选项 综合练习 (二)贪婪、非贪婪与独占模式 1.贪婪模式 2.非贪婪模式(懒惰模式) 3.独占模式…...
Python 中列表(List)、元组(Tuple)、集合(Set)和字典(Dict)四大数据结构的完整对比
以下是 Python 中列表(List)、元组(Tuple)、集合(Set)和字典(Dict)四大数据结构的完整对比分析,结合了核心特性、操作方式和应用场景的深度总结: 一、核心特性…...
LK光流和特征点的关系
uv方程 光流有两个假设: 1.亮度恒定,即图像相同位置的灰度短时不变。两帧中对应像素灰度/亮度相同 2.时间持续性(微小移动),这意味着时间的变化不会引起像素位置的剧烈变化,这样像素的灰度值才能对位置求…...
Rocky Linux 9.5中完美迁移mysql5.6.17到mysql5.7.11
首先Rocky Linux 9.5中,默认官方建议使用的是mysql8.0,项目要兼容以往数据,经过测试跟mysql5.7.11能做兼容。 一:工具准备以及安装步骤 1、官网下载地址:https://downloads.mysql.com/archives/community/ 下载版本…...
练习题:113
目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 定义列表: for 循环遍历列表: 输出元素: 运行思路 结束语 Python题目 题目 使用for循环遍历一个列表并输出每个元素。 题目分析 需求理…...
文件上传存储安全OSS 对象分站解析安全解码还原目录执行
# 文件 - 解析方案 - 执行权限 & 解码还原 1 、执行权限 文件上传后存储目录不给执行权限 2 、解码还原 数据做存储,解析固定(固定协议)(文件后缀名无关) 文件上传后利用编码传输解码还原 # 文件 - 存储方案 - 分站存储…...
NUUO摄像头debugging_center_utils命令执行漏洞
免责声明:本号提供的网络安全信息仅供参考,不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我联系,我将尽快处理并删除相关内容。 漏洞描述 NUUO NVR是中国台湾省NUUO公司旗…...
华为OD机试2025A卷 - 正则表达式替换(Java Python JS C++ C )
最新华为OD机试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 题目描述 为了便于业务互交,约定一个对输入的字符串中的下划线做统一替换。 具体要求如下: 输入字符串,将其中包含的每一个下划线“_”,使用特殊字符串(^|$|[,+])替换,并输出替换后的结果…...
2025-3-30算法打卡
一,小明的彩灯 1.题目描述: 题目描述 小明拥有 NN 个彩灯,第 ii 个彩灯的初始亮度为 aiai。 小明将进行 QQ 次操作,每次操作可选择一段区间,并使区间内彩灯的亮度 xx(xx 可能为负数)。 求…...
【今日半导体行业分析】2025年3月30日
今日探针卡行业分析:把握机遇,应对挑战 一、引言 在半导体产业的精密制造流程中,探针卡作为晶圆测试环节的核心设备,犹如一颗精密的 “心脏”,承担着芯片封装前电学性能测试与筛选的重任。其性能的优劣直接关系到芯片…...
深度学习Note.5(机器学习2)
多项式回归 1.与线性回归联系: 与线性回归大体相似,代码部分复用性高,不同点:公式中为x的次方,可能要规范化处理。 2.paddle的API paddle.sin(x, nameNone) 功能:计算输入的正弦值。 输入:输入…...
三、分类模块,通用组件顶部导航栏Navbar
1.封装通用组件顶部导航栏Navbar 不同效果 Component export struct MkNavbar {Prop title: string Prop leftIcon: ResourceStr $r("app.media.ic_public_left")ProprightIcon: ResourceStr $r("app.media.ic_public_more")PropshowLeftIcon: boolean…...
PipeWire 音频设计与实现分析三——日志子系统
日志子系统 PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log 对象描述日志组件,用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制,不同文件中的日志按功能以不同的…...
playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息
playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息 conftest.py文件下封装两个夹具夹具一:将storage_state登录状态导出为json文件夹具二:重写夹具browser_context_args,添加storage_state登录状态登录…...
Codeforces Round 1014 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1014 (Div. 2) - Codeforces A. Kamilka and the Sheep 思路 最大值-最小值 代码 void solve(){int n;cin>>n;vi a(n10);int mx0;int miinf;for(int i1;i<n;i){cin>>a[i];mimin(mi,a[i]);mxmax(mx,a[i])…...
vulhub靶场—— Tomcat8
目录 一、漏洞描述 二、靶场搭建 三、漏洞复现 1、弱密码 2、文件上传 一、漏洞描述 环境描述: Tomcat 支持后台部署 war 文件,可以直接将 webshell 部署到 web 目录下。tomcat 默认的管理页面 manager 使用 basic 认证用户名和密码登录࿰…...