zoukankan      html  css  js  c++  java
  • linux进程通信之信号

    本节主要学习信号和与信号相关的处理函数,兴许还会更新。

    http://blog.csdn.net/xiaoliangsky/article/details/40264151

    一 信号
    信号是UNIX和Linux系统响应某些条件而产生的一个事件。接收到该信号的进程会对应地採取一些行动。通常信号是由一个错误产生的。但它们还能够作为进程间通信或改动行为的一种方式。明白地由一个进程发送给还有一个进程。一个信号的产生叫生成。接收到一个信号叫捕获。
    二 信号的种类
    Signal         Description
    SIGABRT   由调用abort函数产生。进程非正常退出
    SIGALRM   用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
    SIGBUS    某种特定的硬件异常,通常由内存訪问引起
    SIGCANCEL 由Solaris Thread Library内部使用。通常不会使用
    SIGCHLD   进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
    SIGCONT   当被stop的进程恢复执行的时候,自己主动发送
    SIGEMT    和实现相关的硬件异常
    SIGFPE    数学相关的异常。如被0除,浮点溢出。等等
    SIGFREEZE Solaris专用。Hiberate或者Suspended时候发送
    SIGHUP    发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
    SIGILL    非法指令异常
    SIGINFO   BSD signal。由Status Key产生,一般是CTRL+T。发送给全部Foreground Group的进程
    SIGINT    由Interrupt Key产生,一般是CTRL+C或者DELETE。发送给全部ForeGround Group的进程
    SIGIO     异步IO事件
    SIGIOT    实现相关的硬件异常,一般相应SIGABRT
    SIGKILL   无法处理和忽略。中止某个进程
    SIGLWP    由Solaris Thread Libray内部使用
    SIGPIPE   在reader中止之后写Pipe的时候发送
    SIGPOLL   当某个事件发送给Pollable Device的时候发送
    SIGPROF   Setitimer指定的Profiling Interval Timer所产生
    SIGPWR    和系统相关。和UPS相关。
    SIGQUIT   输入Quit Key的时候(CTRL+/)发送给全部Foreground Group的进程
    SIGSEGV   非法内存訪问
    SIGSTKFLT Linux专用,数学协处理器的栈异常
    SIGSTOP   中止进程。

    无法处理和忽略。
    SIGSYS    非法系统调用
    SIGTERM   请求中止进程,kill命令缺省发送
    SIGTHAW   Solaris专用,从Suspend恢复时候发送
    SIGTRAP   实现相关的硬件异常。通常是调试异常
    SIGTSTP   Suspend Key。通常是Ctrl+Z。发送给全部Foreground Group的进程
    SIGTTIN   当Background Group的进程尝试读取Terminal的时候发送
    SIGTTOU   当Background Group的进程尝试写Terminal的时候发送
    SIGURG    当out-of-band data接收的时候可能发送
    SIGUSR1   用户自己定义signal 1
    SIGUSR2   用户自己定义signal 2
    SIGVTALRM setitimer函数设置的Virtual Interval Timer超时的时候
    SIGWAITING Solaris Thread Library内部实现专用
    SIGWINCH  当Terminal的窗体大小改变的时候。发送给Foreground Group的全部进程
    SIGXCPU   当CPU时间限制超时的时候
    SIGXFSZ   进程超过文件限制大小
    SIGXRES   Solaris专用,进程超过资源限制的时候发送
    三 信号相关的函数
    1 signal函数
    void (*signal(int sig, void (*func)(int)))(int);
    signal是一个带有sig和func两个參数的函数,func是一个类型为void (*)(int)的函数指针。该函数返回一个与func同样类型的指针。指向先前指定信号处理函数的函数指针。准备捕获的信号的參数由sig给出,接收到的指定信号后要调用的函数由參数func给出。事实上这个函数的使用是相当简单的,通过以下的样例就能够知道。注意信号处理函数的原型必须为void func(int),或者是以下的特殊值:
        SIG_IGN:忽略信号
        SIG_DFL:恢复信号的默认行为
    这个函数经常能够用以下要将的sigaction函数替代。

    2 sigaction
    int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
    该函数与signal函数一样,用于设置与信号sig关联的动作,而oact假设不是空指针的话。就用它来保存原先对该信号的动作的位置,act则用于设置指定信号的动作。

    參数结构体sigaction定义例如以下:
    struct sigaction{
        void       (*sa_handler)(int);
        void       (*sa_sigaction)(int, siginfo_t*, void*);
        sigset_t   sa_mask;
        int        sa_flags;
        void       (*sa_restorer)(void);
    };

    信号集sigset_t结构体:
    typedef struct
    {
        unsigned long sig[_NSIG_WORDS];
    }sigset_t;

    信号处理函数能够採用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。究竟採用哪个要看sa_flags中是否设置了SA_SIGINFO位。假设设置了就採用void (*sa_sigaction)(int, siginfo_t *, void *)。此时能够向处理函数发送附加信息;默认情况下採用void (*sa_handler)(int),此时仅仅能向处理函数发送信号的数值。


    sa_handler    此參数和signal()的參数handler同样。代表新的信号处理函数,其它意义请參考signal()。
    sa_mask       用来设置在处理该信号时临时将sa_mask指定的信号集搁置。


    sa_restorer   此參数没有使用。
    sa_flags      用来设置信号处理的其它相关操作,下列的数值可用:
    sa_flags      还能够设置其它标志:
    SA_RESETHAND  当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    SA_RESTART    假设信号中断了进程的某个系统调用,则系统自己主动启动该系统调用
    SA_NODEFER    普通情况下, 当信号处理函数执行时。内核将堵塞该给定信号。可是假设设置了 SA_NODEFER标记, 那么在该信号处理函数执行时,内核将不会堵塞该信号。

    返回值
    运行成功则返回0,假设有错误则返回-1。
    错误代码
    EINVAL 參数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号
    EFAULT 參数act,oldact指针地址无法存取。
    EINTR  此调用被中断

    以下一个简单的样例,程序里面的sigemptyset、kill函数会在后面讲
    action.c实例代码:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>
    
    void sig_handler(int sig)
    {
        switch(sig)
        {
        case 23:
             printf("child : the signo is 23, hehe
    ");
             return;
        case 22:
             printf("father : hello wordl 22!
    ");
             return;
        }
    }
    
    int main()
    {
        struct sigaction act, oact;
        int    status;
    
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        act.sa_handler   = sig_handler;
        sigaction(23, &act, &oact);
        sigaction(22, &act, &oact);
    
        pid_t pid, ppid;
    
        if (!(pid = fork()))
        {
            printf("child begin
    ");
    
            kill(getppid(), 22);
            sleep(3);
    
            printf("child over
    ");
        }
        else
        {
            printf("father begin
    ");
    
            kill(getpid(), 23);
    
            wait(&status);
    
            if (WIFSIGNALED(status))
            {
                printf("child process receive siganl %d
    ", WTERMSIG(status));
            }
    
            printf("father over
    ");
        }
    
        return 0;
    }

    3 sigaddset sigdelset
    int sigaddset(sigset_t *set, int signum);
    sigaddset()用来将參数signum 代表的信号增加至參数set 信号集里。
    返回值
    运行成功则返回0,假设有错误则返回-1。
    错误代码
    EFAULT 參数set指针地址无法存取
    EINVAL 參数signum非合法的信号编号

    int sigdelset(sigset_t *set, int signum)
    sigdelset()用来将參数signum代表的信号从參数set信号集里删除。
    返回值
    运行成功则返回0
    假设有错误则返回-1。


    错误代码
    EFAULT 參数set指针地址无法存取
    EINVAL 參数signum非合法的信号编号

    4 sigemptyset sigfillset
    int sigemptyset(sigset_t *set);
    sigemptyset()用来将參数set信号集初始化并清空。


    返回值
    运行成功返回0;
    失败返回-1。
    错误代码
    EFAULT 參数set指针地址无法存取

    int sigfillset(sigset_t * set);
    sigfillset()用来将參数set信号集初始化。然后把全部的信号增加到此信号集里。
    返回值
    运行成功返回0。
    失败返回-1。
    错误代码
    EFAULT 參数set指针地址无法存取

    5 sigismember
    int sigismember(const sigset_t *set,int signum);
    sigismember()用来測试參数signum 代表的信号是否已增加至參数set信号集里。假设信号集里已有该信号则返回1,否则返回0。
    返回值
    信号集已有该信号则返回1。
    没有则返回0;
    假设有错误则返回-1。


    错误代码
    EFAULT 參数set指针地址无法存取
    EINVAL 參数signum 非合法的信号编号

    6 sigpending
    int sigpending(sigset_t *set);
    sigpending()会将被搁置的信号集合由參数set指针返回
    返回值执
    行成功则返回0。
    假设有错误则返回-1。


    错误代码
    EFAULT 參数set指针地址无法存取
    EINTR  此调用被中断。

    7 sigprocmask
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    一个进程的信号屏蔽字规定了当前堵塞而不能传送给该进程的信号集。sigprocmask()能够用来检測或改变眼下的信号屏蔽字,其操作參数how来决定,假设參数oldset不是NULL指针,那么当前的信号屏蔽字会由此指针返回。假设set是一个非空指针,则參数how指示怎样改动当前信号屏蔽字。每一个进程都有一个用来描写叙述哪些信号递送到进程时将被堵塞的信号集。该信号集中的全部信号在传送到进程后都将被堵塞。
    參数how:
    SIG_BLOCK:该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集,set包括了我们希望堵塞的附件信号。


    SIG_UNBLOCK:该进程新的信号集屏蔽字是其当前信号屏蔽字和set所指向信号集的补集的交集。

    set包括了我们希望解除堵塞的信号。
    SIG_SETMASK:该进程新的信号屏蔽是set指向的值。


    注意:
    1)除SIG_SETMASK外,假设set是个空指针。则不改变该进程的信号屏蔽字,这时how的值也没有意义。


    2)SIG_SETMASK与set空指针结合使用。即清空全部屏蔽的信号。
    返回值:
    运行成功返回0;
    失败返回-1。
    错误码
    EFAULT:參数set、oldsset指针地址无法获取
    EINTR:此调用被中断

    以下是一个測试样例,測试被屏蔽的信号:
    sigprocmask.c的实例代码:

    http://blog.csdn.net/xiaoliangsky/article/details/40264151

    ////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    
    void sig_handler(int signum)
    {
    	switch(signum)
        {
        case SIGINT:
             printf("the signo is SIGINT %d received, hehe
    ", SIGINT);
             return;
        case SIGCHLD:
             printf("the signo is SIGCHLD %d received, hihi!
    ", SIGCHLD);
             return;
    	case SIGUSR1:
             printf("the signo is SIGUSR1 %d received, hihi!
    ", SIGUSR1);
             return;
    	case SIGIO:
             printf("the signo is SIGIO %d received, hihi!
    ", SIGIO);
             return;
    	case SIGALRM:
    		 printf("the signo is SIGALRM %d received, hihi!
    ", SIGALRM);
             return;
    	default:
    		 printf("the signo %d is not processed
    ", signum);
    		 return;
        }
    }
    
    int main()
    {
    	int              i;
    	int              status;
    	pid_t            pid;
    	sigset_t         set;
    	sigset_t         oldset;
    	struct sigaction act;
    	struct sigaction oldact;
    
    	act.sa_flags   = 0;
    	act.sa_handler = sig_handler;
    	sigemptyset(&act.sa_mask);
    	sigaction(SIGINT, &act, &oldact);
    	sigaction(SIGCHLD, &act, &oldact);
    	sigaction(SIGUSR1, &act, &oldact);
    	sigaction(SIGALRM, &act, &oldact);
    	sigaction(SIGIO, &act, &oldact);
    
    	sigemptyset(&set);
    	sigaddset(&set, SIGINT);
    	sigaddset(&set, SIGUSR1);
    
    	if ((pid = fork()) < 0)
    	{
    		fprintf(stdout, "fork error
    ");
    		exit(-1);
    	}
    	else if (pid > 0)
    	{
    		if (sigprocmask(SIG_BLOCK, &set, &oldset) < 0)//屏蔽信SINGINT、SIGUSR1
    		{
    			fprintf(stdout, "sigprocmask error
    ");
    			kill(pid, SIGKILL);
    			exit(-1);
    		}
    /*
    		waitpid(pid, &status, 0);
    
    		if (WIFSIGNALED(status))
            {
                printf("child process receive siganl %d
    ", WTERMSIG(status));
            }
    */
    		pause();//接收SIGIO?
    		pause();//接收SIGALRM?
    		pause();//接收SIGCHLD?

    //pause(); //pause(); } else { sleep(1); kill(getppid(), SIGINT);//信号被屏蔽 sleep(1); kill(getppid(), SIGIO); sleep(1); kill(getppid(), SIGUSR1);//信号被屏蔽 sleep(1); kill(getppid(), SIGALRM); sleep(2); //子进程退出会发送一个SIGCHLD信号 } return 0; }




    8 sigsuspend
    int sigsuspend(const sigset_t *mask);
    函数sigsuspend将进程的信号屏蔽码设置为mask,然后与pause()函数一样等待信号的发生并运行完信号处理函数。信号处理函数运行完后再把进程的信号屏蔽码设置为原来的屏蔽字,然后sigsuspend函数才返回。
    返回值
    sigsuspend总是返回-1
    以下是一个sigsuspend的样例:
    sigsuspend的实例代码:

    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    
    void sig_handler(int sig)
    {
        if (sig == SIGINT) 
    		printf("SIGINT sig
    ");
        else if (sig == SIGQUIT)
    		printf("SIGQUIT sig
    ");
        else if (sig == SIGUSR1)
    		printf("SIGUSR1 sig
    ");
        else if (sig == SIGUSR2)
    		printf("SIGUSR2 sig
    ");
        else 
    		printf("SIGCHLD sig
    ");
    }
    
    int main()
    {
        int                i;
        sigset_t           new;
        sigset_t           old;
        sigset_t           wait;
        struct sigaction   act;
    
        act.sa_handler = sig_handler;
        act.sa_flags   = 0;
    
        sigemptyset(&act.sa_mask);
        sigaction(SIGINT, &act, 0);
        sigaction(SIGQUIT, &act, 0);
        sigaction(SIGUSR1, &act, 0);
        sigaction(SIGUSR2, &act, 0);
        sigaction(SIGCHLD, &act, 0);
    
        sigemptyset(&new);
        sigaddset(&new, SIGINT);
    
        sigemptyset(&wait);
        sigaddset(&wait, SIGUSR1);
        sigaddset(&wait, SIGUSR2);
    
        sigprocmask(SIG_BLOCK, &new, &old);
    
        for (i=0; i < 90; ++i)
        {
            printf("%d
    ", i);
    	sleep(1);
        }
    
        sigsuspend(&wait);
    
        printf("After sigsuspend
    ");
    
        printf("yyyy
    ");
        if (-1 == sigprocmask(SIG_SETMASK, &old, NULL)) 
        {
            perror("sigprocmask");
            exit(-1);
        } 
        for (i=0; i < 30; ++i)
        {
    	printf("%d
    ", i);
    	sleep(1);
        }
        printf("xxxxx
    ");
    
        return 0;
    }


  • 相关阅读:
    Linux日志管理系统rsyslog
    Linux访问权限控制及时间同步实践
    Linux系统自动化安装之cobbler实现
    【转】java取整和java四舍五入方法
    The web application [ ] registered the JDBC driver [net.sourceforge.jtds.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver
    Python学习笔记之函数式编程
    Java去重字符串的两种方法以及java中冒号的使用
    Python学习之字符串格式化
    Python学习之文件操作
    Python学习笔记之爬虫
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5101377.html
Copyright © 2011-2022 走看看