zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    HTML DOM 12 表格排序
    HTML DOM 10 常用场景
    HTML DOM 10 插入节点
    HTML DOM 09 替换节点
    HTML DOM 08 删除节点
    HTML DOM 07 创建节点
    022 注释
    024 数字类型
    005 基于面向对象设计一个简单的游戏
    021 花式赋值
  • 原文地址:https://www.cnblogs.com/liujiahi/p/2287418.html
Copyright © 2011-2022 走看看