• 网络编程:阻塞I/O和进程模型


    父进程和子进程

    进程是程序执行的最小单位,一个进程有完整的地址空间、程序计数器等,如果想创建一个新的进程,使用函数 fork 就可以

    pid_t fork(void)
    返回:在子进程中为0,在父进程中为子进程ID,若出错则为-1
    

    fork函数实现的时候,实际上会把当前父进程的所有相关值都克隆一份,包括地址空间、打开的文件描述符、程序计数器等,连执行代码也会拷贝一份,新派生的进程的表现行为和父进程近乎一样。
    为区别两个不同的进程,实现者可通过改变fork函数的栈空间值来判断,对应的程序中就是返回值的不同。

    
    if(fork() == 0){
      do_child_process(); //子进程执行代码
    }else{
      do_parent_process();  //父进程执行代码
    }
    

    当一个子进程退出时,系统内核还会保留该进程的若干信息,如退出状态,这样的进程如果不回收,就变成了僵尸进程。在Linux中,这样的“僵尸”进程被挂到进程号为1的init进程中,所以,由父进程派生出来的子进程,也必须由父进程负责回收,否则子进程就变成僵尸进程。僵尸进程会占用不必要的内存空间,如果量多到一定数量级,就会耗尽系统资源。
    有两种方式可以在子进程退出后回收资源,分别是调用wait和waitpid函数

    pid_t wait(int *statloc);
    pid_t waitpid(pid_t pid, int *statloc, int options);
    

    函数 wait 和 waitpid 都可以返回两个值,一个是函数返回值,表示已终止子进程的进程 ID 号,另一个则是通过 statloc 指针返回子进程终止的实际状态。这个状态可能的值为正常终止、被信号杀死、作业控制停止等。
    如果没有已终止的子进程,而是有一个或多个子进程在正常运行,那么 wait 将阻塞,直到第一个子进程终止。
    waitpid 可以认为是 wait 函数的升级版,它的参数更多,提供的控制权也更多。pid 参数允许我们指定任意想等待终止的进程 ID,值 -1 表示等待第一个终止的子进程。options 参数给了我们更多的控制选项。

    处理子进程退出的方式一般是注册一个信号处理函数,捕捉信号 SIGCHILD 信号,然后再在信号处理函数里调用 waitpid 函数来完成子进程资源的回收。SIGCHLD 是子进程退出或者中断时由内核向父进程发出的信号,默认这个信号是忽略的。所以,如果想在子进程退出时能回收它,需要像下面一样,注册一个 SIGCHOLD 函数。

    signal(SIGCHLD, sigchld_handler);  
    

    阻塞I/O的进程模型

    为说明使用阻塞 I/O 和进程模型,假设有两个客户端,服务器初始监听在套接字 lisnted_fd 上。当第一个客户端发起连接请求,连接建立后产生出连接套接字,此时,父进程派生出一个子进程,在子进程中,使用连接套接字和客户端通信,因此子进程不需要关心监听套接字,只需要关心连接套接字;父进程则相反,将客户服务交给子进程来处理,因此父进程不需要关心连接套接字,只需要关心监听套接字。

    这张图描述了从连接请求到连接建立,父进程派生子进程为客户服务。

    假设父进程之后又接收了新的连接请求,从 accept 调用返回新的已连接套接字,父进程又派生出另一个子进程,这个子进程用第二个已连接套接字为客户端服务。
    这张图同样描述了这个过程。

    现在,服务器端的父进程继续监听在套接字上,等待新的客户连接到来;两个子进程分别使用两个不同的连接套接字为两个客户服务。

    代码实现

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <netinet/in.h>
    
    
    #define SERV_PORT 43211
    #define LISTENQ 1024
    #define MAX_LINE 4096
    
    int tcp_server_listen(int port)
    {
        int listen_fd;
        listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        int on = 1;
        setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
        int rt1 = bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if(rt1 < 0)
        {
            perror("bind failed");
            return -1;
        }
    
        int rt2 = listen(listen_fd, LISTENQ);
        if(rt2 < 0)
        {
            perror("listen failed");
            return -1;
        }
    
        signal(SIGPIPE, SIG_IGN);
    
        return listen_fd;
    }
    char rot13_char(char c) {
        if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
            return c + 13;
        else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
            return c - 13;
        else
            return c;
    }
    void sigchld_handler(int sig)
    {
        while(waitpid(-1, 0, WNOHANG) > 0);
        return;
    }
    void child_run(int fd)
    {
        char outbuf[MAX_LINE + 1];
        size_t outbuf_used = 0;
        ssize_t result;
    
        while(1)
        {
            char ch;
            result = recv(fd, &ch, 1, 0);
            if(result == 0)
            {
                break;
            }
            else if(result == -1)
            {
                perror("read failed");
                return -1;
            }
    
            if(outbuf_used < sizeof(outbuf))
            {
                outbuf[outbuf_used++] = rot13_char(ch);
            }
            if(ch == '\n')
            {
                send(fd, outbuf, outbuf_used, 0);
                outbuf_used = 0;
                continue;
            }
        }
    }
    
    int main(int argc, char *argv[])
    {
        int listener_fd = tcp_server_listen(SERV_PORT);
        signal(SIGCHLD, sigchld_handler);
        while(1)
        {
            struct sockaddr_storage ss;
            socklen_t slen = sizeof(ss);
            int fd = accept(listener_fd, (struct sockaddr *)&ss, &slen);
            if(fd < 0)
            {
                perror("accept failed");
                return -1;
            }
            if(fork()  == 0)
            {
                close(listener_fd);
                child_run(fd);
                exit(0);
            }
            else
            {
                close(fd);
            }
        }
        return 0;
    }
    

  • 相关阅读:
    IDEA运行测试错误Failed to resolve org.junit.platform:junit-platform-launcher
    mysql索引基本原理
    as3.0声音波形系列03_十组合
    as3.0声音波形系列02_六组合
    as3.0声音波形系列01_八组合
    FiltersEffect(效果)
    AS3 TransitionManager 自带特效类
    as3 判断鼠标移动方向
    AS3代码生成xml方法
    求线段的交点
  • 原文地址:https://www.cnblogs.com/whiteBear/p/16062952.html
Copyright © 2020-2023  润新知