linux进程通信之信号[通俗易懂]

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;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116431.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • HTML里面Textarea换行总结

    HTML里面Textarea换行总结

    2021年11月28日
  • P750 内存插槽

    P750 内存插槽查看p750内存插槽占用情况lscfg-vp|grep-pDIMMMemoryDIMM:RecordName……………..VINIFlagField………………XXMSHardwareLocationCode……U78A0.001.DNWKM02-P1-C13-C2…

  • sublime text 3 激活码【2022免费激活】[通俗易懂]

    (sublime text 3 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~40…

  • 内核杂谈——关于platform device 创建

    内核杂谈——关于platform device 创建当拿到driver,不能用起来的时候需要去检查device了。虽说device和bus通常都是系统中带的,但也不要想当然的认为这个系统是帮你建好的。通常busdevicedriver三者中,bus基本不用干预,device干预的少,driver干预的多。从设备树中生成device从设备树中识别device的入口为arch_initcall_sync(of_platform_default_populate_init);staticint__initof_platform_defa

  • 白话空间统计二十四:地理加权回归(二)

    白话空间统计二十四:地理加权回归(二)六千多字的大篇……诚意满满啊……橘生淮南则为橘,生于淮北则为枳,叶徒相似,其实味不同。所以然者何?水土异也。——《晏子春秋·内篇杂下》水土不服、南北差异,(包括地域歧视)是自古以来的一个大命题……正如在(伪)吃货的眼中,中国的地图是这样的:为什么说上面是伪?吃货呢,因为在真?吃货眼中的中国地图,是这样的:这就是具有全局眼(胃)光(口)和局部眼(胃)光(口),

  • Python之lambda表达式

    Python之lambda表达式一、lambda表达式Lambda表达式并不是Python中特有的,很多语言中都有,例如:Jave、C++、C#中都有。根据百度百科中对“Lambda表达式”(lambdaexpression)的解释,它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambdaabstraction),是一个匿名函数,即没有函数名的函数。Pytho…

    2022年10月18日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号