25-练习
63.7 练习
63-1. 修改程序清单63-2(poll_pipes.c)中的程序,使用poll()来取代select()。
63-2. 编写一个echo服务器(见 60.2 节和60.3节),使其能够同时处理TCP和UDP客户端。要做到这一点,服务器端必须创建一个 TCP 监听套接字和一个UDP监听套接字,然后采用本章中所描述的其中一种技术来同时监视这两个套接字。
63-3. 63.5节中提到select()不能用来同时等待信号和文件描述符,并提出采用信号处理例程加管道的方式来解决。当一个程序需要在文件描述符和System V的消息队列上等待输入时,也会出现相似的问题(因为System V消息队列并不使用文件描述符)。一种解决方法是使用fork()生成一个单独的子进程,子进程从队列中拷贝每条消息到管道中,并且也将由父进程所监视的文件描述符一并拷贝。采用这种方法编写一个程序,通过select()来监视终端和消息队列上的输入。
63-4. 63.5.2节中介绍的self-pipe技巧中,最后一步表明程序应该首先将管道中的所有数据读取完毕,之后再执行信号处理的操作。如果这两步颠倒的话会出现什么问题?
63-5. 修改程序清单63-9中的程序(self_pipe.c),使用poll()来取代select()。
63-6. 编写一个程序使用epoll_create()来创建一个epoll实例,然后立刻调用epoll_wait()在之前返回的文件描述符上等待。在这种情况下,传递给epoll_wait()的兴趣列表是空的,此时会出现什么情况?这么做有什么用处?
63-7. 假设我们有一个epoll文件描述符,它正在监视的多个文件描述全部都一直处于就绪态。如果我们执行一系列的epoll_wait()调用,其中参数maxevents比处于就绪态的文件描述符数量小很多(例如,maxevents为1)。在每次调用之间,我们并不在处于就绪态的文件描述符上执行全部的I/O操作。此时在每次调用中epoll_wait()返回的描述符是什么?编写一个程序来确定答案。(基于这个实验的目的,在epoll_wait()调用之间不执行任何I/O操作就足够了。)为什么这种行为会很有用?
63-8. 修改程序清单63-3中的程序(demo_sigio.c),用实时信号取代SIGIO。修改信号处理例程,使其接受一个siginfo_t参数,并打印出这个结构体的si_fd和si_code字段。
①译者注:本章之前都是用文件描述符(file descriptor)来表示打开了某个文件,这一段又冒出来个文件描述(file description),而文件描述和文件描述符之间还有着关联。其实是这样的:文件描述(file description)表示的是一个打开文件的上下文信息(大小、内容、编码等与文件有关的信息),可以比喻为一个抽屉,这部分内容实际上是由内核来管理的。而用户空间的应用程序如果要操作文件怎么办。就是通过open()这样的系统调用向内核请求,然后内核分配给用户空间一个文件描述符(file descriptor)。这个文件描述符可以比喻为抽屉的把手(handle之所以翻译为“句柄”,这就是原因),有了这个把手(文件描述符),用户就可以操作抽屉(文件描述)里的内容了。但是,一个抽屉可以有多个把手(即文件描述可以对应多个文件描述符),只有当所有的把手(文件描述符)都关闭了,内核就知道此时没有用户空间的程序要用这个抽屉了(文件描述),那么就把它回收。 文件描述实际上是内核中的一个数据结构,而用户空间中的文件描述符只不过是一个整数,epoll的兴趣列表实际关注的是内核中的数据结构。所以作者在这里改进了一下之前的结论,说得更细,更准确,也符合这一节的主题“深入探究epoll的语义”。