aio 高级编程

aio异步读写是在Linux内核2.6之后才正式纳入其标准。之所以会增加此模块,是因为众所周知我们计算机CPU的执行速度远大于I/O读写的执行速度,如果我们用传统的阻塞式或非阻塞式来操作I/O的话,那么我们在同一个程序中(不用多线程或多进程)就不能同时操作俩个以上的文件I/O,每次只能对一个文件进行I/O操作,很明显这样效率很低下(因为CPU速度远大于I/O操作的速度,所以当执行I/O时,CPU其实还可以做更多的事)。因此就诞生了相对高效的异步I/O

在编译aio时 gcc后面要加 -lrt

同步阻塞IO

IO 就是输入输出,对文件进行输入输出

应用层 是不可以与内核层进行直接的数据读取,而是通过内核层把数据拷贝到应用层 ,来进行数据的交换

当内核数据很大,很频繁时,应用层的读取就会非常缓慢,会导致阻塞,降低效率

异步IO

不管你读取是否完成,都将返回,返回后可以执行其他事,而内核还在拷贝数据

可以以两种方式查看是否 拷贝完成

  1. 主动询问:完成就会返回完成状态,未完成就会返回未完成状态
  2. 挂接回调函数:等拷贝完成,内核会自动调用回调函数,而此回调函数,是内核开辟新的线程去执行,不会占用主线程的时间

epoll的优点

  • 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。
  • 效率提升,不是轮询的方式,只管你“活跃”的连接,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数。
  • 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

AIO编程

对象

  • struct aiocb
  1. #include <aiocb.h>
  2. struct aiocb {
  3. int aio_fildes; /* 文件描述符 */
  4. int aio_lio_opcode; /* 操作码是读是写 */
  5. volatile void *aio_buf; /* 缓冲区地址 */
  6. size_t aio_nbytes; /* AIO读取的个数 */
  7. int aio_offset; /* 文件偏移 */
  8. struct sigevent aio_sigevent; /* 回调函数 */
  9. };
  10. ____________________________________
  11. int aio_lio_opcode; /* 操作码是读是写 */
  12. AIO_READ AIO_WRITE
  13. 只有批量处理时才需设置
  14. ____________________________________
  15. volatile void *aio_buf; /* 缓冲区地址 */
  16. aio_buf 是内核来改变的 如果不加 volatile 就会失效
  17. ____________________________________

volatile 关键字

告诉编译器不要擅自对 volatile 修饰的值进行优化

如:

  1. int a = regA //如果regA 一直都没有改变 但是在内核中会变换
  2. // 编译器就会把 a = regA 给优化 删除掉
  3. // 加了volatile 就不会删除

面向对象 编程

aio_read() 请求异步读操作

aio_read 函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以表示一个文件、套接字甚至管道。aio_read 函数的原型如下

int aio_read(struct aiocb *aiocbp); 是立即返回

  • 头文件 #include
  • aiocbp:设置的 struct aiocb
  • 返回值:aio_read 是立即返回 0 为执行成功,-1执行错误

aio_error() 主动询问 AIO是否完成

主动询问 AIO是否完成

int aio_error(const struct aiocb *aiocbp);

  • 头文件 #include

  • aiocbp:aio_read 设置的 struct aiocb

  • 返回值: EINPROGRESS,说明请求尚未完成
    ECANCELLED,说明请求被应用程序取消了
    -1 发生错误 , 0说明完成当前请求

  • 0 请求完成,但不可以直接读取 还需 aio_return()

aio_return()

  • 异步 I/O 和标准块 I/O 之间的另外一个区别是我们不能立即访问这个函数的返回状态,因为我们并没有阻塞在 read 调用上。在标准的 read 调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return 函数。这个函数的原型如下:
  1. ssize_t aio_return( struct aiocb *aiocbp );
  • aio_return 会立刻返回,只是登记,登记完成就可以用 struct aiocb 设置的 fd进行读取

aio_suspend() 挂起当前进程

挂起调用进程,直到一个或多个异步请求已经完成(或失败)

int aiosuspend(const struct aiocb const aiocb_list[],int nitems, const struct timespec _timeout);

  • 头文件:#include
  • aiocb_list[]:发起所有aio的列表
  • nitems:aio的列表中有多少aio
  • timeout:超时时间

lio_listio() 发起多个或多种I/O请求

  1. 发起一系列 I/O 操作

int lio_listio(int mode,struct aiocb _list[],int nent,struct sigevent _sig);

  • mode: LIO_WAIT 阻塞调用 LIO_NOWAIT 非阻塞调用
  • struct aiocb *list[]:aio 链表
  • nent:aio个数
  • sig:回调函数

使用这个函数时 要设置struct aiocb 中的int aio_lio_opcode 是读还写 AIO_READAIO_WRITE

struct sigevent 回调函数结构体

  1. struct sigevent {
  2. int sigev_notify;//不用设置用默认 = STGEV_THREAD
  3. int sigev_signo; // 应用线程的信号.
  4. union sigval sigev_value; // 处理函数的参数
  5. void (*sigev_notify_function)(union sigval); // 回调函数
  6. pthread_attr_t *sigev_notify_attributes; //不用设置用默认 = NULL
  7. };
  8. union sigval{ // 处理函数的参数
  9. int sival_int; // 随意传,可以的处理fd
  10. void *sival_prt; // 可以传对象指针
  11. };

stat() 检测文件是否存在

int stat(const char _file_name, struct stat _buf);

  • 表头文件: #include
  • file_name:文件名字
  • buf: 读取信息存放在 struct stat 结构体中
  • 返回值: 文件存在返回0,文件不存在返回-1

fstat () 通过文件描述符 获取文件属性

**int fstat(int fd, struct stat statbuf);

  • 头文件:#include

  • fd:文件fd

  • statbuf:存放读取内容

lstat () 返回链接文件本身信息

int lstat(const char _pathname, struct stat _statbuf);

  • 头文件:#include
  • pathname:路径名或者文件名
  • statbuf:存放读取内容

struct stat 结构体

  1. struct stat {
  2. dev_t st_dev; //文件的设备编号
  3. ino_t st_ino; //文件编号
  4. mode_t st_mode; //文件的类型和存取的权限
  5. nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
  6. uid_t st_uid; //用户ID
  7. gid_t st_gid; //组ID
  8. dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
  9. off_t st_size; //文件字节数(文件大小)
  10. unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
  11. unsigned long st_blocks; //块数
  12. time_t st_atime; //最后一次访问时间
  13. time_t st_mtime; //最后一次修改时间
  14. time_t st_ctime; //最后一次改变时间(指属性)
  15. };
  16. st_dev 与对方 st_dev st_ino 与对方 st_ino 完全相同时 两个文件为同文件

判断文件是什么类型 > 0 都为是

S_ISLNK(st_mode):是否是一个连接.
S_ISREG(st_mode):是否是一个常规文件
S_ISDIR(st_mode):是否是一个目录
S_ISCHR(st_mode):是否是一个字符设备.
S_ISBLK(st_mode):是否是一个块设备
S_ISFIFO(st_mode):是否 是一个FIFO文件.
S_ISSOCK(st_mode):是否是一个SOCKET文件

链接文件不可以拷贝

getpagesize() 获取系统一叶内存大小

  1. **int getpagesize(void);**
  • 头文件: #include

  • 返回值:返回系统一叶内存大小

open() 打开/新建文件

open建立了一条到文件或设备的访问路径。

int open(const char *path, int oflags,mode_t mode);

  • 头文件: #include
  • path: 路径名或者文件名
  • oflags: 打开文件所采取的动作
  • mode: 设置文件访问权限的初始值 //可以直接 复制 stat 结构体中的 st_mode
  1. 打开文件所采取的动作:
  2. O_RDONLY(只读) O_WRONLY(只写) O_RDWR(可读可写)
  3. O_APPEND 每次写操作都写入文件的末尾
  4. O_CREAT 如果指定文件不存在,则创建这个文件
  5. o_EXCL 如果要创建的文件已存在,则返回 -1,并且修改errno的值
  6. O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
  7. O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端
  8. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9. 文件访问权限:
  10. S_IRUSR 文件所属的用户可读
  11. S_IWUSER 文件所属的用户可写
  12. S_IXUSR 文件所属的用户可执行
  13. S_IRGRP 文件所属的组可读
  14. S_IWGRP 文件所属的组写
  15. S_IXGRP 文件所属的组执行
  16. S_IROTH 其他用户可读
  17. S_IWOTH 其他用户可写
  18. S_IXOTH 其他用户可执行

posix_fadvise() 适用与文件拷贝的malloc函数

  1. **此函数 只是告诉系统要分配那么大的内存**

malloc 会分配很大的区块,posix_fadvise是吧 malloc 分配的区块给继续使用切割分配,而不重新malloc

int posix_fadvise(int fd, off_t offset, off_t len, int advice);

  • 头文件:#include
  • fd:文件fd
  • offset:0
  • len:需要分配的文件大小
  • advice:以下参数
  • 返回值:-1错误
  1. POSIX_FADV_NORMAL
  2. 表示该应用程序没有建议提供有关其指定的数据访问模式。如果没有意见,给出了一个打开的文件,这是默认的假设。
  3. POSIX_FADV_SEQUENTIAL 该应用程序需要访问指定的数据顺序(与以前高的人读低偏移)
  4. POSIX_FADV_RANDOM 将指定的数据将会以随机顺序进行访问。
  5. POSIX_FADV_NOREUSE 将指定的数据将只访问一次。
  6. POSIX_FADV_WILLNEED 将指定的数据将在不久的将来访问。
  7. POSIX_FADV_DONTNEED 指定的数据不会在短期内被访问。

fallocate() 指定目标拷贝函数

此函数 与posix_fadvise() 函数搭配 posix_fadvise() 是告诉内核 需要怎么大的拷贝空间,而fallocate() 是告诉内核是想此文件中拷贝

int fallocate(int fd, int mode, off_t offset, off_t len);

  • 头文件:#include
  • fd:要拷贝到我文件fd
  • mode:0
  • offset:0
  • len:拷贝大小
  • 返回值:-1错误
  1. struct stat fd_si,wri_si;
  2. stat("1.txt",&fd_si);
  3. int fd = open("1.txt",O_RDWR,fd_si.st_mode);
  4. int wrfd = open("2.txt",O_RDWR|O_CREAT,fd_si.st_mode);
  5. posix_fadvise(fd,fd_si.st_size,POSIX_FADV_RANDOM);//可以if
  6. fallocate(wrfd,0,0,fd_si.st_size);