- aio 高级编程
- 在编译aio时 gcc后面要加 -lrt
- AIO编程
- 面向对象 编程
- aio_read() 请求异步读操作
- aio_error() 主动询问 AIO是否完成
- aio_return()
- aio_suspend() 挂起当前进程
- lio_listio() 发起多个或多种I/O请求
- struct sigevent 回调函数结构体
- stat() 检测文件是否存在
- fstat () 通过文件描述符 获取文件属性
- lstat () 返回链接文件本身信息
- struct stat 结构体
- 判断文件是什么类型 > 0 都为是
- getpagesize() 获取系统一叶内存大小
- open() 打开/新建文件
- posix_fadvise() 适用与文件拷贝的malloc函数
- fallocate() 指定目标拷贝函数
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
不管你读取是否完成,都将返回,返回后可以执行其他事,而内核还在拷贝数据
可以以两种方式查看是否 拷贝完成
- 主动询问:完成就会返回完成状态,未完成就会返回未完成状态
- 挂接回调函数:等拷贝完成,内核会自动调用回调函数,而此回调函数,是内核开辟新的线程去执行,不会占用主线程的时间
epoll的优点
- 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。
- 效率提升,不是轮询的方式,只管你“活跃”的连接,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数。
- 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
AIO编程
对象
- struct aiocb
#include <aiocb.h>
struct aiocb {
int aio_fildes; /* 文件描述符 */
int aio_lio_opcode; /* 操作码是读是写 */
volatile void *aio_buf; /* 缓冲区地址 */
size_t aio_nbytes; /* AIO读取的个数 */
int aio_offset; /* 文件偏移 */
struct sigevent aio_sigevent; /* 回调函数 */
};
____________________________________
int aio_lio_opcode; /* 操作码是读是写 */
AIO_READ 读 AIO_WRITE 写
只有批量处理时才需设置
____________________________________
volatile void *aio_buf; /* 缓冲区地址 */
aio_buf 是内核来改变的 如果不加 volatile 就会失效
____________________________________
volatile 关键字
告诉编译器不要擅自对 volatile 修饰的值进行优化
如:
int a = regA //如果regA 一直都没有改变 但是在内核中会变换
// 编译器就会把 a = regA 给优化 删除掉
// 加了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 函数。这个函数的原型如下:
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请求
发起一系列 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_READ
读AIO_WRITE
写
struct sigevent 回调函数结构体
struct sigevent {
int sigev_notify;//不用设置用默认 = STGEV_THREAD
int sigev_signo; // 应用线程的信号.
union sigval sigev_value; // 处理函数的参数
void (*sigev_notify_function)(union sigval); // 回调函数
pthread_attr_t *sigev_notify_attributes; //不用设置用默认 = NULL
};
union sigval{ // 处理函数的参数
int sival_int; // 随意传,可以的处理fd
void *sival_prt; // 可以传对象指针
};
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 结构体
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //文件编号
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
当 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() 获取系统一叶内存大小
**int getpagesize(void);**
头文件: #include
返回值:返回系统一叶内存大小
open() 打开/新建文件
open建立了一条到文件或设备的访问路径。
int open(const char *path, int oflags,mode_t mode);
- 头文件: #include
- path: 路径名或者文件名
- oflags: 打开文件所采取的动作
- mode: 设置文件访问权限的初始值 //可以直接 复制 stat 结构体中的 st_mode
打开文件所采取的动作:
O_RDONLY(只读) O_WRONLY(只写) O_RDWR(可读可写)
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件
o_EXCL 如果要创建的文件已存在,则返回 -1,并且修改errno的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
文件访问权限:
S_IRUSR 文件所属的用户可读
S_IWUSER 文件所属的用户可写
S_IXUSR 文件所属的用户可执行
S_IRGRP 文件所属的组可读
S_IWGRP 文件所属的组写
S_IXGRP 文件所属的组执行
S_IROTH 其他用户可读
S_IWOTH 其他用户可写
S_IXOTH 其他用户可执行
posix_fadvise() 适用与文件拷贝的malloc函数
**此函数 只是告诉系统要分配那么大的内存**
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错误
POSIX_FADV_NORMAL:
表示该应用程序没有建议提供有关其指定的数据访问模式。如果没有意见,给出了一个打开的文件,这是默认的假设。
POSIX_FADV_SEQUENTIAL 该应用程序需要访问指定的数据顺序(与以前高的人读低偏移)
POSIX_FADV_RANDOM: 将指定的数据将会以随机顺序进行访问。
POSIX_FADV_NOREUSE: 将指定的数据将只访问一次。
POSIX_FADV_WILLNEED 将指定的数据将在不久的将来访问。
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错误
struct stat fd_si,wri_si;
stat("1.txt",&fd_si);
int fd = open("1.txt",O_RDWR,fd_si.st_mode);
int wrfd = open("2.txt",O_RDWR|O_CREAT,fd_si.st_mode);
posix_fadvise(fd,fd_si.st_size,POSIX_FADV_RANDOM);//可以if
fallocate(wrfd,0,0,fd_si.st_size);