信号:信号(signal)是一种软件中断,是UNIX系统中最为古老的进程之间的通信机制。用于在一个或多个进程之间传递异步信号。它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式。在Linux中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件,还可以给进程传递数据。
了解命令: kill -l:查看系统中所有的信号; man 7 signal:查看系统中信号默认动作的定义;
*信号是从1号开始的,没有0号,32号,33号信号:在这些信号中,1~31号信号是普通信号,都是继承自UNIX系统,是不可靠信号,34~64号信号是实时信号,也称可靠信号。
信号的处理方式:忽略,捕捉,和默认三种方式;
忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景。
捕捉信号:告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理
系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
signal函数:注册信号函数
函数原型:
#include <signal.h>
typedef void (*sighandler_t)(int);//需要调用函数指针
sighandler_t signal(int signum, sighandler_t handler);
signum :信号的编号。
handler :中断函数的指针
函数例子:捕捉一些信号 SIGINT(2) SIGUSR1(10)
#include<stdio.h>
#include <signal.h>
//真实处理信号的函数
void handler(int signum)
{
printf("get signum=%d\n",signum);
switch(signum){
case 2:
printf("SIGINT\n"); //按下 ctrl+c 会打印此行代码
break;
case 10:
printf("SIGUSR1\n"); //输入 kill -10 进程ID 会打印此行代码
break;
}
}
int main()
{
signal(SIGINT,handler);//信号处理函数的注册
signal(SIGUSR1,handler);//信号处理函数的注册
while(1);
return 0;
}
函数例子:用编程的方式实现 kill的功能 (一般杀死某个进程 采用 kill -9 进程ID ) kill()函数原型: int kill(pid_t pid,int sig);
#include<stdio.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc,char **argv)
{
int signum=0;
int pid=0;
//char cmd[128]={0};//配合system函数处理命令的大小
signum= atoi(argv[1]);//输入的是char型 需要转为整型
pid= atoi(argv[2]);//输入的是char型 转为整型
printf("num=%d\n",signum);
printf("pid=%d\n",pid);
kill(pid,signum);
// sprintf(cmd,"kill -%d %d",signum,pid);
// system(cmd);
printf("send signal ok\n");
return 0;
}
输入 ./a.out 2 某个进程PID 可以杀死某个进程
补充上面sprintf()函数知识:将“kill -%d %d ” 都存放在cmd为char型的字符串里面
sigaction函数:信号处理函数的注册(配置信号功能)
函数原型:
#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表示能够接受数据
};
//结构体函数指针一般选择sa_sigaction
//void (*sa_sigaction)(int, siginfo_t *, void *)里面siginfo_t结构体包含的东西
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 */
}
signum:参数指出要捕获的信号类型,也就是信号的编号
act:参数指定新的信号处理方式,struct sigaction类型如果不为空说明需要对该信号有新的配置。
oldact:备份,如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复,一般设置为NULL。
sigqueue函数:向指定进程发送一个信号和数据
函数原型:
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
//第三个参数是个联合体,包含的东西
union sigval {
int sival_int;
void *sival_ptr;
};
pid:目标进程的进程号
sig:发的是什么信号,信号编号
value:发送的消息(int或者char*),是一个联合体,表示信号附带的数据,附带数据可以是一个整数也可以是一个指针
函数例子:
//信号处理函数的注册(配置信号功能)
#include<stdio.h>
#include <signal.h>
/*信号处理函数*/
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum %d\n",signum);
if(context!=NULL){//如果有内容
printf("get data=%d\n",info->si_int);
printf("get data=%d\n",info->si_value.sival_int);
}
int main()
{
struct sigaction act;
printf("pid = %d\n",getpid());
act.sa_sigaction = handler;//信号处理程序,能够接受额外数据和sigqueue配合使用
act.sa_flags = SA_SIGINFO;//影响信号的行为SA_SIGINFO表示能够接受数据
sigaction(SIGUSR1,&act,NULL);
//sigaction(SIGINT,&act,NULL);
while(1);
return 0;
}
//运行后阻塞
//向指定进程发送一个信号和数据
#include <signal.h>
#include <stdio.h>
int main(int argc,char **argv)
{
int pid = atoi(argv[2]);
int signum = atoi(argv[1]);
union sigval value;
value.sival_int = 100;
sigqueue(pid,signum,value);//信号处理发送函数
return 0;
}