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字节,并试图将它们写到标准输出上。

 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 }

程序结果如下:

Unix环境高级编程(十五)高级I/O相关推荐

  1. Unix环境经典编程书籍推荐

    转自:http://blog.csdn.net/beachman/article/details/6600837 Unix环境经典编程书籍推荐 在Unix环境编程领域涉及的知识面比较宽,很多新人进来的 ...

  2. python高级编程函数_Python高级编程之十大装B语法

    for - else 什么?不是 if 和 else 才是原配吗?No,你可能不知道,else 是个脚踩两只船的家伙,for 和 else 也是一对,而且是合法的.十大装B语法,for-else 绝对 ...

  3. python高级属性 用法 编程_python高级编程之面向对象高级编程

    1 面向对象编程 面向对象这节比较简单,就稍微总结几个特殊的点. 特殊方法__init__前后分别有两个下划线,__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init ...

  4. 西门子840d高级编程手册_Sinumerik840D840Di810D高级编程手册.pdf

    Sinumerik840D840Di810D高级编程手册 编程说明 版本03.2004 SINUMERIK 840D/840Di/810D 工作准备部分 灵活的NC编程 1 子程序技术, 2 宏指令技 ...

  5. Unix环境高级编程(十)信号续

    1.signal函数 Unix系统的信号机制最简单的接口是signal函数,函数原型如下: #include <signal.h> typedef void (*sighandler_t) ...

  6. UNIX 环境高级编程(五)—— unistd.h

    1. POSIX POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX 标准定义了操作系统应该为应用程序提供 ...

  7. C++面向对象高级编程十九讲

    文章目录 0 背景 1 内容 0 背景 因为本人的C++基础不是很扎实,很多面向对象的基本语法掌握的不是很熟练,导致写程序出错时,经常会犯错,于是就去网上找到了侯捷老师的C++课进行观摩学习. 本文是 ...

  8. JavaSE复习之十五 高级特性:数据库及数据库连接 补充(1)

    数据库MYSQL 今日内容介绍 MySQL数据库 SQL语句 第1章 数据库 1.1 数据库概述 l 什么是数据库 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用 ...

  9. c++面向对象高级编程 学习五 组合、委托与继承

    组合 composition 表示has a queue类中有一个deque容器,这种关系叫做 组合 queue中的六个函数都是调用c的函数完成的 template <class T> c ...

最新文章

  1. 分解例题及解析_【高考物理】考前梳理,高中物理经典常考例题(带解析),收藏起来考试不低于90+!...
  2. LinuxUNIX系统编程手册(英文版)pdf
  3. STM32为何在诸多的单片机中脱颖而出?
  4. 问题:图片怎么保存到数据库, 以及怎么把图片从数据库中取出来使用?(已解决)...
  5. ArcGIS地理坐标系与投影坐标系
  6. AutoML 与 Bayesian Optimization 概述
  7. 清理神器CleanMyMac X 空间透镜——可视化您的磁盘空间
  8. 谷歌技术三宝之MapReduce(转)
  9. 一个有趣的IP不同的问题?
  10. 转:Oracle中的rownum不能使用大于的问题
  11. t.cn短链接是怎么生成的?
  12. idea添加目录时,Mark Directory as的几个选项详解
  13. 国美易卡RMAN工具使用流程,国美易卡文件路径信息
  14. 密度计算机公式,密度浓度换算公式(浓度和密度的换算关系)
  15. Android MediaPalyer实现视频播放
  16. 简单且强大的PHP调试工具 Kint
  17. java过剩_中国的程序员数量是否已经饱和或者过剩?
  18. 机器学习之鸢尾花实战
  19. 机器学习实战:小麦种子(封装函数进行调参、标准化、绘图查看数据分布)
  20. php pos机刷卡,一清POS机不能刷卡的这些原因你知道几个?

热门文章

  1. php随机数怎么获取?一个简单的函数就能生成
  2. Best Practices
  3. [合作] 钢结构结构健康监测研究与实验
  4. OkHttp 3.x 源码解析之Dispatcher分发器
  5. Vue 作者尤雨溪:以匠人的态度不断打磨完善 Vue
  6. html+css做的丝带标签
  7. 使用mongoose和bcrypt做认证
  8. jsp 中实现点击按钮 实现页面跳转到HTML
  9. 转载浅谈MFC内存泄露检测及内存越界访问保护机制
  10. ContOS 7 安装Jenkins