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

Linux高性能服务器编程学习记录——十、信号

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

Linux高性能服务器编程学习记录——十、信号

1、发送信号

Linux下,一个进程给其他进程发送信号的API是kill函数

#include 
#include 
int kill(pid_t pid, int sig);

该函数把信号sig发送给由pid指定的目标进程,pid的含义如下

Linux定义的信号值都大于0,如果sig取值为0,则kill函数不发送任何信号。

2、信号处理方式

目标进程收到信号后,需要定义一个接收函数来处理,信号处理函数原型如下

#include 
typedef void (*__sighandler_t) (int);

信号处理函数只有一个整型参数,表示信号类型,信号处理函数应该是可重入的,否则容易引发一些竞态条件。所以在信号处理函数中严禁调用一些不安全的函数。
除了用户自定义信号处理函数外,也可以指定系统提供的其他处理方式,主要有两种

#include 
#define SIG_DFL ((__sighandler_t) 0)	//忽略目标信号
#define SIG_IGN ((__sighandler_t) 1) 	//使用信号的默认处理方式

默认处理方式主要有:

  • Term:结束进程
  • Ign:忽略信号
  • Core:结束进程并生成核心转储文件
  • Stop:暂停进程
  • Cont:继续进程

如果程序在执行处于阻塞状态的系统调用时接收到信号,并且我们为该信号设置了信号处理函数,则默认请款下系统调用将被中断,并且errno被设置为EINTR。可以使用sigaction函数为信号设置SA_RESTART标志以自动重新启动被该信号中断的系统调用。

3、网络编程相关的Linux信号 SIGHUP

当挂起进程的控制终端时,SIGHUP信号将被触发,默认行为Term

SIGPIPE

默认情况下,往一个读端关闭的管道或socket连接中写数据将引发SIGPIPE信号,我们在代码中应该捕获处理它,或至少忽略它,因为它的默认行为是结束进程。

SIGURG

Linux环境下,内核通知应用程序带外数据到达主要有两种方法:一种是I/O复用技术,select等系统调用在接收到带外数据时将返回(异常事件),另外一种是使用SIGURG信号。SIGURG的默认行为是Ign。

4、信号函数 signal系统调用

signal时旧的为信号设置处理函数的调用

#include 
_sighandler_t signal (int sig, _sighandler_t _handler)

sig指定信号类型,_handler是_sighandler_t类型的函数指针。signal函数成功时返回一个函数指针,指向上次调用signal时传入的信号处理函数,或者信号sig对应的默认处理函数指针SIG_DEF(如果是第一次调用signal的话)。失败返回SIG_ERR,并设置errno。

sigaction

设置信号处理函数的更健壮的系统调用是sigaction

#include 
int sigaction (int sig, const struct sigaction* act, struct sigaction* oact);

struct sigaction
{
	_sighandler_t sa_handler;
	_sigset_t sa_mask;
	int sa_flags;
	void (*sa_restorer) (void);
}

sig指定信号类型,act指定新的处理方式,oact输出旧的处理方式。
sigaction结构体中sa_handler指定信号处理函数,sa_mask设置进程的信号掩码,以指定哪些信号不能发送给本进程,sa_mask是信号集_sigset_t类型,该类型指定一组信号。sa_flags设置程序收到信号时的行为。sa_restorer成员已过时,最好不要使用。

信号集和进程信号掩码

进程信号掩码用来屏蔽不被进程接收的信号,即指定在信号集中的信号不想被进程接收到。
sigset_t定义如下:

#include 
#define _SIGSET_NWORDS (1024 / (8 * sizeof(unsigned long int)))
typedef struct
{
	unsigned long int __valP{_SIGSET_}
}

sigset_t的定义类似fd_set,每一位表示一个类型的 信号。通过下面的函数来操作信号集

#include 
int sigemptyset(sigset_t* _set) 			// 清空信号集
int sigfillset(sigset_t* _set) 				// 填满信号集
int sigaddset(sigset_t* _set, int signo) 	// 将信号signo添加到信号集中
int sigdelset(sigset_t* _set, int signo)
int sigismember(_const sigset_t* _set, int signo)

下面的函数用来设置或查看进程的信号掩码

#include 
int sigprocmask(int _how, _const sigset_t* _set, sigset_t* _oset);

参数_how指定设置进程信号的掩码方式,可选值如下

  • SIG_BLOCK:新的掩码是旧的掩码集加上_set里的信号
  • SIG_UNBLOCK:新的掩码是旧的掩码集里减去_set里的信号
  • SIG_SETMASK:直接将进程信号掩码设置为_set

_set为空时,可通过_oset获取当前掩码。
sigprocmask成功返回0,失败返回-1,并设置errno。

被挂起的信号

掩码里的信号无法被进程接收,是因为当被屏蔽的信号发生时被操作系统挂起了, 如果取消被挂起信号的屏蔽,则它能立即被进程接收到。如下函数可获取进程当前被挂起的信号集

#include 
int sigpending(sigset_t* set);

被挂起的信号将会被保存到set中。

5、统一事件源

信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。而信号处理函数需要尽可能快的执行,以确保信号不被屏蔽。统一事件源就是在信号发生时,用信号处理函数把事件写入管道,然后在I/O复用系统调用中监听管道读端。这样便将信号真正的处理逻辑加入主循环中。下面是书中代码清单10-1的源码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_EVENT_NUMBER	1024
static int pipefd[2];

int setnonblocking(int fd)
{
	int oldopt = fcntl(fd, F_GETFL);
	int newopt = oldopt | O_NONBLOCK;
	fcntl(fd, F_SETFL, newopt);
	return oldopt;
}

void addfd(int epollfd, int fd)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN | EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
	setnonblocking(fd);
}


void sig_handler(int sig)
{
	
	int save_errno = errno;
	int msg = sig;
	printf("write sig %d to pipen", sig);
	send(pipefd[1], (char*)&msg, 1, 0);
	errno = save_errno;
}


void addsig(int sig)
{
	struct sigaction sa;
	memset(&sa, '', sizeof(sa));
	sa.sa_handler = sig_handler;
	sa.sa_flags |= SA_RESTART;   // SA_RESTART 重新调用被该信号终止的系统调用
	sigfillset(&sa.sa_mask);  //设置信号集中所有信号
	assert(sigaction(sig, &sa, NULL) != -1);
}

int main(int argc, char* argv[])
{
	if (argc <= 2)
	{
		printf("usage: %s ip_address portn", argv[0]);
		return 1;
	}
	const char* ip = argv[1];
	int port = atoi(argv[2]);

	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	inet_pton(AF_INET, ip, &address.sin_addr);

	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);
	int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);
	ret = listen(listenfd, 5);
	assert(ret != -1);

	epoll_event events[MAX_EVENT_NUMBER];
	int epollfd = epoll_create(5);
	assert(epollfd != -1);
	addfd(epollfd, listenfd);

	
	ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
	assert(ret != -1);
	setnonblocking(pipefd[1]);
	addfd(epollfd, pipefd[0]);

	
	addsig(SIGHUP);
	addsig(SIGCHLD);
	addsig(SIGTERM);
	addsig(SIGINT);

	bool stop_server = false;

	while (!stop_server)
	{
		int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
		if ((number < 0) && (errno != EINTR))
		{
			printf("epool failuren");
			break;
		}

		for (int i = 0; i < number; ++i)
		{
			int sockfd = events[i].data.fd;
			if (sockfd == listenfd)
			{
				struct sockaddr_in client_address;
				socklen_t addresslen = sizeof(client_address);
				int connfd = accept(sockfd, (struct sockaddr*)&client_address, &addresslen);
				addfd(epollfd, connfd);
			}
			else if ((sockfd == pipefd[0]) && (events[i].events & EPOLLIN))
			{
				//信号
				int sig;
				char signals[1024];
				ret = recv(sockfd, signals, sizeof(signals), 0);
				if (ret == -1)
				{
					continue;
				}
				else if (ret == 0)
				{
					continue;
				}
				else
				{
					
					for (int i = 0; i < ret; ++i)
					{
						switch (signals[i])
						{
						case SIGHUP:
						case SIGCHLD:
						{
							continue;
						}
						case SIGTERM:
						case SIGINT:
						{
							stop_server = true;
						}
						default:
							break;
						}
					}
				}
			}
			else
			{
			}
		}
	}
	
	printf("close fdsn");
	close(listenfd);
	close(pipefd[0]);
	close(pipefd[1]);
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/676119.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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