文章目录
1. Linux目录
目录本身也是文件。文件的属性被保存在inode(节点)中。
- 目录是用来保存其他文件节点号和名字的文件
- 文件节点号可用ls -i来查看
- 目录中每个数据项都是指向某个文件节点的链接
- 通过ln命令可以增加指向同一个文件的链接。
2. 系统调用
系统调用是对文件和设备进行访问控制的函数
常用的系统调用
- open:打开文件或者设备
- read:读取打开的文件或设备中的数据
- write:对文件或设备写入数据
- close:关闭文件和设备
- ioctl:将控制信息传递给设备驱动程序
系统调用效率低的原因
- 系统调用需要在用户代码和内核代码之间来回切换,开销大
- 硬件本身限制系统调用一次所能读写的块大小
3. 库函数
标准库函数效率更高。
下图为文件函数、用户、设备驱动程序、内核和硬件之间的关系
4. 底层文件访问
运行的程序被称为进程,进程有与其关联的文件描述符,通过文件描述符可以访问打开的文件或设备。
3个文件描述符
- 0:标准输入
- 1:标准输出
- 2:标准错误
4.1 write系统调用
函数原型
#include <unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
示例程序
1 #include <unistd.h>
2 #include <stdlib.h>
3
4 int main()
5 {
6 if ((write(1, "Here is some data\n", 18)) != 18)
7 write(2, "A write error has occurred on file descriptor 1\n", 46);
8
9 exit(0);
10 }
程序解释
write函数是将buf缓冲区的前nbytes个字节写入和文件描述符fildes相关联的文件中,并返回实际写入的字节数。
- 返回0:表示未写入数据
- 返回-1:表示函数调用出现错误
- 错误代码保存在errno中
4.2 read系统调用
函数原型
#include <unistd.h>
size_t read(int fildes, void *buf, size_t nbytes);
示例程序
1 #include <unistd.h>
2 #include <stdlib.h>
3
4 int main()
5 {
6 char buffer[128];
7 int nread;
8
9 nread = read(0, buffer, 128);
10 if (nread == -1)
11 write(2, "A read error has occurred\n", 26);
12
13 if ((write(1, buffer, nread)) != nread)
14 write(2, "A write error has occurred\n", 27);
15
16 exit(0);
17 }
程序解释
- 形参fildes:文件描述符
- 形参buf:缓冲区
- 形参nbytes:期望读取的字节数
- 返回值:实际读取的字节数
$ ./simple_read < file.txt
- 含义:将file.txt里的数据重定向到程序simple_read中。
- 注意:不可file.txt > ./simple_read,这么做的意思是将simple_read的内容改为file.txt
4.3 open系统调用
函数原型
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode);
sys/types.h和sys/stat.h在POSIX规范的系统上不需要包含
- open作为文件或设备访问的桥梁
- open调用成功会返回一个可以被其他系统调用使用的文件描述符
- 文件描述符在进程间不共享,多个进程操作一个文件,会有多个文件操作符
- 如果同时写一个文件会相互覆盖,而不是交织
- 可以使用锁防止冲突
oflags参数通过命令文件访问模式与其他可选模式相结合的方式来指定。
open调用必须指定下表的访问模式之一:
模 式 | 说 明 |
---|---|
O_RDONLY | 以只读方式打开 |
O_WROLY | 以只写方式打开 |
O_RDWR | 以读写方式打开 |
open调用可用按位或方式和其他可选模式进行组合:
模 式 | 说 明 |
---|---|
O_APPEND | 写入数据追加到文件末尾 |
O_TRUNC | 把文件长度设置为0,丢弃现有内容 |
O_CREAT | 按照参数mode指定的访问模式创建文件 |
O_EXCL | 和O_CREAT一起使用,确保文件被创建 |
- open为原子操作,即只执行一个函数调用,若多个程序同时创建同一个文件将失败
下面命令可以查找open信息
man 2 open
要是没有则需要安装开发文档
sudo apt install manpages-posix-dev
- 函数调用成功返回新文件描述符,失败返回-1并设置变量errno指名原因
- 新文件描述符将使用未用数值的最小值
4.4 访问权限初始值
使用O_CREAT标志必须使用三个参数的open系统调用
第三参数mode是按位或来组合的,可组合内容见下表:
mode | 含 义 |
---|---|
S_IRUSR | 读权限,文件属主 |
S_IWUSR | 写权限,文件属主 |
S_IXUSR | 执行权限,文件属主 |
S_IRGRP | 读权限,文件所属组 |
S_IWGRP | 写权限,文件所属组 |
S_IXGRP | 执行权限,文件所属组 |
S_IROTH | 读权限,其他用户 |
S_IWOTH | 写权限,其他用户 |
S_IXOTH | 执行权限,其他用户 |
*mode的值最终是否生效要受到umask的限制
4.5 close系统调用
- close可终止文件描述符和文件之间的联系,文件描述符被释放可重新使用
- 成功调用返回0,失败返回-1
函数原型
#include <unistd.h>
int close(int fildes);
4.6 综合训练
文件复制程序
- 功能:两文件之间内容复制
- 测试文件file.in
程序一
1 #include <unistd.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5
6 int main()
7 {
8 char c;
9 int in, out;
10
11 in = open("file.in", O_RDONLY);
12 out = open("file.out", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
13 while (read(in, &c, 1) == 1)
14 write(out, &c, 1);
15
16 exit(0);
17 }
执行:
TIMEFORMAT="" time ./copy_system
输出:
0.57user 1.23system 0:01.81elapsed 99%CPU (0avgtext+0avgdata 1084maxresident)k
0inputs+2048outputs (0major+64minor)pagefaults 0swaps
0.57用户时间,1.23系统时间,1.81总共时间,CPU占用99%
一次只操作一个字符效率很低
程序二
1 #include <unistd.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5
6 int main()
7 {
8 char block[1024];
9 int in, out;
10 int nread;
11
12 in = open("file.in", O_RDONLY);
13 out = open("file.out", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
14 while((nread = read(in, block, sizeof(block))) > 0)
15 write(out, block, nread);
16
17 exit(0);
18 }
执行过程同上一个例子
输出
0.00user 0.00system 0:00.00elapsed 85%CPU (0avgtext+0avgdata 1128maxresident)k
0inputs+2048outputs (0major+65minor)pagefaults 0swaps
这次很快哈,这是数据块操作的优势,因为系统调用很费事,数据块大大减少系统调用次数
5. 其他系统调用
5.1 lseek系统调用
函数原型
#include <unistd.h>
#include <sys/types.h>
off_t lseek(int fildes, off_t offset, int whence);
- lseek设置fildes的读写指针,即打开文件之后从哪里开始读或写
- 读写指针可设置为相对位置或者绝对位置
offset指定位置,whence设置偏移值用法
whence取值见下表:
取值 | 含义 |
---|---|
SEEK_SET | offset为绝对位置 |
SEEK_CUR | offset是相对于当前位置的相对位置 |
SEEK_END | offset是相对于文件尾的相对位置 |
- 返回值:返回文件头到读写指针的字节偏移值,失败返回-1
5.2 fstat、stat和lstat系统调用
- fstat:此函数返回与打开的文件描述符相关的文件的状态信息,该信息将会写到一个buf结构中,buf的地址以参数形式传递给fstat。
- stat:返回通过文件名查到的状态信息,若文件是符号链接,则返回该链接指向的文件的信息
- lstat:返回通过文件名查到的状态信息,若文件是符号链接,则返回该符号链接本身的信息
函数原型
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes, struct stat *buf);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
stat结构成员内容见下表:
stat成员 | 说明 |
---|---|
st_mode | 文件权限和文件类型信息 |
st_ino | 与该文件联系的inode |
st_dev | 保存文件的设备 |
st_uid | 文件属主的UID号 |
st_gid | 文件属主的GID号 |
st_atime | 文件上一次被访问的时间 |
st_ctime | 文件的权限、属主、组或内容上一次被改变的时间 |
st_mtime | 文件的内容上一次被修改的时间 |
st_nlink | 该文件的硬链接数 |