素材巴巴 > 程序开发 >

信号signal编程测试

程序开发 2023-09-04 21:42:34

信号会打断系统调用,慎用,就是用的时候测一测。

下面是信号的基础测试

信号

信号(signal)机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。信号可以由各种异步事件产生,例如键盘中断等。Shell也可以使用信号将作业控制命令传递给它的子进程。
Linux系统中定义了一系列的信号,这些信号可以由内核产生,也可以由系统中的其他进程产生,只要这些进程有足够的权限。可以使用kill命令(kill -l)在机器上列出所有的信号,如下所示:

lkmao@ubuntu:~$ kill -l1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
 11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
 16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
 21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
 26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
 31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
 63) SIGRTMAX-1  64) SIGRTMAX
 lkmao@ubuntu:~$
 

进程可以屏蔽掉大多数的信号,除了SIGSTOP和SIGKILL。SIGSTOP信号使一个正在运行的进程暂停,而信号SIGKILL则使正在运行的进程退出。进程可以选择系统的默认方式处理信号,也可以选择自己的方式处理产生的信号。信号之间不存在相对的优先权,系统也无法处理同时产生的多个同种的信号,也就是说,进程不能分辨它收到的是1个或者是42个SIGCONT信号。

SIGCONT:此作业控制信号送给需要继续运行的处于停止状态的进程。如果接收到此信号的进程处于停止状态,则操作系统的默认动作是使该停止的进程继续运行,否则默认动作是忽略此信号。

SIGEMT:指示一个实现定义的硬件故障。

SIGFPE:此信号表示一个算术运算异常,例如除以0,浮点溢出等。

SIGHUP:如果终端界面检测到一个连接断开,则将此信号送给与该终端相关的进程。 SIGILL:此信号指示进程已执行一条非法硬件指令。

SIGINT:当用户按中断键(一般采用Delete或Ctrl+C)时,终端驱动程序产生这个信号并将信号送给前台进程组中的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。

SIGIO:此信号指示一个异步IO事件。

SIGIOT:这指示一个实现定义的硬件故障。

SIGPIPE:如果在读进程时已终止写管道,则产生此信号。

SIGQUIT:当用户在终端上按退出键(一般采用Ctrl+C)时,产生此信号,并送至前台进程组中的所有进程。

SIGSEGV:指示进程进行了一次无效的存储访问。

SIGSTOP:这是一个作业控制信号,它停止一个进程。

SIGSYS:指示一个无效的系统调用。由于某种未知原因,某个进程执行了一条系统调用命令,但是调用命令所用的参数无效。

SIGTERM:这是由kill命令发送的系统默认终止信号。

SIGTRAP:指示一个实现定义的硬件故障。

SIGTSTP:交互停止信号,当用户在终端上按挂起键(一般采用Ctrl+Z)时,终端驱动程序产生此信号。

SIGTTIN:当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号。 SIGTTOU:当一个后台进程组进程试图写其控制终端时产生此信号。

SIGURG:此信号通知进程已经发生一个紧急情况。在网络连接上,接到非规定波特率的数据时,此信号可选择地产生。

SIGUSR1:这是一个用户定义的信号,可用于应用程序。

SIGUSR2:这是一个用户定义的信号,可用于应用程序。

 信号截取函数signal()

signal()函数用于截取系统的信号,对此信号挂接用户自己的处理函数。其原型如下:

NAMEsignal - ANSI C signal handlingSYNOPSIS#include typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
 

signal()函数的原型说明此函数要求两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。第1个参数signo是一个整型数,第2个参数是函数指针,它所指向的函数需要一个整型参数,无返回值。用一般语言来描述就是要向信号处理程序传送一个整型参数,而它却无返回值。当调用signal设置信号处理程序时,第2个参数是指向该函数(也就是信号处理程序)的指针。signal的返回值指向以前信号处理程序的指针。
如下代码截取了系统的信号SIGSTOP和SIGKILL,用命令kill杀死其是不可能的。

测试一:尝试截获信号SIGSTOP和SIGKILL

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_kill(int signo){DEBUG_INFO("signo = %dn", signo);
 }static void sig_stop(int signo){DEBUG_INFO("signo = %dn", signo);
 }int main(int argc, char **argv){sighandler ret;ret = signal(SIGKILL, sig_kill);if(ret == SIG_ERR){perror("signal sig_kill");DEBUG_INFO("signal SIGKILL error");//exit(-1);}ret = signal(SIGSTOP, sig_stop);if(ret == SIG_ERR){perror("signal sig_stop");//exit(-1);DEBUG_INFO("signal SIGSTOP error");}for(;;){sleep(1);}return 0;
 }

执行结果:

signal sig_kill: Invalid argument
 main:28 -- signal SIGKILL error
 signal sig_stop: Invalid argument
 main:35 -- signal SIGSTOP error

结论:这根本截获不了啊。

测试二:捕获SIGINT,捕获成功以后,将SIGINT信号设置为默认处理方式

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){DEBUG_INFO("signo = %dn", signo);switch(signo) {case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:DEBUG_INFO("SIGINT");signal(SIGINT, SIG_DFL);default:break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGKILL, sig_func);if(ret == SIG_ERR){perror("signal sig_kill");DEBUG_INFO("signal SIGKILL error");//exit(-1);}ret = signal(SIGSTOP, sig_func);if(ret == SIG_ERR){perror("signal sig_stop");//exit(-1);DEBUG_INFO("signal SIGSTOP error");}ret = signal(SIGINT, sig_func);if(ret == SIG_ERR){perror("signal sig_stop");//exit(-1);DEBUG_INFO("signal SIGSTOP error");}for(;;){sleep(1);}return 0;
 }

测试结果:按两次CTRL+C,第一次进入sig_func函数,第二次退出程序。

signal sig_kill: Invalid argument
 main:37 -- signal SIGKILL error
 signal sig_stop: Invalid argument
 main:44 -- signal SIGSTOP error
 ^Csig_func:16 -- signo = 2sig_func:25 -- SIGINT

kill函数和raise函数

NAMEkill - send signal to a processSYNOPSIS#include #include int kill(pid_t pid, int sig);
 
NAMEraise - send a signal to the callerSYNOPSIS#include int raise(int sig);
 

测试三:kill函数和raise函数发送信号

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;DEBUG_INFO("signo = %dn", signo);switch(signo) {case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}default:break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGKILL, sig_func);if(ret == SIG_ERR){perror("signal sig_kill");DEBUG_INFO("signal SIGKILL error");//exit(-1);}ret = signal(SIGSTOP, sig_func);if(ret == SIG_ERR){perror("signal sig_stop");//exit(-1);DEBUG_INFO("signal SIGSTOP error");}ret = signal(SIGINT, sig_func);if(ret == SIG_ERR){perror("signal sig_stop");//exit(-1);DEBUG_INFO("signal SIGSTOP error");}raise(SIGINT);kill(getpid(),SIGINT);for(;;){sleep(1);}return 0;
 }

测试结果:raise产生一次SIGINT信号,kill产生一次SIGINT信号,此时计数值count变成2,信号处理函数恢复默认值,最后CTRL+C退出进程。

signal sig_kill: Invalid argument
 main:43 -- signal SIGKILL error
 signal sig_stop: Invalid argument
 main:50 -- signal SIGSTOP error
 sig_func:17 -- signo = 2sig_func:27 -- SIGINT count = 1
 sig_func:17 -- signo = 2sig_func:27 -- SIGINT count = 2
 ^C

SIGCHLD信号

测试程序:子进程退出时,会自动向父进程发送信号

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;//DEBUG_INFO("signo = %dn", signo);switch(signo) {case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}break;case SIGCHLD:DEBUG_INFO("%u get a SIGCHLD signal",getppid());break;       default:DEBUG_INFO("unknow signo = %d",signo);break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGCHLD, sig_func);if(ret == SIG_ERR){perror("signal sig_kill");DEBUG_INFO("signal SIGKILL error");//exit(-1);}pid_t pid = fork();if(pid == 0){DEBUG_INFO("child running %u",getpid());DEBUG_INFO("send a SIGCHLD to %u",getppid());exit(0);}for(;;){sleep(1);}return 0;
 }

执行结果:

main:54 -- child running 109007
 main:55 -- send a SIGCHLD to 109006
 sig_func:36 -- 

SIGABRT信号测试

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;switch(signo) {case SIGABRT:break;case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}break;case SIGCHLD:DEBUG_INFO("%u get a SIGCHLD signal",getppid());break;       default:DEBUG_INFO("unknow signo = %d",signo);break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGABRT, sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}DEBUG_INFO("send a SIGABRT signal by abort()");abort();sleep(1);return 0;
 }

测试结果:

main:52 -- send a SIGABRT signal by abort()
 ./test.sh: 行 10: 111335 已放弃               (核心已转储) ./_build_test_cpp_test/signal

SIGCONT信号测试:实验中,父进程先睡眠,子进程向父进程发送SIGCONT信号,父进程退出睡眠,继续执行,然后退出程序。子进程变成孤儿进程,最后由进程1接管

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;switch(signo) {case SIGABRT:break;case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}break;case SIGCHLD:DEBUG_INFO("%u get a SIGCHLD signal",getppid());break;       case SIGCONT:DEBUG_INFO("SIGCONT");break;default:DEBUG_INFO("unknow signo = %d",signo);break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGCONT, sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}if(fork() == 0){DEBUG_INFO("%u send a SIGCONT signal",getppid());DEBUG_INFO("my parent is %u",getppid());kill(getppid(),SIGCONT);sleep(500);DEBUG_INFO("my parent is %u",getppid());DEBUG_INFO("bye bye");abort();}sleep(100);DEBUG_INFO("parent sleep is interrupt by signal SIGCONT");return 0;
 }

执行结果:符合推到

main:58 -- 116350 send a SIGCONT signal
 main:59 -- my parent is 116350
 sig_func:41 -- SIGCONT
 main:67 -- parent sleep is interrupt by signal SIGCONT
 lkmao@ubuntu:/big/csi_driver/rty/01$ main:62 -- my parent is 1
 main:63 -- bye bye

最后通过进程名字终止子进程的睡眠,它继续运行后,自行退出。 

killall -SIGCONT signal

SIGHUP信号

问:如何产生一个SIGHUP信号

可以通过以下几种方式产生SIGHUP信号:
1. 在终端中使用kill命令,指定进程ID和信号类型:

```
kill -SIGHUP <进程ID>
```

2. 在终端中使用kill命令,指定进程名称和信号类型:

```
killall -SIGHUP <进程名称>
```

3. 在程序中使用kill函数,指定进程ID和信号类型:

```c
kill(<进程ID>, SIGHUP);
```

4. 在程序中使用raise函数,产生当前进程的SIGHUP信号:

```c
raise(SIGHUP);
```

在什么情况下需要发送一个SIGHUP信号

SIGHUP信号通常用于重新加载配置文件或重新启动进程。以下是一些需要发送SIGHUP信号的情况:
1. 当进程的配置文件发生更改时,可以发送SIGHUP信号以重新加载配置文件。

2. 当进程需要重新启动时,可以发送SIGHUP信号以通知进程重新启动。

3. 当需要重新初始化进程时,可以发送SIGHUP信号以重新初始化进程。

4. 当需要重新读取进程的日志文件时,可以发送SIGHUP信号以重新打开日志文件。

5. 当需要重新加载进程的插件或模块时,可以发送SIGHUP信号以重新加载插件或模块。
 

测试代码:

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;switch(signo) {case SIGABRT:break;case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}break;case SIGCHLD:DEBUG_INFO("%u get a SIGCHLD signal",getppid());break;       case SIGCONT:DEBUG_INFO("SIGCONT %u",getppid());break;case SIGHUP:DEBUG_INFO("SIGHUP = %u",getpid());break;case SIGSYS:DEBUG_INFO("SIGSYS");break;case SIGTTOU:DEBUG_INFO("SIGTTOU");break;default:DEBUG_INFO("unknow signo = %d",signo);break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGHUP,sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}DEBUG_INFO("pid = %u,ppid = %u",getpid(),getppid());if(fork() == 0){DEBUG_INFO("child %u",getpid());sleep(100);DEBUG_INFO("child %u",getpid());}sleep(100);DEBUG_INFO("parent sleep is interrupt by a signal");return 0;
 }

 测试结果:结果证明

1 SIGHUP信号可以被捕获 

2 SIGHUP信号可以打断sleep睡眠。

3 父进程和子进程都收到了信号

 SIGUSR1和SIGUSR2

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include #define DEBUG_INFO(format, ...) printf("%s:%d -- " format "n", __func__, __LINE__,##__VA_ARGS__)typedef void (*sighandler)(int signo);static void sig_func(int signo){static int count = 0;switch(signo) {case SIGABRT:break;case SIGKILL:DEBUG_INFO("SIGKILL");break;case SIGSTOP:DEBUG_INFO("SIGSTOP");break;case SIGINT:count++;DEBUG_INFO("SIGINT count = %d",count);if(count == 2){signal(SIGINT, SIG_DFL);}break;case SIGCHLD:DEBUG_INFO("%u get a SIGCHLD signal",getppid());break;       case SIGCONT:DEBUG_INFO("SIGCONT %u",getppid());break;case SIGHUP:DEBUG_INFO("SIGHUP = %u",getpid());break;case SIGSYS:DEBUG_INFO("SIGSYS");break;case SIGTTOU:DEBUG_INFO("SIGTTOU");break;case SIGUSR1:DEBUG_INFO("SIGUSR1 = %u",getpid());break;case SIGUSR2:DEBUG_INFO("SIGUSR2 = %u",getpid());break;default:DEBUG_INFO("unknow signo = %d",signo);break;}
 }
 int main(int argc, char **argv){sighandler ret;ret = signal(SIGHUP,sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}ret = signal(SIGUSR1,sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}ret = signal(SIGUSR2,sig_func);if(ret == SIG_ERR){perror("signal");exit(-1);}DEBUG_INFO("pid = %u,ppid = %u",getpid(),getppid());if(fork() == 0){DEBUG_INFO("child %u",getpid());kill(getppid(), SIGUSR1);kill(getpid(), SIGUSR2);sleep(100);DEBUG_INFO("child %u",getpid());}for(int i = 0; i < 10;i++){sleep(100);DEBUG_INFO("parent sleep is interrupt by a signal");}return 0;
 }

执行结果: 

main:80 -- pid = 126176,ppid = 126084
 main:82 -- child 126177
 sig_func:56 -- SIGUSR2 = 126177
 sig_func:53 -- SIGUSR1 = 126176
 main:92 -- parent sleep is interrupt by a signal

小结

 


标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。