网络编程:信号处理

28 阅读2分钟

子进程何时终止,调用waitpid后要无休止的等待吗?

操作系统负责将子进程终止的消息告诉给父进程

信号处理机制:在特定事件发生时由操作系统向进程发送消息,为了响应该消息,执行与消息相关的自定义的操作。

信号与signal函数

信号注册函数

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);

函数名: signal
参数: int signo, void(*func)(int)
返回类型: 参数为int,返回void型函数指针

子进程终止时调用child函数,child的参数为int,返回值为void signal(SIGCHLD, child)

alarm函数

unsigned int alarm(unsigned int seconds);
返回0或者以秒为单位的距SIGALRM信号发生所剩时间

调用时传一个正整型参数,对应时间后产生 SIGALRM信号,若传0,则取消之前对SIGALRM信号的预约。如果预约后没有指定处理函数,则终止进程,不做任何处理

可以在 signal 函数中注册的信号

EBD9CE82-D331-4F28-96DB-0615E4D899A4.png

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void timeout(int sig) {
    if (sig == SIGALRM) {
        puts("time out");
    }
    alarm(2);
}

void keycontrol(int sig) {
    if (sig == SIGINT) {
        puts("ctrl+c pressed");
    }
}

int main(int argc, char *argv[]) {
    int i;
    signal(SIGALRM, timeout);
    signal(SIGINT, keycontrol);
    alarm(2);
    
    for (i = 0; i < 3; i++) {
        puts("wait....");
        sleep(100);
    }
    return 0;
}

结果:

DDD14782-7520-434B-95B3-209A56F030F9.png 很快就结束了,这是因为“发生信号时将唤醒由于调用 sleep 函数而进入睡眠状态的线程,并且线程一旦被唤醒,就不会再进入睡眠状态”。

sigaction 函数

int sigaction(int signo, const struct sigaction * act,
	    struct sigaction * oldact);
signo:与 signal 相同,传递信号信息
act:对应于第一个参数的信号处理函数信息
oldact:通过此参数获取之前注册的信号处理函数的指针,不需要则传 0
struct sigaction {
	void (*sa_handler)(int); //保存信号处理函数的指针值
	sigset_t sa_mask; //默认为 0 
	int sa_flags;	//默认为 0
}

完整示例

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void timeout(int sig) {
    if (sig == SIGALRM) {
        puts("time out");
    }
    alarm(2);
}

int main(int argc, char *argv[]) {
    struct sigaction act;
    act.sa_handler = timeout;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGALRM, &act, 0);
    alarm(2);
    
    for (int i = 0; i < 3; i++) {
        puts("wait....");
        sleep(100);
    }
    return 0;
}

利用信号处理技术消灭僵尸进程

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

void read_childproc(int sig) {
    int status;
    pid_t id = waitpid(-1, &status, WNOHANG);
    if (WIFEXITED(status)) {
        printf("remove proc id: %d \n", id);
        printf("child send: %d \n", WEXITSTATUS(status));
    }
}


int main(int argv, char *argc[]) {
    pid_t pid;
    struct sigaction act;
    act.sa_handler = read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    
    sigaction(SIGCHLD, &act, 0);
    
    pid = fork();
    if (pid == 0) { //子进程
        puts("hi, i'm child process1111");
        sleep(10);
        return 12;
    } else { //父进程
        printf("child proc id qqqqq: %d \n", pid);
        pid = fork();
        if (pid == 0) { //另一子进程
            puts("hi i am child process2222");
            sleep(10);
            exit(24);
        } else {
            printf("child proc id xxxxxx: %d \n", pid);
            for (int i = 0; i < 5; i++) {
                puts("wait....");
                sleep(5);
            }
        }
    }
    return 0;
}