您的位置:首页 > 运维架构 > Linux

Linux信号学习笔记

2016-10-17 19:57 771 查看
信号是事件发生时对进程的通知机制,也称软件中断。信号通常都源于内核,引发内核产生信号的事件类型如下:内核产生信号各类事件:  1.硬件发生异常  2.用户键入能够产生信号的中断特殊字符 eg:Ctrl+c产生SIGINT信号 3.发生了软件事件。eg:进程的某个子进程退出会发出SIGCHLD信号信号分类:1.标准信号,(不可靠信号)用于内核向进程通知事件,编号范围1~31  unix早期信号存在一些问题,主要是:每次处理完信号后会对信号的响应设置为默认操作,某些情况下会导致错误处理;如果用户不希望这样的操作就要在信号处理函数结尾再一次调用signal()重新安装该信号。进程可能对信号做出错误的反应,以及信号可能丢失。不会对信号进行排队,只保留一个,后续信号会被丢弃掉。linux支持不可靠信号,并做了改进,在调用完处理函数时可以不再调用安装函数,因为linux不可靠信号主要指的是信号会丢失。2.实时信号 (可靠信号)支持排队不会丢失新的信号发送函数sigqueue()和信号安装函数sigaction()前32种信号已经有预定义值,有确定的用途和含义,后32个信号表示可靠信号,可以保证发送多个实时信号都被接收,可用于应用进程。查看信号:进程对到达信号的默认操作:忽略信号终止进程,通常是异常终止,而不是调用exit()的正常终止产生核心转储文件,同时进程终止。 core dump包含对进程虚拟内存的镜像,可以加载到调试器分析终止状态。 停止进程,暂停进程执行于暂停之后再度恢复进程执行。进程执行系统调用时,在系统调用完成后退出内核时,都会顺便查看信箱里的信息。如果有信号,进程会执行对应该信号的操作,程序可以对信号进行处置(disposition)设置,自定义处理方式:采取默认行为忽略信号执行信号处理器程序常用信号类型和默认行为:SIGINT 用户键入终端中断字符 通常为control-c时,中断驱动发送该信号给前台进程组,默认行为终止进程。SIGKILL 必杀信号,这个信号不能被阻塞或略或者捕捉,总能终止进程。   kill -9 pidSIGCHLD/SIGCLG  子进程终止内核向父进程发送该信号,默认处理忽略,如果父进程需要了解子进程状态需要捕捉该信号。SIGPIPE 当某一进程试图向管道、FIFO或者套接字写入信息时,如果设备没有相应的读进程,系统将产生该信号。通常是因为读进程关闭了作为IPC通道的文件描述符导致。eg:在linux下写socket的程序的时候,如果尝试send到一个disconnectedsocket上,就会让底层抛出一个SIGPIPE信号。这个信号的缺省处理方法是退出进程,大多数时候这都不是我们期望的。因此我们需要重载这个信号的处理方法。调用以下代码,即可安全的屏蔽SIGPIPE:signal (SIGPIPE, SIG_IGN);系统定义的的三种信号处理方法:(1)SIG_DFL信号专用的默认动作:  (a)如果默认动作是暂停线程,则该线程的执行被暂时挂起。当线程暂停期间,发送给线程的任何附加信号都不交付,直到该线程开始执行,但是SIGKILL除外。  (b)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)。(2)SIG_IGN忽略信号  (a)该信号的交付对线程没有影响  (b)系统不允许把SIGKILL或SIGTOP信号的动作设置为SIG_DFL(3)SIG_ERR  unix可以使用signal()和sigaction()改变信号的处置signal和sigaction主要区别:sigaction更具有移植性,signal不同unix实现有差异sigaction可以实现更精确的控制故应首选sigactionsignal 函数原型:
 
#include <signal.h>
 
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum :要处理的信号标号handler:信号处理函数,处理函数一般形式如下:
void handler(int signum)
{
}
signal在调用handler之前先把信号的handler指针恢复;sigaction调用之后不会恢复handler指针,直到再次调用sigaction修改handler指针。即signal函数每次设置具体的信号处理函数(非SIG_IGN)只能生效一次,每次在进程响应处理信号时,随即将信号处理函数恢复为默认处理方式.所以如果想多次相同方式处理某个信号,通常的做法是,在响应函数开始,再次调用signal为不同信号建立同一处理函数:
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
 
#define errExit(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
} while(0)
 
static void sigHandler(int sig)
{
static int count = 0;
 
/* UNSAFE: This handler uses non-async-signal-safe functions
(printf(), exit(); see Section 21.1.2) */
 
if (sig == SIGINT) {
count++;
printf("Caught SIGINT (%d)\n", count);
return;                 /* Resume execution at point of interruption */
}
 
/* Must be SIGQUIT - print a message and terminate the process */
 
printf("Caught SIGQUIT - that's all folks!\n");
exit(EXIT_SUCCESS);
}
 
int
main(int argc, char *argv[])
{
/* Establish same handler for SIGINT and SIGQUIT */
 
if (signal(SIGINT, sigHandler) == SIG_ERR)
errExit("signal");
if (signal(SIGQUIT, sigHandler) == SIG_ERR)
errExit("signal");
 
for (;;)                    /* Loop forever, waiting for signals */
pause();                /* Block until a signal is caught */
}
输出:发送信号:kill()函数原型:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数pid的4种情况:pid>0,发送给指定pid进程pid=0,发送给与调用进程同组的每个进程包括自己pid<-1会发给组ID等于pid绝对值的进程组内所有下属进程发信号 pid=-1,调用进程有权将信号发往每个目标进程,出去init,通常也称广播信号调用失败返回-1,errno = ESRCH 没有该进程其他发送信号方式
int raise(int sig);        //等价于 kill(getpid(),sig)
int killpg(int pgrp, int sig); //等价于 kill(-pgrp,sig)
显示信号描述
char *strsignal(int sig);
void psignal(int sig,const char *msg);检查进程是否存在:利用kill()系统调用发送空信号0,调用失败errno = ESRCH则不存在,errno=EPERM则存在wait系统调用信号量和排他锁fifo之类的ipc通道stat  /proc/PID接口信号集:
#include <signal.h>
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 sigisemptyset(const sigset_t *set);
int sigorset(sigset_t *dest, const sigset_t *left,const sigset_t *right);
int sigandset(sigset_t *dest, const sigset_t *left,const sigset_t *right);  //1 空,0非空
改变信号处置:sigaction()
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
sigaction结构定义如下:
struct sigaction {
void     (*sa_handler)(int);  //信号捕捉的函数地址
void     (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t   sa_mask;  //在执行handler期间可以屏蔽其他信号
int        sa_flags;
void     (*sa_restorer)(void);
       };
带参数信号处理示例:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
union sigval mysigval;
int i;
int sig;
pid_t pid;
char data[10];
memset(data,0,sizeof(data));
for(i=0;i < 5;i++)
data[i]='2';
mysigval.sival_ptr=data;
sig=atoi(argv[1]);
pid=getpid();
sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;//三参数信号处理函数
act.sa_flags=SA_SIGINFO;//信息传递开关,允许传递参数信息给new_op
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
sigqueue(pid,sig,mysigval);//向本进程发送信号,并传递附加信息
}
}
void new_op(int signum,siginfo_t *info,void *myact)//三参数信号处理函数的实现
{
int i;
for(i=0;i<10;i++)
{
printf("%c ",(*( (char*)((*info).si_ptr)+i)));
}
printf("handle signal %d over\n",signum);
}
参考 Linux_Unix系统编程手册
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux