zoukankan      html  css  js  c++  java
  • APUE 学习笔记(九) 高级I/O

    1. 非阻塞I/O

    低速系统调用时可能会使进程永远阻塞的一类系统调用,包括以下调用:
    (1)某些文件类型你(网络socket套接字、终端设备、管道)暂无可使用数据,则读操作可能会使调用者永远阻塞
    (2)如果数据不能立即被(1)中文件类型接受,则写操作会使调用者永远阻塞
    (3)某些进程间通信函数
     
    非阻塞I/O使我们可以调用open、read、write这样的I/O操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回
     
    对于一个给定的文件有两种方法对其指定非阻塞I/O:
    (1)调用open打开文件时,指定 O_NONBLOCK标志
    (2)对于一个打开的描述符,可以调用fcntl函数,将文件设置为非阻塞I/O
     

    2. fcntl记录锁

    当一个进程正在读或修改文件的某个部分时,它可以组织其它进程修改同一文件区,它锁定的只是文件的一个区域(也可能是整个文件)
    struct flock {
        short     l_type;      /* F_RDLCK, F_WRLCK, F_UNLCK */
        off_t      l_start;      /* offset in bytes, relative to l_whence */
        short     l_whence; /*  SEEK_SET, SEEK_CUR, SEEK_END */
        off_t      l_len;        /*  length in bytes, 0 means lock to EOF */
        pid_t     l_pid;        /*  returned with F_GETLK */
    };
    #include <fcntl.h>
    int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
    {
        struct flock lock;
        lock.l_type = type;
        lock.l_start = offset;
        lock.l_whence = whence;
        lock.l_len = len;
        int ret = fcntl(fd, cmd, &lock);
        return ret;
    }

    如果两个进程相互等待对方持有并且锁定的资源时,则这两个进程处于死锁状态

    3. select

    #include <select.h>
    int select(int maxfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

    中间三个参数 readfds, writefds, exceptfds是指向描述符集的指针,每个描述符集存放在一个fd_set中:

       

    这三个参数指针任意一个可以为空指针,表示对相应状态并不关心。如果三个指针都是空指针,则select提供了比sleep更精确的计时器,因为sleep只能等待整数秒,

    而select的struct timeval可以精确到微秒

    int  FD_ISSET(int fd, fd_set* set);
    void FD_CLR(int fd, fd_set* set);
    void FD_SET(int fd, fd_set* set);
    void FD_ZERO(fd_set* set);

    select的第一个参数maxfds是三个描述符集中最大的fd数值加1,也可以将此参数设置为FD_SETSIZE,表明最大的描述符数

    fd_set readset, writeset;
    FD_ZERO(&readset);
    FD_ZERO(&writeset);
    FD_SET(0, &readset);
    FD_SET(3, &readset);
    FD_SET(1, &writeset);
    FD_SET(2, &writeset);
    select(4, &readset, &writeset, NULL, NULL);

      

    因为描述符编号从0开始,所以要在最大描述符编号值加1,第一个参数实际上就是要检查的描述符数(从描述符0开始)

    select有3个可能的返回值:
    (1)返回值-1表示出错
    (2)返回值0表示没有描述符准备好或者超时
    (3)返回值为正数表示已经准备好的描述符数,该值是三个描述符集中已准备好的描述符之和,如果同一描述符已经准备好读和写,那么返回值中计为2
     

    4. readn和writen

    int readn(int fd, char* ptr, size_t n)
    {
        size_t  nleft = n;
        int     nread = 0;
        while (nleft > 0) {
            if ((nread = read(fd, ptr, nleft)) < 0) {
                if (nleft == n) {
                    return -1;
                } else {
                    break;
                }
            } else if (nread == 0) {
                break;
            }
            nleft -= nread;
            ptr   += nread;
        }
        return n - nleft;
    }
    int writen(int fd, char* ptr, size_t n)
    {
        size_t  nleft = n;
        int     nwrite = 0;
        while (nleft > 0) {
            if ((nwrite = write(fd, ptr, nleft)) < 0) {
                if (nleft == n) {
                    return -1;
                } else {
                    break;
                }
            } else if (nwrite == 0) {
                break;
            }
            nleft -= nwrite;
            ptr   += nwrite;
        }
        return n - nleft;
    }
     

    5. 存储映射I/O mmap

    Memory-mapped I/O 使一个外存磁盘文件与内存空间中的一个缓冲区相映射,当从缓冲区中取数据,就相当于读磁盘文件中的相应字节,写情况亦如此
    #include <sys/mman.h>
    void* mmap(void* addr, size_t len, int prot, int flag, int fd, off_t off);
    addr参数指定映射存储区的起始位置,通常设置为0,表示由内核选择该映射区的起始地址
    fd指定被映射文件的描述符,len是映射的字节数,off是映射字节在文件中的起始偏移量
     
    addr和off的值通常应当是系统虚拟页长度的倍数,因为addr和off常常指定为0,所以这要求一般不重要
    因为映射文件的起始偏移量受系统虚拟页大小限制。假定文件长12字节,系统页长512字节,则系统通常提供512字节的映射区,其中后500字节被设置为0,对该500字节的任何修改都不会在文件中反映出来。
    #include <unistd.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <stdio.h>
    
    int main(int argc, char* argv[])
    {
        int fdin = 0;
        int fdout = 0;
        char* src = NULL;
        char* dst = NULL;
        struct stat statbuf;
        if (argc != 3) {
            fprintf(stderr, "usage: %s <fromfile> <tofile>
    ", argv[0]);
            return 1;
        }
        if ((fdin = open(argv[1], O_RDONLY)) < 0) {
            fprintf(stderr, "cannot open %s for reading
    ", argv[1]);
        }
        if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC)) < 0) {
            fprintf(stderr, "cannot creat %s for writing
    ", argv[2]);
        }
        if (fstat(fdin, &statbuf)) {
            fprintf(stderr, "fsat error
    ");
        }
    
        if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) {
            fprintf(stderr, "lseek error
    ");
        }
        if (write(fdout, "", 1) != 1) {
            fprintf(stderr, "write error
    ");
        }
    
        if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) {
            fprintf(stderr, "mmap error for input
    ");
        }
    
        if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED) {
            fprintf(stderr, "mmap error for output
    ");
        }
        memcpy(dst, src, statbuf.st_size);
        munmap(src, statbuf.st_size);
        munmap(dst, statbuf.st_size);
        return 0;
    }
     
     
  • 相关阅读:
    sql交集、差集、并集
    控件自适应文本宽度
    pivot列行转换,自动计算分组,解决groupby问题
    echart-scatter使用散点图,带坐标和项目名称
    下载文件根据浏览器判断文件名,解决兼容性问题
    sql中类型转换涉及的性能差异之convert和cast
    js使用正则表达式对文本框进行限制输入
    Aspose.Words.dll根据模板生成word详解
    Windows服务开发
    SqlBulkCopy学习(导入海量数据的类)
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3726498.html
Copyright © 2011-2022 走看看