Linux进程间通信——信号

概念

什么是信号?
  • 信号是linux系统中一种常用的通信机制,A给B发送信号,B在收到信号之前执行自己的代码,收到信号后,不管执行什么程序,都暂停运行,去处理信号,处理完毕后再继续执行原来的程序,是一种软中断。
特点
  • 由于信号是通过软件方法实现的,具有很强的延时性,对用户来讲,时间非常短,不易察觉
  • 每个进程收到的所有信号,都是由内核负责发送,内核处理

与信号相关的事件或者名词

产生信号的基本方法
  • 系统调用当前进程的某些函数
  • 通过命令产生,如kill指令
  • 硬件异常、段错误、内存出错、总线错误
  • 软件条件产生,如alarm定时器
  • 硬件产生,如ctrl+c按键
信号分类及信号一览表

1.可靠信号与不可靠信号

  • 不可靠信号
    Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号,它的主要问题是信号可能丢失。
  • 可靠信号
    随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失

2.指令kill -l 查看所有信号

  • 信号的名称是在头文件 <signal.h>里定义的
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN
+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
  • Linux提供两个特殊信号:9号和19号信号,无法修改,提供终止进程的手段
  • 写信号时最好使用信号宏名称,因为不同系统下宏的数值可能是不同的,如下有些信号宏可能有多个数值
    在这里插入图片描述
递达

从信号产生,会先发送给内核,通过内核处理再发送到进程,进程再处理信号这一过程称为递达

未决信号集

从信号产生,还未递达至进程,信号没有被处理掉,这过程中会有一个未决信号集存贮这些信号

阻塞信号集

也叫做信号屏蔽字,每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。

在这里插入图片描述
假设发送第三个信号,若阻塞信号集第三位为1,那么进程就不会收到该信号,因此未决信号集中第三位将保持状态为1

信号处理方式

  • 系统默认动作,例如ctrl+c将进程杀死
  • 忽略(丢弃)该信号,就象没有收到该信号似的继续运行
  • 捕捉信号,自定义动作

信号相关函数

头文件<signal.h>

  • signal函数
    函数原型:
    typedef void (*sighandler_t)(int);//返回类型为空的函数指针,整型参数
    sighandler_t signal(int signum, sighandler_t handler)
    功能:接收某个信号sig(第一个参数),使程序接收到信号时执行对应函数func(第二个参数),func这个函数必须有一个int类型的参数(即接收到的信号)
    func也可以是下面两个特殊值:
    SIG_IGN 屏蔽该信号
    SIG_DFL 恢复默认行为

  • int kill(pid_t pid, int sig);
    功能:给指定进程发送信号(不一定杀死),第一个参数是进程ID,第二个参数是发送信号的类别,
    返回值:成功返回0, 失败返回-1

  • int sigemptyset(sigset_t *set);
    功能:将某个信号集清0
    返回值:成功返回0, 失败返回-1

  • int sigfillset(sigset_t *set);
    功能:将某个信号置1
    返回值:成功返回0, 失败返回-1

  • int sigaddset(sigset_t *set, int signo);
    功能:将某个信号加入到信号集中
    返回值:成功返回0, 失败返回-1

  • int sigdelset(sigset_t *set, int signo);
    功能:用来将参数signo信号从参数set信号集里删除。
    返回值:成功则返回0,如果有错误则为-1。

  • int sigismember(const sigset_t *set, int signo);
    功能:用来测试参数signo信号是否已加入至参数set信号集里。
    返回值:如果信号集里已有该信号则返回1,否则返回0。错误则为-1

  • int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    功能:读取或更改进程的信号屏蔽字。
    返回值:若成功则为0,若出错则为-1

    参数how的含义在这里插入图片描述

  • int sigpending(sigset_t *set);
    功能: sigpending读取当前进程的未决信号集,通过set参数传出。
    返回值:若成功则为0,若出错则为-1

  • void abort(void);
    头文件:<stdlib.h>
    功能:向进程发送sigabort信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。
    说明:即使sigabort被进程设置为阻塞信号,调用abort()后,sigabort仍然能被进程接收。该函数无返回值

讲了这么多,做个例子,主要实现的功能是:打印当前进程未决信号集,并测试屏蔽信号

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>

void printf_ped(sigset_t* ped)
{
	int i;
	for (i = 0; i < 32; i++)
	{
		if (sigismember(ped, i) == 1)
		{
			putchar('1');	
		}
		else
		{
			putchar('0');
		}
	}
	printf("\n");
}

int main(void)
{
	sigset_t myset, oldset, ped_set;

	sigemptyset(&myset);//清空信号集为0
	sigaddset(&myset, SIGQUIT);//添加信号到信号集中

	sigprocmask(SIG_BLOCK, &myset, &oldset); //设置进程的信号屏蔽字

	while (1)
	{
		sigpending(&ped_set);//读取未决信号集到ped_set中
		printf_ped(&ped_set);//打印打印未决信号集
		sleep(3);
	}
	return 0;
}

运行结果如下
在这里插入图片描述
一开始没有产生信号 未决信号集都为0,当我们按下crtl+\时产生3号信号,但是由于我们屏蔽了3号信号,未决信号集中第三位一直为1,表示该信号还未被处理

定时器相关函数

头文件 #include <sys/time.h>

alarm函数

  • 每个进程只有一个alarm
  • 无论进程处于何钟状态下,定时器都在计时

函数原型:
unsigned int alarm(unsigned int seconds);

功能:专门为sigalrm信号而设,在指定的时间seconds秒后,将向进程本身发送sigalrm信号

返回值:如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0

说明:进程调用alarm后,任何以前的alarm()调用都将无效

setitimer函数

函数原型:
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

参数:
第一个参数 which指定定时器类型
第二个参数 结构itimerval的一个实例
第三个参数 可不做处理,old_value一般设为NULL

结构体类型如下
struct itimerval
 {
        struct timeval it_interval; /* 下一次的取值 */
        struct timeval it_value; /* 本次的设定值 */
};

struct timeval 
{
        long tv_sec; /* 秒 */
        long tv_usec; /* 微秒,1秒 = 1000000 微秒*/
};

功能:这个函数可以周期性计时,定时器将it_value递减到0时,产生一个信号,并将it_value的值设定为it_interval的值,然后重新开始计时,如此往复,若it_interval为0则定时器停止。参数ovalue如果不为空,则其中保留的是上次调用设定的值

返回值:成功返回0,失败返回-1

定时器类型:

itimer_real: 按实际时间计时(系统时间+用户时间+等待时间),经过指定的时间后,内核将发送SIGALRM信号给本进程
itimer_virtual :只计算进程占用cpu的时间,经过指定的时间后,内核将发送SIGVTALRM信号给本进程
itimer_prof :计算占用cpu及执行系统调用的时间,经过指定的时间后,内核将发送SIGPROF信号给本进程

写个例子,每3秒打印一次hello world

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>


void myfun(int signo)
{
	printf("hello world\n");
}

int main()
{
	struct itimerval it, oldit;
	int ret;

	signal(SIGALRM, myfun);

	//本次设定值
	it.it_value.tv_sec = 3;
	it.it_value.tv_usec = 0;

	//下次设定值
	it.it_interval.tv_sec = 3;
	it.it_interval.tv_usec = 0;

	ret = setitimer(ITIMER_REAL, &it, &oldit);
	if (ret == -1)
	{
		printf("error\n");
		exit(1);
	}

	while (1);
	return 0;
}

运行结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值