当前位置: 首页 > news >正文

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;
}

相关文章:

8、linux c 信号机制

一、信号概述 1. 信号概念 信号是一种在软件层次上对中断机制的模拟&#xff0c;是一种异步通信方式。信号的产生和处理都由操作系统内核完成&#xff0c;用于在进程之间传递信息或通知某些事件的发生。 2. 信号的产生 信号可以通过以下方式产生&#xff1a; 按键产生&…...

Set,Map,WakeSet,WakeMap

简介 Set、Map、WeakMap 和 WeakSet 是 ES6 引入的高级数据结构&#xff0c;它们的底层实现和特性与传统的对象和数组有显著差异 强弱引用了解: link Set ​Set对象 是一种用于存储 ​唯一值 的可迭代集合&#xff0c;可存储任意类型的值&#xff08;原始值、对象引用等&…...

NSSCTF(MISC)—[HITCTF 2021]PNG

相应的做题地址&#xff1a;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…...

只出现一次的数字

这个题目动了点脑筋&#xff0c;由于它们时无序的&#xff0c;所以我们如果去找的话比较费劲&#xff0c;可能要循环嵌套再嵌套&#xff0c;所以我们先利用库中自带的sort函数进行排序&#xff0c;把这些数从小到大以此排列&#xff0c;然后我们进行判断哪个数出现了一次即可。…...

【编程中的框架】

编码中常用的框架及其使用方法和好处 框架&#xff08;Framework&#xff09;是一种为解决特定问题而设计的软件架构&#xff0c;它提供了一组预定义的组件、模式和工具&#xff0c;帮助开发者更高效地构建应用程序。框架通常不仅仅是方法库&#xff0c;它们提供了一种结构化的…...

Python-常用关键字

基础值 1. False - 意义&#xff1a;布尔类型假值&#xff08;首字母大写&#xff09; - 用法示例&#xff1a; if condition is False: print("条件为假") 2. True - 意义&#xff1a;布尔类型真值&#xff08;首字母大写&#xff09; - 用法示例&…...

【计算机网络】DHCP工作原理

DHCP(动态主机配置协议) Dynamic Host Configuration Protocol 基于UDP协议传输 DHCP分配IP地址的过程 &#xff08;1&#xff09;DHCP DISCOVER客户机请求 IP 地址&#xff1a; 当一个 DHCP 客户机启动时&#xff0c;客户机还没有 IP 地址&#xff0c;所以客户机要通过 DHC…...

python 原型链污染学习

复现SU的时候遇到一道python原型链污染的题&#xff0c;借此机会学一下参考&#xff1a; 【原型链污染】Python与Jshttps://blog.abdulrah33m.com/prototype-pollution-in-python/pydash原型链污染 文章目录 基础知识对父类的污染命令执行对子类的污染pydash原型链污染打污染的…...

量子计算:未来计算技术的革命性突破

在当今科技飞速发展的时代&#xff0c;量子计算正逐渐从理论走向实践&#xff0c;成为计算技术领域最具潜力的革命性突破之一。与传统计算机基于二进制的计算方式不同&#xff0c;量子计算利用量子比特&#xff08;qubit&#xff09;的叠加和纠缠特性&#xff0c;能够在处理复杂…...

Maven:Java项目构建与依赖管理工具

Maven 是什么 Maven 将项目开发过程和管理过程抽象成一个项目对象模型&#xff08;POM&#xff09;&#xff0c;本质上是一个项目管理工具。Maven 主要用于Java项目的依赖管理、编译、测试、打包和部署等操作。 Maven的核心设计围绕标准化和自动化&#xff0c;通过一系列约定和…...

内积相似系数——内积度量相似系数

内积与相似系数 内积&#xff08;Inner Product&#xff09; 内积&#xff08;Inner Product&#xff09;&#xff0c;也称为点积&#xff08;Dot Product&#xff09;或标量积&#xff0c;两个向量点积的结果是一个标量&#xff08;通常是实数或复数&#xff09;。 内积&…...

问题:md文档转换word,html,图片,excel,csv

文章目录 问题&#xff1a;md文档转换word&#xff0c;html&#xff0c;图片&#xff0c;excel&#xff0c;csv&#xff0c;ppt**主要职责****技能要求****发展方向****学习建议****薪资水平** 方案一&#xff1a;AI Markdown内容转换工具打开网站md文档转换wordmd文档转换pdfm…...

GET 和 POST 有什么区别

GET 和 POST 是 HTTP 协议中两种最常见的请求方法&#xff0c;它们在用途、安全性、数据传递方式等方面有显著的区别。以下是它们的主要区别&#xff1a; 1. 用途 • GET&#xff1a; • 用于从服务器获取资源&#xff08;数据&#xff09;。 • 是一种无状态的操作&#xf…...

AI Agent 人工智能相关公开比赛汇总

参与 AI 相关比赛是提升技术能力、接触前沿算法、积累项目经验的绝佳方式。以下是全球知名的比赛&#xff0c;以及适合不同水平选手的竞赛分类。 1. 全球知名 AI & 计算机竞赛 (1) Kaggle 竞赛&#xff08;Kaggle Competitions&#xff09; 简介&#xff1a;全球最知名的…...

Java 多线程编程之 Object.wait 方法(工作原理、高级特性、notify 方法与 notifyAll 方法)

一、wait 方法 1、基本介绍 wait 方法是 Java 中每个对象都拥有的方法&#xff0c;它继承自 Object 类 wait 方法使当前线程进入等待状态&#xff0c;直到其他线程调用该对象的 notify 方法或 notifyAll 方法 wait 方法必须在同步代码块中使用&#xff0c;否则抛出 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背包理论基础&#xff08;二维&#xff09; 1.有n个物品每个物品只有一个 2.完全背包是有n个物品每个物品有无限多个 3.多重背包是有n个物品每种物品个数各不相同 &#xff08;1&#xff09;题目描述&#xff1a; &#xff08;2&#xff09;解题思路&#xff1b; 1…...

01 相机标定与相机模型介绍

学完本文,您将了解不同相机模型分类、内参意义,及对应的应用代码模型 标定的意义 建模三维世界点投影到二维图像平面的过程。标定输出的是相机模型。 相机模型 相机模型可以解理解为投影模型 +...

鸿蒙学习手册(HarmonyOSNext_API16)_应用开发UI设计:相对布局

概述 RelativeContainer 就像个「智能拼图板」&#xff0c;帮你把界面组件像拼图一样自由组合&#xff0c;不用一层套一层地堆叠。每个组件可以直接「贴」到其他组件旁边或容器边缘&#xff0c;省去多层嵌套的麻烦&#xff0c;让复杂界面更高效。 举个接地气的例子 &#x1f3…...

关于为什么使用redis锁,不使用zk锁的原因

实际项目中&#xff0c;redis一直是最为稳定、可靠的部分&#xff0c;你根本不用担心redis本身的问题。至于ap模型的问题&#xff0c;绝大多数分布式锁只是用于避免一些极端情况的&#xff0c;若单一数据会有那么高的并发量你还加锁&#xff0c;那就要考虑这个业务场景设置的合…...

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 几何先验导致空间推理缺陷。传统方案&#xff1a;1&#xff09;3D->2D 投影&#xff0c;造成几何信息损失&#xff1b;2&#xff09;3D 数据集少。PointVLA 保留原有 VLA&#xff0c;提取点云特征&#xf…...

MySQL数据库精研之旅第四期:解锁库操作高阶技能

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、查看所有表 1.1. 语法 二、创建表 2.1. 语法 2.2. 示例 2.3. 表在磁盘上对应的⽂件 三、查看表结构 3.1. 语法 3.2. 示例 四、修改表 4.1. 语法 4.2. 示例 五、删除表 5.1. 语法 5.2.…...

自定义一个C语言字符串取整函数

一、字符串取整的主要思路 1、遍历每个字符&#xff1b; 2、获得0到9的字符对应的整数值&#xff1b; 3、把对应位置的十进制权重相乘&#xff1b; 4、把所有的相乘结果相加&#xff1b; 5、返回相加结果&#xff1b; 二、主要代码 // 主要是把十进制的整数字符转成十进制变量值…...

Ruby 命令行选项

Ruby 命令行选项 概述 Ruby 是一种广泛使用的编程语言,它拥有强大的命令行工具,可以帮助开发者进行各种任务。了解 Ruby 的命令行选项对于提高开发效率至关重要。本文将详细介绍 Ruby 的常用命令行选项,帮助开发者更好地利用 Ruby 的命令行功能。 Ruby 命令行选项概述 R…...

3.29:数据结构-绪论线性表-上

一、时间复杂度 1、ADT 2、定义法计算时间复杂度&#xff1a;统计核心语句的总执行次数 &#xff08;1&#xff09;例题1&#xff0c;与2022年的真题对比着写 此题关键在于求和公式的转化&#xff0c;类型为&#xff1a;线性循环嵌套非线性循环 2022年那道题如果考场上实在脑…...

【百日精通 JAVA | SQL篇 | 第一篇】初识数据库

一、数据库是什么&#xff1f; 数据库是一类软件&#xff0c;数据库的作用用于管理系统(这是一款成品软件&#xff0c;内部应用了很多数据结构)。 二、数据库分为两大类 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 保存到任意文件夹&#xff0c;并将此文件夹加入到环境变量 Path。 2.2 存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到 Node.js 所在的文件夹。 2.…...

maven高级

1.分模块开发与设计 理解并实现分模块开发 能够使用聚合工程快速构建项目 能够使用继承简化项目配置 能够根据需求配置生成、开发、测试环境&#xff0c;并在各个环境间切换运行 了解Maven的私服 1.1分模块开发&#xff1a;将别人写好的功能或是包直接使用&#xff0c; 引入依赖…...

MyBatis-Plus 多数据源配置与读写分离实战

一、引言 在实际的项目开发中&#xff0c;我们常常会遇到需要操作多个数据库的情况&#xff0c;比如纯粹多库、读写分离、一主多从、混合模式等。本文将详细介绍如何使用 MyBatis-Plus 实现纯粹多库的场景&#xff0c;并探讨读写分离的实现思路。 二、环境准备 开发工具&…...

pip install cryptacular卡住,卡在downloading阶段

笔者安装pip install cryptacular卡在downloading阶段&#xff0c;但不知道为何 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解析企业内容管理与内容中台核心差异

企业内容管理技术架构解析 在企业数字化进程中&#xff0c;企业内容管理系统&#xff08;ECM&#xff09;以结构化技术框架为核心&#xff0c;通过文档全生命周期管理与元数据控制实现内容资产的高效治理。其架构通常包含分布式存储引擎、多层级权限体系及标准化工作流模块&am…...

力扣每日一题:2716——最小化字符串长度

2716——最小化字符串长度 题目示例示例 1示例 2示例 3 题解理解 题目 给你一个下标从 0 开始的字符串 s &#xff0c;重复执行下述操作任意次&#xff1a; 在字符串中选出一个下标i &#xff0c;并使 c 为字符串下标i处的字符。并在 i 左侧&#xff08;如果有&#xff09;和…...

掌握正则表达式:从基础到实用示例

目录 一、简单谈谈正则 二、基础知识学习 &#xff08;一&#xff09;正则元字符 1.特殊单字符 2.空白符 3.量词 4.范围备和选项 综合练习 &#xff08;二&#xff09;贪婪、非贪婪与独占模式 1.贪婪模式 2.非贪婪模式&#xff08;懒惰模式&#xff09; 3.独占模式…...

Python 中列表(List)、元组(Tuple)、集合(Set)和字典(Dict)四大数据结构的完整对比

以下是 Python 中列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;、集合&#xff08;Set&#xff09;和字典&#xff08;Dict&#xff09;四大数据结构的完整对比分析&#xff0c;结合了核心特性、操作方式和应用场景的深度总结&#xff1a; 一、核心特性…...

LK光流和特征点的关系

uv方程 光流有两个假设&#xff1a; 1.亮度恒定&#xff0c;即图像相同位置的灰度短时不变。两帧中对应像素灰度/亮度相同 2.时间持续性&#xff08;微小移动&#xff09;&#xff0c;这意味着时间的变化不会引起像素位置的剧烈变化&#xff0c;这样像素的灰度值才能对位置求…...

Rocky Linux 9.5中完美迁移mysql5.6.17到mysql5.7.11

首先Rocky Linux 9.5中&#xff0c;默认官方建议使用的是mysql8.0&#xff0c;项目要兼容以往数据&#xff0c;经过测试跟mysql5.7.11能做兼容。 一&#xff1a;工具准备以及安装步骤 1、官网下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 下载版本…...

练习题:113

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 定义列表&#xff1a; for 循环遍历列表&#xff1a; 输出元素&#xff1a; 运行思路 结束语 Python题目 题目 使用for循环遍历一个列表并输出每个元素。 题目分析 需求理…...

文件上传存储安全OSS 对象分站解析安全解码还原目录执行

# 文件 - 解析方案 - 执行权限 & 解码还原 1 、执行权限 文件上传后存储目录不给执行权限 2 、解码还原 数据做存储&#xff0c;解析固定(固定协议&#xff09;&#xff08;文件后缀名无关&#xff09; 文件上传后利用编码传输解码还原 # 文件 - 存储方案 - 分站存储…...

NUUO摄像头debugging_center_utils命令执行漏洞

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 NUUO NVR是中国台湾省NUUO公司旗…...

华为OD机试2025A卷 - 正则表达式替换(Java Python JS C++ C )

最新华为OD机试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 题目描述 为了便于业务互交,约定一个对输入的字符串中的下划线做统一替换。 具体要求如下: 输入字符串,将其中包含的每一个下划线“_”,使用特殊字符串(^|$|[,+])替换,并输出替换后的结果…...

2025-3-30算法打卡

一&#xff0c;小明的彩灯 1.题目描述&#xff1a; 题目描述 小明拥有 NN 个彩灯&#xff0c;第 ii 个彩灯的初始亮度为 aiai​。 小明将进行 QQ 次操作&#xff0c;每次操作可选择一段区间&#xff0c;并使区间内彩灯的亮度 xx&#xff08;xx 可能为负数&#xff09;。 求…...

【今日半导体行业分析】2025年3月30日

今日探针卡行业分析&#xff1a;把握机遇&#xff0c;应对挑战 一、引言 在半导体产业的精密制造流程中&#xff0c;探针卡作为晶圆测试环节的核心设备&#xff0c;犹如一颗精密的 “心脏”&#xff0c;承担着芯片封装前电学性能测试与筛选的重任。其性能的优劣直接关系到芯片…...

深度学习Note.5(机器学习2)

多项式回归 1.与线性回归联系&#xff1a; 与线性回归大体相似&#xff0c;代码部分复用性高&#xff0c;不同点&#xff1a;公式中为x的次方&#xff0c;可能要规范化处理。 2.paddle的API paddle.sin(x, nameNone) 功能&#xff1a;计算输入的正弦值。 输入&#xff1a;输入…...

三、分类模块,通用组件顶部导航栏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 对象描述日志组件&#xff0c;用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制&#xff0c;不同文件中的日志按功能以不同的…...

playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息

playwright解决重复登录问题&#xff0c;通过pytest夹具自动读取storage_state用户状态信息 conftest.py文件下封装两个夹具夹具一&#xff1a;将storage_state登录状态导出为json文件夹具二&#xff1a;重写夹具browser_context_args&#xff0c;添加storage_state登录状态登录…...

Codeforces Round 1014 (Div. 2)(A-D)

题目链接&#xff1a;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、文件上传 一、漏洞描述 环境描述&#xff1a; Tomcat 支持后台部署 war 文件&#xff0c;可以直接将 webshell 部署到 web 目录下。tomcat 默认的管理页面 manager 使用 basic 认证用户名和密码登录&#xff0…...