linux网络高级编程之 fcntl和select使用

	connet、recv、send都是阻塞性函数,若资源没有准备好,则调用该函数的进程将进入睡眠状态,这样就无法处理I/O 多路复用的情况了。本节给出了两种解决I/O 多路复用的解决方法,fcntl 和select。可以看到,由于在Linux中把socket也作为一种特殊文件描述符,这给用户的处理带来了很大的方便。 

1.fcntl 

函数fcntl针对socket编程提供了如下的编程特性。  ·  非阻塞I/O:可将cmd设置为F_SETFL,将lock设置为O_NONBLOCK。 
	O_NONBLOCK 以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中。
·  信号驱动I/O:可将cmd设置为F_SETFL,将lock设置为O_ASYNC。 

(1)fcntl函数说明  在文件已经共享的情况下如何操作,也就是当多个用户共同使用、操作一个文件的情况,这时,Linux通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。 文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。所谓建议性锁就是假定人们都会遵守某些规则去干一件事。例如,人与车看到红灯都会停,而看到绿灯才会继续走,我们可以称红绿等为建议锁。但这只是一种规则而已,你并不防止某些人强闯红灯。而强制性锁是你想闯红灯也闯不了。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。
	建议性锁只在cooperating processes之间才有用,对cooperating process的理解是最重要的,它指的是会影响其它进程的进程或被别的进程所影响的进程。
	在Linux中,实现文件上锁的函数有lock和fcntl,其中lock用于对文件施加建议性锁,而fcntl 不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁。 记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁。在任何只对于文件只能有一个write lock存在,read lock和wrtie lock不能共存。

2.select 
	使用fcntl 函数虽然可以实现非阻塞I/O 或信号驱动I/O,但在实际使用时往往会对资源是否准备完毕进行循环测试,这样就大大增加了不必要的CPU资源。在这里可以使用select函数来解决这个问题,同时,使用select函数还可以设置等待的时间,可以说功能更加强大。
	(1)select函数说明 
前面的fcntl 函数解决了文件的共享问题,接下来该处理I/O 复用的情况了。 总的来说,I/O 处理的模型有5种。 
·  阻塞I/O模型:在这种模型下,若所调用的I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。 
·  非阻塞模型:在这种模型下,当请求的I/O 操作不能完成时,则不让进程睡眠,而且返回一个错误。非阻塞I/O使用户可以调用不会永远阻塞的I/O 操作,如open、write
和read。如果该操作不能完成,则会立即出错返回,且表示该I/O 如果该操作继续执行就会阻塞。
·  I/O多路转接模型:在这种模型下,如果请求的I/O 操作阻塞,且它不是真正阻塞I/O,而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如本节要介绍的select函数和poll 函数,就是属于这种模型。 
·  信号驱动I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O 操作决定的。 
·  异步I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O 时,进程会通知内核。现在,并不是所有的系统都支持这种模型。 可以看到,select的I/O 多路转接模型是处理I/O 复用的一个高效的方法。它可以具体设置每一个所关心的文件描述符的条件、希望等待的时间等,从select函数返回时,内核会通知用户已准备好的文件描述符的数量、已准备好的条件等。通过使用select返回值,就可以调用相应的I/O 处理函数了。 
	具体分类可参考前一篇博文:Linux——I/O复用http://blog.csdn.net/xqmoo8/article/details/7861829

#include <sys/select.h>

int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds,
struct timeval *restrict timeout);

(1)参数nfds:指定待测试描述字(就是描述符)的个数(不是最大值,概念上一定要区分),因为描述符是从0开始的,所以他的值就是待测试的最大描述符加1(多一个0)。
(2)参数readfds、writefds、errorfds:3个参数都是fd_set类型。这3个参数在函数调用地不同时段有两种用途:
	(2.1)作为参数传入:我们把关心的描述符按照关心事件的事件类型分别放入其中。怎么放?见下。
	(2.2)作为函数返回时的返回值:这3个参数装载这我们关心并且I/O条件就绪的描述符,未就绪的描述被清楚了。所以每次调用select时都要重新设置这三个参数,这非常重要。
(3)参数timeout:select中地类型,超时间类型分别:

struct timespec{
__time_t tv_sec;  /*seconds 秒*/
long int tv_nsec; /*nanoseconds 纳秒*/
} 

struct timeval {
__kernel_time_t         tv_sec;         /* seconds */
__kernel_suseconds_t    tv_usec;        /* microseconds */
};

(4)参数sigmask:信号屏蔽掩码。
 
(1)描述符fd和描述符集合fdset操作宏:
void FD_CLR(int fd, fd_set *fdset);//congfdset删除fd。
int FD_ISSET(int fd, fd_set *fdset);//测试fd是否在fdset当中
void FD_SET(int fd, fd_set *fdset);//向fdset添加fd
void FD_ZERO(fd_set *fdset);//清零,类似bzero。
(2)select的FD_SETSIZE限制。
现在的linux所能打开的描述符个数,只受内存和管理性的限制。但由于在select函数设计之初,select的内部实现参考了FD_SETSIZE,就是select最多能处理的描述符个数是FD_SETSIZE个,一般为1024。
#define __FD_SETSIZE 1024
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值