Linux的信号处理 | 情字何解,怎落笔都不对
Cobb 670 1

信号处理

Linux的信号处理 | 情字何解,怎落笔都不对

知识储备 kill -l : 查看Linux支持的信号 man 7 signal : 查看信号所有相关信息

信号处理的三种方式: 1、执行信号默认的处理方式 2、忽略该信号 3、给信号绑定处理函数

1、信号处理函数执行过程中,不会再响应同类型的信号 2、信号不排队,PCB中对于每一种信号,最多只能存储一个。


void sig_handler(int sig)
{
    cout << "recv signal:" << sig << endl;
    sleep(20);
}

int main()
{
    // 忽略指定的信号
    //signal(SIGINT, SIG_IGN);
    //signal(SIGQUIT, SIG_IGN);

    // 给信号绑定处理函数
    signal(SIGINT, sig_handler);

    for (;;)
    {
        cout << "main doing..." << endl;
        sleep(3);
    }
}

信号处理过程

Linux的信号处理 | 情字何解,怎落笔都不对

如果信号处理动作是用户自定义的函数,由于用户自定义函数在用户空间执行,处理过程: 1、进程在用户空间正常执行main()函数,这是发生中断或异常切换到内核态。 2、在中断处理完成后(ctrl+c => SIGINT放入进程的PCB中)要返回用户态继续执行main代码之前,检查是否有信号未处理。 3、内核决定返回用户态,但不是恢复main函数的上下文继续执行,而是执行信号注册的处理函数handler, handler信号处理函数和main使用的是不同的堆栈空间,之间不存在调用和被调用的关系。 4、handler信号处理函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 5、如果没有新的信号要处理,这次再返回用户态就恢复main函数的上下文继续执行了。


处理所有状态改变的子进程

Linux的信号处理 | 情字何解,怎落笔都不对

知识储备 子进程终止时会向父进程发送SIGCHLD信号(创建线程的fork函数可以看到),告知父进程回收自己,但该信号的默认处理动作为忽略。 所以要父进程注册信号处理函数, 处理子进程


void handler(int sig)
{
    // 调用waitpid获取子进程状态,子进程释放PCB资源,防止僵尸进程产生
    // 由于信号不排队,如果同一时间有很多子进程结束,这里最多处理两个子进程,还是会出现很多僵尸进程
    // waitpid(-1, NULL, 0);
    // WNOHANG 函数立即返回
    // while 读取所有状态改变的子进程
    while(waitpid(-1, NULL, WNOHANG) > 0)
    {
    }
}

int main()
{
    FILE *pf = NULL;

    // 父进程注册信号处理函数
    signal(SIGCHLD, handler);

    // fork创建一个子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        exit(-1);
    }

    if (pid > 0)
    {
        // 父进程执行的地方
        cout << "father pid:" << getpid() << endl;

        // wait(NULL) => waitpid(-1, NULL, 0) 等待任一子进程结束,该函数返回
        // 阻塞等待子进程执行完,子进程就可以直接释放PCB资源,防止僵尸进程出现
        // waitpid(pid, NULL, 0);

        sleep(100);
    }
    else // pid == 0
    {
        // 子进程执行的地方
        cout << "child pid:" << getpid() << endl;
        exit(0);  // 子进程执行完,会发送SIGCHLD信号给父进程
    }
}
评论区

索引目录