进程间通信——信号
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
可靠信号与不可靠信号
信号: man 7 signal -》信号的英文帮助文档,信号在实现时,通常是一个整数。
Standard signals:
Signal Value Action Comment
────────────────────────────────────────
SIGHUP 1 Term 控制终端检测到挂起操作或控制进程死亡产生该信号
SIGINT 2 Term CTRL+C
SIGQUIT 3 Core CTRL+Z
SIGILL 4 Core 遇到非法指令时产生,Illegal Instruction
SIGABRT 6 Core 调用abort函数产生,Abort signal from abort(3)
SIGFPE 8 Core 浮点运算出错,Floating point exception
SIGKILL 9 Term Kill signal *不可忽略
SIGSEGV 11 Core 内存非法引用时产生,Invalid memory reference
SIGPIPE 13 Term 当pipe是非阻塞方式打开时,如果往管道里写数据时,没有进程读时,产生该信号。
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term 终止信号。Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign(忽略) 子进程停止或终止运行时产生。
SIGCONT 19,18,25 Cont 在停止状态继续运行时产生该信号Continue if stopped
SIGSTOP 17,19,23 Stop Stop process *不可忽略
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked,
or ignored.
-----------------------------------------------------------------------------------------------------------------
命令kill的使用
kill 信号发送命令
kill -数字 +PID --->向某个进程发送一个信号
kill -s 信号名称 +PID ---》向某个进程发送一个信号
可以通过 kill -l命令查看所有的信号
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 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
信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。
对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。
信号的实现步骤
1.创建信号
2.信号在进程中注册
3.信号的执行
4.信号注销
信号相关函数
1.signal() ---》信号捕抓函数
头文件:
#include <signal.h>
函数原型:
void(*sighandler_t)(int);-》指针 ---》 void func(int arg) ->信号处理函数,该函数必须为 void func(int arg)类型
sighandler_t signal(int signum, sighandler_t handler);
参数一:捕抓的信号值
参数二:信号的处理方式
SIG_IGN, -》忽略改信号
SIG_DFL -》使用默认方式处理
自定义处理
返回值:失败返回SIG_ERR ,成功返回信号值
阻塞该信号(需要使用第三方函数处理)
练习:signal函数的使用
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
//信号处理函数
void func(int arg)
{
printf("I GET signal arg=%d\n",arg);
}
int main()
{
//捕抓信号
//signal(SIGINT,SIG_DFL); //使用默认方式处理
//signal(SIGINT,SIG_IGN); //忽略该信号
signal(SIGTERM,func); //使用自定义方式处理
printf("wati signal %d\n",getpid());
int i=0;
while(1)
{
if(i==5)
{
//发送信号
//kill(getpid(),SIGTERM);
raise(SIGTERM);
break;
}
printf("i=%d\n",i++);
sleep(1);
}
//pause();
printf("signal run\n");
return 0;
}
-----------------------------------------------------------------------------------------------------------------
2. pause()---》等待信号函数
头文件:
#include <unistd.h>
函数原型:
int pause(void);
返回值:失败返回-1;
------------------------------------------------------------------------------------------------------------------
3. kill()---》信号发送
头文件:
#include <sys/types.h>
#include <signal.h>
函数原型:
int kill(pid_t pid, int sig);
参数一:指定信号的接受者
pid > 0 接受者的PID号
pid == 0 发给 与进程同组的所有进程
pid == -1 发给 拥有该进程权限的 所有进程
pid < 0 发给 与组id 和pid绝对值相同的进程
参数二:信号类型
返回值:成功返回0,失败返回-1
----------------------------------------------------------------------------------
4. raise()---》给自己发送信号
头文件:
#include <signal.h>
函数原型:
int raise(int sig); 《=》kill(getpid(),int sig);
----------------------------------------------------------------------------------
5. alarm()---》时钟信号
头文件:
#include <unistd.h>
函数原型:
unsigned int alarm(unsigned int seconds);
设置:一个时钟,以秒为单位,当时钟来时,会触发SIGALRM信号
练习:alarm函数的使用
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int i=0;
void func(int arg)
{
printf("sig=%d,%d\n",arg,i++);
}
int main()
{
//产生一个时钟
while(1)
{
alarm(2);
//捕抓时钟信号
signal(SIGALRM,func);
pause();
}
return 0;
}
运行后每隔一秒时钟信号触发
------------------------------------------------------------------------------
6. sigprocmask()---》信号阻塞
头文件:
#include <signal.h>
函数原型:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
返回值:成功返回0,失败返回-1
参数一:
SIG_BLOCK 阻塞
SIG_UNBLOCK 不阻塞
SIG_SETMASK 阻塞
参数二:阻塞集合 -》该集合中存放要阻塞的信号 ***重点
参数三:原来的阻塞集合
注意:阻塞集合会把所有信号都保存起来,当解除阻塞后才去执行相应的信号操作。
练习:信号阻塞
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void func(int arg)
{
printf("sig****************=%d\n",arg);
}
int main()
{
//阻塞信号集
//1.设置信号集中要阻塞的信号
sigset_t set;//定义一个信号集
sigemptyset(&set); //清空信号集
sigaddset(&set,SIGALRM);//添加要阻塞的信号
//开启阻塞集合
sigprocmask(SIG_BLOCK ,&set,NULL);
//捕抓一个信号
signal(SIGALRM,func);
int i = 0;
for(i=0;i<10;i++)
{
printf("i=%d,pid=%d\n",i,getpid());
sleep(1);
}
//关闭阻塞集合
sigprocmask(SIG_UNBLOCK,&set,NULL);
pause();
return 0;
}
运行程序,10s后阻塞等待14号信号,在另一个终端用14号信号杀掉进程。
---------------------------------------------------------------------------
7. sigaction()---》信号传参:
头文件:
#include <signal.h>
函数原型:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数一:要捕抓的信号
参数二:处理信号结构体
struct sigaction {
void (*sa_handler)(int); //信号处理函数不带参
void (*sa_sigaction)(int, siginfo_t *, void *);//信号处理函数带参
sigset_t sa_mask;//阻塞信号集
int sa_flags;//标志
void (*sa_restorer)(void);//不常用
};
参数三:原有的信号处理参数
处理函数中的结构体:
siginfo_t {
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
......................
}
练习:sigaction函数的使用
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void func(int arg, siginfo_t *info, void *arc)
{
printf("GET SIGNAL info%d\n",info->si_int);
}
int main()
{
//捕抓信号
struct sigaction act;
bzero(&act,sizeof(act));
act.sa_sigaction = func;
act.sa_flags = SA_SIGINFO;//把原来的信号处理函数替换掉
sigaction(SIGTERM,&act,NULL);
//signal(SIGTERM,func);
//等待信号
printf("getpid=%d\n",getpid());
pause();
}
运行程序后,在另一个终端kill掉这个线程,可以捕获该信号。
---------------------------------------------------------------------------
8. sigqueue()---》带参的发送函数:
头文件:
#include <signal.h>
函数原型:
int sigqueue(pid_t pid, int sig, const union sigval value);
参数一:要发送的PID号
参数二:要发送的信号
参数三:要传输的数据
union sigval {
int sival_int;
void *sival_ptr;
};
练习:sigqueue函数的使用
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
union sigval value;
value.sival_int = 2222;
//发送带参信号
sigqueue(atoi(argv[1]),SIGTERM,value);
}
--------------------------------------------------------------------------