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\n");
         return;
    case 22:
         printf("father : hello wordl 22!\n");
         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\n");

        kill(getppid(), 22);
        sleep(3);

        printf("child over\n");
    }
    else
    {
        printf("father begin\n");

        kill(getpid(), 23);

        wait(&status);

        if (WIFSIGNALED(status))
        {
            printf("child process receive siganl %d\n", WTERMSIG(status));
        }

        printf("father over\n");
    }

    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\n", SIGINT);
         return;
    case SIGCHLD:
         printf("the signo is SIGCHLD %d received, hihi!\n", SIGCHLD);
         return;
	case SIGUSR1:
         printf("the signo is SIGUSR1 %d received, hihi!\n", SIGUSR1);
         return;
	case SIGIO:
         printf("the signo is SIGIO %d received, hihi!\n", SIGIO);
         return;
	case SIGALRM:
		 printf("the signo is SIGALRM %d received, hihi!\n", SIGALRM);
         return;
	default:
		 printf("the signo %d is not processed\n", 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\n");
		exit(-1);
	}
	else if (pid > 0)
	{
		if (sigprocmask(SIG_BLOCK, &set, &oldset) < 0)//屏蔽信SINGINT、SIGUSR1
		{
			fprintf(stdout, "sigprocmask error\n");
			kill(pid, SIGKILL);
			exit(-1);
		}
/*
		waitpid(pid, &status, 0);

		if (WIFSIGNALED(status))
        {
            printf("child process receive siganl %d\n", 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\n");
    else if (sig == SIGQUIT)
		printf("SIGQUIT sig\n");
    else if (sig == SIGUSR1)
		printf("SIGUSR1 sig\n");
    else if (sig == SIGUSR2)
		printf("SIGUSR2 sig\n");
    else
		printf("SIGCHLD sig\n");
}

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\n", i);
	sleep(1);
    }

    sigsuspend(&wait);

    printf("After sigsuspend\n");

    printf("yyyy\n");
    if (-1 == sigprocmask(SIG_SETMASK, &old, NULL))
    {
        perror("sigprocmask");
        exit(-1);
    }
    for (i=0; i < 30; ++i)
    {
	printf("%d\n", i);
	sleep(1);
    }
    printf("xxxxx\n");

    return 0;
}
时间: 2024-10-25 13:33:59

linux进程通信之信号的相关文章

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是

linux 进程通信之 信号

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式. 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它:如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程. 信号的产生 1.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如ctr+c产生SIGINT, ctr + \产生SIGQUI信号,ctr +

浅析linux进程通信的方式

求职笔试中,考察进程通信方式是一个老生长谈的问题,每次都让我答得一头雾水,于是我总结了一下 这些必须了解的知识点. 实现linux进程通信的方式有6种: --内存共享 --信号(Singal) --管道(Pipe) --消息队列(Message) --信号量(Semaphore) --socket 消息队列通信 请关注:http://blog.csdn.net/ljianhui/article/details/10287879 内存共享通信 请关注:http://blog.csdn.net/lj

linux进程通信之SYSTEM V信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有.信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒. 一.信号量的分类: 在学习信号量之前,我们必须先知道--Linux提供两种信号量: (1) 内核信号量,由内核控制路径使用. (2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量. POSIX信号量又分为有名信号量和无名信号量.有名信号量,其值保存在文件

Linux进程通信----匿名管道

Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组 表示的文件描述字.这个数组有两个文件描述字,第一个是用 于读数据的文件描述符第二个是用于写数据的文件描述符. 不能将用于写的文件描述符进行读操作或者进行读的文件描述 符进写操作,这样都会导致错误. 关于匿名管道的几点说明: 1.匿名管道是半双工的,即一个进程只能读,一个进程只能写    要实现全双工,需要两个匿名管道. 2.只能在父子进程或者兄弟进程进行通信. 3.在读的时候关闭写文件描

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication) 进程是操作系统实现程序独占系统运行的假象的方法,是对处理器.主存.I/O设备的抽象表示.每个进程都是一个独立的资源管理单元,每个进程所看到的是自己独占使用系统的假象,因此各个进程之间是不能够直接的访问对方进程的资源的,不同的进程之间进行信息交互需要借助操作系统提供的特殊的进程通信机制. 进程之间的通信,从物理上分,可以分为同主机的进程之间的通信和不同主机间的进程之间的通信.从通信内容方式上分,可以分为数据交互.同

linux进程通信

e14: 进程间通信(进程之间发送/接收字符串/结构体): 传统的通信方式: 管道(有名管道 fifo,无名管道 pipe) 信号 signal System V(基于IPC的对象):                             IPC对象:                                                    ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ... 消息队列 mes

Linux笔记--Linux进程通信

Linux进程间通信 文章来源: http://www.cnblogs.com/linshui91/archive/2010/09/29/1838770.html 一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到.C.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).D.

Linux进程通信的几种方式总结

进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程). 资源共享 多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制. 进程控制 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变.