APUE阅读笔记--第十四章(第二部分)

高级IO

  1. 1.     多路复用

Poll函数

#include <poll.h>

 

int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);

Returns: count of ready descriptors, 0 on timeout, 1 on error

 

返回:准备就绪的描述符数,若超时则为0,若出错则为- 1

与s e l e c t不同,p o l l不是为每个条件构造一个描述符集,而是构造一个p o l l f d结构数组,每

个数组元素指定一个描述符编号以及对其所关心的条件。

 

   struct pollfd {
     int   fd;       /* file descriptor to check, or <0 to ignore */
     short events;   /* events of interest on fd */
     short revents;  /* events that occurred on fd */
   };

 

  1. 2.     异步IO

在4 . 3 + B S D中,异步I / O是两个信号S I G I O和S I G U R G的组合。前者是通用异步I / O信号,后者则只被用来通知进程在网络连接上到达了非规定波特率的数据。

为了接收S I G I O信号,需执行下列三步:

(1) 调用s i g n a l或s i g a c t i o n为该信号建立一个信号处理程序。

(2) 以命令F _ S E TO W N(见3 . 1 3节)调用f c n t l来设置进程I D和进程组I D,它们将接收对于该描述符的信号。

(3) 以命令F _ S E T F L调用f c n t l设置O _ A S Y N C状态标志,使在该描述符上可以进行异步I / O(见表3 - 2)。

第( 3 )步仅用于指向终端或网络的描述符,这是4 . 3 + B S D异步传输设施的一个基本的限制。

对于S I G U R G信号,只需执行第( 1 )步和第( 2 )步。该信号仅对于指向支持带外数据的网络连接的描述符而产生。

r e a d v和w r i t e v函数用于在一个函数调用中读、写多个非连续缓存。有时也将这两个函数称为散布读(scatter re a d)和聚集写(gather write)

#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);

Both return: number of bytes read or written, 1 on error

这两个函数的第二个参数是指向i o v e c结构数组的一个指针:

struct iovec {

void *iov_base; /* starting address of buffer */

size_t iov_len; /* size of buffer */

} ;

i o v数组中的元素数由i o v c n t说明。

  1. 3.     存储映射IO

存储映射I / O使一个磁盘文件与存储空间中的一个缓存相映射。于是当从缓存中取数据,就相当于读文件中的相应字节。与其类似,将数据存入缓存,则相应字节就自动地写入文件。

这样,就可以在不使用r e a d和w r i t e的情况下执行I / O。

为了使用这种功能,应首先告诉内核将一个给定的文件映射到一个存储区域中。这是由m m a p函数实现的。

#include <sys/mman.h>

 

void *mmap(void *addr, size_t len, int prot, int flag, int filedes,off_t off );

Returns: starting address of mapped region if OK, MAP_FAILED on error

数据类型c a d d r _ t通常定义为char *。a d d r参数用于指定映射存储区的起始地址。通常将其设置为0,这表示由系统选择该映射区的起始地址。此函数的返回地址是:该映射区的起始地址。

f i l e d e s指定要被映射文件的描述符。在映射该文件到一个地址空间之前,先要打开该文件。l e n是映射的字节数。o f f是要映射字节在文件中的起始位移量(下面将说明对o f f值有某些限制)。

在说明其余参数之前,先看一下存储映射文件的基本情况。图1 2 - 1 2显示了一个存储映射文件。(见图7 - 3中进程存储空间的典型安排情况。)

 

在此图中,“起始地址”是m m a p的返回值。在图中,映射存储区位于堆和栈之间:这属于实现细节,各种实现之间可能不同。

p ro c参数说明映射存储区的保护要求。见表1 2 - 8。

 

对于映射存储区所指定的保护要求与文件的o p e n方法匹配。例如,若该文件是只读打开的,那么对映射存储区就不能指定P R O T _ W R I T E。

f l a g参数影响映射存储区的多种属性:

• MAP_FIXED 返回值必须等于a d d r。因为这不利于可移植性,所以不鼓励使用此标志。

如果未指定此标志,而且a d d r非0,则内核只把a d d r视为何处设置映射区的一种建议。

通过将a d d r指定为0可获得最大可移植性。

• MAP_SHARED 这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件—也就是,存储操作相当于对该文件w r i t e。必须指定本标志或下一个标志(M A P _ P R I VAT E)。

• MAP_PRIVATE 本标志说明,对映射区的存储操作导致创建该映射文件的一个副本。所有后来对该映射区的存访都是存访该副本,而不是原始文件。

4 . 3 + B S D还有另外一些M A P _ x x x标志值,它们是这种现实所特有的。详细情况请参见4.3+BSD mmap(2)手册页。

o f f和a d d r的值(如果指定了M A P _ F I X E D)通常应当是系统虚存页长度的倍数。在S V R中,虚存页长度可用带参数S C _ PA G E S I Z E的s y s c o n f函数(见2 . 5 . 4节)得到。在4 . 3 + B S D之下,页长度由头文件< s y s / p a r a m . h >中的常数N B P G定义。因为o f f和a d d r常常指定为0,所以这种要求一般并不是问题。

因为映射文件的起动位移量受系统虚存页长度的限制,那么如果映射区的长度不是页长度的整数倍时,将如何呢?假定文件长1 2字节,系统页长为5 1 2字节,则系统通常提供5 1 2字节的映射区,其中后5 0 0字节被设为0。可以修改这5 0 0字节,但任何变动都不会在文件中反映出来。

与映射存储区相关有两个信号: S I G S E G V和S I G B U S。信号S I G S E G V通常用于指示进程试图存取它不能存取的存储区。如果进程企图存数据到用m m a p指定为只读的映射存储区,那么也产生此信号。如果存取映射区的某个部分,而在存取时这一部分已不存在,则产生S I G B U S信号。例如,用文件长度映射一个文件,但在存访该映射区之前,另一个进程已将该文件截短。

此时,如果进程企图存取对应于该文件尾端部分的映射区,则接收到S I G B U S信号。

在f o r k之后,子进程继承存储映射区(因为子进程复制父进程地址空间,而存储映射区是该地址空间中的一部分),但是由于同样的理由,e x e c后的新程序则不继承此存储映射区。进程终止时,或调用了m u n m a p之后,存储映射区就被自动去除。关闭文件描述符f i l e d e s并不解除映射区。

#include <sys/types.h>

#include <sys/mman.h>

int munmap(caddr_at d d r,size_tl e n) ;

返回:若成功则为0,若出错则为- 1

munmap 并不影响被映射的对象—也就是说,调用m u n m a p并不使映射区的内容写到磁盘文件

上。对于M A P _ S H A R E D区磁盘文件的更新,在写到存储映射区时按内核虚存算法自动进行。

某些系统提供了一个m s y n c函数,它类似于f s y n c函数(见4 . 2 4节),但对存储映射区起作用。

实例

程序1 2 - 1 4用存储映射I / O复制一个文件(类似于c p ( 1 )命令)。首先打开两个文件,然后调用f s t a t得到输入文件的长度。在调用m m a p和设置输出文件长度时都需使用输入文件长度。调用l s e e k,然后写一个字节以设置输出文件的长度。如果不设置输出文件的长度,则对输出文件

调用m m a p也可以,但是对相关存储区的第一次存访会产生S I G B U S。也可使用f t r u n c a t e函数来设置输出文件的长度,但是并非所有系统都支持该函数扩充文件长度(见4 . 1 3节)。

然后对每个文件调用m m a p,将文件映射到存储区,最后调用m e m c p y将输入缓存的内容复制到输出缓存。在从输入缓存( s r c)取数据字节时,内核自动读输入文件;在将数据存入输出缓存(d s t)时,内核自动将数据写到输出文件中。

#include "apue.h"
#include <fcntl.h>
#include <sys/mman.h>
 
int
main(int argc, char *argv[])
{
    int         fdin, fdout;
    void        *src, *dst;
    struct stat statbuf;
 
    if (argc != 3)
        err_quit("usage: %s <fromfile> <tofile>", argv[0]);
 
    if ((fdin = open(argv[1], O_RDONLY)) < 0)
        err_sys("can't open %s for reading", argv[1]);
 
    if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC,
      FILE_MODE)) < 0)
        err_sys("can't creat %s for writing", argv[2]);
 
    if (fstat(fdin, &statbuf) < 0)   /* need size of input file */
        err_sys("fstat error");
 
    /* set size of output file */
    if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1)
        err_sys("lseek error");
    if (write(fdout, "", 1) != 1)
        err_sys("write error");
 
    if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED,
      fdin, 0)) == MAP_FAILED)
        err_sys("mmap error for input");
 
    if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE,
      MAP_SHARED, fdout, 0)) == MAP_FAILED)
        err_sys("mmap error for output");
 
    memcpy(dst, src, statbuf.st_size); /* does the file copy */
    exit(0);
}
posted @ 2011-12-14 12:39  jialejiahi  阅读(316)  评论(0编辑  收藏  举报