Linux中的信号
每个信号都有以SIG开头的名称,并定义为唯一的正整数。在 Shell 命令行提示符 下,输入kill -l命令,将显示所有信号的信号值和相应的信号名,类似如下所示:
几个比较常用的信号
几个比较常用的信号
SIGIGN:忽略改信号
SIGDEF:采用系统默认方式处理信号
SIGCHLD:在一个进程终止或者停止时,将该信号发送给其父进程,父进程的wait函数通常用来捕捉这个信号
SIGINT:当用户按中断键(delete/ctrl+c)时将产生这个信号
SIGKILL:此信号可以用于杀死一个进程
SIGSTOP:这是个作业控制信号,用于停止一个进程 这个信号和SIGKILL是仅有的两个不能被捕获或忽略的信号
SYSUSR1/2:用户定义的信号,用于应用程序
这些信号具体的作用下面列举一些:
信号的基本操作
- 发送信号
- 安装中断
- 递送信号
- 捕获信号
- 屏蔽信号
如何发起异步操作
kill
kill 命令 eg:kill -signum pid
int kill(pid,signum);
pid>0 发给pid进程
pid=0 发给当前进程组的所有进程
pid=-1 发送给所有进程
pid<0 发送给|PID|所对应的组上
再发起的时候如何获得pid呢?
pid = getpid(); //获得进程的pid号
gpid = getgpid(); //获得进程组的pid号
如何给自己发送信号呢?
raise
自举信号,会给自己发送一个信号
int raise(int sig);
int kill(getpid(),signum);
安装和捕获信号
- 忽略信号
- 自定义捕捉函数
- 系统默认信号函数
安装信号
第一种安装方式
signal
signal(signum,handler)
kill -SIGUSR1 pid
SIGKILL SIGSTOP 不能够被安装
通过signal安装信号只能传递一个参数,这个参数就是信号的值
此处附上一个demo:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sig_usr(int sig);
int main(int argc,char *argv[])
{
int i = 0;
if(signal(SIGUSR1,sig_usr) == SIG_ERR)
printf("Cannot catch SIGUSR1\n");
if (signal(SIGUSR2,sig_usr) == SIG_ERR)
printf("Cannot catch SIGUSR2\n");
//signal(SIGINIT,sig_usr)
while(1) {
printf("%2d\n", i);
pause();
/* pause until signal handler
has processed signal */
i++;
}
return 0;
}
void sig_usr(int sig)
{
if (sig == SIGUSR1)
printf("Received SIGUSR1\n");
else if (sig == SIGUSR2)
printf("Received SIGUSR2\n");
else
printf("Undeclared signal %d\n", sig);
}
运行效果及如何触发
第二种安装方式
sigaction对象
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);
};
SA_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 */
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) */
}
传递参数
设置参数通过 struct siginfo_t 中的sigval_t si_value来设置
然后通过sigqueue来发送信号
int sigqueue(pid_t pid, int sig, const union sigval val)
调用成功返回 0;否则,返回 -1。
sigval_t
union sigval {
int sival_int; //传递int类型的数据
void *sival_ptr; //传递任意类型的数据
};
demo
- 发送端 sigqueue
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("arguments error!");
exit(0);
}
pid_t pid=atoi(argv[1]);//将进程号转化为整数
union sigval v;
v.sival_int=100;
//这儿只是利用SIGINT来发送数据,
//任何信号都行,只需要发送端与接收端规定一致即可
sigqueue(pid,SIGINT,v);
return 0;
}
- 接收端
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<string.h>
//#include<sys/siginfo.h>
void handler(int,siginfo_t *,void *);
int main(void)
{
struct sigaction act;
act.sa_sigaction=handler;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
if(sigaction(SIGINT,&act,NULL)<0)
{
printf("error");
exit(0);
}
for(;;)
pause();
return 0;
}
void handler(int sig,siginfo_t * info,void *ctx)
{
printf("recv a sid=%d data=%d data=%d\n",sig,info->si_value.sival_int,info->si_int);
}
忽略信号
signal(signum,SIG_IGN)
忽略就是不对这个或者这些信号进行任何操作
signal(SIGINT,SIG_IGN) //忽略SIGINT信号
signal(SIGPIPE,SIG_IGN) //忽略SIGPIPE信号
信号集合
- sigset_t sa_mas
int sigemptyset(sigset_t *set); //清空信号集合
int sigfillset(sigset_t *set); //填满信号集合
int sigaddset(sigset_t *set, int signum); //向信号集合中添加一个信号
int sigdelset(sigset_t *set, int signum); //删除信号集合中的摸个信号
int sigismember(const sigset_t *set, int signum);
*设置信号屏蔽集合
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);//批量进行屏蔽集合的操作
SIG_BLOCK 设置屏蔽信号
SIG_UNBLOCK 设置解除屏蔽信号
SIG_SETMASK 重新设置系统信号设置
未决信号
sigpending(sigset)
我们之前设置了信号屏蔽集合,如果我们发送的信号是信号屏蔽集合中的信号,那么这个信号就会被放到未决信号的队列中,而且只有一次放入未决信号的机会,意思就是如果你发送了10次,那么这个信号只会有一次被加入到未决信号的队列中
在取消信号集合的阻塞的时候,这时候未决信号就发挥了作用,他会立马依次执行信号队列中被取消屏蔽的信号函数
sigsuspend 函数 等待信号
int sigsuspend(const sigset_t *mask);
sigsuspend函数作用 :如果在等待信号发生时希望去休眠,则使用sigsuspend函数是非常合适的
demo
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
void handler(int sig)
{
printf("get a sig ,num is: %d\n",sig);
}
void print_sig(sigset_t *p)
{
int i=1;
for(;i<32;i++)
{
if(sigismember(p,i))
{
printf("1");
}else
{
printf("0");
}
}
printf("\n");
}
int main()
{
signal(2,handler);
sigset_t s,p,o;
sigemptyset(&s); //清空信号集合S
sigemptyset(&o); //清空信号集合o
sigaddset(&s,SIGINT); //把SIGINT信号加入到信号集合S中
sigprocmask(SIG_SETMASK,&s,&o); //把信号集合s中所有的信号设置成阻塞 信号集合o做备份
int count=0;
while(1)
{
sigemptyset(&p); //清空信号集合p
sigpending(&p); //获取一个未决信号组
print_sig(&p); //探测有哪几个个信号在未决信号中
sleep(1);
if(count++ ==10)
{
sigprocmask(SIG_SETMASK,&o,NULL); //还原信号集合o
printf("recover block\n");
sleep(3);
}
}
return 0;
}
在SIGINT在被阻塞之前 运行程序会探测到SIGINT在未决信号组中,当SIGINT被解除阻塞之后,未决信号立马执行。
最后关闭这个程序CTRL+c 肯定是不行了,可以使用kill -9 pid 来结束此进程