一. 信号的名字和编号
1. 每个信号都有一个编号和名称,这些名字都以“SIG”开头。
(kill -l命令可显示出当前系统支持的信号的编号和名称 )
二. 信号的处理
1. 信号的处理有三种方法: 忽略,捕捉,默认动作
忽略:有俩种信号不可被忽略(SIGKILL和SIGSTOP),其向 内核 和 超级用户提供了进程终止的方法 (SIG_ICN可忽略信号,但不能忽略上述俩种信号)
捕捉:当捕捉到信号时,由内核来调用用户自定义的函数
默认动作:当捕捉到信号时,由内核来执行默认操作,如Ctrl + C 终止进程
三. 低级信号通信
1. kill命令
kill -l 显示出当前系统支持的信号的编号和名称
kill -9 8066 发送编号为9的信号到PID为8066的进程当中
2.kill函数
int kill(pid_t pid, int sig); //用于向指定的进程发送信号
3. Signal函数
功能: 用于捕获到某一信号然后产生对应用户自定义的动作
函数原型:typedef void (*sighandler_t)(int); //函数指针,别名为sighandler_t
sighandler_t signal(int signum, sighandler_t handler);
signum | 信号的编号,如SIGKILL的编号是9 |
handler | 指向中断函数的指针,接收到信号时,调用自定义的handler函数执行相关操作 |
低级信号通信实例:
源程序 p1.c:
a.通过signal函数捕获信号,并调用hander指针指向的函数执行相应操作
b.建立副终端输入ps -aux|grep p1.out查询当前进程PID,输入kill -9 8199结束当前终端
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
printf("get signum is %d\n",signum);
printf("not quit\n");
switch(signum)
{
case 2:
printf("SIGINT\n");
break;
case 9:
printf("SIGKILL\n");
break;
}
}
int main()
{
signal(SIGINT,handler);
//signal(SIGINT,SIG_ICN);//该宏可以达到忽略信号的目的
signal(SIGKILL,handler);
//signal(SIGKILL,SIG_ICN);//此处不起作用,无法忽略终止信号
while(1);
return 0;
}
运行结果 p1.out:
源程序p2.c:
通过kill函数发送信号向p1.out进程中
/**
* 直接调用kill函数向p1进程发送信号
*/
#include <stdio.h>
#include <signal.h>
int main(int argc,char **argv)
{
int signum;
int pid;
//将命令行参数中的信号值和进程ID转换为整数。
signum = atoi(argv[1]);
pid = atoi(argv[2]);
printf("num=%d,pid=%d\n",signum,pid);
//向指定进程ID发送指定信号
kill(pid,signum);
printf("send signal ok!\n");
return 0;
}
运行结果 p2.out:
源程序p3.c:
相比p2.c区别在于调用system函数执行kill命令向p1.out中发送信号,而不是kill函数发送
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int signum;
int pid;
char cmd[128] = {0};
//将命令行参数中的信号值和进程ID转换为整数。
signum = atoi(argv[1]);
pid = atoi(argv[2]);
printf("num=%d,pid=%d\n",signum,pid);
sprintf(cmd,"kill -%d %d",signum,pid);
//系统会调用命令解释器来执行这条命令
system(cmd);
printf("send signal ok!\n");
return 0;
}
运行结果 p3.out:
五. 高级信号通信
低级信号通信存在一个问题就是,只能发送和接收到了信号,无法携带一些数据,因此引入了高级信号通信。
接受函数:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
};
第一个参数:要捕捉的信号的编号 | |||||||
第二个参数:是一个指向结构体的指针
| |||||||
第三个参数:如果不为NULL,那么可以对之前的信号配置进行备份,以方便之后进行恢复。 |
sigaction函数中的第二个参数是一个指向结构体的指针,这个结构体的第二个参数是一个函数指针,函数指针指向hander函数时,hander函数的第二个参数又是一个指向struct siginfo
这个结构体的指针。这时,我们可以打印出携带的信息。
struct siginfo
这个结构体主要适用于记录接收信号的一些相关信息。
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
发送函数:
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int;
void *sival_ptr;
};
第一个参数:接收端PID |
第二个参数:要发送的命令编号signum |
第三个参数:结构体 |
实例:
源程序:
接受程序recive.c
#include <stdio.h>
#include <signal.h>
void handler(int signum, siginfo_t *info, void *context) {
printf("Received signal: %d\n", signum); // 打印接收到的信号编号
if (context != NULL) {
printf("Received data: %d\n", info->si_int); // 打印额外信息中的整数值
printf("get data=%d\n",info->si_value.sival_int);
}
}
int main() {
struct sigaction act;
//无需配置第一个参数,因为第一个参数是无需接受额外信息
//无需配置第三个参数,因为默认为阻塞
act.sa_sigaction = handler; // 第二个参数,设置信号处理函数为handler
act.sa_flags = SA_SIGINFO; // 第四个参数,设置标志为允许接收额外信息
//int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction(SIGUSR1, &act, NULL);
printf("Waiting for signals...\n");
while(1);
return 0;
}
发送程序send.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc,char *argv[]){
int signum;
int pid;
//将命令行参数中的信号值和进程ID转换为整数。
signum = atoi(argv[1]);
pid = atoi(argv[2]);
//value 参数是一个 union sigval 类型的联合体,用于携带额外的数据
union sigval value;
value.sival_int = 12345;
//int sigqueue(pid_t pid, int sig, const union sigval value);
sigqueue(pid,signum,value);
printf("done\n");
return 0;
}