美文网首页LinuxLinux学习之路
APUE读书笔记-17高级进程通信(17)

APUE读书笔记-17高级进程通信(17)

作者: QuietHeart | 来源:发表于2020-08-22 09:29 被阅读0次

使用select实现的loop函数

loop函数导致服务进程进入无限死循环。我们将会看到这个函数的两个版本。下面的代码显示了使用select的版本。后面的代码将会显示使用poll的版本。

#include    "opend.h"
#include    <sys/time.h>
#include    <sys/select.h>

void loop(void)
{
    int     i, n, maxfd, maxi, listenfd, clifd, nread;
    char    buf[MAXLINE];
    uid_t   uid;
    fd_set  rset, allset;

    FD_ZERO(&allset);

    /* obtain fd to listen for client requests on */
    if ((listenfd = serv_listen(CS_OPEN)) < 0)
        log_sys("serv_listen error");
    FD_SET(listenfd, &allset);
    maxfd = listenfd;
    maxi = -1;

    for ( ; ; ) {
        rset = allset;  /* rset gets modified each time around */
        if ((n = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0)
            log_sys("select error");

        if (FD_ISSET(listenfd, &rset)) {
            /* accept new client request */
            if ((clifd = serv_accept(listenfd, &uid)) < 0)
                log_sys("serv_accept error: %d", clifd);
            i = client_add(clifd, uid);
            FD_SET(clifd, &allset);
            if (clifd > maxfd)
                maxfd = clifd;  /* max fd for select() */
            if (i > maxi)
                maxi = i;   /* max index in client[] array */
            log_msg("new connection: uid %d, fd %d", uid, clifd);
            continue;
        }
        for (i = 0; i <= maxi; i++) {   /* go through client[] array */
            if ((clifd = client[i].fd) < 0)
                continue;
            if (FD_ISSET(clifd, &rset)) {
                /* read argument buffer from client */
                if ((nread = read(clifd, buf, MAXLINE)) < 0) {
                    log_sys("read error on fd %d", clifd);
                } else if (nread == 0) {
                    log_msg("closed: uid %d, fd %d",
                      client[i].uid, clifd);
                    client_del(clifd);  /* client has closed cxn */
                    FD_CLR(clifd, &allset);
                    close(clifd);
                } else {    /* process client's request */
                    request(buf, nread, clifd, client[i].uid);
                }
            }
        }
    }
}

这个函数调用serv_listen创建服务器端用于接收客户连接请求。剩下的部分就是一个循环,循环首先使用select调用。select返回的时候,可以确保两件事情:

  1. 文件描述符号listenfd已经准备好了读取,也就是说,有某个新的客户进程调用了cli_conn。为了处理这个,我们调用serv_accept并且之后更新客户进程的数组以及相关的记录索引信息。(我们保存最大的文件描述符号号码以便传递select的第一个参数,我们也保存客户数组中被使用元素的最大索引)
  2. 一个已经存在的客户连接准备好了被读取。这个意思是说,客户进程要么结束了,要么发送了一个新的请求。我们通过read返回0(文件结束符号)来确定客户进程已经终止。如果read返回正数,那么表明需要处理一个新的请求,这个请求通过我们调用request来进行处理。

我们在allset文件描述符号集合中保存当前使用的文件描述符号。一个新的客户连接到服务器上的时候,会打开这个文件描述符号集合中的合适的位;当客户进程终止的时候,会关闭相应其中合适的位。

无论客户进程的终止是自发的还是非自发的,当它终止的时候,我们经常能够知道,因为所有的客户的文件描述符号(包括连接到服务进程的)都会被内核自动地关闭。这一点和XSI的IPC机制有所不同。

相关文章

网友评论

    本文标题:APUE读书笔记-17高级进程通信(17)

    本文链接:https://www.haomeiwen.com/subject/gghwrktx.html