linux系统编程-深入文件IO

注:文章的所有内容均出自《Linux/UNIX 系统编程手册》一书,文章的内容在于将书中的一些重要知识点提取出来并加以自己的理解,形成一个类型提纲的东西,方便查阅和复习。

0、open函数flag参数解析

a、读写权限:O_RDONLY O_WRONLY O_RDWR
b、打开存在并有内容的文件时:O_APPEND、O_TRUNC:O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,则原来的内容会被丢弃;O_APPEND属性去打开文件时,如果这个文件中本来是有内容的,则新写入的内容会接续到原来内容的后面。
c、同时指定 O_EXCL 与 O_CREAT 作为 open() 的标志位时,如果要打开的文件已然存在,则 open() 将返回一个错误。
d、O_NONBLOCK:以非阻塞的方式打开文件。
e、O_SYNC:write阻塞等待底层完成写入(同步到硬盘)才返回到应用层。

1.原子操作的重要性

举两个例子:

以独占方式创建一个文件

同时指定 O_EXCL 与 O_CREAT 作为 open() 的标志位时,如果要打开的文件已然存在,则 open() 将返回一个错误。这提供了一种机制,保证进程是打开文件的创建者。对文件是否存在的检查和创建文件属于同一原子操作。

向文件尾部追加数据

考虑如下代码:

if(lseek(fd,0,SEEK_END)==-1)
  errExit("lseek");
if(write(fd,buf,len)!=len)
  fatal("Partial/failed write");

若多个进程执行此操作,可能出现数据覆盖的现象,为了防止此现象的发生,需要将文件偏移量的移动与数据写操作纳入同一原子操作。在打开文件时加入 O_APPEND 标志就可以保证这一点。

2.文件控制操作 fcntl()

fcntl() 的用途是获取,修改或添加 open 打开文件的 flag 标志或访问模式。

获取与判断

int flags,accessMode;

flags = fcntl(fd,F_GETFL);
if(flags=-1)
  errExit("fcntl");

在上述代码之后,可以以如下代码测试文件是否以同步写方式打开:

if(flags & O_SYNC)
 printf("writes are synchronized\n")

判定访问模式,需使用掩码 O_ACCMODE 与 flag 相与:

accessMode = flags & O_ACCMODE;
if(accessMode == O_WRONLY || accessMode == O_RDWR )
 printf("file is writable\n"); 

更改
调用 fcntl() 函数的 F_SETFL 命令,使用 |= 添加状态标志:

int flags;

flags =fcntl(fd,F_GETFL);
if(flags == -1)
   errExit("fcntl");
flags |= O_APPEND;
if(fcntl(fd,F_SETFL,flags)==-1)
   errExit("fcntl");

3.文件描述符、文件句柄和inode之间的关系

在这里插入图片描述

  • 每个进程对应一个文件描述符表,每一个文件描述符表包含了文件描述符fd0到fd?,进程每次打开一个文件,都会为文件分配一个文件描述符
  • 打开文件表每一项对应一个句柄,句柄包含了文件偏移量和状态标志(flag参数),文件访问模式(mode参数)等信息
  • i-node表的每一项对应到一个具体的文件,包括文件类型和文件锁等信息

可能出现以下几种情况:
a.同一进程的不同文件描述符可以指向同一句柄:例如通过调用dup()或fcntl()。
b.不同进程的同一文件描述符可以指向同一句柄:通过fork()调用。
在这里插入图片描述

c.不同文件句柄可以指向相同的i-node:两个进程对同一文件发起了open调用。

4.复制文件描述符

newfd=dup(1); //返回值为3,因为0,1,2已占用,创建1的副本
dup2(1,2); //创建1的副本,返回值为2
newfd=fcntl(oldfd,f_DUPFD,startfd); //为oldfd创建副本,使用大于等于startfd的编号

5.在文件特定偏移量处读写:pread和pwrite

ssize_t pread(int fd,void *buf,size_t count,off_t offset); //count为写入大小,offset表偏移
ssize_t pwrite(...)
  • 需要注意的是这两个函数并不会改变文件的当前偏移量,read和write调用会改变。

6.截断文件:truncate和ftruncate

int truncate(const char *pathname,off_t length); //pathname为文件路径
int ftruncate(int fd,off_t length); //fd为文件描述符

7.创建临时文件:mkstemp和tmpfile

示例程序:

int fd;
char template[]="tmp/someXXXXXX"; //必须以6个X结尾
fd = mkstemp(template);
if(fd == -1)
	errExit("mkstemp");
unlink(template); //删除临时文件

8.标准输入输出

在这里插入图片描述
第三列为POSIX名称,用于write,read等系统调用。
第四列为stdio流名称,用于fwrite,fread等stdio库函数。

9.lseek函数调节文件偏移量

off_t lseek(int fd,off_t offset,int whence);

whence参数解释:
SEEK_SET
将文件偏移量设置为从文件头部起始点开始的 offset 个字节。
SEEK_CUR
相对于当前文件偏移量,将文件偏移量调整 offset 个字节。
SEEK_END
将文件偏移量设置为起始于文件尾部的 offset 个字节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Linux系统编程可以按照以下步骤进行: 1. 熟悉Linux环境:首先要了解Linux操作系统的基本概念和基本命令,比如文件操作、进程管理、权限控制等。可以通过使用Linux系统日常操作来熟悉这些知识。 2. 学习C语言C语言Linux系统编程的主要编程语言,因此要先掌握C语言的基本语法和编程技巧。可以选择一本C语言教程来学习,掌握C语言的基本知识。 3. 学习系统调用:Linux系统提供了一组系统调用接口,用于与操作系统进行交互。学习系统调用是Linux系统编程的重要一环,可以通过阅读相关的文档和教程来学习系统调用的使用方法和原理。 4. 理解进程和线程:Linux系统是一个多任务操作系统,理解进程和线程的概念以及它们之间的关系对于系统编程至关重要。可以通过学习进程和线程的基本知识以及相关的API来深入了解。 5. 学习文件IO和网络编程:在Linux系统编程中,文件IO和网络编程是常见的任务。可以学习如何使用标准IO库进行文件的读写操作,以及如何使用套接字进行网络通信。 6. 掌握调试和性能优化技巧:在实际的系统编程中,调试和性能优化是非常重要的技能。可以学习如何使用调试工具来定位和解决问题,以及如何进行性能分析和优化。 7. 实践项目:最后,通过实践项目来巩固所学知识。可以选择一些小型的系统编程项目,例如开发一个简单的命令行工具或网络服务,来应用所学知识并提升技能。 总之,学习Linux系统编程需要对Linux操作系统有基本的了解,掌握C语言的基本知识,学习系统调用和相关API,并进行实践项目来提升技能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值