请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

Linux——信号处理函数与阻塞状态的进程

2023-07-13

这篇博客记录一下我在编写一个简单的多进程回声服务器的时候出现的问题。

这个问题就在于忽略了几个有关于信号处理函数的基本常识:

  1. 用通俗的话讲信号注册函数(signal、sigaction)的功能:进程告诉操作系统,当以后收到向信号注册函数传入的信号时,你帮我调用一下信号处理函数。
  2. 当该进程在之后收到指定的信号之后,操作系统就会帮助进程调用指定的信号处理函数。但是,如果该进程处于阻塞状态,那么操作系统会强制唤醒进程!并且不会再陷入之前的阻塞!

下面是我所写的代码中的一个片段(C和C++混编出来的屎,别喷):

 void start()
    {
        // 开始接受客户端请求
        struct sigaction act;
        act.sa_handler = wait_child_proc;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGCHLD,&act,0);

        sockaddr_in client;
        socklen_t len = sizeof(client);
        while(true)
        {

            int sockfd = accept(_listenfd,(sockaddr *)&client,&len);
            if(sockfd == -1)
            {
                printf("accept error!\n");
                continue;
            }
            std::cout << "有新的客户端..." << std::endl;
            pid_t id = fork();
            if(id == 0)
            {
                close(_listenfd);//子进程不负责监听业务
                int recv_len = 0;
                char buffer[1024] = {0};
                while((recv_len = read(sockfd,buffer,sizeof(buffer))) != 0)
                {
                    std::cout << "recv_len = " << recv_len << std::endl;
                    write(sockfd,buffer,recv_len);
                }
                close(sockfd);
                std::cout << "子进程处理完业务!退出!" << getpid() << std::endl;
                exit(0);
            }
            else if(id > 0)
            {
                close(sockfd);// 父进程不处理业务
            }
            else 
            {
               std::cout << "fork error!" << std::endl;
                exit(-1);
            }
        }
    }

这段代码会有两个地方陷入阻塞:一是父进程调用accept时,二是子进程调用read时。而子进程并没有调用信号注册函数,因此子进程与本篇文章研究的问题无关。

这段代码的意思大概是这样:调用start()后,调用sigaction()注册了一个SIGCHLD信号,然后继续往下走直到accept()阻塞。如果accept()返回了一个正确的套接字,那么主进程就创建一个子进程去处理I/O。子进程当中的read()如果返回值为0,就说明连接已经断开了,此时子进程就会退出。同时,在子进程处理I/O的时候,父进程已经在accept()处阻塞了,此时如果子进程处理的I/O连接断开,子进程就会退出,父进程就会收到SIGCHLD信号,然而此时的父进程正处于阻塞状态,所以操作系统会强制唤醒父进程以便调用信号处理函数,而父进程的accept()没有收到任何可用连接并且又从accept()处唤醒,因此accept()的返回值为-1,所以会打印一个"accept error!"。

虽然这段代码并不会给服务器带来任何功能上的差错,但是一个连接断开就打印一次"accept error!"确实是比较奇怪的。因此写下该篇文章以做记录。

相关阅读

热门文章

    手机版|MSIPO技术圈 皖ICP备19022944号-2

    Copyright © 2024, msipo.com

    返回顶部