精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>C/C++>>资料汇编----------藏经阁>>Unix编程FAQ>>Unix编程FAQ 第2章

主题:Unix编程FAQ 第2章
发信人: tengel()
整理人: softlag(2000-08-24 01:58:39), 站内信件
我和我的一个朋友打算吧这本书翻译一下,他负责第一章,
可惜这两天他有事,我只好先贴第二章,这是第一节,请诸
位大虾指教


2.普通文件处理(包括管道和套接字)
参照套接字FAQ:

http://www.ibrado.com/sock-faq/ 

http://kipper.york.ac.uk/~vic/sock-faq/ 

ftp://rtfm.mit.edu/pub/usenet/news.answers/unix-faq/socket 

2.1如何管理多连接?
我不得不同时监视超过一个的文件描述符/连接/流(fd/connection/stream).我如
何管理他们?

使用 select() 或者 poll()

提示:select() 是从BSD引入的, 然而 poll()是一个SysV流的产物.像这样,对于
可移植的产品要考虑到,纯粹的BSD系统仍然缺少poll()函数,而且一些就版本的 
SVR3系统可能没有select()函数.SVR4添加了select()函数,Posix.lg对他们都进
行了定义.

注意:不管select还是poll在应用到普通文件时都没有什么作用,他们主要应用在
套接字,管道,ptys和可能的其他字符设备,这些都与具体的系统有关.

2.1.1如何使用select()?
select起初是基于fd_set的概念,fd_set时FDs的集合,通常使用位向量来实现.在
过去,它通常假定FDs小于32,正好可以使用一个整数来存储这个集合,但是现在,通
常需要使更多的Fds可用,所以使用下面的标准宏来维护fd_sets是很重要的:

fd_set set;
FD_ZERO(&set);      /* empties the set */
FD_SET(fd,&set);    /* adds FD to the set */
FD_CLR(fd,&set);    /* removes FD from the set */
FD_ISSET(fd,&set)   /* true if FD is in the set */

在大多数情况下,确定fdsets能够处理文件描述符的范围是系统的责任,但是在一
些情况下,你可能需要预定义FD_SETSIZE宏,这与具体的系统有关;请查看你的sel
ect手册.另外,一些系统在select函数中处理超过1024个文件描述符时会产生问题
.

select的基本接口很简单:

int select(int nfds, fd_set *readset, 
                     fd_set *writeset,
                     fd_set *exceptset, struct timeval *timeout);

说明:

nfds
要检查的FDs的数量;必须大于fdsets中的FD的最大值,不是真实的FDs的指定
readset
要检查的可读的FDs的集合
writeset
要检查的可写的FDs的集合
exceptfds 
要检查的产生异常(提示:错误不是异常)的FDs的集合
timeout 
对于无限超时为空,或者是指向指定最大等待时间的timeval时间值(如果tv_sec和
tv_usec都等于0,那么FDs的状态将被监测,但是调用不会被阻塞)

这个调用返回已经"准备好"的FDs的数量,三种fdsets被相应的修改,仅仅"准备好
"的FDs在集合里.可以使用FD_ISSET宏来检查该集合.

下面是测试单个FD可读的简单的例子

int isready(int fd)
{
    int rc;
    fd_set fds;
    struct timeval tv;

    FD_ZERO(&fds);
    FD_SET(fd,&fds);
    tv.tv_sec = tv.tv_usec = 0;

    rc = select(fd+1, &fds, NULL, NULL, &tv);
    if (rc < 0)
return -1;

return FD_ISSET(fd,&fds) ? 1 : 0;
}

提示:对于我们不感兴趣的fdsets,我们可以传递NULL参数给select函数

2.1.2如何使用poll函数
poll函数接收一个pollfd结构的列表的指针,pollfd结构存储你希望检查的描述符
和相应的事件.poolfd结构中的events用来指定事件的位掩码,用来标识你感兴趣
的事件.该结构的实例将在后面填充,当你指定的事件发生时,poll返回SVR4在pol
l.h中定义了一些宏,用来指定events字段.超时可以被指定为毫秒,用整数来表示
.超时为0时,poll函数立即返回,指定为-1时,poll将在事件触发之前一直挂起.
struct pollfd {
int fd; /* The descriptor. */
short events; /* The event(s) is/are specified here. */
short revents; /* Events found are returned here. */
};
有点像select,返回值反映了有多少满足事件的描述符.如果超时则返回0.当产生
错误时,函数离子返回一个负数.

如果没有事件产生,revents被清空,对此你不需要做任何事.

返回的revents值被用来测试包含的事件.下面是一个例子:
/* Poll on two descriptors for Normal data, or High priority data.
If any found call function handle() with appropriate descriptor
and priority. Don't timeout, only give up if error, or one of the
descriptors hangs up. */

#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <stropts.h>
#include <poll.h>

#include <unistd.h>
#include <errno.h>
#include <string.h>

#define NORMAL_DATA 1
#define HIPRI_DATA 2

int poll_two_normal(int fd1,int fd2)
{
    struct pollfd poll_list[2];
    int retval;

    poll_list[0].fd = fd1;
    poll_list[1].fd = fd2;
    poll_list[0].events = POLLIN|POLLPRI;
    poll_list[1].events = POLLIN|POLLPRI;

    while(1)
    {
        retval = poll(poll_list,(unsigned long)2,-1);
        /* Retval will always be greater than 0 or -1 in this case.
           Since we're doing it while blocking */

        if(retval < 0)
{
fprintf(stderr,"Error while polling: %s\n",strerror(errno)
);
return -1;
}

if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
((poll_list[0].revents&POLLERR) == POLLERR) ||
((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
((poll_list[1].revents&POLLHUP) == POLLHUP) ||
((poll_list[1].revents&POLLERR) == POLLERR) ||
((poll_list[1].revents&POLLNVAL) == POLLNVAL))
return 0;

if((poll_list[0].revents&POLLIN) == POLLIN)
handle(poll_list[0].fd,NORMAL_DATA);
if((poll_list[0].revents&POLLPRI) == POLLPRI)
handle(poll_list[0].fd,HIPRI_DATA);
if((poll_list[1].revents&POLLIN) == POLLIN)
handle(poll_list[1].fd,NORMAL_DATA);
if((poll_list[1].revents&POLLPRI) == POLLPRI)
handle(poll_list[1].fd,HIPRI_DATA);
}
}

2.1.3我能够在使用select或者poll的同时使用SysV的IPC吗?
不能(除了IBM的AIX系统,通过复杂的组合来实现这个功能)
一般情况下,试图联合使用select或者poll和SysV消息队列是很麻烦的.SysV IPC
对象不被作为文件描述符处理,所以他们不能传递给select和poll.对于这个问题
有难度不同的多种解决措施:

.完全放弃SysV IPC
.fork(),由子进程来处理SysV IPC,子进程与父进程之间通过管道或者套接字来通
信,那么父进程可以调用select
.同上,但是有子进程调用select,与父进程之间的通信使用消息队列
.安排进程发送消息给你,对于每个消息发送相应信号.警告:正确的处理是很重要
的,使用这种方法写的代码很容易发生丢失消息或者死锁.
还有其他办法.

--
_
O @___ G : \
/|__ /|/ /\|\ : \
/|/_ / /\ \ X_ : \
/ | __/ \ | \ 0 : \
/ 0 0| / : \

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 210.14.224.57]

[关闭][返回]