您的位置:首页 > 理论基础 > 计算机网络

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

2016-09-23 10:19 369 查看
转自:http://blog.csdn.net/xqmoo8/article/details/7857836
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添加fdvoid FD_ZERO(fd_set *fdset);//清零,类似bzero。(2)select的FD_SETSIZE限制。现在的linux所能打开的描述符个数,只受内存和管理性的限制。但由于在select函数设计之初,select的内部实现参考了FD_SETSIZE,就是select最多能处理的描述符个数是FD_SETSIZE个,一般为1024。#define __FD_SETSIZE 1024
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: