Linux文件操作

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_SEToffset为绝对位置
SEEK_CURoffset是相对于当前位置的相对位置
SEEK_ENDoffset是相对于文件尾的相对位置
  • 返回值:返回文件头到读写指针的字节偏移值,失败返回-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该文件的硬链接数

6 标准I/O库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LogosGe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值