当前位置: 首页 > news >正文

linux系统编程07-文件IO\系统调用IO

目录
  • 介绍
  • 文件描述符的概念
  • open\close
  • read\write\lseek
  • 标准IO与系统调用IO的区别
  • 其他内容
  • dup\dup2
  • 文件同步
  • fcntl\iocntl

介绍

Untitled

文件描述符的概念

Untitled

  • 备用图

    Untitled

  1. 文件是一块磁盘空间,有一个编号 inode ,每次 open 一个文件时,会创建一个结构体,链接 inode ,存储文件的信息,结构体的首地址以指针的形式(类似于 FILE* )存放在一个数组中,向用户返回数组的下标,这就是文件描述符 fd 。因此,拿到下标就能拿到指针,拿到指针就能拿到结构体,从而操作文件。 结构体和数组是在 内存空间 中。 每个进程有各自的数组和结构体
  2. 由于标准IO是建立在系统调用IO的基础上的,因此 FILE 结构体中必然存在整型成员 fd
  3. 数组的大小是1024个,可以用 ulimit -a 查看,进程打开时, fd 0 1 2 对应 stream stdin stdout stderr,是默认打开的,所以 fd 从3开始
  4. 进程间的链接同一个 inode 的结构体互不影响,进程内链接同一个结构体的 fd 也互不影响。 结构体和 inode 都有 引用计数 。结构体的引用计数是用来操作打开文件的,记录有多少个 fdl链接向自己, inode 的引用计数是用来决定文件是否存在的(而不是记录有多少个结构体链接向自己):当结构体的引用计数为0时,结构体才会被销毁,所以不会出现把 fd4 close后, fd6 变成野指针的情况;当 inode 引用计数(初始化为1)为0时,文件会被销毁

软硬链接:

  • 硬链接就是 目录项 ,使 inode 引用计数加1;软链接类似于windows中的快捷方式,是另外的一个独立文件,这个文件记录了源文件的路径,从而实现链接。

    Untitled

    Untitled

  • 优缺点:硬链接不能跨分区建立,不能给目录建立; 符号链接可以跨分区,可以给分区建立。

  • 文件在什么时候会真正从磁盘上被删除?

    1. 硬链接数 == 0
    2. 没有打开的文件描述符【没有进程或线程用到该文件】
  • 概念:https://xzchsia.github.io/2020/03/05/linux-hard-soft-link/

  • 概念练习【关键把握软连接是一个独立的文件】:https://blog.csdn.net/weixin_42306122/article/details/108351874

  • 操作:https://www.runoob.com/note/29134

open\close

##include <sys/types.h>
##include <sys/stat.h>
##include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

不是用重载,而使用变参实现

  • 参数:

    • pathname : 路径

    • flags :位图,用来控制打开方式、文件创建和文件状态

      O_READONLY O_WRONLYO_RDWR 是必选项

      Untitled

  • 返回值:成功返回文件描述符,失败返回-1

##include<unistd.h>
int close(int fd);

关闭一个文件描述符

read\write\lseek

##include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);##include <sys/types.h>
##include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  • read :从fd中读count个字节到buf
  • write :将buf中的count个字节写入fd中
  • 返回值:返回成功读入或写入的字节数,出错返回-1
  • lseek :用法同 fseek ,成功返回举例开头的字节数,失败返回-1 【等价于 fseek + ftell

Untitled

例子:实现 mycopy.c

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<fcntl.h>
##include<unistd.h>##define BUFSIZE 1024
int main(int argc, char **argv)
{if(argc < 3){fprintf(stderr, "Usage:...\n");exit(1);}size_t sfd, dfd;int len=0, ret, pos;char buf[BUFSIZE];sfd = open(argv[1], O_RDONLY);if(sfd < 0){perror("open()");exit(1);}dfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);if(dfd < 0){perror("open()");exit(1);}while(1){len = read(sfd, buf, BUFSIZE);if(len < 0){perror("read()");break;}if(len == 0)break;pos = 0;//确保写够len个字节while(len > 0){ret = write(dfd, buf+pos, len);if(ret < 0){perror("write()");break;}pos += ret;len -= ret;}}close(dfd);close(sfd);exit(0);
}

运行结果:

Untitled

几点说明:

  • open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600); :只写打开,有则创建,无则删除,创建的权限为 0600
  • write 的循环是为了确保 写够len个字节,如果只写 if(ret<0) {perror(); break;} 会出现读入7byte,写入3byte但不报错的情况。场景:该进程被信号打断,没有写够;对设备进行io。

标准IO与系统调用IO的区别

举例:传达室老大爷跑邮局[拿一封送一封,一起送(缓冲区满)\加急时送(刷新缓冲区)]

区别:响应速度&吞吐量

面试:如何使一个程序变快

提醒:标准IO与系统调用IO不可混用

转换: fileno, fdopen

类型 响应时机 优势
标准IO 缓冲区满、刷新缓冲区 吞吐量大
系统调用IO 立刻执行 响应速度快

不可混用的原因:虽然FILE内部包含fd,并且FILE和fd可以通过两个函数转换,但是FILE内的属性如pos和fd指向的结构体内的属性往往是不同的,因为写的时候存在buf,读的时候存在cache。

比如:写10个byte,FILE的pos+10,但是此时缓冲区还没有刷新,所以fd指向的结构体内的pos不变

同理,读1个byte时,FILE的pos+1,但是由于cache存在,可能会预读取,所以fd指向的结构体的pos可能+10

Untitled

例子: 标准IO用系统调用IO实现,合并系统调用IO

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>int main()
{putchar('a');write(1, "b", 1); putchar('a');write(1, "b", 1); putchar('a');write(1, "b", 1); exit(0);
}

运行结果:

Untitled

通过指令 strace ./ab 查看系统调用情况

Untitled

三次调用putchar(’a’)相当于底层调用一次 write(1,”aaa”,3)

其他内容

Untitled

Untitled

思路:类似于删除数组的中间元素。将11行后的内容一次搬到10行,知道搬完,最后减去10行的大小。truncate改变文件大小。

Untitled

通过两次打开同一个文件,简化系统调用,也可以用两个线程或进程

Untitled

dup\dup2

int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup :把 oldfd 文件描述符,复制到当前文件描述符数组的最低位。

dup2 :把 oldfd 复制到 newfd ,如果 newfd 已被占用,会把占用 newfd 的文件关闭,等价于 close()+dup() ,由于是原子操作所以可以避免并发竞争

例子:在程序中把标准输出重定向到一个文件中,并且输出

示例代码1:关闭 fd1 ,重新打开文件, fd 就会占用1

##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<fcntl.h>
##include<unistd.h>##define FNAME "/tmp/out"
int main()
{int fd; close(1);fd = open(FNAME, O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){   perror("open()");exit(1);}   /***************/puts("Hello");  exit(0);
}

示例代码2:用 dup 实现

##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<fcntl.h>
##include<unistd.h>##define FNAME "/tmp/out"
int main()
{int fd; //close(1);fd = open(FNAME, O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){   perror("open()");exit(1);}   close(1);dup(fd);/***************/puts("Hello");  exit(0);
}

示例代码3:用 dup2 实现,避免并发竞争

##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<fcntl.h>
##include<unistd.h>##define FNAME "/tmp/out"
int main()
{int fd; //close(1);fd = open(FNAME, O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){   perror("open()");exit(1);}   dup2(fd,1);if(fd != 1)close(fd);/***************/puts("Hello");  exit(0);
}

示例代码1、2都有并发问题:如代码2中, close(1) 之后,兄弟线程创建了一个文件,就占用了 fd1 ,所以 dup(fd) 占用的就再是 fd1 了。

示例代码3注意关闭 fd 内存泄漏:同时注意, if(fd != 1) 是为了防止一开始 fd1 就是空缺的,这样 fd=1 ,虽然 dup2(fd,1) 在oldfd==newfd的时候do nothing,如果不加判断,就是把 fd 关闭,导致后面的 puts 无法输出。

其他问题:程序结束后应该还原 fd1 到标准输出。

运行结果:

Untitled

要当自己在写一个模块,考虑前后文以及与其他模块的并发,而不仅仅是写一个main函数

文件同步

将 buffer 和 cache 中的内容同步到 disk(磁盘)

sync fsync fdatasync

fcntl\iocntl

int fcntl(int fd, int cmd, ... /* arg */ );

一切关于文件描述符的魔术,都来源于该函数。 管家级函数.

参数: cmd 指命令,不同的命令有不同的参数,返回值的含义也不同

ioctl :管家级别的函数。用来管理设备。【一切皆文件的理念的受害者,因为文件只有open\close\write\read\lseek五种基本操作,但是设备显然不仅仅如此,于是都交给 ioctl 来管, man ioctl_list

fcntl 设置对 fd 的IO为非阻塞:

Untitled

/dev/fd :虚目录,显示的是当前进程文件描述符的信息。

Untitled

相当于照镜子:谁全查看反映的就是谁,所以这里显示的是 ls 进程使用的文件描述符。

在程序中要得到当前程序所在进程使用的文件描述符,只需要在程序中打开该目录即可。

http://www.wxhsa.cn/company.asp?id=6130

相关文章:

  • linux系统编程06-标准IO2
  • linux系统编程08-高级IO
  • 第03周 预习、实验与作业:面向对象入门2与类的识别
  • 第8篇、Kafka 监控与调优实战指南
  • linux系统编程02-进程基本知识
  • linux系统编程03-并发:信号
  • linux系统编程04-并发:线程
  • 新手高效制作PPT的3个步骤:告别逻辑混乱,从构思到完成!
  • Avalonia:用 ReactiveUI 的方法绑定数据、事件和命令
  • 【pyQT 专栏】程序设置 windows 任务栏缩略图(.ico)教程
  • Say 题选记(9.14 - 9.20)
  • vm的配置
  • 力扣72题 编辑距离
  • 数学基本结构框架
  • 2025.9.16总结
  • 在 Tailscale 中禁用 DNS
  • 软件工程实践一:Git 使用教程(含分支与 Gitee)
  • 【青少年低空飞行玩意】设计图以及项目概况
  • C++ 多态
  • Python实现对比两个Excel表某个范围的内容并提取出差异
  • 我用AI给自己做了一整套专属表情包!攻略
  • 20250916 之所思 - 人生如梦
  • Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现)
  • 20250915
  • Python Socket网络编程(4)
  • 今日学习 dos命令和Java基础语法
  • Photoshop 2025 v26.0软件下载免费版安装教程(含 Photoshop 软件一键安装包免费下载渠道)
  • 课前问题列表
  • switch中初始化变量
  • office2024免费永久激活安装包下载安装教程包含(下载安装配置激活)