第14章 高级IO

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/82829136

请移步到:

http://note.youdao.com/noteshare?id=d654b94cb2f69813324821b779a79338&sub=42F0C7DC55444E6D8680570BFB0B15CF

14.2 非阻塞I/O

系统调用分成两类: “低速”系统调用和其他。

低速系统调用可能会使进程永远阻塞

包括:

  1. 如果某些文件类型(如读管道、终端设备和网络设备)的数据并不存在,读操作可能会使调用者永远阻塞;
  2. 如果数据不能被相同的文件类型立即接受(如管道中无空间、网络流控制),写操作可能会使调用者永远阻塞;
  3. 在某种条件发生之前打开某些文件类型可能会发生阻塞(如要打开一个终端设备,需要先等待与之连接的调制解调器应答,又如若以只写模式打开FIFO,那么在没有其他进程已用读模式打开该FIFO时也要等待);
  4. 对已经加上强制性记录锁的文件进行读写;
  5. 某些ioctl操作;
  6. 某些进程间通信函数(见第15章)。

注意:

我们也曾说过,虽然读写磁盘文件会暂时阻塞调用者,但并不能将与磁盘IO有关的系统调用视为“低速".

对于open,write和read有两种方法使其非阻塞

(1)如果调用open获得描述符,则可指定O_NONBLOCK标志(见3.3节)。

(2)对于调已经打开的一个描述符,则可用fcntl,由该函数打开O_NONBLOCK文件状态标志(见3.14节),图3-12中的函数可用来为一个描述符打开任一文件状态标志。

14.3 记录锁

记录锁(record locking)的功能是:

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

“记录”这个词是一种误用,因为UNIX系统内核根本没有使用文件记录这种概念。

一个更适合的术语可能是字节范围锁 (byte-range locking),因为它锁定的只是文件中的一个区域(也可能是整个文件)。

  1. struct flock {
  2. short l_type; /* 锁类型: F_RDLCK, F_WRLCK, F_UNLCK */
  3. short l_whence; /* 取值为SEEK_SET(文件头), SEEK_CUR(文件当前位置), SEEK_END(文件尾) */
  4. off_t l_start; /* 相对于l_whence字段的偏移量 */
  5. off_t l_len; /* 需要锁定的长度 */
  6. pid_t l_pid; /* 当前获得文件锁的进程标识(F_GETLK) */
  7. };

l_whence + l_start 指定起始位置

l_len 指定要上锁的字节范围

如:

当前区域若有一把写锁,则不能再加任何锁

当前区域若有一把读锁,可以再有一把读锁,新的锁会替换旧的锁,

强制锁和建议性锁

如果一个进程试图读(read)或写(write)一个强制性锁起作用的文件,而欲读、写的部分又由其他进程加上了锁,此时会发生什么呢?

对这一问题的回答取决于3方面的因素;

  1. 操作类型(read或write)
  2. 其他进程持有的锁的类型(读锁或写锁)以及read或write的描述符
  3. 是阻塞还是非阻塞的

图14-11列出了8种可能性,

执行exec后,fork后的新程序可以继承原执行程序的文件锁。但是,如果一个文件描述符设置了close-on-exec标志,在执行exec时,会关闭该文件描述符,所以对应的锁也就被释放了,也就无所谓继承了。

注意:这里的文件锁不是线程的读写锁

14.4 I/O多路转接

以下是问题实例:

telnet程序从终端(标准输入)读,将所得数据写到网络连接上,同时从网络连接读,将所得数据写到终端上(标准输出),在网络连接的另一端, telnetd守护进程读用户键入的命令,并将所读到的送给shell,这如同用户登录到远程机器上一样。telnetd守护进程将执行用户键入命令而产生的输出通过telnet命令送回给用户,并显示在用户终端上。图14-13显示了这种工作情景。

telnet进程有两个输入,两个输出。

我们不能对两个输入中的任一个使用阻塞read,因为我们不知道到底哪一个输入会得到数据。

处理这种特殊问题的一种方法是,将一个进程变成两个进程(用fork),每个进程处理一条数据通路。如果使用两个进程,则可使每个进程都执行阻塞read。

但是这也产生了问题:

操作什么时候终?

如果子进程接收到文件结束符(telnetd守护进程使网络连接断开),那么该子进程终止,然后父进程接收到SIGCHLD信号。

但是,如果父进程终止(用户在终端上键入了文件结束符),那么父进程应通知子进程停止。

为此可以使用一个信号(如SIGUSR1),但这使程序变得更加复杂。

我们可以不使用两个进程,而是用一个进程中的两个线程。虽然这避免了终止的复杂性,但却要求处理两个线程之间的同步,在复杂性方面这可能会得不偿失。

上面的问题是,既要让系统调用一直在那里检测条件是否满足,又要不能因阻塞而影响下面的程序执行,

解决方法:

1.轮询:

优点:

简单

缺点:

浪费CPU时间

2.异步I/O——通过信号的方式控制

优点:

不浪费CPU时间

缺点:

每种Unix的处理不同,移植性差

对信号的处理更复杂,即每个需要轮询但不阻塞的调用,都要设置一个信号,不然不知道该怎样处理

3.I/O多路转接

1.select函数

int select(int nfds, fd_set *restrict readfds,

fd_set *restrict writefds, fd_set *restrict errorfds,

struct timeval *restrict timeout);

FD_ISSET(int fd, fd_set *set) 这个函数很有意思,

  1. 当注册完一个文件后,再调用这个函数检查这个文件描述符,得到的值式非零,
  2. 当调用了select()后,再退出后,如果这个文件描述符的阻塞没有发生,则再调用这个函数就会返回0,如果发生了就会返回非0,
  3. 可以用于检测阻塞有没有发生

timerval结构用来描述一段时间长度,如果在这个时间内,需要监视的文件描述符没有之间发生则函数返回,返回值为0。

struct timeval {

long tv_sec; /* seconds */

long tv_usec; /* microseconds */

};

2.select的变体pselect函数:

改进:

更精准的超时时间:

select的超时值用timeval结构指定, timespec结构以秒和纳秒表示超时值,而非秒和微秒。

pselect的超时值被声明为const,这保证了调用pselect不会改变此值。

pselect可使用可选信号屏蔽字。

siamask=NULL,那么在与信号有关的方面, pselect的运行状况和select相同。

否则, sigmask指向一信号屏蔽字,在调用, pselect时,以原子操作的方式安装该信号屏蔽字。在返回时,恢复以前的信号屏蔽字。

3.poll函数

timeout ==-1 永远等待

timeout ==0 不等待

timeout> 0 等待tiweout毫秒

优点:不会浪费太多CPU时间,且达到while加非阻塞的效果

缺点:

1.select和poll本身时阻塞式的,当里面的条件没满足或者超时,就会一直阻塞,

2.这两个函数可以同时注册多个阻塞,但是只要有一个阻塞发生就会马上退出函数,而不会等其它阻塞发生,更不会等全部阻塞发生后才退出

14.5 异步I/O

3.6.6.1、何为异步IO

(1)几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统。

(2)异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。

类似中断

3.6.6.2、涉及的函数:

(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)

设置文件的异步IO属性

(2)signal或者sigaction(SIGIO)

注册异步IO处理函数

3.6.3.代码实践

优点:

1.类似中断处理,可以一直检测,

2.当一个鼠标阻塞发生,然后执行相应处理函数后,还可以继续检测鼠标阻塞,而不用手动又来重新添加一个鼠标阻塞,

缺点:还没发现

展望:如何撤销这个阻塞事件

14.6 函数readv和writev

参考这个博客:

https://blog.csdn.net/ieearth/article/details/46730669

注意这个读和写是阻塞的

14.7 函数readn和writen

参考这个博客:

https://blog.csdn.net/atefrice/article/details/47861891

14.7 存储映射I/O

内存的I/O映射是将文件某区域的内容映射到进程的虚拟空间的技术

通过对文件的内存IO映射,可使用用户对文件的操作转换为对内存的操作,这样不仅使用方便而且提高了存储速度。

参考我的博客:

https://blog.csdn.net/qq_40732350/article/details/82828740

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/82829136