多路I/O转接


与select函数不同,poll不是为每个状态(可读性、可写性和异常状态)构造一个描述符集,而是构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及其所关心的状态


readv和writev函数


作用:在一次函数调用中读、写多个非连续缓存区


总结:应当用尽量少的系统调用次数来完成任务。如果只写少量的数据,会发现自己复制数据然后使用一次write会比用writev更合算。但也可能发现,这样获得的性能提升并不值得,因为管理中间缓冲区会增加程序复杂度。


readn和writen函数


针对管道、FIFO以及某些设备,特别是终端、网络和STREAMS设备,一次读、写返回值可能小于要求值的情况,apue中定义了这两个函数,用于处理这类情况。


用户CPU时间、系统CPU时间、时钟时间


在阅读本章的过程,一直对三个概念比较模糊,即:用户CPU时间、系统CPU时间、时钟时间。参考了网上几篇文章后,总结如下:

  • 时钟时间:就是一个进程从开始运行到结束运行后,你的时钟走过了多少时间,这其中包含了进程在阻塞和等待状态的时间。

  • 用户CPU时间:就是用户的进程获得了CPU资源以后,在用户态执行的时间。

  • 系统CPU时间:用户进程获得了CPU资源以后,在内核态的执行时间。


习题


14.1

#include "apue.h"
#include <fcntl.h>
#include <errno.h>
void sigint(int signo)
{
}
int main(void)
{
    pid_t pid1, pid2, pid3;
    int fd;
    
    setbuf(stdout, NULL); // 将标准输出设置为无缓存
    signal_intr(SIGINT, sigint);
    if ((fd = open("lockfile", O_RDWR|O_CREAT, 0666) < 0))
    {
        err_sys("can't open/creat lockfile");
    }
    if ((pid1 = fork()) < 0)
    {
        err_sys("fork failed");
    }
    else if (0 == pid1)
    {
        if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0)
        {
            err_sys("child1: can't read-lock file");
        }
        printf("child1: obtained read-lock on file\n");
        pause(); // 使调用进程挂起直至捕获一个信号 
        printf("child1: exit after pause\n");
        exit(0);
    }
    else
    {
        sleep(2);
    }
    if ((pid2 = fork()) < 0)
    {
        err_sys("fork failed");
    }
    else if (0 == pid2)
    {
        if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0)
        {
            err_sys("child2: can't read-lock file");
        }
        printf("child2: obtained read-lock on file\n");
        pause();
        printf("child2: exit after pause\n");
        exit(0);
    }
    else
    {
        sleep(2);
    }
    if ((pid3 = fork()) < 0)
    {
        err_sys("fork failed");
    }
    else if (0 == pid3)
    {
        // 0和SEEK_SET决定了加锁区域的开始位置, 即从文件头部开始偏移量为0的地方开始加锁
        // 加锁失败立刻返回
        if (lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0) // 长度为0意味着到文件尾
        {
            printf("child3: can't set write-lock: %s\n", strerror(errno));
        }
        printf("child3 about to block in write-lock...\n");
        // F_SETLKW为F_SETLK的阻塞版本, 若加锁失败则进程进入休眠, 直至可以加锁或被信号中断唤醒
        if (lock_reg(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0)
        {
            err_sys("child3: can't write-lock file");
        }
        printf("child3 return and got write lock???\n");
        pause();
        printf("child3: exit after pause\n");
        exit(0);
    }
    else
    {
        sleep(2);
    }
    
    if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0)
    {
        printf("parent: can't set read-lock: %s\n", strerror(errno));
    }
    else
    {
        printf("parent: obtained additional read-lock while, write-lock is pending\n");
    }
    printf("killing child1...\n");
    kill(pid1, SIGINT);
    printf("killing child2...\n");
    kill(pid2, SIGINT);
    printf("killing child3...\n");
    kill(pid3, SIGINT);
    exit(0);
}


14.2

在Linux下fd_set的定义为一个包含长×××数组的结构,为什么不直接定义成数组,原因是结构体之间可以通过C语言的赋值运算符直接赋值。

FD_* 都是通过内嵌汇编实现的位操作