I/O操作主要指
文件I/O操作:打开 (open)、读 (read)、写 (write)、跳转 (lseek:因为文件的当前位置记录在文件表项上,每次获取文件后建议重新设置为:上次的位置)、关闭 (close)。
特点:
- I/O的操作对象:本地文件、网络IPC套接字、unix域套接字.
- 不同I/O:都是在这个基础上通过缓存改变read/write的次数来改变CPU占用时间。
方法
| 方法名 | 操作对象 | 特点 | 成功 | 出错 |
|---|---|---|---|---|
| 文件I/O | 操作文件系统,读写磁盘数据 | |||
| open | 路径/fd | 获取新的文件操作符 | 文件描述符 | -1 |
| creat | 路径 | 创建新的文件描述符 | 0 | -1 |
| read | fd | 从内核读取数据将数据读到进程中 | 已读取的字节数;若已到文件尾->0;偏移量向后移动读取的字节大小 | -1 |
| write | fd | 将程序中的数据放到内核块缓存中,程序没有缓冲。当buf值大于磁盘块长度时(传入的数据大小比内核I/O通道大),整体的操作时间变化不大 | 已写的字节数;偏移量向后移动写入的字节大小 | -1 |
| lseek | fd | 设置文件项中的偏移量 | 新的文件偏移量 | -1 |
| close | fd | 关闭文件 | 0 | -1 |
| dup | fd | 获取新的文件描述符,指向相同的文件表项 | 新的描述符 | -1 |
| sync | fd | write调用后调用,将块缓冲区排入写队列,然后返回。不关心是否成功 | ||
| fdatasync | fd | write调用后调用,等待内核将块缓冲刷新到磁盘,直接返回 | 0 | -1 |
| fsync | fd | write调用后调用,等待内核将块缓冲刷新到磁盘,并修改完文件状态和偏移量,然后返回 | 0 | -1 |
| fcntl | fd | 获取、设置新的文件描述符、文件表中的文件状态 | -1 | |
| 标准I/O | 自带缓冲区(进程里面)的文件I/O | |||
| fwide | 获取或设置流的定向 | 宽字节:正值;字节:负值;设置返回0 | ||
| fflush | 刷新数据 | 0 | -1 | |
| fopen、freopen、fdopen | 打开文件流 | 文件指针 | NULL | |
| fclose | 关闭文件流 | 0 | EOF | |
| fgetc | 字节读 | 下一个字符 | 文件尾部或出错:EOF | |
| fputc | 字节写文件流 | C | EOF | |
| fgets | 行读 | 返回buf | 文件尾部或出错:NULL | |
| fputs | 行写 | 非负 | EOF | |
| fread | 块写 | 读的对象数 | EOF | |
| fwrite | 快写 | 写的对象数 | EOF | |
| ftell、fseek、rewind、ftello、fseeko、fgetpos、fsetpos | 快写 | 写的对象数 | EOF | |
| tmpnam和tmpfile | ISOC标准的临时文件 | |||
| mkdtemp和mkstemp | SUS标准的临时文件 | |||
| fmemopen | 内存流 | 流指针 | NULL | |
| open_memstream | 内存流 | 流指针 | NULL | |
| open_wmenstream | 内存流 | 流指针 | NULL | |
| 高级I/O | 同步非阻塞I/O | 这几个接口不再是严格意义上的阻塞,而是只阻塞自定的时间,等返回事件时, | ||
| select/pselect | 注册fd感兴趣的事件,注册三个描述符集合(readfds、writefds、exceptfds) | 准备好的描述符数目(及描述符处理的数据都加载到内核内存中,不会在等待内存做读写操作) ;超时,返回0 | -1 | |
| poll | 注册fd感兴趣的事件,通过 pollfd 的结构体数组注册 | 缺点:1.支持文件描述符太小(默认1024);2.并不知道哪个fd准备好了,必须挨着去轮训对比fd | 准备好的描述符数目(及描述符处理的数据都加载到内核内存中,不会在等待内存做读写操作) ;超时,返回0 | -1 |
| FD_ISSET | 检测是否fd的事件 | 0 | -1 | |
| FD_CLR | 删除fd到fdset中 | 0 | -1 | |
| FD_SET | 设置fd到fdset中 | 0 | -1 | |
| FD_ZERO | 清楚fdset中的fd | 0 | -1 | |
| 高级I/O | 异步I/O:此操作是通过aio_return获取值,read/write只作为事件注册 | |||
| aio_read | 异步读,将读取事件注册给内核。内核从缓冲中读取数据到目标addr | 0 | -1 | |
| aio_write | 异步写,将写取事件注册给内核。内核从目标addr写入到缓冲中 | 0 | -1 | |
| aio_fsync | 强制所有等待中的异步操作不等待而写入持久化的存储中 | 0 | -1 | |
| aio_error | 测试异步I/O是否成功 | 0 | -1 | |
| aio_return | 返回操作结果。异步操作完成前,不要调用。否则os会释放掉包涵I/O操作返回值的记录 | read、write、fsync的的结果 | -1 | |
| aio_suspend | 阻塞进程,直到I/O操作完成 | 0 | -1 | |
| lio_listio | 提交一系列I/O请求 | 0 | -1 | |
| readv | 从内涵中读取数据到多个非连续缓冲区 | 读的字节数 | -1 | |
| writev | 将多个非连续缓冲区数据写到内核缓冲区 | 读的字节数 | -1 | |
| mmap | 将一个磁盘文件映射到存储空间的一个缓冲区 | 返回映射区的起始地址 | MAP_FAILED |
名词解释
- 文件描述符:对于内核而言,所有打开(open/create)的文件都通过文件描述符引用,是一个非负整数。
| 符号 | 对象 | 常量 |
|---|---|---|
| 0 | 读 | STDIN_FILENO |
| 1 | 写 | STDOUT_FILENO |
| 2 | 错误 | STDERR_FILENO |
一、文件I/O
open和openat
#include <fcntl.h>
int open(const char *path,int oflag,.../*mode_t mode*/)
int openat(int fd ,const char *path,int oflag,.../*mode_t mode*/)
-- 返回:成功,返回文件操作符;若出错,返回-1
| 符号 | 功能 |
|---|---|
| path | 打开或创建的名字 |
| oflag | 选项 |
| ... | 可变,当oflag=O_CREATE的时候才使用 |
- oflag
| 符号 | 说明 |
|---|---|
| O_RDONLY | 只读打开 |
| O_WRONLY | 只写打开 |
| O_EXEC | 只执行打开 |
| O_SEARCH | 只搜索打开(应用于目录) |
| O_APPEND | 每次追加到文件的尾端 |
| O_CLOEXEC | 设置文件项的文件状态为FD_CLOEXEC常量,在执行exec调用的新程序中关闭,且为原子操作。 |
| O_CREAT | 若此文件不存在则创建它,需要传入open的第3个参数mode(访问权限位) |
| O_DIRECTORY | 如果path引用的不是目录,则出错 |
| O_EXCL | 1.测试一个文件是否存在,如果不存在则创建此文件(原子操作)。2.如果同时指定O_CREATE,而文件已存在,则出错。 |
| O_NOCTTY | 如果path引用的是终端设备,则不将该设备分配作为次进程的控制终端 |
| O_NOFOLOOW | 如果path引用的是一个符号链接 |
| O_SYNC | (同步写)每次write等待物理I/O操作完成,包括由write引起的文件属性更新所需的I/O |
| O_TRUNC | 如果此文件存在,而且为写或读-写成功打开,则将其长度截断为0 |
| O_DSYNC | 每次write要等待物理I/O操作完成,但是如果该写操作并不影响读写刚写入的数据,则不需等待文件属性被更新 |
| O_RSYNC | (同步读)read操作等待,直到所有对文件同一部分挂起的操作都完成 |
creat
#include <fcntl.h>
int creat(const char *path,mode_t mode);
--- 成功:返回为写打开的文件描述符;出错,返回-1
等价于:
open(path,O_WRONLY|O_CREATE|O_TRUNC,mode);
修改后使用:
open(path,O_RDWR|O_CREATE|O_TRUNC,mode)
-
create不足:以写的方式打开创建的文件,在操作前需要close下,然后在用open.create--->write--->read--->close-->open
-
修改:处理方法:open(path,O_RDWR|O_CREATE|O_TRUNC,mode)
close
#include <unistd.h>
int close(int fd)
--- 成功:返回0;出错:返回-1
- 关闭一个文件时,会释放该进程加载该文件上的所有记录锁。
- 进程终止时,内核自动关闭所有打开的文件。
lseek (移动)
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
--- 成功:返回新的文件偏移量;出错:返回-1
-
每个打开文件都有一个与其关联的"当前文件偏移量"(current file offset)。它通常是一个非负整数。通常,read、write都是从当前文件的偏移量开始,并使偏移量增加所读写的字节数。
-
系统默认,打开一个五年级,除非指定O_APPEND选项,否则该偏移量被设置为*
-
whence参数
| 参数 | 作用 |
|---|---|
| SEEK_SET | 文件开始处offset个字节 |
| SEEK_CUR | 文件偏移量设置为当前值加offset,offset可为正或负 |
| SEEK_END | 文件偏移量设置为文件长度加offset,offset可为正或负 |
- 特殊用法:
# 如果指向管道、FIFO或网络套接字,则返回-1
off_t curpos;
currpos = lseek(fd,0,SEEK_CUR);
read
#include <unistd.h>
ssize_t read(int fd,void *buf, size_t nbytes);
--- 成功:读到的字节数,若已到文件尾,返回0;出错:返回-1
# POSIX修改:
int read(int fd,char *buf,unsigned nbytes);
- 参数
- fd:文件描述符
- buf:从内核缓存读取到程序空间的缓存
- nbytes:一次读取字节数
- 特点
- 成功返回之前,该偏移量增加实际读到的字节数。
- 倒数第二次不满 nbytes,则返回读取到的个数;最后一次返回0(文件尾部)
- 终端设备读时,通常一次最多读一行
- 网络读,网络中的缓冲机制可能造成返回值小于所要读的字节数
- 管道或FIFO读,广岛包涵的字节少于所需的数量,那么read只返回实际可用的字节数
- 记录设备(如磁盘),一次返回一个记录
- 当已信号造成终端,而已经读了部分数据量时。
write
#include <unistd.h>
ssize_t write(int fd,const void *buf ,size_t nbytes);
--- 成功:返回已写的字节数;出错:返回-1
- 参数:和read相同
- 特点:
- 出错常见原因:磁盘写满、文件长度设置了限制
- 普通文件,写从当前偏移量处开始。
- 1.成功后,偏移量移动到写入后的位置。
- 2.如果打开文件时,指定了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处,在一次成功后,文件偏移量增加时间写的字节。
dup和dup2
#include <unistd.h>
int dup(int fd);
int dup2(int fd,int fd2);
----成功:返回新的描述符;出错:返回 -1
- 特点
- 清楚文件项的 FD_CLOEXEC 状态
- dup(fd) : 返回新的文件描述符,fd不关闭。=fcntl(fd,F_DUPFD,0)
- dup2(fd,fd2) : 返回新的描述fd2,旧的fd1关闭。若fd=fd2,则不关闭。=fcntl(fd1,F_DUPFD,fd2)
sync,fsync,fdatasync
#include <unistd.h>
int fsyn(int fd);
int fdatasync(int fd);
--- 成功:返回0;出错;返回-1
void sync(void);
- 特点:
- sync:将修改过的块缓冲区排入写队列,然后就返回,不等待实际的些盘操作。
- fsync: 等待写磁盘操作完成才返回 (内核将写队列中的块缓冲区刷新到磁盘,并修改文件属性(长度)等信息) ,适用于数据库。
- fdatasync: 等待内核将块缓冲区数据刷新进磁盘,不等待文件属性(长度)修改完成。
fcntl (修改文件属性)
修改文件属性: 文件表信息中的文件状态标志:只读,只写,非阻塞...
#include <fcntl.h>
int fcntl(int fd, int cmd, .../*struct flock *flockptr */)
---成功:根据cmd返回;出错:返回-1
1. struct flock {
short l_type; /*F_RDLCK,F_WRLCK,F_UNLCK*/
short l_whence; /*SEEK_SET,SEEK_CUR,SEEK_END*/
off_t l_start; /*F_RDLCK,F_WRLCK,F_UNLCK*/
off_t l_len; /*F_RDLCK,F_WRLCK,F_UNLCK*/
pid_t l_pid; /*F_RDLCK,F_WRLCK,F_UNLCK*/
}
- 参数:
- cmd
| 参数 | 作用 | 返回 | 第三位 |
|---|---|---|---|
| F_DUPFD | 复制一个已有的描述符,与老的文件描述符共享同一文件项。清除FD_CLOEXEC的标志 | 成功:返回新的文件描述符;出错:-1 | 0 |
| F_DUPFD_CLOEXEC | 复制一个已有的描述符,不清除FD_CLOEXEC的标志 | 成功:返回新的文件描述符;出错:-1 | 0 |
| F_GETFD | 获取文件描述符标志(文件描述符信息) | 成功:返回新的文件描述符;出错:-1 | 0 |
| F_SETFD | 设置文件描述符标志(文件描述符信息) | 成功:根据第三个参数返回新的描述符;出错:-1 | 0 |
| F_GETFL | 获取文件状态标志(文件表信息) | 成功:文件状态;出错:-1 | 0 |
| F_SETFL | 获取文件状态标志(文件表信息) | 成功:1;出错:-1 | 0 |
| F_GETOWN | 获取异步I/O的所有权 | 成功:获取当前接受到的SIGIO和SIGURG信号的进程ID或进程组ID;出错:-1 | 0 |
| F_SETOWN | 设置异步I/O的所有权 | 成功:1;出错:-1 | 0 |
| F_GETLK | 获取记录锁,判断描述的锁是否被另外一把锁排斥(阻塞) | 成功:有:重写flock,返回现有锁;否:flock不变;出错:-1 | flock |
| F_SETLK | 设置取记录锁,非阻塞版 | 成功:1;出错:errno=EACCESS或EAGAIN;出错:-1 | flock |
| F_SETLKW | 设置取记录锁,是F_SETLK的阻塞(wait)版本。如果设置不成功则休眠,当锁可以用或者休眠由信号中断、则该进程被唤醒 | 成功:1;出错:-1 | flock |
- 特点:
-
flock结构:
- 锁类型(l_type):F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)、F_UNLCK(解锁一个区域)
- 要加锁或解锁区域的起始偏移量:l_start和l_whence
- 区域的字节长度:l_len
- 进程的ID(l_pid)持有的锁能阻塞当前进程,仅F_GETLK返回
-
记录锁(进程间)注意项:当第一个进程正在读或修改文件的某个部分时,使用记录数可以阻止其它进程修改同一文件区。
- l_len=0:表示锁的方位可以扩张到最可能偏移量。
- 整个文件加锁:l_start=0和l_whence=SEEK_SET指向文件的起始位置。指定l_len=0。
- 读锁共享:多个进程在给定的字节上可以共享读锁,即有读锁还能再加。
- 读锁替换:同一进程在已有一把读锁的同一文件区间加读锁,锁将被替换。
- 写锁独占:在一个文件的同一字节区间只能有一把写锁,不能有其它读锁或写锁。
- 分裂和合并:对一把锁的中间字段解锁,则分裂成2变锁。中间字段被加同样的锁,则合并为一把锁。
-
二、标准I/O
标准I/O处理了很多细节,如缓冲区分配、优化的块长度执行I/O等。这使得用户不必担心如何选择正确的块长度。
- 名称解释
- 宽字节:ASCII字符集:一个字符用一个字节表示。国际字符集:一个字符可用多个字节表示。标准I/O文件流可用于单字节或多字节(宽)字符集
- 流的定向:
- 1.当在为定向的流上使用一个多字节I/O函数,将设置流向为宽定向。
- 2.当在为定向的流上使用单字节I/O函数,将该流的定向设置为字节流向
- 3.freopen函数清除一个流的定向
- 4.fwide用于设置流的定向
- 缓冲:
- 目的:减少Read和write的调用次数
- 全缓冲:(磁盘)在填满标准I/O缓冲区后才进行实际I/O操作。1.磁盘上的文件通常都是由标准I/O实施全缓冲,在一个流执行第一次I/O操作时,相关标准I/O函数通常调用malloc获取缓冲区。
- 行缓冲:(终端输入输出),遇到换行符时,标准I/O库执行I/O操作。
- 不带缓冲:标准I/O不对字符进行缓冲存储。
- 特点(与文件I/O比较):
- 标准I/O 移植性好,很多UNIX之外的系统都实现了这些标准。
- fopen打开一个指向FILE对象的指针。该指针对象通常是一个结构:实际I/O的文件描述符、指向用于该流缓冲区的指针(进程空间缓冲区,和read/write的内核空间缓冲有区别)、当前在缓冲区中的字符数、出错标志etc
fwide 设置流的定向
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp,int mode);
--- 返回值:若流是宽定向,返回正值;如流是字节定向,返回负值;若流是为定向,返回0
- 参数
| mode | 说明 |
|---|---|
| 负数 | 试图使指定的流是字节流定向 |
| 正数 | 试图使指定的流是宽定向 |
| 0 | 不试图试图设置流的定向,饭返回标识该流定向的值 |
- 特点
setbuf/setvbuf 设置缓冲类型
#include <stdio.h>
void setbuf(File *restrict fp,char *restrict buf);
int setvbuf(File *restrict fp,char *restrict buf,int mode,size_t size);
---成功:返回;出错,返回EOF
- 参数及特点
- setbuf
| buf | 缓冲区及长度 | 缓冲类型 |
|---|---|---|
| 非空 | 长度为BUFSIZ的用户缓冲区buf | 全缓冲或行缓冲 |
| NULL | 无缓冲 | 不带缓冲 |
- setvbuf
| buf | buf | 缓冲区及长度 | 缓冲类型 |
|---|---|---|---|
| _IOFBUF | 非空 | 长度为BUFSIZ的用户缓冲区buf | 全缓冲 |
| _IOFBUF | NULL | 合适长度的系统缓冲区buf | 全缓冲 |
| _IOLBUF | 非空 | 长度为BUFSIZ的用户缓冲区buf | 行缓冲 |
| _IOLBUF | NULL | 合适长度的系统缓冲区buf | 行缓冲 |
| _IONBUF | 忽略 | 无缓冲 | 不带缓冲 |
fflush 刷新数据
#include <stdio.h>
int fflush(FILE *fp)
---成功:返回;出错,返回EOF
- 特点:
- 是该流所有未写的数据都被传送至内核
- 如果fp=null,则将导致所有的输出流被冲洗
fopen、freopen、fdopen
#include <stdio.h>
FILE *fopen(const char *restrict pathname,const char *restrict type);
FILE *freopen(const char *restrict pathname,const char *restrict type ,FILE *restrict fp);
FILE *fdopen(int fd, const char *type);
--- 成功:返回文件指针;出错:返回NULL
- 参数:
| type | 说明 | oopen标志 |
|---|---|---|
| r或rb | 为读而打开 | O_RDONLY |
| w或wb | 把文件截断至0长,或为写而创建 | O_WRONLY/O_CREAT/O_TRUNC |
| a或ab | 追加:为在文件尾写而打开,或为写而创建 | O_WRONLY/O_CREAT/O_APPEND |
| r+或r+b或rb+ | 为读和写 | O_RDWR |
| w+或w+b或wb+ | 把文件截断至0长,或为读和写而创建 | O_RDWR/O_CREAT/O_TRUNC |
| a+或a+b或ab+ | 为在文件尾读和写而打开或创建 | O_RDWR/O_CREAT/O_APPEND |
- 特点:
| 函数 | 说明 |
|---|---|
| fopen | 打开路径为pathname的一个指定文件 |
| freopen | 用于标准输入输出或标准错误。 |
| fdopen | 是一个标准I/O流与一个文件描述符结合(open、dup、dup2、fcntl、pipe、socket、socketpair、accept),用于创建管道和网络通信通道函数返回的描述符 |
fclose
#include <stdio.h>
int fclose(FILE *fp)
---成功:0;出错:EOF
- 特点:文件被关闭前,冲洗缓冲中的输出数据,输入呗丢弃。释放分配的缓冲
读和写
- 字符流(宽)读写
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(voide);
-- 成功:返回下一个字符;若刀刀文件尾端或出错:返回EOF
#include<stdio.h>
int putc(int c,FILE *fp);
int fputc(int c,FILE *fp);
int putchar(int c);
-- 成功:返回C;出错:返回EOF
- 行读写
#include <stdio.h>
char *fgets(char *restrict buf,int n,FILE *restrict fp);
char *gets(char *buf);"不能指定缓存大小,不推荐使用"
---成功:返回buf;到文件尾部或出错:返回NULL
#include <stdio.h>
int fputs(const char *restrict str,FILE *restrict fp);
int puts(const char *str);"不推荐使用"
--成功:返回非负;出错:返回EOF
- 二进制读写:(这里的二进制操作会一次读/写一个完整的结构,比一个字符大)
#include <stdio.h>
size_t fread(void *restrict ptr,size_t size,size_t boby,File *restrict fp);
size_t fwrite(const void *restrict ptr,size_t bobj,FILE *restrict fp);
--返回读或写的对象数
定位流
#include <stdio.h>
long ftell(FILE *fp); "成功:返回当前文件位置;失败:返回-1L"
int fseek(FILE *fp,long offset ,int whence); "成功:返回0;失败:返回-1"
void rewind(FILE *fp);
off_t ftello(FILE *fp); "成功:返回当前文件位置;出错:返回-1"
int fseeko(FILE *fp,off_t offset, int whence); "成功:返回0;出错:返回-1"
int fgetpos(FILE *restrict fp,fpos_t *restrict pos);
int fsetpos(FILE *FP, const fpost_t *pos);
--"成功:返回0;出错:返回非0"
临时文件
- ISO C 标准I/O :同时使用下面函数
#include <stdio.h>
char *tmpnam(char *ptr); "指向唯一路径名的指针"
FILE *tmpfile(void); "成功:返回文件指针;出错:返回NULL"
- Single UNIX Specification
#include <stdlib.h>
char *mkdtemp(char *template); 成功:返回指向"目录"名的指针;出错:返回NULL
int mkstemp(char *template); 成功:返回"文件"描述符;出错:返回NULL
内存流
#include <stdio.h>
1."成功:返回流指针;出错:返回NULL"
FILE *fmemopen(void *restrict buf,size_t size,const *restrict type);
2.字节:成功:返回流指针;出错:返回NULL
FILE *open_memstream(char **bufp,size_t *sizep);
3.宽字节:成功:返回流指针;出错:返回NULL
FILE *open_wmenstream(wchar_t **bufp,size_t *sizep);
三、高级I/O
非阻塞I/O、记录锁、I/O多路转接(select 和poll 函数)、异步I/O、readv和writev函数、存储映射I/O(mmmp)
名词解释
-
低速系统:
-
- 某些文件类型(如:管道、终端设备、网络设备)的数据不存在,读操作可能会是调用者永远阻塞。
-
- 数据不能被相同的文件类型立即接受(如:管道中无空间、网络流控制),写操作可能会被调用者永久阻塞。
-
- 某中条件发生之前打开某些文件类型可能会发生阻塞。
- 1.打开一个终端设备,需要线等待与之连接的调制解调器应答.
- 2.以写模式打开FIFO,那么在没有其它进程已用读打开该FIFO时也要等待)
-
- 对已加上强制性记录锁的文件进行读写;
-
- 某些ioctl操作;
-
- 某些进程间通信函数。
-
- 读写磁盘文件会暂时阻塞调用者,但并不能将与磁盘I/O有关的系统调用视为低速。
-
-
非阻塞:如果发起open、read、write这样的I/O操作,不能完成,则调用立即出错(errno=NAGAIN)返回,表示该操作如继续执行将阻塞。
- open函数,直接设置为O_NONBLOCK标志。
- 已经打开的文件描述符,调用fcntl将文件状态标志设置为O_NONBLOCK。
-
记录锁():当第一个进程正在读或修改文件的某个部分时,使用记录数可以阻止其它进程修改同一文件区。
#include<fcntl.h>
int fcntl(int fd,int cmd,.../*struct flock *flockprt*/);
---'成功:依赖下面CMD命令;出错:返回-1'
select 和 pselect
#include <sys/select.h>
int select(int maxfdpl,fd_set *restrict readfds,fd_set *restrict writefds, fd_set *restrict execeptfds,struct timeval * restrict tvprt );
--'成功:1.准备就行的描述符数目;超时,返回0;出错:-1'
- 参数
- fd_set:文件描述符集合
- maxfdpl:三个集合中最大文件描述符号值+1.
- tvptr:当文件描述符没准备好
| 参数 | 说明 |
|---|---|
| tvptrNULL | 永久等待,直到有一个关心的描述符准备好 |
| tvptr->tv_set0 && tvptr->tv_usec 0 | 不等待,直接返回 |
| tvptr->tv_set !=0 && tvptr->tv_usec !=0 | 等待指定的秒数和微秒数。 |
- 特点
-
告诉内核 :
- 关心的描述符及每个文件描述符关心的条件(每个描述符每次只能关心一种条件,readfds、writefds、excepfds中文件描述符不能重复)
- 等待时间
-
内核返回:
- 准备好(不会阻塞,就是所有数据都加载到了内核内存中)的描述符总数量
- 准备好(不会阻塞,就是所有数据都加载到了内核内存中)的文件描述符
-
准备好:
- read、write操作不阻塞。内核已经把读数据整理完毕,把写数据完成传输。
-
#include <sys/select.h>
1.检测fd是否已经准备好
int FD_ISSET(int fd,fd_set *fdset);
--'若fd在描述符集合中,返回非0值;否则,返回0'
2.设置、删除、清除
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
#include <sys/select.h>
int pselect(int maxfdp1,fd_set *restrict readfds, fd_set *restrict writefds,fd_set *restrct execptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask);
--'成功:1.准备就行的描述符数目;超时,返回0;出错:-1'
- 特点:
- timespec : 纳秒
- sigmask : 信号屏蔽只字,以原子操作的方式安装信号屏蔽字。
poll
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds,int timeout);
-- '返回值:准备就绪的描述符数目;若超时,返回 0;若出错,返回-1'
1. pollfd
struct pollfd {
int fd;
short events; 'fd感兴趣的事件'
short revents; 'fd发生了什么'
}
-
参数:
- events和revents标志
| 标志名 | 输入至events? | 从revents得到结果? | 说明 |
|---|---|---|---|
| POOLIN | . | . | 可以不阻塞地读高优先数据意外的数据(等效于POLLRDNORM |
| POLLRDNORM | . | . | 可以不阻塞地读普通数据 |
| POLLRDBAND | . | . | 可以不阻塞地读优先数据 |
| POLLPRI | . | . | 可以不阻塞地读高优先数据 |
| POLLOUT | . | . | 可以不阻塞地写普通数据 |
| POLLWRNORM | . | . | 与POLLOUT相同 |
| POLLWRBAND | . | . | 可以不阻塞地写高优先数据 |
| POLLERR | . | 已出错 | |
| POLLHUP | . | 已挂断 | |
| POLLNVAL | . | 描述符没有引用一个打开文件 |
* timeout
| 参数 | 说明 |
|---|---|
| timeout-1 | 永远等待 |
| timeout0 | 不等待,直接返回 |
| timeout > 0 | 等待指定的秒数和微秒数。 |
异步 I/O
-
步骤
-
- 打开文件
-
- 设置aio的缓冲区、信号及信号处理
-
- 启用for循环处理
-
- 根据append、read、write来做处理。
-
- read:
- aio_error 验证是否出错
- aio_return 将字节数据读取到aio中
- 处理
- 设置写的缓冲大小、字节数
- 调用aio_write
-
- write的操作与read类似
-
#include <aio.h>
int aio_read(struct aiocb *aiocb);
int aio_write(struct aiocb *aiocb);
--'成功:返回0;出错:-1'
1. aiocb
struct aiocb {
int aio_fildes; '文件描述符'
off_t aio_offset; '偏移量'
volatitle void *aio_buf; '缓冲区开始地址'
size_t aio_nbytes; '读/写的字节数'
int aio_reqprio; '异步I/O请求提示顺序'
struct sigvent aio_sigevent; 'I/O完成后,通知应用程序处理'
int aio_lio_opcode; '用于基于列表的异步I/O'
}
2. sigevent
struct sigevent {
int sigev_notify; '通知类型'
int sigev_signo; '通知数量'
union sigval sigev_value; '处理方法入数'
void (*sigev_notify_function) (union sigval); '处理方法'
pthread_attr_t *sigev_notify_attributes; '通知参数'
}
- 参数
- sigev_notify
| 参数 | 说明 |
|---|---|
| SIGEV_NONE | 请求完成后,不通知进程 |
| SIGEV_SIGNAL | 请求完成,返回sigev_signo指定信号。sigev_value会作为siginfo结构中的si_value,并将signinfo放入信号列表,等待处理程序处理 |
| SIGEV_THREAD | 请求完成,由sigev_notify_function指定的函数处理。sigev_value作为它的唯一参数。除非,sigev_notify_attributes字段被设定为pathread属性结构的地址,且该结构指定了另外一个县城的属性,否则该函数将在分离状态下的一个单独的线程中执行。 |
- 特点:
-
- 异步请求被放入等待处理的队列中,等待系统的AIO控制块处理.
-
aio_fsync :强制所有等待中的异步操作不等待而写入持久化的存储中
#include <aio.h>
int aio_fsync(int op,struct aiocb *aiocb);
--'成功:返回0;出错:-1'
- 参数:
- op:
| 参数 | 说明 |
|---|---|
| O_DSYNC | 和fdatasync一样:等待数据写入持久存储就返回 |
| O_SYNC | fsycn一样:等待数据和文件状态都完成 |
aio_error 和 aio_return
#include <aio.h>
int aio_error(const struct aiocb *aiocb);
-
特点:
- 用改函数测试异步I/O是否成功
-
返回:
| 参数 | 说明 |
|---|---|
| 0 | 异步操作成功完成。需要调用aio_return 函数获取操作返回值 |
| -1 | aio_error的调用失败 |
| EINPROGRESS | 异步读、写或同步操作仍在等待 |
| 其它情况 | 其它任何返回值是相关的异步操作失败返回的错误码 |
#include <aio.h>
int aio_return(const struct aiocb *aiocb);
-- '成功:read、write、fsync的的结果;出错:-1,设置errno'
- 特点
- 异步操作完成前,不要调用。否则os会释放掉包涵I/O操作返回值的记录
aio_suspend : 阻塞进程,直到I/O操作完成
#include <aio.h>
int aio_suspend(const struct aiocb *const list[] , int nent, const struct timespec *timeout);
-- '成功:0;出错:-1,设置errno'
-
参数:
- list:AIO控制块数组的指针,空指针被跳过。
- nent:数组中的条目
-
返回:
| 条件 | 返回 |
|---|---|
| 被信号中断 | 返回-1,将errno设置为EINTR |
| 没有完成任何I/O操作,超过timeout的时间 | 返回-1,errno=EAGAIN |
| I/O完成 | 返回 0 |
aio_cancel
#include <aio.h>
int aio_cancel(inf fd, struct aiocb *aiocb);
-
特点
- I/O操作取消成功,对应的AIO控制块调用aio_error函数将会返回errno=ECANCELED。
-
返回:
| 值 | 说明 |
|---|---|
| AIO_ALLDONE | 所有操作在尝试取消之前已完成 |
| AIO_CANCELED | 所有要求的操作已取消 |
| AIO_NOTCANCELED | 至少有一个要求的操作没有被取消 |
| -1 | 调用失败 |
lio_listio 提交一系列I/O请求
#include <aio.h>
int lio_listio(int mode,struct aiocb *restrict const list[restrict], int nent , struct sigevent *restrict sigev);
-- '成功:0;出错:-1'
-
参数:
- argv:异步通知信息
- mode:I/O是否是真的异步
- mode=LIO_WAIT:sigev参数被忽略。I/O操作完成后返回。
- mode=LIO_NOWAIT:放入I/O请求队列后,立即返回。
- argv = NULL, 完成时,每个AIO模块本身启动各自异步通知
- argv = 其它异步通知信息,I/O完成后自动发送。
-
特点:
readv 和 wrtiev
#include <sys/uio.h>
ssize_t readv(int fd,const struct iovec *iov , int iovcnt);
ssize_t writev(int fd,const struct iovec *iov, int iovcnt);
--'已读或已写的字节数;出错:-1'
1. strct iovec {
void *iov_base; '开始地址'
size_t iov_len; '缓冲大小'
}
-
参数:
- iov:缓冲区数组
- iovcnt: 数组个数
-
特点:
- 一次函数中读、写多个非连续缓冲区的数据到内核的缓冲区。
- writev好理解,数组顺序来写。
- readv则按照数组顺序,将一个数组一个数组填满。
- 配合真实的read/write。当需要复制数据增加,能极大的减少数据复制。

存储映射I/O
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int port,int flag,int fd, off_t off );
-- '成功:返回映射区的起始地址;出错:MAP_FAILED'
-
参数:
- addr:映射存储区的起始位置
- fd:要被映射文件的描述符。
- port:映射存储区的保护要求
| 参数 | 说明 |
|---|---|
| PROT_READ | 映射区可读 |
| PROT_WRITE | 映射区可写 |
| PROT_EXEC | 映射区可执行 |
| PROT_NONE | 映射区不可访问 |
- flag:
| 参数 | 说明 |
|---|---|
| MAP_FIXED | 返回值必须等于addr |
| MAP_SHARED | 本进程对映射区所进行的存储操作的配置 |
| MAP_PRIVATE | 对映射区的存储操作导致创建映射文件的一个私有副本 |
-
特点:
- 将一个磁盘文件映射到存储空间的一个缓冲区
浙公网安备 33010602011771号