Linux下,一个进程给其他进程发送信号的API是kill函数
#include#include int kill(pid_t pid, int sig);
该函数把信号sig发送给由pid指定的目标进程,pid的含义如下
Linux定义的信号值都大于0,如果sig取值为0,则kill函数不发送任何信号。
目标进程收到信号后,需要定义一个接收函数来处理,信号处理函数原型如下
#includetypedef 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信号,我们在代码中应该捕获处理它,或至少忽略它,因为它的默认行为是结束进程。
SIGURGLinux环境下,内核通知应用程序带外数据到达主要有两种方法:一种是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
#includeint 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,每一位表示一个类型的 信号。通过下面的函数来操作信号集
#includeint 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)
下面的函数用来设置或查看进程的信号掩码
#includeint 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。
掩码里的信号无法被进程接收,是因为当被屏蔽的信号发生时被操作系统挂起了, 如果取消被挂起信号的屏蔽,则它能立即被进程接收到。如下函数可获取进程当前被挂起的信号集
#includeint 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; }



