UNIX环境高级编程(3) 文件IO - Charles-Charmless/Charles-Charmless.github.io GitHub Wiki
- 可用的文件IO函数:打开文件,读文件,写文件等(不带缓冲的IO函数(指每个read或write函数都调用内核的一个系统调用)),不是ISO C的组成部分,但是时POSIX和SUS的组成部分
- UNIX系统中常用的文件IO函数:open,read,wirte,lseek,close函数
- 当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符(非负整数)
- UNIX的shell把文件描述符0与标准输入关联,文件描述符与标准输出关联,文件描述符2与标准错误输出关联,在POSIX标准中,对应的被替换为符号常量STDIN_FLLENO,SEDOUT_FLLENO,STDERR_FLLENO <unistd,h>
int open(const char* path,int oflag,.../*mode_t mode*/);int openat(int fd,const char* path,int oflag,.../*mode_t mode*/);
-
将最后一个参数写为…,表明余下的参数的数量以及类型是可变的
-
由open和openat函数返回的文件描述符一定是最小的未用的描述符数值
- 若path参数指定为绝对路径,fd参数无用,openat函数相当于open函数
- path指定的是相对路径,fd参数指出相对路径名在文件系统中的开始位置
- path指定相对路径名,fd参数具有特殊值AT_FDCWD,路径名在当前工作目录中获取,openat函数在操作上与open函数类似
- mode:文件权限
- oflag参数: 参数 描述 ————– —— O_RDONLY
O_WRONLY
O_RDWR
O_EXEC
O_SEARCH
O_APPEND
O_CLOEXEC
O_CREAT
O_DIRECTORY
O_EXCL
O_NOCTTY
O_NOFOLLOW
O_NONBLOCK
O_SYNC
O_TRUNC
O_TTY_INIT
O_DSYNC
O_RSYNC
✰可以通过逻辑或同时使用多个参数
int creat(const char *path,mode_t mode);
等效于
open(path,0_WRONLY|O_CREAT|O_TRUNC,mode)
int close(int fd);
off_t lseek(int fd,off_t offset,int whence)
- 系统默认情况下,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0
- 调用lseek函数可以显式的为打开一个文件设置偏移量
- 如果whence是SEEK_SET,则将该文件的偏移量设置为距文件开头处offset个字节
- 如果whence是SEEK_CUR,则将该文件的偏移量设置为当前值加offset,offset可正可负
- 如果whence是SEEK_END,将该文件的偏移量设置为文件长度加offset,offset可正可负
ssize_t read(int fd,void *buf,size_t nbytes);
返回值:读到的字节数,若已到文件尾,返回0,若出错,返回-1 (可能因为数据的本身大小的原因导致读到的字节数少于要求读的字节数)
- 在ISO C中,void*表示通用指针
ssize_t write (int fd,void *buf,size_t nbytes)
返回值:若成功,返回已写字节数,若出错,返回-1
- 对于普通文件,写操作会从当前偏移量开始
IO效率:当缓冲区大小与磁盘block大小一致时系统cpu时间最少(效率最高)当继续增大缓冲区大小时并不会提高效率
UNIX系统支持在不同的进程间共享打开的文件
- 进程表项(每个进程都有一个记录项,记录项包含一张打开文件描述符表)
- 文件描述标志
- 指向一个文件表项的指针
- 文件表项(内核为所有文件维持一张文件表)
- 文件标志状态
- 当前文件偏移量
- 指向该文件V节点表项的指针
- V节点表项(每个打开文件(或设备)都有一个V节点结构)
- 多个进程同时对一个文件进行操作导致混乱的原因在于使用了多个操作符(先定位再操作)
- 解决这种混乱只需使这两个操作对于其他进程而言成为一个原子操作
- 原子操作指多步组成的一个操作,任何要求多于一个函数调用的操作不是原子操作 ,如果该操作原子的执行,那么执行完所有的操作,要么一步也不执行,不可能只执行所有步骤的一个子集
ssize_t pread(int fd,void* buf,size_t nbytes,off_t offset);ssize_t pwrite(int fd,const void* buf,size_t nbytes,off_t offset);
- 调用pread相当于调用lseek后调用read
- pread与上面的顺序调用的区别:调用pread时无法中断其定位与操作 不会更新当前文件偏移量
- 调用pwrite相当于调用lseek后调用write
- pwrite也有类似的区别
描述:复制一个现有的文件描述符int dup(int fd);int dup2(int fd,int fd2);
- 由dup返回的新文件描述符一定是当前可用文件爱你描述符中最小数值
- 对于dup2可以用fd2参数指定新描述符的值,如果fd2已经打开,则先将其关闭,如果fd等于fd2,则dup2返回fd2,而不关闭他,否则fd2的FD_CLOEXEC文件描述符标志就会被清空,这样fd2在调用exec时总是打开的状态。
int fsync(int fd);int fdatasync(int fd);void sync(void);
- 传统的UNIX系统内核内没有设置缓冲区高速缓存或页高速缓存,大多数磁盘IO都通过缓冲区进行,等到修改过的数据需要写到磁盘中,或者当内核需要重用缓冲区来存放其他磁盘块数据时,再将缓冲区内的数据块(延时写数据)写入磁盘
- sync只是将所有修改过的块缓冲区排入写队列,然后就返回,不等待实际写磁盘操作的完成
- fsync函数只针对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束才返回
- fdatasync类似于fsync,但他只影响文件的数据部分,而除数据外,fsync还会同步更新文件的属性
int fcntl(int fd,int cmd,.../*int arg*/);
** 描述:
**改变已经打开文件的属性
**功能:
**
功能 | cmd参数 |
---|---|
复制一个已有的操作符 | (cmd=F_EUPFD或 F_DUPED_CLOEXEC) |
获取/设置文件描述符标志 | (cmd=F_GETFD 或F_SETFD) |
获取/设置文件状态标志 | (cmd=F_GETFL或 F_SETEL) |
获取/设置异步IO所有权 | (cmd=F_GETOWN或 F_SETOWN) |
获取/设置记录锁 | (cmd=F_GETLK 或F_SETLK 或F_SETLKW)(在记录锁处在详解) |
cmd值 | 描述 |
---|---|
F-DUPFD | |
F-DUPFD_CLOEXEC | |
F-GETFD | |
F-SETFD | |
F-GETFL | |
F-SETFL | |
F-GETOWN | |
F-SETOWN |
对于fcntl的文件状态标志 | 描述 |
---|---|
O_RDONLY | |
O_WRONLY | |
O_RDWR | |
O_EXEC | |
O_SEARCH | |
O_APPEND | |
O_NONBLOCK | |
O_SYNC | |
O_DSYNC | |
O_RSYNC | |
O_FSYNC | |
O_ASYNC |
➤前面5中访问方式标志并不各占一位,一个文件的访问方式只能是这5个值之间的一个,必须先用屏蔽字O_ACCMODE取得访问方式位再将结果与这5个值之间的每一个进行比较返回值:
- 如果出错,所有命令返回-1
- 如果成功返回某个其他值,
- 下列四个命令具有特定返回值:
- F_DUPFD,F_GETFD,F_GETFL,F_GETOWN第一个命令返回一个新的文件描述符
- 第二个第三个命令返回相应的标志,
- 最后一个命令返回一个正的或负的进程组ID
- 正的表示对应的进程ID,负的进程组ID表示绝对值对应的一个进程组ID
int ioctl(int fd,int request);
(SUS标准扩展)
- ioctl函数一直是IO操作的杂货箱,不能用其他函数表示的IO操作通常都能用ioctl函数表示 终端最常用
/dev/fd 用文件来表示文件描述符
- 特指标准输入或标准输出