系统IO
1. shell man command(查看信息的最佳选择)
1. 2 man 的使用
$ man number target
## number对应的数值:
# 1 可执行的程序 或 shell 命令
# 2 系统调用(内核提供的函数接口 (系统IO))
# 3 库调用(程序库中的函数 (标准IO的函数就在里面))
# 4 特殊文件(通常位于 /dev(如设备文件))
# 5 文件格式和规范,如 /etc/passwd
# 6 game 游戏
# 7 杂项 (包括宏包和规范)
# 8 系统管理的命令 (通常只针对超级用户(root))
# 9 内核例程 (非标准的)
## target为要查询的对象
2 系统IO相关函数
2.1 open 打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int flags);
int open(const char *path, int flags, mode_t mode);
int openat(int fd, const char *path, int flags, ...);
· 函数参数和返回值介绍:
· int :成功返回的int是当前进程的操作文件的 fd 文件描述符,失败返回-1
· const char *path: 要操作的文件的路径(绝对路径)
· int flags:宏定义文件操作模式
· mode :是创建的文件的文件权限,该参数仅在flags 包含时生效
· flags宏定义详解:
· 文件打开方式(必选其中一项)
· O_RDONLY 只读打开方式(write only)
· O_WRONLY 只写打开方式(read only)
· O_RDWR 读写打开方式(write read)
· O_EXEC 只执行打开(但Linux不支持)
· O_SEARCH 只搜索打开(仅应用于目录)(但Linux不支持)
· 文件打开后的附加属性(任选项)
· O_APPEND 每次读写都追加到文件的尾端,读写文件光标会每次都写都自动移到文件末尾
· O_CLOEXEC 把FD_CLOEXEC常量设置为文件描述符标志
· O_CREAT 若文件不存在则创建文件,使用此选项时,需要同时传入mode参数设置创建的文件的文件权限
· O_DIRECTORY 如果path引用的不是目录,则出错
· O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则报错
· O_NOCTTY 如果path是终端设备,则不将该设备分配作为此进程的控制终端
· O_NOFOLLOW 非符号链接模式,一般如果操作的对象是一个符号链接的话,会自动指向符号链接指向的文件,但如果加了这个标志,如果是符号链接就会报错
· O_NONBLOCK 如果引用的 path 不是一个FIFO,一个特殊文件或是一个字符特殊文件,则此选项将文件的本次打开操作和后续的IO操作设置为非阻塞的方式
· O_DIRECT 表示使用直接 I/O 操作,即数据直接从用户缓冲区传送到物理设备,或者从物理设备传送到用户缓冲区, 需要加上宏定义开启 #define _GNU_SOURCE
· 直接IO的对齐限制因为直接 I/O 涉及到对磁盘设备的直接访问,所以在执行直接 I/O 时,必须要遵守以下三个对齐限制要求
· 应用程序中用于存放数据的缓冲区,其内存起始地址必须以块大小的整数倍进行对齐
· 写文件时,文件的位置偏移量必须是块大小的整数倍
· 写入到文件的数据大小必须是块大小的整数倍
· 如果不满足以上任何一个要求,调用 write0均为以错误返回 Invalid argument。以上所说的块大小指的是磁盘设备的物理块大小 (bloak size),常见的块大小包括 512 字节、1024字节、2048 以及 4096 字节
· 确定磁盘分区的大小命令:tune2fs -l/dev/sdal grep "Block size""
2E
· O_SYNC 同步模式,使每次write等待物理IO操作完成,包括该write操作引起的文件属性更新所需的IO
· O_TRUNC 如果文件存在,而且为只写或读成功打开,则将其长度截断为 0
· O_TTY_INIT 如果打开一个未打开的终端设备,设置非标准的 termios 参数值,使其符合Single UNIX Specification
Single UNIX Specification中同步输入和输出选项的部分:
·O_DSYNC 每次write都要等待物理IO操作的完成,但是如果该读写并不影响读取刚写入的数据,则不需等待文件属性被刷新
·O_RSYNC 使得每一次以文件描述符作为参数进行read操作等待,直到所有对文件同一部分挂起的写操作都完成.
open and openat 的区别:
· opennat 传入的 path 如果是绝对路径,这个情况下 fd 参数是被忽略的,openat 就相当于 open
· opennat 传入的 path 如果是相对路径,fd 参数指出了相对路径名在文件系统中的开始地址,fd 参数是通过打开相对路径名所在的目录来获得的(目录也是文件的一种,也是可以打开的)
· path 参数指定了相对路径名,fd 参数具有特殊值 AT_DPCWD, 这种情况下,路径为当前工作目录中获得,openat 函数在操作上与 open 函数类似
2.2 close 关闭文件
#include <unistd.h>
int close(int fd);
· 函数参数和返回值说明:
· int :执行成功返回0,失败返回 -1
· int fd :为要关闭的文件在当前进程的文件描述符
· 特性::
· 关闭一个文件时还会释放该进程在该文件上的所有记录锁
· 当一个进程终止时,内核会自动关闭他所打开的所有文件。许多程序都利用这一功能不显式的用close关闭已打开的文件
·文件描述符是一个很小的指针指向操作,占用的内存也不会很大,都是文件描述符是有限的,不能无限的打开文件(默认一个进程是1024个文件描述符)
2.3 creat 创建文件
#include <fcntl.h>
int creat(const char *path, mode_t mode);
· 函数参数和返回值说明:
· int:创建成功返回只写打开的文件描述符;创建失败返回 -1
· const char *path:文件的绝对路径
· mode_t mode:是创建的文件的文件权限,该参数仅在flags 包含时生效
2.4 write 文件写入
#include <unistd.h>
issize_t write(int fd, const void *buf, size_t count);
· 函数参数和返回值说明:
· issize_t:返回的是成功写入的字节数,失败返回 -1
· int fd:为要写入的文件在当前进程中的文件描述符
· const void *buf:为写入缓冲区,存放要写入的数据
· size_t count:为申请写入的字节个数
· 特性:
· write 是根据 *buf指针作为要写入的数据的首地址的,根据count为内存段大小,并不管内存是否合法,只要程序能正常写入都会根据count执行写入对应的数据(即使内存段没申请,但访问到没权限或是其他特殊段的内存可能就会报错)
· rg: write(1, "abc", 1024); write会根据“abc”的首地址然后偏移1024的内存段写入到fd中,然后返回1024
· 但如果你把缓冲区写满了就会触发写阻塞(在open时以阻塞模式打开的文件),进程进入休眠状态等待缓冲区有空间
· 非阻塞模式下,写的缓冲区为已满时,会返回0,表示写入0个字节的数据,不会让程序因阻塞而等待系统资源进入休眠
2.5 read 文件读取
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
· 函数参数和返回值说明:
·ssize_t:返回的是成功读取的字节数,失败返回 -1
·int fd:fd为要写入的文件在当前进程中的文件描述符
·void *buf:为读取缓冲区,系统会将读取后的数据放在改缓冲区中
·size_t count:为申请读取的字节个数
· 特性:
· 如果以阻塞的模式打开文件进行读操作,读的缓冲区为空时,就会触发读阻塞,等到读缓冲区有数据可以供进程读取时,进程才继续继续读操作
· 非阻塞模式下,读的缓冲区为空时,会返回0,表示读取到0个字节的数据,不会让程序因阻塞而等待系统资源进入休眠
2.6 lseek 设置文件操作光标
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
· 函数参数和返回值说明:
· off_t: 返回执行后的文件光标所在处
· off_t offset: 申请的光标偏移量
· 但基准不在文件首处时,允许whence为负数,但不能超过文件的首位置
· 简而言之,文件大小只是是0 到 正无穷,没有负大小的文件
· int whence: whence为宏定义,设置要执行光标时的光标基准
· SEEK_SET 光标在文件首处,再执行偏移操作
· SEEK_CUR 光标在文件当前的光标位置,再执行偏移操作
· SEEK_END 光标在文件尾处,再执行偏移操作
2.7 sync 刷新页缓存区
· 调用sync函数可以强制把页缓存区中的数据写入磁盘或其他硬件设备中去
描述:
· 页缓冲区的数据同步到硬件中,需要缓冲区满或是间隔一定的时间,当然你也可以调用sync函数刷新页缓存区
· 这只是对普通的文件来说的。对与设备文件来说,设备在内核上是没有对应的页缓存区的,设备文件一般都通过DMA直接与访问设备
· 但有些设备文件也使用内核中的页缓存区,以此来减少对设备的读写次数
· 函数原型:
#include <fcntl.h>
void sync(void);