栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

--信号--

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

--信号--

文章目录
  • 前言
  • 一、信号概念
    • 信号的响应过程
    • 常用信号
    • 信号术语
  • 二、信号函数
    • signal
    • sigaction
    • 信号集
    • sigprocmask
    • sigpending
    • sigsuspend
  • 三、实例分析——统一事件源

前言

信号是软件中断(但不是中断,可以认为信号的响应依赖于中断)。它提供了一种处理异步事件的方法。Linux 信号可由如下条件产生:

  1. 命令行输入中断键或kill命令,例如ctrl+c中断前台进程
  2. 硬件异常,比如非法内存访问,除以0
  3. 通过函数设置产生信号(kill函数)

一、信号概念

信号都有一个名字都以SIG开头(kill -l查看),当某个信号出现时,进程可以告诉内核用下列三种方式来执行。
1、忽略信号。
2、捕捉信号。
3、执行默认动作,大多数信号的默认动作是终止该进程。

信号的响应过程

进程由于系统调用或者中断进入内核,完成相应任务返回用户空间之前,会检查有无信号到达,如果没有就返回用户态恢复上下文数据后继续执行程序;如果有信号,也会返回用户态,但这时会执行信号处理函数,执行完毕后,再次返回内核态,循环。
信号是在内核态回到用户态的路上被响应的。

常用信号

SIGALRM 调用alarm函数设置的定时器超时时,产生此信号。
SIGCHLD 在子进程终止时,会产生此信号返回给父进程。
SIGINT 当用户按中断键时,终端将产生此信号发生给前台进程。
SIGPIPE 在管道读的进程已经终止时还往管道写将产生此信号。
SIGTERM 此信号由kill命令发出的,意义是系统默认终止。

信号术语

捕捉信号:如果信号的处理动作是用户自定义的函数,在信号抵达时就调用这个函数,这个动作称为捕捉信号。

阻塞信号:不忽略该信号,在其发生时记住它,在进程做好准备后再通知它。

pending:在信号产生和递送之间,称之为pending(未决的)。如果有一个阻塞的信号,且内核对该信号不忽略,则该信号就是pending的,直到解除阻塞或者忽略该信号。调用sigpending函数能获得pending状态的信号集。

可重入函数:产生信号后,当前进程会立马调用信号处理函数handler,假设当前进程在malloc时信号到来,handler中也调用了malloc,这时候就会出错。所以规定handler中必须调用安全的函数,称为可重入函数,且这些函数会阻塞任何引起不一致的信号。

信号屏蔽字:规定了当前要阻塞递送到该进程的信号集。

二、信号函数 signal

为一个信号设置处理函数

#include 
typedef void (*sighandler_t)(int);//不懂的可以搜一下typedef函数指针
sighandler_t signal(int signum, sighandler_t handler);

--signum 信号名
--handler 是常量SIG_IGN,就代表忽略该信号
		  是常量SIG_DFL,就代表执行默认动作
		  是函数,则在信号发生时调用该函数,也就是捕捉信号(信号处理函数)
成功返回一个函数指针,指向前一次调用signal传入的handler函数指针,失败返回SIG_ERR
sigaction

signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。而POSIX 标准定义的信号处理接口是 sigaction 函数,其接口头文件及原型如下:

#include 

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
--signum 信号名 
--act 指定新的处理方式
--oldact 若不为空,则输出信号之前的处理方式
成功返回0,失败返回-1并设置errno

struct sigaction {
    void (*sa_handler)(int);
    //作用等同于signal函数中的handler
    
    void (*sa_sigaction)(int, siginfo_t *, void *);
    
    sigset_t sa_mask;
    
    
    int sa_flags;
    
 
    void (*sa_restorer)(void);
}
信号集

信号集是一个能表示多个信号的数据类型,本质上是一个数组。数组的每个位代表了一个信号

 #include 
int sigemptyset(sigset_t *set);
//初始化由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);
//检测信号集中是否存在signum这个信号
sigprocmask

检查或改变进程的信号掩码(信号屏蔽字)

#include 

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
-- how
	SIG_BLOCK:设置新的(Set中的)信号掩码,与原先的信号掩码相并
	SIG_UNBLOCK:为(Set中的)信号解除阻塞
	SIG_SETMASK:为该进程设置新的(Set中的)信号掩码
-- set
	新的信号屏蔽字由参数set(非空)指定
--oldset
	如果oset不为空,则把当前进程的信号屏蔽字保存到oset中
成功返回0,失败返回-1并设置errno

sigprocmask(SIG_UNBLOCK,&set,&saveset);
//对set里的信号屏蔽字解阻塞,把之前信号屏蔽字的状态保存到saveset

sigprocmask(SIG_BLOCK,&set,&oldset);
//对set里的信号屏蔽字加阻塞,把之前的状态保存到oldset中

sigpromask(SIG_SETMASK,&oldset,NULL);
//恢复了上一次的状态,也就是第一次调用sigpromask后的状态

sigpromask(SIG_SETMASK,&saveset,NULL);
//恢复到最开始的状态,第一次调用sigpromask之前的状态
sigpending

返回由pending信号构成的pengind集

#include 

int sigpending(sigset_t *set);
set--用于保存返回的信号集
成功返回0,失败返回-1并设置errno
sigsuspend

进程的信号屏蔽字替换为由参数mask指向的信号集。在捕捉到一个信号或发生一个会终止该进程的信号前,该进程会被阻塞。可以简单看作是解除信号屏蔽字的阻塞和pause的原子操作。

#include 
int sigsuspend(const sigset_t *mask);


三、实例分析——统一事件源

信号是异步事件,信号处理函数handler和主函数是两条不一样的执行路线,为了不让主函数等待很久,所以加快执行的速度。

解决方法:把信号的主要处理逻辑放到程序的主循环中,当调用handler时,通过管道把信号值传递给主函数,主函数根据信号值执行目标信号对应的逻辑代码。

handler往管道的写端写信号值,主函数则从管道的读端读出该信号值。那么主函数怎么知道管道上何时有数据可读呢?这很简单,我们只需要使用I/O复用来监听管道的读端文件描述符上的可读事件。这样,信号事件就能和其他I/O事件一样被处理,称为统一事件源。
下面给出伪码

#include <头文件>
//主函数
int main(int argc,char *argv[])
{
	1建立监听套接字
	
	int listenfd=socket(PF_INET,SOCK_STREAM,0);
	struct sockaddr_in serv_addr;
	serv_addr.sin_family...
	serv_addr.sin_port....
	serv_addr.sin_addr...
	bind(listenfd,(sockaddr*)serv_addr,sizeof(serv_addr));
	listen(listenfd,listenfd_size);
	
	2创建管道--用socketpair来创建全双工的通信方式
	//使用socketpair函数能够创建一对套节字进行进程间通信
	
	socketpair(PF_INET,SOCK_STREAM,0,pipefd);
	//pipefd[0]、pipefd[1]都能进行双向读写操作
	
	SetNonBlock(pipefd[1]);
	
	3将listenfd和管道的读端fd加入监听
	
	addfd(epfd,listenfd);
	addfd(epfd,pipefd[0]);
	
	
	4为信号设置处理方式
	addsig(SIGINT);
	
	
	
	while(1)
	{
		5监听
		struct epoll_event events[MAX_EVENTS];
		int nread=epoll_wait(epfd,events,MAX_EVENTS,-1);
		
		for(int i=0;i
			int fd=events[i];
			if(fd==listenfd)
			{
				
				int clntsock=accept(...);
				
				addfd(epfd,clntsock);
			}
			else if(fd==pipefd[0])//管道有信号传来,处理该信号
			{
				char signals[size]
				int read_len=recv(pipefd[0],msg,sizeof(msg),0);//接收信号
				//信号值占一个字节,通过for循环遍历,用switch case来处理每个信号
				for(int i=0;i
					switch(signals[i])
					{
						case SIGINT
						...
						break;
						case ...
						...
						break;
					}
				}
			}
		}
	}
	close(listenfd);
	close(pipefd[0]);
	close(pipefd[1]);
	exit(0);
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/860602.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号