Chinaunix首页 | 论坛 | 博客
  • 博客访问: 521948
  • 博文数量: 95
  • 博客积分: 1415
  • 博客等级: 上尉
  • 技术积分: 1202
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-20 01:23
文章分类

全部博文(95)

文章存档

2010年(28)

2009年(67)

我的朋友

分类: C/C++

2009-09-09 21:09:45

select()系统调用 && 文件描述符集fd_set
 
例程:
---------------------------------------------------------
#include
#include
#include
#include
#include
#include
int main ()
{
    int keyboard;
    int ret, i;
    char c;
    fd_set readfd;
    struct timeval timeout;
    keyboard = open("/dev/tty", O_RDONLY | O_NONBLOCK); //(1)
    assert(keyboard>0);                                
    while(1) {
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        FD_ZERO(&readfd);
        FD_SET(keyboard, &readfd);
        ret = select(keyboard+1, &readfd, NULL, NULL, &timeout);
        if(FD_ISSET(keyboard, &readfd)) {
            i = read(keyboard, &c, 1);
            if('\n' == c)
                continue;
            printf("the input is %c\n",c);
            if ('q'==c)
                break;
        }
    }
}

(1) int open(const char *pathname, int flags);
    O_NONBLOCK   
    如果pathname指的是一个FIFO、 一个块特殊文件或者一个字符特殊文件,则此选项将此文件的本次打开操作和后续的I/O操作设置为非阻塞方式
    非阻塞I/O使我们可以调用不会永远阻塞的I/O操作,例如open, read和write。 如果这种操作不能完成,则立即出错返回,表示该操作如果继续执行将阻塞下去。


select()系统调用
---------------------------------------------------------
    在网络程序中,一个进程同时处理多个文件描述符是很常见的情况。select()系统调用可以使进程检测同时等待的多个I/O设备,当没有设备准备好时,select()阻塞,其中任一设备准备好时,select()就返回。
    这个函数真是神通广大,用来定时也不错。select(0, NULL, NULL, NULL, &tv); tv在每次执行select前都要重新设定一遍,不然就变成0了。感觉它的精度比usleep()要高一些。

    select()的调用形式为:
    #include
    #include
    int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout);

    select的第一个参数是文件描述符集中要被检测的比特数,这个值必须至少比待检测的最大文件描述符大1;参数readfds指定了被读监控的文件描述符集;参数writefds指定了被写监控的文件描述符集;而参数exceptfds指定了被例外条件监控的文件描述符集。
    参数timeout起了定时器的作用:到了指定的时间,无论是否有设备准备好,都返回调用。timeval的结构定义如下:
    struct timeval{
        long tv_sec; //秒
        long tv_usec; //微秒
    }
    timeout取不同的值,该调用就表现不同的性质:
    timeout为0,调用立即返回;
    timeout为NULL,select()调用就阻塞,直到知道有文件描述符就绪;
    timeout为正整数,就是一般的定时器。
    select调用返回时,除了那些已经就绪的描述符外,select将清除readfds、writefds和exceptfds中的所有没有就绪的描述符。select的返回值有如下情况:
    正常情况下返回就绪的文件描述符个数;
    经过了timeout时长后仍无设备准备好,返回值为0;
    如果select被某个信号中断,它将返回-1并设置errno为EINTR。
    如果出错,返回-1并设置相应的errno。

    系统提供了4个宏对描述符集进行操作:
    #include
    #include
    void FD_SET(int fd, fd_set *fdset);
    void FD_CLR(int fd, fd_set *fdset);
    void FD_ISSET(int fd, fd_set *fdset);
    void FD_ZERO(fd_set *fdset);

    FD_SET    设置文件描述符集fdset中对应于文件描述符fd的位(设置为1)
    FD_CLR    清除文件描述符集fdset中对应于文件描述符fd的位(设置为 0)
    FD_ZERO   清除文件描述符集fdset中的所有位(既把所有位都设置为0)。
    使用这3个宏在调用select前设置描述符屏蔽位

    在调用select后使用
    FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置。


fd_set
---------------------------------------------------------
typedef __kernel_fd_set     fd_set;

typedef struct {
    unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;

#define __FDSET_LONGS   (__FD_SETSIZE/__NFDBITS)
#define __FD_SETSIZE    1024
#define __NFDBITS       (8 * sizeof(unsigned long))


    过去,描述符集被作为一个整数位屏蔽码得到实现,但是这种实现对于多于32个的文件描述符将无法工作。描述符集现在通常用整数数组中的位域表示,数组元素的每一位对应一个文件描述符。例如,一个整数占32位,那么整数数组的第一个元素代表文件描述符0到31,数组的第二个元素代表文件描述符32到63,以此类推。宏FD_SET设置整数数组中对应于fd文件描述符的位为1,宏FD_CLR设置整数数组中对应于fd文件描述符的位为0,宏FD_ZERO设置整数数组中的所有位都为0。

    假设执行如下程序后:
    #include
    #include
    fd_set readset;
    FD_ZERO(&readset);
    FD_SET(5, &readset);
    FD_SET(33, &readset);
    再执行如下程序后:
    FD_CLR(5, &readset);

    通常,操作系统通过宏_FD_SETSIZE来声明在一个进程中select所能操作的文件描述符的最大数目。例如:
    #define _FD_SETSIZE 1024
    既定义_FD_SETSIZE为1024,一个整数占4个字节,既32位,那么就是用包含32个元素的整数数组来表示文件描述符集。我们可以在头文件中修改这个值来改变select使用的文件描述符集的大小,但是必须重新编译内核才能使修改后的值有效。当前版本的unix操作系统没有限制_FD_SETSIZE的最大值,通常只受内存以及系统管理上的限制。




select和poll()系统调用查询是否可对设备进行无阻塞的访问
---------------------------------------------------------
调用select(),确保在fd可写的情况下调用write操作,避免了write阻塞

select(fd + 1, &rfds, &wfds, NULL, NULL);
if (FD_ISSET(fd, &wfds)) {
    printf("Poll monitor:can be written\n");
    ret = write(fd, buf, 13);
    if (ret < 0)
        perror("write error");
}
阅读(2038) | 评论(0) | 转发(0) |
0

上一篇:errno

下一篇:关于socket中的超时处理笔记

给主人留下些什么吧!~~