Fileio
1.open() 系统调用
头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
原型
int open(const char * name,int flags);
int open(const char * name,int flags,mode_t mode);
flags O_RDONLY O_WRONLY O_RDWR
O_APPEND每次写操作都写入文件的末尾
O_CREAT如果指定文件不存在,则创建这个文件
O_EXCL如果要创建的文件已存在,则返回-1,并且修改errno的值
O_TRUNC如果文件存在,并且允许写,则清空文件全部内容(即将其长度截短为0)
O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK如果路径名指向FIFO/块文件/字符文件,则把文件的打开和后继I/O设置为非阻塞模式
以下三个常量同样是选用的,它们用于同步输入输出
O_DSYNC等待物理I/O结束后再write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
O_RSYNCread等待所有写入同一区域的写操作完成后再进行
O_SYNC等待物理I/O结束后再write,包括更新文件属性的I/O
注意:
O_CREAT 选择后一定要填写mode参数
Mode
参数S_I + R+W+X + U
U表示设置所有者 单独设置一项填USR
G表示设置组用户 单独设置一项填GRP
O表示设置其他人 单独设置一项填OTH
Mode
还可以设置为八进制数值如 0644
2.creat()
头文件
#include <sys/types.h>
#include
<sys/stat.h>
#include <fcntl.h>
原型
Int creat(const char* name,mode_t mode);
相当于
Open(name ,O_RDONLY|O_CREAT|O_TRUNC, mode)
3.Read()
头文件
#include<unistd.h>
函数原型
Ssize_t
read(int fd ,void buf,size_t len);
Read 是否会阻塞由所读的设备决定,在open等函数中设定
Size_t 最大值为SIZE_MAX
Ssize_t最大值为 SSIZE_MAX
4.Write()
头文件
#include<unistd.h>
原型
Ssize_t
write(int fd, const void * buf,size_t count);
Read和write实际都是和缓冲区交互数据,不是直接和磁盘交换数据
详解https://www.cnblogs.com/JohnABC/p/5821660.html
5.同步IO
Fsync() 和fdatasync()
头文件
#include<unistd.h>
Int fsync(int fd);
直到fd的数据写入磁盘缓冲之后才返回
,同时还会建立时间戳和inode信息等元数据
Int fdatasync(int fd);
直到fd的数据写入磁盘缓冲之后才返回
可能导致的问题是文件数据更新,但是目录没有更新,那么数据会成功写入,但是目录项没有更新,文件无法访问。
解决方法:目录也要调用fsync();
Sync()
Void sync(void);
同步所以缓存。
同步标志 O_SYNC
在open()的时候,加上这个标志,所有在文件上的IO都会同步
O_DSYNC
只同步普通数据
O_RSYNC
直接IO
使用0_DIRECT open(),系统将最小化IO管理,直接将用户缓冲区和设备进行初始化,所有的IO都是同步的,操作在完成之前不会返回。
4.关闭文件close()
头文件
#include<unistd.h>
Int close(int fd);
解除fd和文件的关联文件描述符不在有效
同一个程序绑定的fd如果close了,然后在打开,之前的fd和后面的指相同,是可以用来执行文件操作的
5.Lseek()
头文件
#include<sys/types.h>
#include<unistd.h>
函数原型
Off_t
lseek(int fd,off_t pos,int origin);
Origin 可以设置为
SEEK_CUR 从文件当前位置移动 pos位
SEEK_END 从文件尾开始移动pos位
SEK_SET 从文件开始移动pos位
返回新的文件位置
Ret = -1 表示出错
定位读写
Pread()
头文件
#include<unistd.h>
也就是说:
#define _XOPEN_SOURCE
是为了可以使用 5. The X/Open
Portability Guide 的功能。
函数原型
Ssize_t
pread(int fd,void *buf,size_t count,off_t pos);
Pwrite
函数原型
Ssize_t
pwrite(int fd,const void * buf,size_t count,off_t pos);
在pos处进行读写
不会改变fd的位置,和read write 混合用会导致覆盖
文件截断
头文件
#include<unistd>
#include<sys/types.h>
函数原型:
Int
ftruncate(int fd,off_t len);
Int
truncate(char * path,off_t len);
IO 多路复用
6.Select
头文件
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
函数原型:
Int
select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval
*timeout);
n
所有集合中文件描述符最大值加一
timeout
结构体
#include<sys/time.h>
Struct{
Long
tv_sec;//seconds
Long
tv_usec; //microseconds
};
Select返回时这个结构体状态未定义,最好重新初始化,较新版本会把他初始化为剩余时间
如果时间为0,select立即返回,报告文件描述符不可用,不在等待后续操作。
管理文件描述符宏定义
FD_CLR(int
fd,fd_set *set);
FD_ISSET(int
fd,fd_set *set); //测试一个文件描述符是否在集合内
FD_SET(int fd,fd_set *set); //添加一个文件描述符到指定集合
FD_ZERO(fd_set *set); //移除指定集合中所有描述符 ,每次调用select()前调用
文件描述符集合是静态建立的,最大值有限制,为 FD_SETSIZE; linux 为1024
通过将select 三个集合设置为0 ,将超时设置为非空可以实现sleep;
7.Pselect
posix版本的select
头文件
#include<sys/select.h>
#define _XOPEN_SOURCE 600
原型
Int
pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
Const struct timespec *timeout,
Const sigset_t * sigmask);
和select比区别
超时参数类型改变,pselect 使用timespec精度为秒和纳秒,比秒和毫秒精度高,事实是都在秒之后不可靠
Pselect 不修改timeout参数
Select没有sigmask参数,pselect添加是为了解决信号和文件描述符之间的竞争条件
一般还是选择使用select
注意:
Select在有文件描述符状态变化时返回,返回时修改了文件描述符集合,返回后的集合中只有状态改变的fd,每次使用时要重新赋值。
8.Poll
System v 的I/O多路复用
头文件:
#include<sys/poll.h>
函数原型:
Int poll(struct pollfd *fds,unsigned int
nfds, int timout);
Pollfd
结构
头文件
#include<sys/poll.h>
Struct
pollfd
{
int fd; //文件描述符
Short event;// 监视的文件描述符上的事件位掩码
Short revent;// 发生在文件描述上的事件位掩码
}
常量 |
说明 |
POLLIN |
普通或优先级带数据可读 |
POLLRDNORM |
普通数据可读 |
POLLRDBAND |
优先级带数据可读 |
POLLPRI |
高优先级数据可读 |
POLLOUT |
普通数据可写 |
POLLWRNORM |
普通数据可写 |
POLLWRBAND |
优先级带数据可写 |
POLLERR |
发生错误 |
POLLHUP |
发生挂起 |
POLLNVAL |
描述字不是一个打开的文件 |
注意:
Poll在返回后只修改了fds的revents字段,所以可以反复使用。
与select区别。
9.Ppoll
和pselect 类似
标准I/O
打开文件
10.fopen()
头文件
#include<stdio.h>
函数原型:
FILE*
fopen(const char* path,const char *mode);
文件打开后关联到一个新的流
打开方式有: r r+ w w+ a a+
成功返回指针,失败返回NULL。
11.fdopen
头文件
#include<stdio.h>
函数原型:
FILE* fdopen(int
fd,const char *mode);
讲一个打开的文件的描述符转化为流,转化时mode要一致
12.fclose
头文件
#include<stdio.h>
函数原型:
Int
fclose(FILE * stream);
成功返回0 并将缓冲中没写入数据写入,失败返回EOF
13. fcloseall
#include <stdio.h>
#define _GUN_SOURCE
Int fcloseall();
14.fgetc()
#include<stdio.h>
Int fgetc(FILE*stream);
函数结果必须以int保存
,EOF会被返回,输出时要检查
c== EOF
15.ungetc()
#include<stdio.h>
Int ungetc(int c,FILE*stream);
成功返回0,失败返回EOF;
16.fgets
#include<stdio.h>
Char * fgets(char *str,int size,FILE*
stream);
读取size-1 个字节到str,成功返回str失败返回NULL。
读到EOF或者换行符的时候结束,如果读到换行符就写入str。
最大输入长度为LINE_MAX 在limits.h中定义
17.fread
18.fputc()
#include<stdio.h>
Int fputc(int c,FILE *stream);
19fputs()
#include<stdio.h>
Int fputs(const char *str,FILE *stream);
20. fwrite
21.fseek()
#include<stdio.h>
Int fseek(FILE *stream,long offset,int whence);
Whence有如下值:
SEEK_CUR 文件位置为 当前加offset
SEEK_END文件位置为文件尾加offset
SEEK_SET文件位置为0+offset
错误返回-1;正确返回0
22.fsetpos()
#include<stdio.h>
Int fsetpos(FILE * stream,fpos_t *pos);
23.rewind
#include<stdio.h>
Void rewind(FILE* stream);将流置于初始位置
24 ftell()
#include<stdio.h>
long ftell(FILE* stream);
返回文件的当前流位置。错误返回-1.
25fgetpos
#include<stdio.h>
int fgetpos(FILE* stream,fpos_t *pos);
26.fflush
#include<stdio.h>
Int fflush(FILE* stream); 将流中数据写回文件
成功返回0;
Int ferror
检查是否有错
Int feof 检查是否文件尾
Void clearer 清空失败和文件尾标志,不可恢复
27.fileno
#include<stdio.h>
Int fileno (FILE *stream);
返回文件描述符失败返回-1;
28.Setvbuf
#include<stdio.h>
Int setvbuf(FILE * stream, char * buf, int
mode, size_t size);
_IONBF
_IOLBF
_IOFBF
函数在除了无缓存模式下,buf可以指向一个size大小的缓冲区,如果buf为空则glic分配
Stdio.h中的BUFSIZE定义默认的缓冲区大小
29.readv 和writev
#include<sys/uio.h>
Ssize_t readv(int fd ,const struct iovec
*iov,int count);
Ssize_t writev(int fd ,const struct iovec
*iov,int count);
从iov描述的缓冲区读或者写count个segment的数据到fd。
Struct iovec 描述的缓冲区成为segment
#include<sys/uio.h>
Struct iovec
{
Void *iov_base;
Size_t iov_len;
}
30.epoll
#include<sys/epoll.h>
Int epoll_create(int size);
返回与这个实例关联的文件描述符 size为要监测的文件描述符数目。出错返回-1
EINVAL
size不是正数
ENFILE 系统打开文件数达到上限
ENOMEN 没有足够内存
返回的文件描述符需要用close关闭。
#include<sys/epoll.h>
Int epoll_ctl(int epfd,int op,int fd,struct
epoll_event *event);
#include<sys/epoll.h>
Struct epoll_event{
_u32 events;
Union{
Void *ptr;
Int fd;
_u32 u32;
_64 u64;
} data;};
Epoll_ctl 调用会关联epoll实例和epfd, op指定要进行的操作,event描述更具体的行为
Op的取值
EPOOL_CTL_ADD 把fd加到监测列表
EPOOL_CTL_DEL 删除fd到监测列表
EPOOL_CTL_MOD 使用event修改监测列表中的fd
Event取值:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要使用epoll_ctl_mod修改fd
#include<sys/epoll.h>
Int epoll_wait ( int epfd,struct
epoll_event * events,int maxevents,int timeout );
Timeout 毫秒
返回成功
events指向epoll_event结构体,其中最多有maxevents 返回值为事件个数,出错返回-1
边缘触发和水平触发
Epoll_ctl中events设置为 RPOLLE时,设置边缘触发。
例子:
- 生产者向管道写入数据1KB
- 消费者管道调用epoll_wait等待数据。
在边缘触发中,直到1数据写完epoll_wait才返回,水平触发则epoll_wait立即返回
水平触发为默认形式,select和poll都是水平模式。水平触发只关心状态,边缘触发关系事件。
内存映射
31.Mmap
#include<sys/mman.h>
void*
mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset);
addr 参数告诉内核映射文件的最佳地址,只是提示不是强制。一般设置为 0;调用返回内存映射的开始地址。
Prot 设置访问权限 PROTNONE 无法访问,一般这样设置没有意义。
PROT_READ 可读
PROT_WRITE 可写
PROT_EXEC 可执行
访问的权限不能与打开文件的访问模式冲突。
Flag参数描述了映射的类型和一些行为
MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
MAP_DENYWRITE //这个标志被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
MAP_FILE //兼容标志,被忽略。
MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
off_toffset:被映射对象内容的起点。
成功返回映射区域的地址
SIGBUS //进程访问无效的映射去死产生
SIGSEGV
//进程试图在只读映射区写入数据时产生。
32.Sysconf
#include<unistd.h>
Long sysconf(int name);
返回name属性的值失败返回-1.
例子:
long pagesize = sysconf(SC_PAGR_SIZE);获取页面大小
#include<unistd.h>
Int getpagesize(void);
#include<asm/pages.h>
Int page_size = PAGE_SIZE;
33.munmap
#include<sys/mman.h>
int munmap(void*
addr,size_t len);
移除从addr开始的len字节长度的映射。移除后访问会产生SIGSEGV信号
成功返回0,失败返回-1;
34.mremap()
#include<sys/mman.h>
#include<unistd.h>
#define _GNU_SOURCE
Void * mremap(void *addr,size_t
old_size,size_t new_size,unsigned long flags);
修改文件大小size 从old到new
35.mprotect
#include<sys/mman.h>
Int mprotect(const void *addr,size_t
len,int prot);
修改映射区域权限
在有些系统中mprotect只能修改mmap分配的区域,在linux可以修改所有的
36.msync
#include<sys/mman.h>
Int msync(void *addr,size_t len,int flags);
Flag 取值
MS_ASYNC 同步操作一部发生,系统调度更新,mysnc立即返回。
MS_INVALIDATE 所有该块映射的拷贝失效,未来对该映射的操作直接写到硬盘
MS_SYNC
所有同步同步进行,在write执行完毕后msync才返回。
#include<sys/mman.h>
Int Madvise(void *addr,size_t len,int len);
#include<fcntl.h>
Int posix_fadvise(int fd ,odd_t
offset,off_t len,int advise);
#include<fcntl.h>
Ssize_t readahead(int fd ,off64_t
offset,size_t count);
同步Synchronized ,同步Synchronous 异步asynchronous
同步在读写操作执行完之后才返回。
异步不用读写操作执行完就可以返回
Synchronized保证事件发生,属于更深层次,NoSynchronized写操作加入队列就返回,只确保到内核缓存区域。