《APUE》读书笔记-第十四章高级I/O

1、非阻塞I/O

对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况:
(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道、终端设备和网络设备)。
(2)如果数据不能立即被接受,则写这些同样的文件也会使调用者永远阻塞;
(3)在某些条件发生之前,打开文件会被阻塞(例如以只写方式打开一个FIFO,那么在没有其他进程已用读方式打开该FIFO时);
(4)对已经加上强制性锁的文件进行读、写;
(5)某些ioctl操作;
(6)某些进程间通信函数;

非阻塞I/O调用open、read和write等I/O操作函数使上述的慢速系统调用在不能立即完成的情况下,立即出错返回。

对一个给定的描述符有两种方法设置其为非阻塞:
(1)如果是调用open以获得该描述符,则可指定O_NONBLOCK标志;
(2)对于已经打开的一个描述符,则可调用fcntl打开O_NONBLOCK文件状态标志(注意:设置文件状态标志的方法)。

写个非阻塞I/O的程序,程序功能是从标准输入读入100 000字节,并试图将它们写到标准输出上。

View Code

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <fcntl.h>
 7
 8 char buf[100000];
 9 //将描述符设置为阻塞状态
10 void set_fl(int fd,int flags)
11 {
12     int val;
13     val = fcntl(fd,F_GETFL,0);  //获取描述符
14     val |= flags;   //添加状态
15     fcntl(fd,F_SETFL,val);  //设置描述符
16 }
17 void clr_fl(int fd,int flags)
18 {
19     int val;
20     val = fcntl(fd,F_GETFL,0);
21     val &= ~flags;   // 取消状态
22     fcntl(fd,F_SETFL,val);
23 }
24 int main()
25 {
26     int ntowrite,nwrite;
27     char *ptr;
28     ntowrite = read(STDIN_FILENO,buf,sizeof(buf));
29     fprintf(stderr,"read %d bytes\n",ntowrite);
30     set_fl(STDOUT_FILENO,O_NONBLOCK); //设置为阻塞状态
31     ptr = buf;
32     while(ntowrite > 0)
33     {
34         errno = 0;
35         nwrite = write(STDOUT_FILENO,ptr,ntowrite);
36         fprintf(stderr,"nwrite = %d,errno =%d\n",nwrite,errno);
37         if(nwrite > 0)
38         {
39             ptr += nwrite;
40             ntowrite -= nwrite;
41         }
42     }
43     clr_fl(STDOUT_FILENO,O_NONBLOCK); //设置为非阻塞状态
44     exit(0);
45 }

程序执行结果如下:若标准输出是普通文件,则可以期望write只执行一次。

若标识输出是终端,则期望write有时会返回小于100000的一个数字,有时会出错返回。按照下面要求执行,结果如图所示:

anker@killer-anker:~/Programs$ ./nonblock </lib/ld-linux.so.2 2> temp.file
anker@killer-anker:~/Programs$ cat temp.file

程序发出很多write操作,只有几个是正确输出数据的,其余的则出错返回。这种方式叫做轮询,在多用法系统上面浪费了CPU时间。

2、记录锁

  记录锁的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。对于UNIX,实际上,由于内核没有“记录”的概念,因此,“记录锁”实际上是“区域锁”。

fcntl记录锁:int fcntl(int fd, int cmd, ……/* struct flock *flockptr */);对于记录锁,cmd是F_GETLK,f_SETLK,或F_SETLKW。第三个参数是一个指向flock结构的指针:

struct flock {
short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */
off_t l_start;
short l_whence;
off_t l_len;
pid_t l_pid;
}

   关于加锁和解锁区域需要注意以下几点:该区域可以在当前文件尾端处开始或越过其尾端处开始,但不能在文件起始位置之前或越过该起始位置;若l_len为0,则表示锁的区域从其起点开始,直至最大可能位置为止。

  两种锁:共享读锁(L_RDLCK)和独占写锁(L_WRLCK)的基本规则是,多个进程在一个给定的字节上可以有一把共享的读锁。但是在一个给定字节上的写锁则只能由一个进程独用。加读锁时,该描述符必须是读打开;加写锁时,该描述符必须是写打开的。

不同类型锁之间的兼容性如下:


 


读锁

写锁

无锁

允许

允许

一把或多把读锁

允许

拒绝

一把写锁

拒绝

拒绝

关于记录锁的自动继承和释放有三条规则:

(1)锁与进程、文件两方面有关:第一,当一个进程终止时,它锁建立的锁全部释放;第二,任何时候关闭一个描述符,则该进程通过这一描述符可以访问的文件上的任何一把锁都被释放。

(2)由fork产生的子进程不继承父进程所设置的锁。

(3)在执行exec后,新程序可以继承原执行程序的锁。

3、I/O多路转接

  其基本思想是:先构造一张有关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好进行I/O时才返回。在返回时,它告诉进程哪一个描述符已准备好。select、pselect和poll函数能执行I/O多路转接。函数原型如下:

int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);返回准备就绪的描述符数,超时则为0,出错为-1

参数timeout指定内核等待的时间,其结构如下:

struct timeval {

long tv_sec; /* seconds */

long tv_usec; /* microseconds */

}

传给select的参数告诉内核:我们所关心的描述符;对于每个描述符我们所关心的条件;希望等待多长时间。

从select返回时,内核告诉我们:已准备好的描述符数量;哪一个描述符已准备好读、写或异常条件。
写个程序练习select函数,每隔5秒进行读取操作,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <sys/select.h>
 7
 8 int main()
 9 {
10     fd_set rfds;
11     struct timeval tv;
12     int retval;
13     char buf[1024];
14     for(;;)
15     {
16         FD_ZERO(&rfds);
17         FD_SET(STDIN_FILENO, &rfds);
18         /* Wait up to five seconds. */
19         tv.tv_sec = 5;
20         tv.tv_usec = 0;
21         retval = select(1, &rfds, NULL, NULL, &tv);
22         /* Don't rely on the value of tv now! */
23         if (retval)
24         {
25             printf("Data is available now.\n");
26             if(FD_ISSET(STDIN_FILENO, &rfds))
27             {
28                 read(STDIN_FILENO,buf,1024);
29                 printf("Read buf is: %s\n",buf);
30             }
31         }
32         else
33             printf("No data within five seconds.\n");
34     }
35     exit(0);
36 }

程序执行结果如下:

int pselect (int maxfdp1, fd_set *readset, fd_set * writeset, fd_set * exceptset, const struct timespec * timeout, const sigset_t *sigmask);

返回:准备好的描述字个数,0-超时,-1-出错。

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {

int fd; /* file descriptor */

short events; /* requested events */

short revents; /* returned events */

};

关于I/O多路转接将会在Unix网络编程中详细学习,在本章主要是了解这思想。

 4、异步I/O

  SVR4和4.3+BSD提供了使用一个信号(在SVR4中是SIGPOLL,在4.3+BSD中是SIGIO)的异步I/O方法,该信号通知进程,对某个描述符所关心的某个事件已经发生。异步I/O的一个限制是每个进程只有一个信号。

5、readv和writev函数

头文件:#include <sys/uio.h>
函数原形:     ssize_t readv(int filedes,const struct iovec *iov,int iovcnt);
                 ssize_t writev(int filedes,const struct iovec *iov,int iovcnt);
参数:filedes     文件描述符
         iov         指向iovec结构数组的一个指针。
         iovcnt     数组元素的个数
返回值:若成功则返回已读、写的字节数,若出错则返回-1

struct iovec {

  void *iov_base; /* 起始地址 */

  size_t iov_len; /* 需要传输的字节数 */

};

readv() 系统调用从文件描述符 fd 关联的文件里读取数据到 iovcnt 个由 iov 结果描述的缓存区里。(分散读)

writev() 系统调用把 iovcnt 个由 iov 结构描述的缓存区数据写入文件描述符 fd 关联的文件里。(聚合写)

写个程序从标准输入读取数据存放到多个缓冲区中,然后从标准输出显示结果,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/uio.h>
 6 #include <fcntl.h>
 7 #include <string.h>
 8
 9 int main(int argc,char *argv[])
10 {
11     int fd1,fd2;
12     char *buf1 = malloc(10);
13     char *buf2 = malloc(1024);
14     struct iovec iov[2];
15     memset(buf1,0,11);
16     memset(buf2,0,1025);
17     ssize_t nwritten;
18     iov[0].iov_base = buf1;
19     iov[0].iov_len = 10;
20     iov[1].iov_base = buf2;
21     iov[1].iov_len = 1024;
22     readv(STDIN_FILENO,iov,2);
23     printf("call readv:\n");
24     printf("buf1 is: %s\tlength is: %d\n",buf1,strlen(buf1));
25     printf("buf2 is: %s\tlength is: %d\n",buf2,strlen(buf2));
26     printf("call writev:\n");
27     iov[0].iov_base = buf1;
28     iov[0].iov_len = strlen(buf1);
29     iov[1].iov_base = buf2;
30     iov[1].iov_len = strlen(buf2);
31     nwritten = writev(STDOUT_FILENO, iov, 2);
32     free(buf1);
33     free(buf2);
34     exit(0);
35 }

程序结果如下:

时间: 2024-08-03 03:59:40

《APUE》读书笔记-第十四章高级I/O的相关文章

《APUE》读书笔记—第十二章线程控制

本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制:  Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询.和其它的限制使用目的一样,为了应用程序的在不同操作 系统的可移植性. 一些限制:  PTHREAD_DESTRUCTOR_ITERATIONS: 销毁一个线程数据最大的尝试次数,可以通过_SC_THREAD_DESTRUCTOR_ITERATIONS作为sysconf的参数查询.  PTHREA

《APUE》读书笔记-第十五章进程间通信

进程间通信(IPC)是指能在两个进程间进行数据交换的机制.现代OS都对进程有保护机制,因此两个进程不能直接交换数据,必须通过一定机制来完成. IPC的机制的作用: (1)一个软件也能更容易跟第三方软件或内核进行配合的集成,或移植.如管道,在shell 下执行 ps –aux | grep bash. (2)简化软件结构, 可以把一个软件划分多个进程或线程,通过IPC,集成在一起工作.如消息队列. (3)让操作系统各个模块交换数据,包括内核与应用程序机制. (4)提供进程之间或同一进程之间多线程的

《APUE》读书笔记-第十九章伪终端

1.综述 伪终端对于一个应用程序而言,看上去像一个终端,但事实上伪终端并不是一个真正的终端.从内核角度看,伪终端看起来像一个双向管道,而事实上Solaris的伪终端就是用STREAMS构建的.伪终端总是成对地使用的,就好像是个管道的两端.一端的设备称为"主设备"(master),另一端的设备称为"从设备"(slave),每一对伪终端设备,例如/dev/ptys0和/dev/ttys0,就好像是通过一个管道连在一起,其"从设备"一端与普通的终端设备

《APUE》读书笔记-第十六章网络IPC:套接字

通过网络套接字可以使得不同计算机上运行的进程相互通信. 1.创建套接字 #include <sys/socket.h> Int socket( int domain, int type, int protocol); 注意:AF_LOCAL域是AF_UNIX的别名,AF_UNSPEC域可以代表任何域. 2.套接字通信是双向的,禁止套接字上的输入/输出 #include < sys/socket.h> Int shutdown ( int sockfd, int how); 3.处理

第十四章 数组[《.net框架程序设计》读书笔记]

.net框架|笔记|程序|设计|数组 第十四章 数组. 内容摘要: 本章讨论了数组的方方面面,对于这种常用类型进行深入研究. 一. 数组简介 三种类型:一维数组.多维数组.交错数组(jagged aray) l 一维数组: Int32[] myIntegers; myIntegers = new Int32[100]; l 多维数组: Int32[,] myIntegers; myIntegers = new Int32[100,100]; l 交错数组:交错数组不受CLS支持 Point[][

MYSQL必知必会读书笔记第十和十一章之使用函数处理数据_Mysql

 mysql简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. 拼接字段 存储在数据库表中的数据一般不是应用程序所需要的格式.我们需要直接从数据库中检索出转换.计算或格式化过的数据:而不是检索出数据,然后再在客户机应用程序或报告程序中重新格式化. 计算字段(字段 = 列,不过数据库列一般称为列,而字段通常用于计算字段中)并不实际存在于数据库表中,计算字段是运行时在select语句内创建的

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 这一章很多,但是很有趣,也是这书的最后一章知识点了,我现在还在考虑要不要写这个拼图和2048的案例,在此之前,我们先来玩玩Android5.X的新特性吧!

VSTO学习笔记(十四)Excel数据透视表与PowerPivot

原文:VSTO学习笔记(十四)Excel数据透视表与PowerPivot 近期公司内部在做一种通用查询报表,方便人力资源分析.统计数据.由于之前公司系统中有一个类似的查询使用Excel数据透视表完成的,故我也打算借鉴一下. 测试代码下载    原有系统是使用VBA编写的,难以维护,且对新的操作系统如Windows 7.64位架构不支持,我准备用VSTO进行重写. 数据透视表是一种交互式的表,可以进行某些计算,如求和与计数等.所进行的计算与数据跟数据透视表中的排列有关. 数据透视表是一种完全自助式

Mysql学习笔记(十四)备份与恢复

原文:Mysql学习笔记(十四)备份与恢复 学习内容: 1.数据库的重要数据备份... 2.什么时候需要使用到数据库备份.. 3.如何恢复备份的数据..   1.备份: 说到备份,相比大家都不应该陌生,比如说我们平时在为我们的电脑重新做系统的时候,那么我们保存在电脑里的重要文件我们都需要进行备份..或者是当我们在使用一键还原系统的时候,我们也需要进行备份..那么数据库也不例外,我们需要对数据库里面保存的重要数据进行备份..比如说一个大型的企业,那么必然会有自己的一个大型数据库,那么数据库管理员必