一、简述
记--进程之间使用信号进行通信。异步信号包含: 非实时信号 、实时信号。
实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)
信号一般有以下设定:
1,捕捉 (收到某个信号,做指定的动作,而不是做默认的)
2,忽略 (收到某个信号,不做什么动作)
3,阻塞 (收到某个信号,先做完当前事情,然后在响应信号)
4,按照默认动作
注:SIGKILL,SIGSTOP不能被捕捉
关于信号的详细说明请参考man手册:man 7 signal。
二、signal()与kill()
功能 | 简单的信号处理函数(信号捕捉函数) |
头文件 | #include |
原型 | typedef void (*sighandler_t)(int); //信号处理函数的 指针 sighandler_t signal(int signum, sighandler_t handler); |
参数 | signum:要捕捉的信号 handler:处理方式 1、SIG_IGN:忽略这个信号
|
返回值 | 成功:返回信号处理程序的先前值 出错:返回SIG_ERR,并设置errno |
备注 | 详细请查看man手册:man 2 signal (第7本也有)。可以配合kill()函数使用,kill()函数向某个进程发送某个指定的信号。 |
测试代码1:捕捉中断信号(SIGINT),接收到中断信号后,打印一句话"receive signal %d\n"。其中在终端按下Ctrl+c就发出终端信号,未捕捉之前,终端信号默认结束程序。
#include
#include
#include
void SIGINT_handle(int sig_num)
{
printf("reveice signal %d \n", sig_num);
}
int main(int argc, char argv[])
{
int i;
signal(SIGINT, SIGINT_handle);
for(i=0; i<10; i++)
{
printf("i=%d \n", i);
sleep(1);
}
return 0;
}
运行结果
捕捉终端信号:
未捕捉的情况
功能 | 向一个进程发送信号 |
头文件 | #include #include |
原型 | int kill(pid_t pid, int sig); |
参数 | pid:进程ID (process id) sig:要发送的信号 |
返回值 | 成功:返回0 失败:返回-1,并设置error。 |
备注 | 详细请查看man手册:man 2 kill |
测试代码2:使用kill()函数向signal程序发送SIGUSR1信号,a程序捕捉到之后打印一句话"receive signal %d\n",然后退出。
kill.c文件
#include
#include
#include
int main(int argc, char *argv[])
{
int pid, ret;
if(argc <2)
{
printf("arg error![./kill pid]\n");
return -1;
}
sscanf( argv[1], "%d", &pid);
ret = kill(pid, SIGUSR1);
if(ret != 0 )
{
perror("kill SIGUSR1 error");
return -1;
}
printf("send SIGUSR1 to [pid]%d successful!\n", pid);
return 0;
}
signal.c文件
#include
#include //sleep()
#include
#include //exit()
void SIGUSR1_handle(int sig_num)
{
printf("reveice signal %d \n", sig_num);
exit(0);
}
int main(int argc, char argv[])
{
int i;
signal(SIGUSR1, SIGUSR1_handle);
for(i=0; i<30; i++)
{
printf("i=%d \n", i);
sleep(1);
}
return 0;
}
运行结果:
三、sigprocmask()信号的阻塞
信号的阻塞:在阻塞过程中,信号不会丢失,会被挂起,等解开阻塞的时候再响应。
功能 | 阻塞信号,延迟响应 。(检查并更改阻塞的信号) |
头文件 | #include |
原型 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); |
参数 | how:对信号的操作(应用于信号集合中的所有函数) SIG_BLOCK:添加信号集合里面的信号进行阻塞(原本的设置上添加设置) set:要设置的信号集合 oldset:用来保存旧的信号集合设置 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | 在阻塞状态时,非实时信号可能会丢失 |
例子:对中断信号、退出信号进行阻塞。然后向sigprocmask程序发送中断信号(Ctrl+c)、退出信号(Ctrl+\)。那么sigprocmask程序收到信号后,不会立即响应,直到解除阻塞,才进行响应。其中,发送了两次中断信号(姑且命名为中断A,中断B),但是只响应了一次,是因为: 在某个时刻或阻塞时 接收到多个同一种非实时信号时,只会响应一次,其余丢弃,如果有一个已经开始响应了但是还没有结束,然后又来一个,那么这一个也会响应。
测试代码:
#include
#include
#include
void signal_handle(int sig_num)
{
printf("receive signal=%d\n", sig_num);
}
int main(void)
{
int i;
sigset_t sigset;
signal(SIGINT, signal_handle);
signal(SIGQUIT, signal_handle);
sigemptyset(&sigset);//清空集合
sigaddset(&sigset, SIGINT);//将中断信号添加到 阻塞集合中
sigaddset(&sigset, SIGQUIT);//将退出信号添加到 阻塞集合中
sigprocmask(SIG_BLOCK, &sigset, NULL);//对信号进行阻塞
for(i=0; i<10; i++)
{
printf("i=%d \n", i);
sleep(1);
}
sigprocmask(SIG_UNBLOCK, &sigset, NULL);//解开对信号的阻塞
printf("hello\n");
return 0;
}
运行结果:
四、带数据的信号的发送与处理:sigaction()与sigqueue()
功能 | 检查并改变信号动作(捕捉信号,指定处理动作) |
头文件 | #include |
原型 | int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); |
参数 | signum:捕捉的信号 act:信号动作结构体,用来登记对这个信号进行处理的动作。
struct sigaction //指定的信号过来将会被挂起,等函数结束后再执行
oldact:用来保存原来的设置,如果是NULL则不保存。 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | 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 */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since Linux 2.6.32) */ void *si_lower; /* Lower bound when address violation occurred (since Linux 3.19) */ void *si_upper; /* Upper bound when address violation occurred (since Linux 3.19) */ int si_pkey; /* Protection key on PTE that caused fault (since Linux 4.6) */ void *si_call_addr; /* Address of system call instruction (since Linux 3.5) */ int si_syscall; /* Number of attempted system call (since Linux 3.5) */ unsigned int si_arch; /* Architecture of attempted system call (since Linux 3.5) */ }; |
功能 | 将信号和数据发送到指定进程 |
头文件 | #include |
原型 | int sigqueue(pid_t pid, int sig, const union sigval value); |
参数 | pid:进程ID sig:要发送的信号 value:附加数据 union sigval //注:这是共用体、两者取其一 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | 跟sigaction结合使用 |
例子:sigqueue程序向sigaction程序发送SIGUSR1信号,并附带int型数据123,sigaction捕捉到信号后,打印出信号发送方的PID和附加的数据,然后退出。
测试代码:
sigaction.c文件
include
#include
#include
#include
void sigaction_handle(int signum, siginfo_t *info, void * ucontext)
{
printf("info.si_pid=%d\n", info->si_pid);//打印信号发送方的pid
printf("info.si_int=%d\n", info->si_int);//打印信号发送方的附加数据
exit(0);
}
int main(void)
{
int i;
struct sigaction act, oldact;
act.sa_flags = SA_SIGINFO;//使用带参数的信号处理函数
act.sa_sigaction = sigaction_handle;
//sigemptyset(&act.sa_mask);//清空原来集合
//sigfillset(&act.sa_mask);//将所有信号添加到集合
sigaction(SIGUSR1, &act, &oldact);//捕捉sigqueue发送的SIGUSR1信号
for(i=0; i<30; i++)
{
printf("i=%d\n", i);
sleep(1);
}
return 0;
}
sigqueue.c文件
#include
#include
#include //getpid()
#include
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("arg error\n");
return -1;
}
union sigval value;
value.sival_int = 123;
int sig_num, pid;
sscanf(argv[1], "%d", &pid);
sigqueue(pid, SIGUSR1, value);//向指定的pid发送SIGUSR1信号
printf("mypid=%d\n", getpid());//打印当前进程的ID号
return 0;
}
运行结果:
五、补充
1、查看进程相关信息
ps:查看进程状态 (一个瞬间的进程状态,静态)
top:“实时查看” ,按q退出 (实时动态显示)
pstree 树状查看 (可以看出所属子进程)
2、对某个进程发送信号
kill -s 指定发送一个信号 进程的pid
killall:指定一个应用程序名字去发送信号
kill -l 查看系统有什么信号
其中SIGKILL,SIGSTOP只能按照系统默认动作执行,不能捕捉,阻塞及忽略
前面31个是非实时信号:
1,每一个非实时信号对应一个信号名
2,每一个非实时信号基本上都会对应一个系统事件
3,信号有可能会丢失
后面的31个是实时信号:
1,信号不会丢失
2,优先级比非实时信号要高
2、信号发送:
1,自己发给自己: raise()函数
2,发送信号给其他进程:
kill -s 信号 进程号
killall -s 信号 进程名字
3、信号的继承
1,信号初始化的设置是会被子进程继承的
2,信号的阻塞设置是会被子进程继承的
3,被挂起的信号是不会被子进程继承的
4、信号处理函数里面应该注意的地方:
1,不应该在信号处理函数里面操作全局变量(如果要操作,记得加锁)
2,应该注意一下一些函数调用是否安全,可以查看man 7 signal
5、测试所有信号的响应优先级
实时信号34-64:数字大的优先响应;
从1号信号开始发送
测试代码:
kill.c文件
#include
#include
int main(int argc, const char *argv[])
{
int signum, pid;
if(argc < 2)
{
printf("arg error\n");
return -1;
}
sscanf(argv[1], "%d", &pid);
//向指定的进程发送所有的信号(不发送SIGKILL、SIGSTOP)
for(signum=1; signum<=64; signum++)
{
//系统没有32、33编号的信号,避免程序被kill掉或停止,不发送SIGKILL、SIGSTOP
if(signum == 33 || signum == 32 || signum == SIGKILL || signum == SIGSTOP)
continue;
kill(pid, signum );
}
printf("send signal over\n");
return 0;
}
signal.c文件
#include
#include
#include
void signal_handle(int signum)
{
printf("signal_handle signum=%d\n", signum);
}
int main(void)
{
int signum, i;
struct sigaction act, oldact;
//捕捉所有的信号
for(signum = 1; signum<65; signum++)
{
if(signum == 33 || signum == 32)
continue;
signal(signum, signal_handle);
}
printf("wait signal\n");
//延时30秒,等待信号处理完毕
for(i=0; i<30; i++)
{
sleep(1);
}
return 0;
}
运行结果:
signal_handle signum=64
signal_handle signum=63
signal_handle signum=62
signal_handle signum=61
signal_handle signum=60
signal_handle signum=59
signal_handle signum=58
signal_handle signum=57
signal_handle signum=56
signal_handle signum=55
signal_handle signum=54
signal_handle signum=53
signal_handle signum=52
signal_handle signum=51
signal_handle signum=50
signal_handle signum=49
signal_handle signum=48
signal_handle signum=47
signal_handle signum=46
signal_handle signum=45
signal_handle signum=44
signal_handle signum=43
signal_handle signum=42
signal_handle signum=41
signal_handle signum=40
signal_handle signum=39
signal_handle signum=38
signal_handle signum=37
signal_handle signum=36
signal_handle signum=35
signal_handle signum=34
signal_handle signum=30
signal_handle signum=29
signal_handle signum=28
signal_handle signum=27
signal_handle signum=26
signal_handle signum=25
signal_handle signum=24
signal_handle signum=23
signal_handle signum=22
signal_handle signum=21
signal_handle signum=20
signal_handle signum=17
signal_handle signum=16
signal_handle signum=15
signal_handle signum=14
signal_handle signum=13
signal_handle signum=12
signal_handle signum=10
signal_handle signum=6
signal_handle signum=3
signal_handle signum=2
signal_handle signum=1
signal_handle signum=31
signal_handle signum=11
signal_handle signum=8
signal_handle signum=7
signal_handle signum=5
signal_handle signum=4
================================================================================================
从64号开始发送
运行结果:
signal_handle signum=64
signal_handle signum=63
signal_handle signum=62
signal_handle signum=61
signal_handle signum=60
signal_handle signum=59
signal_handle signum=58
signal_handle signum=57
signal_handle signum=56
signal_handle signum=55
signal_handle signum=54
signal_handle signum=53
signal_handle signum=52
signal_handle signum=51
signal_handle signum=50
signal_handle signum=49
signal_handle signum=48
signal_handle signum=47
signal_handle signum=46
signal_handle signum=45
signal_handle signum=44
signal_handle signum=43
signal_handle signum=42
signal_handle signum=41
signal_handle signum=40
signal_handle signum=39
signal_handle signum=38
signal_handle signum=37
signal_handle signum=36
signal_handle signum=35
signal_handle signum=34
signal_handle signum=30
signal_handle signum=29
signal_handle signum=28
signal_handle signum=27
signal_handle signum=26
signal_handle signum=25
signal_handle signum=24
signal_handle signum=23
signal_handle signum=22
signal_handle signum=21
signal_handle signum=20
signal_handle signum=17
signal_handle signum=16
signal_handle signum=15
signal_handle signum=14
signal_handle signum=13
signal_handle signum=12
signal_handle signum=10
signal_handle signum=6
signal_handle signum=3
signal_handle signum=2
signal_handle signum=1
signal_handle signum=31
signal_handle signum=11
signal_handle signum=8
signal_handle signum=7
signal_handle signum=5
signal_handle signum=4