[AUPE chapter 14] 高级IO

作者:isshe
日期:2016.10.30
邮箱:[email protected]
github: https://github.com/isshe

1. 基础知识

1.1 问题

1.1.1 非阻塞I/O

a1. 什么是低速系统调用?都有哪些?
a2. 与磁盘I/O有关的系统调用的相关描述?
a3. 如何指定非阻塞I/O?

1.1.2 记录锁

b1. 记录锁的功能是什么?
b2. 对于记录锁相关的fcntl函数。
b3. 关于加锁解锁区域的说明和注意事项。
b4. 锁的隐含继承和释放3原则。
b0. 注意事项。
1.1.3 readv和writev
c1.readv和writev介绍。

1.1.4 存储映射I/O

d1.mmap函数介绍。
d0.注意事项

1.2 解答

1. 2.1 非阻塞I/O

a1. 低速系统调用:可能会使进程永远阻塞的一类系统调用,包括:

  • 读操作,如果某些文件类型(读管道、终端设备、网络设备)的数据不存在。
  • 写操作,如果数据不能被相同的文件类型立即接受(如管道无中间空间、网络流控制)
  • 在某种条件发生之前打开某些文件类型可能会发生阻塞。(不大理解这个)
  • 对已经加上强制性记录锁的文件进行读写。
  • 某些ioctl操作。
  • 某些进程间通信函数。(15章)

a2. 不能将与磁盘I/O有关的系统调用视为“低速”,尽管读写磁盘文件会暂时阻塞调用者。

a3. 指定非阻塞的I/O的方法:

  • 指定O_NONBLOCK标志调用open获取描述符。
  • 已打开的描述符,用O_NONBLOCK标志调用fcntl。(apue chapter3的3-12图)
  • 注意:非阻塞I/O常使用轮询的方法,会造成量非CPU时间。有时可用多线程代替非阻塞I/O,但是要考虑线程间同步的开销以及复杂程度。(当然还可以用下面的高级I/O)

1.2.2 记录锁

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

  • 当第一个进程在读或修改文件的某个部分时,使用记录锁可以组织其他进程修改同一文件区。(记录锁更合适的术语是:字节范围锁(byte-range-locking)

b2. 对于记录锁相关的fcntl函数:

  • 原型:
       #include 
       #include 

        //注意第3个参数
       int fcntl(int fd, int cmd, ... /* struct flock *flockptr */ );
  • 参数:
    • cmd:记录锁相关:
      • F_GETLK: 判断由flockptr所描述的锁是否被另外一把锁所排斥。(如无锁,则l_type = F_UNLCK)
      • F_SETLK: 设置由flockptr所描述的锁,或者清除锁(l_type = F_UNLCK)。
      • F_SETLKW: (W代表wait)F_SETLK的阻塞版本。当锁无法设置的时候,休眠等待锁可用再唤醒。
    • 第3个参数:使用一个指向flock结构的指针,结构内容如下:
           struct flock {
               ...
               short l_type;    /* 锁类型: F_RDLCK(共享读锁),
                                   F_WRLCK(独占写锁), F_UNLCK(解锁一个区域 */
               short l_whence;  /* 和l_start配合,l_whence取值:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* 锁定区域的开始 *偏移*!*/
               off_t l_len;     /* 区域的字节长度 */
               pid_t l_pid;     /* PID为pid的进程持有的锁可以阻塞当前进程
                                   (set by F_GETLK and F_OFD_GETLK) */
               ...
           };

b3. 关于加锁解锁区域的说明和注意事项:

  • 指定区域起始偏移量的两个元素(l_whence, l_start)和lseek函数的最后两个参数类似。
  • 锁可以在当前文件的尾端处开始 或 越过尾端处开始, 但不能在文件起始位置之前开始。
  • l_len== 0, 表示锁的方位可以扩大到最大可能偏移量。
  • 对整个文件加锁,设置l_whence和l_start指向文件起始,并指定l_len=0。(_l_whence = SEEK_SET, l_start = 0)
  • 同一进程对文件同一区域多次加锁,则只保留最新的(前一次总被后一次替换)。
  • 读/写锁时,该描述符必须是读/写打开的。

b4. 锁的隐含继承和释放有3条规则:

  • 锁与进程和文件两者相关联。
    • 当进程终止时,它所建立的所有锁都被释放。
    • 当关闭一个描述符时,它关联的文件上的任何一把锁都被释放(这些锁都是该进程建立的)(例如进程中有两个fd0,fd1某个文件的两次打开,当在其中一个lock后,另一个如果关闭,此lock也会被关闭)
  • 由fork产生的子进程不继承父进程锁设置的锁。
  • 在执行exec后,新程序可以继承原程序的锁。(如果设置执行时关闭标志,则锁会被释放)

b0. 注意事项:

  • 1.用F_GETLK测试能否建立一把锁,然后用F_SETLK或F_SETLKW尝试建立那把锁。
  • 2.如果不希望在等待锁变为可用时阻塞,就必须处理由F_SETLK返回的可能的错误。

1.2.3 readv和writev

c1.readv和writev介绍:

  • 原型:
       #include 

       ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
       ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

       //结构:
           struct iovec {
               void  *iov_base;    /* Starting address */
               size_t iov_len;     /* Number of bytes to transfer */
           };
  • 功能:用于一次函数调用读/写多个非连续缓冲区。(散布读,聚集写)
  • 参数:
    • fd:文件描述符。
    • iov:struct iovec数组,个数由iovcnt指定,最大值受限于IOV_MAX(图2-11)。
  • 返回:已读/已写的字节数,出错返回-1。

c0. 注意事项:

1.2.4 存储映射I/O

d1.mmap函数介绍
* 原型:

       #include 

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
  • 功能:将一个给定的文件映射到一个存储区域中。
  • 参数:
    • addr:指定映射存储区的起始地址(函数返回值通常也是这个)。如果addr == 0, 则由系统选择。
    • fd:指定要被映射的文件的描述符。(在文件映射前,必须先打开文件)
    • length:映射的字节数。
    • offset:要映射字节在文件中的偏移。
    • prot:指定映射存储区的保护要求。(不能超过open模式访问权限)
prot 说明
PROT_READ 映射区可读
PROT_WRITE 映射区可写
PROT_EXEC 映射区可执行
PROT_NONE 映射区不可访问
  • flags:影响映射存储区的多种属性:
    • MAP_FIXED:返回值必须是addr。(不鼓励使用,因为不利于可移植性)
    • MAP_SHARED:映射存储区是共享的,存储操作会直接作用到文件。
    • MAP_PRIVATE:对映射区的存储操作(write)导致创建该映射文件的一个副本,所有后来对该映射区的引用都引用该副本。任何修改只影响副本,不影响原文件(常用于调试程序,p424)
    • 不同实现可能还有不同的MAP_XXX。

d0. 注意事项:

  • offset和addr的值,通常被要求是系统虚拟存储页长度的倍数。(页长通过_SC_PAGESIZE 或 _SC_PAGE_SIZE的sysconf函数获取)(offset和addr常常为0,所以这种要求一般不重要)
  • 子进程能通过fork继承映射存储区。
  • 关闭文件描述符并不解除映射存储区。
  • mprotect:更改现有映射的权限。
  • msync:冲洗共享映射中的修改页到被映射的文件中。
  • munmp:解除映射。(对共享映射,文件的更新会根据系统算法更新到文件,对MAP_PRIVATE映射,丢弃修改)

拓展知识

  • 哪些是与磁盘I/O有关的系统调用?

参考资料

  • 《unix环境高级编程》
  • ubuntu16.04 man pages

你可能感兴趣的:(【初探】操作系统)