unix环境高级编程-高级IO

非阻塞IO:之前曾经把系统调用分为两类,低速系统调用和其他,低速系统调用可能会使进程永远阻塞的一类系统调用。包括:

某些文件类型(终端设备、读管道)的数据并不存在,读操作可能会使调用者永远阻塞。

如果数据不能被相同的文件类型立即接受,写操作可能会使调用者永远阻塞

对已经加上强制记录锁的文件进行读写

某些ioctl操作

某些进程通信函数


非阻塞IO使我们可以发出open、read和write这样的IO操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回,表示该操作如继续执行将阻塞。

对于一个给定的描述符,有两种为其指定非阻塞IO的方法:

1.如果调用open获得描述符,则可指定O_NONBLOCK标志

2.对于已经打开的一个描述符,则可调用fcntl,由该函数打开O_NONBLOCK文件状态标志


记录锁:

当两个人同时编辑一个文件时,该文件的最后状态取决于写该儿那件的最后一个进程,但有时候进程需要确保它正在单独写一个文件,也就是记录锁功能。

记录锁功能是当第一个进程正在读或修改一个文件时,使用记录锁可以阻止其他进程修改同一文件区。


IO多路转接:

当从一个描述符读,然后又写到另一个描述符时,可在下列形式的循环中使用阻塞IO


这种形式的阻塞IO到处可见,但如果必须从两个描述符中读,在这种情况下,我们不能在任何一个描述符上进行阻塞读(read),否则可能因为被阻塞在一个描述符的读操作而导致另一个描述符有数据也无法处理,因此需要另一种不同的技术。

Telnet命令的结构如下,


该程序从终端(标准输入)读,将所得数据写到网络连接上,同时从网络连接读,将所得数据写到终端上(标准输出)。在网络连接另一端Telnetd守护进程读用户键入的命令,并将所读到的送给shell,产生的输出通过Telnet命令送回给用户。

这个进程有两个输入,两个输出,不能对两个输入的任意一个使用阻塞read,因为不知道哪个输入会得到数据。

处理该种问题的方法是,将一个进程变成两个进程(fork),每个进程处理一条数据通路

unix环境高级编程-高级IO_第1张图片

如果使用两个进程,则可使每个进程都执行阻塞read,但是也有问题,操作什么时候终止?如果子进程接收大文件结束符,子进程终止。如果父进程终止,应通知子进程停止,这使程序复杂。

可以不使用两个进程,而是用一个进程中的两个线程。虽然这避免了终止的复杂性,却要求处理两个线程之间的同步,复杂性也高。

一种比较好的技术是使用IO多路转换,先构造一张我们感兴趣的描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行IO时,该函数返回。poll,pselect和select函数使我们能够执行io多路转接。


函数select:

select函数使我们可以执行IO多路转换,传给select的参数告诉内核:

1.我们所关心的描述符

2.对于每个描述符我们所关心的条件(是否想从一个给定的描述符读,是否想写一个给定的描述符)

3.愿意等待多长时间

从select返回时,内核告诉我们:

1.已准备好的描述符总数量

2.对于读,写或异常者三个条件中的每一个,哪些描述符已经准备好

使用这种返回信息,就可调用相应的IO函数(一般是read或write),并且确知该函数不会阻塞。

下面是select函数:

unix环境高级编程-高级IO_第2张图片

最后一个参数:指定愿意等待的时间长度,单位为秒和微秒,有以下三种情况:

tvptr==NULL 永远等待

tvptr->tv_sec == 0 && tvptr->tv_usec ==0 不等待

tvptr->tv_sec != 0 || tvptr->tv_usec !=0 等待指定的秒数和微秒数,如果超时还没有一个描述符准备好,返回值为0

中间三个参数readfds、writefds和exceptfds是指向描述符集的指针,这三个描述符集说明我们关心得可读、可写或处于异常条件的描述符集合。


函数poll:poll函数类似于select,但是程序接口有所不同


与select不同,poll不是为每个条件(可读,可写或异常条件)构造一个描述符集,而是构造一个pollfd结构的数组,每个数组的元素指定了一个描述符编号以及我们对该描述符感兴趣的条件。

poll最后一个参数指定等待时间,与select一样,有三种不同情形

timeout==-1 永远等待

timeout==0 不等待

timeout>0 等待timeout毫秒


你可能感兴趣的:(Linux)