Linux基础I/O

news2024/10/6 18:36:01

一,系统文件I/O

写文件:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
	umask(0);
	int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	int count = 5;
	const char* msg = "hello bit!\n";
	int len = strlen(msg);
	while (count--) {
		write(fd, msg, len);//fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数
		据。 返回值:实际写了多少字节数据
	}
	close(fd);
	return 0;
}

读文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
	int fd = open("myfile", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	const char* msg = "hello bit!\n";
	char buf[1024];
	while (1) {
		ssize_t s = read(fd, buf, strlen(msg));//类比write
		if (s > 0) {
			printf("%s", buf);
		}
		else {
			break;
		}
	}
	close(fd);
	return 0;
}

接口介绍

open man open

#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: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
 O_RDONLY: 只读打开
 O_WRONLY: 只写打开
 O_RDWR : 读,写打开
 这三个常量,必须指定一个且只能指定一个
 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
 O_APPEND: 追加写
返回值:
 成功:新打开的文件描述符
 失败:-1
mode_t 理解:文件权限,具体直接 man 手册,比什么都清楚
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要 open创建,则第三个参数表示创建文件的默认权限 , 否则,使用两个参数的 open

文件描述符fd

  • Linux六字真言,先组织,在描述
  • 组织好的文件数据,会被统一的管理起来
  • 而每个管理起来的文件数据,都会有一个对应的下标,或者是编号,对应的就是文件描述符fd
  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2
  • 0,1,2 对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	char buf[1024];
	ssize_t s = read(0, buf, sizeof(buf));
	if (s > 0) {
		buf[s] = 0;
		write(1, buf, strlen(buf));
		write(2, buf, strlen(buf));
	}
	return 0;
}

而现在知道,文件描述符就是从 0 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件

文件描述符的分配规则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	int fd = open("myfile", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}
输出发现是 fd: 3
关闭 0 或者 2 ,在看
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(0);
	//close(2);
	int fd = open("myfile", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}
发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符

重定向

那如果关闭1呢?看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
	close(1);
	int fd = open("myfile", O_WRONLY | O_CREAT, 00644);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	fflush(stdout);

	close(fd);
	exit(0);
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中, fd 1 。这种现象叫做输出 重定向。常见的重定向有:>, >>, <

使用 dup2 系统调用

函数原型如下:

#include <unistd.h>
int dup2(int oldfd, int newfd);
示例代码
#include <fcntl.h>
int main() {
	int fd = open("./log", O_CREAT | O_RDWR);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	close(1);
	dup2(fd, 1);
	for (;;) {
		char buf[1024] = { 0 };
		ssize_t read_size = read(0, buf, sizeof(buf) - 1);
		if (read_size < 0) {
			perror("read");
			break;
		}
		printf("%s", buf);
		fflush(stdout);
	}
	return 0;
}
printf C 库当中的 IO 函数,一般往 stdout 中输出,但是 stdout 底层访问文件的时候,找的还是 fd:1, 但此时, fd:1 下标所表示内容,已经变成了myfile 的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写 入,进而完成输出重定向

理解文件系统

每行包含 7 列:

FILE

我们在读写文件时数据不会直接传输,会先存储到缓冲区之中,然后再以特定的刷新策略,把数据冲文件的缓冲区中刷新出来

缓冲区的刷新策略有3种

  1. 直接刷新(即刚写入就刷新)
  2. 行刷新(遇到\n就会触发刷新机制连同\n在内的所有数据全部刷新出去)对应实例  显示器
  3. 满内存刷新(数据缓冲区达到数据存取的上限)对应实例  磁盘
#include <stdio.h>
#include <string.h>
int main()
{
 const char *msg0="hello printf\n";
 const char *msg1="hello fwrite\n";
 const char *msg2="hello write\n";
 printf("%s", msg0);
 fwrite(msg1, strlen(msg0), 1, stdout);
 write(1, msg2, strlen(msg2));
 fork();
 return 0;
运行出结果:
hello printf
hello fwrite
hello write
但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了:
hello write
hello printf
hello fwrite
hello printf
hello fwrite

我们想磁盘中写入数据发现有俩份,这其实是因为子进程会继承父进程的代码和数据,当其中一个进程要刷新缓冲区的时候会发生写时拷贝,所以当父子进程结束是会有俩份数据

如果有兴趣,可以看看 FILE 结构体
typedef struct _IO_FILE FILE ; /usr/include/stdio.h
在 / usr / include / libio.h
struct _IO_FILE {
	int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 //缓冲区相关
 /* The following pointers correspond to the C++ streambuf protocol. */
 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
	char* _IO_read_ptr; /* Current read pointer */
	char* _IO_read_end; /* End of get area. */
	char* _IO_read_base; /* Start of putback+get area. */
	char* _IO_write_base; /* Start of put area. */
	char* _IO_write_ptr; /* Current put pointer. */
	char* _IO_write_end; /* End of put area. */
	char* _IO_buf_base; /* Start of reserve area. */
	char* _IO_buf_end; /* End of reserve area. */
	/* The following fields are used to support backing up and undo. */
	char* _IO_save_base; /* Pointer to start of non-current get area. */
	char* _IO_backup_base; /* Pointer to first valid character of backup area */
	char* _IO_save_end; /* Pointer to end of non-current get area. */
	struct _IO_marker* _markers;
	struct _IO_FILE* _chain;
	int _fileno; //封装的文件描述符
#if 0
	int _blksize;
#else
	int _flags2;
#endif
	_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
 /* 1+column number of pbase(); 0 is unknown. */
	unsigned short _cur_column;
	signed char _vtable_offset;
	char _shortbuf[1];
	/* char* _save_gptr; char* _save_egptr; */
	_IO_lock_t* _lock;
#ifdef _IO_USE_OLD_IO_FILE
};

理解文件系统

​
[root@localhost linux]# ls -l
总用量 12
-rwxr-xr-x. 1 root root 7438 "9月 13 14:56" a.out
-rw-r--r--. 1 root root 654 "9月 13 14:56" test.c

​
每行包含 7 列:
  • 模式或者权限
  • 硬连接数
  • 文件所有者
  • 文件所属组
  • 大小
  • 最后修改时间
  • 文件名
inode
为了能解释清楚 inode 我们先简单了解一下文件系统
Linux ext2 文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被 划分为一个个的block 。一个 block 的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs -b 选项可以设 定block 大小为 1024 2048 4096 字节。而上图中启动块( Boot Block )的大小是确定的
  • Block Group ext2 文件系统会根据分区的大小划分为数个 Block Group 。而每个 Block Group 都有着相 同的结构组成。政府管理各区的例子
  • 数据区:存放文件内容
  • i 节点表 : 存放文件属性 如 文件大小,所有者,最近修改时间等
  • inode 位图( inode Bitmap ):每个 bit 表示一个 inode 是否空闲可用
  • 块位图( Block Bitmap ): Block Bitmap 中记录着 Data Block 中哪个数据块已经被占用,哪个数据块没 有被占用
  • GDT Group Descriptor Table :块组描述符,描述块组属性信息
  • 超级块( Super Block ):存放文件系统本身的结构信息。记录的信息主要有: bolck inode 的总量, 未使用的block inode 的数量,一个 block inode 的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block 的信息被破坏,可以说整个 文件系统结构就被破坏了

理解硬链接 

硬链接:

我们对main.c进行硬链接发现main1.c   main.c的文件第一例(操作系统在硬盘中寻址编号)的数字相等第三列的数字变为2

所以我们我们可以知道硬链接就是给寻址编号起别名,而第三列数据就是该寻址编号对应的文件名数目

软链接:

通过观察第一列的数据可以发现test.c     和     test的寻址空间是不一样的所以二者是俩个独立的文件

那么二者之间有什么关系?

我们对test 使用cat 命令可以发现,能看见test.c 里面的内容

软链接是通过名字引用另外一个文件,在shell中的做法,如果类比到Windows我们可以理解为是一种快捷方式。

动态库和静态库

  • 静态库( .a ):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚 拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘间
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个 过程称为动态链接(dynamic linking
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文 件的整个机器码
  • 动态库( .so ):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1807006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java】解决Java报错:StackOverflowError

文章目录 引言1. 错误详解2. 常见的出错场景2.1 无限递归2.2 递归深度过大2.3 方法调用层次过深 3. 解决方案3.1 优化递归算法3.2 尾递归优化3.3 增加调用栈大小3.4 检查递归终止条件 4. 预防措施4.1 使用迭代替代递归4.2 尾递归优化4.3 合理设计递归算法4.4 调整JVM参数4.5 定…

【Qt】Qt QTreeWidget隐藏列名称(横向表头)

1. 效果 未隐藏 隐藏 2. 方法 方法1 ui->treeWidget->header()->hide();方法2 ui->treeWidget->header()->setVisible(false);

RDK X3(aarch64) 测试手柄

0. 环境 - 亚博智能的ROSMASTER-X3 标准版 - XDK X3 1.0 - 冰原狼等win10免驱的手柄 1. RDK X3 1.0 串口通信 波特率 921600 root/root mobaterm -> Session -> VNC -> 192.168.8.108:5900 -> runrise 2. 测试 ROSMASTER-X3 标准版 配套的手柄 安装 …

【Java SE】字符串常量池详解,什么情况下字符串String对象存在常量池,通过==进行判断,字符串创建及截取后是否同一个对象

复习字符串创建方式 字符串的31种构造方法 public String();创建一个空白字符串&#xff0c; 不含有任何内容public String(char[] array);根据字符数组的内容&#xff0c;来创建对应的字符串public String(byte[] array);根据字节数组的内筒&#xff0c;来创建对应的字符串 …

物联网设计竞赛_8_Jetson Orin Nano安装pytorch与torchvision

我的新板子到了&#xff0c;型号是jetson orin Nano与之前的jetson nano稍有不同我发现库又得从新下载 我的pip3的版本是3.8.10&#xff0c;jetpack版本5.1.1&#xff0c;又得重新开始下载库&#x1f62d; 安装pytorch: 得科学上网&#xff1a; PyTorch for Jetson - Jetson …

U-Net: Convolutional Networks for Biomedical Image Segmentation--论文笔记

U-Net: Convolutional Networks for Biomedical Image Segmentation 资料 1.代码地址 2.论文地址 https://arxiv.org/pdf/1505.04597 3.数据集地址 论文摘要的翻译 人们普遍认为&#xff0c;深度网络的成功训练需要数千个带注释的训练样本。在本文中&#xff0c;我们提出…

nodejs最新某东h5st(4.7.2)参数分析与javascript逆向纯算法还原(含算法源码)(2024-06-09)

一、作者声明&#xff1a; 文章仅供学习交流与参考&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01;如有侵权&#xff0c;请联系作者本人进行删除&#xff01; 二 、写在前面 h5st从4.1一路更新到4.7.2&#xff0c;逐渐vmp…

57.Semaphore信号量

用来限制能同时访问共享资源的线程上限。只是适合限制单机线程数量。 Slf4j public class SemaphoreDemo {public static void main(String[] args) {Semaphore semaphore new Semaphore(3);for (int i 0; i < 10; i) {new Thread(() -> {try {semaphore.acquire();//…

Spring配置多数据库(采用数据连接池管理)

一&#xff0c;前言 大家在开发过程中&#xff0c;如果项目大一点就会遇到一种情况&#xff0c;同一个项目中可能会用到很多个数据源&#xff0c;那么这篇文章&#xff0c;博主为大家分享在spring应用中如何采用数据库连接池的方式配置配置多数据源。 本篇文章采用大家用的最…

【PLG洞察】| 飞书成功之路:关键在分销裂变

引言 随着企业服务市场的发展&#xff0c;Product-Led Growth&#xff08;PLG&#xff0c;产品驱动增长&#xff09;模式逐渐成为众多SaaS企业的首选战略。在这个背景下&#xff0c;字节跳动旗下的企业协作与管理平台——飞书&#xff0c;凭借其独特的分销裂变策略&#xff0c…

【教程】让小爱音箱Play增强版接入Mi-GPT

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 项目地址&#xff1a;https://github.com/idootop/mi-gpt MiIOT&#xff1a;小米小爱音箱Play 增强版 - 产品规格 - Xiaomi Miot Spec 实现效果图&…

ABC 357 G Stair-like Grid

link 其实是我之前写的一篇博客的推广 大意&#xff1a; 一个阶梯型&#xff0c;第 i i i行有 ⌈ i / 2 ⌉ ∗ 2 \left \lceil i/2 \right \rceil*2 ⌈i/2⌉∗2个方块&#xff0c;总共有n行。在其中给定 m m m个点无法经过&#xff0c;求从左上角到右下角的方案数。其中每次移…

macOS优化工具CleanMyMac2024免费版电脑性能提升 存储空间释放 电脑维护 高效易用 延长电脑使用寿命

【CleanMyMac】是一款专为macOS系统设计的优化和清理软件&#xff0c;它的核心特性就是帮助我们提升电脑性能&#xff0c;释放存储空间。&#x1f680; CleanMyMac绿色免费版下载如下&#xff1a;记得保存哈&#xff0c;以防失效&#xff1a; https://pan.quark.cn/s/9b08114…

【电机控制】FOC算法验证步骤

【电机控制】FOC算法验证步骤 文章目录 前言一、PWM——不接电机1、PWMA-H-50%2、PWMB-H-25%3、PWMC-H-0%4、PWMA-L-50%5、PWMB-L-75%6、PWMC-L-100% 二、ADC——不接电机1.电流零点稳定性、ADC读取的OFFSET2.电流钳准备3.运放电路分析1.电路OFFSET2.AOP3.采样电路的采样值范围…

Spring03-依赖注入(DI)

依赖注入 概念 依赖注入&#xff08;Dependency Injection,DI&#xff09;。 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 . 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 . 构造器注入 前面已经介绍过&#xff0c;参考4、IOC创建对象的方式 Set方…

2024最新华为OD机试-C/D卷 - 在线OJ使用说明

文章目录 &#x1fa90;在线 OJ 入口&#x1f3a7;申请OD使用权限&#x1f353;在线 OJ 的使用说明OJ主界面专题系列语言支持评测结果 &#x1fa90;在线 OJ 入口 &#x1f517; 2024最新华为OD机试 - 在线OJ入 &#x1f3a7;申请OD使用权限 本专栏配套 OJ 的为了配合考友更高…

Vue数据动态代理机制的实现

Object.defineProperty() &#xff08;1&#xff09;这个方法是ES5新增的 &#xff08;2&#xff09;这个方法的作用是&#xff1a;给对象新增属性&#xff0c;或者设置对象原有的属性 &#xff08;3&#xff09;用法&#xff1a;Object.defineProperty(给哪个对象新增属性,‘…

【简单介绍下DALL-E2,什么是DALL-E2?】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Functional ALV系列 (09) - 双击跳转到另外一个ALV

在查看数据的时候&#xff0c;不总是只有一个界面&#xff0c;为了让用户更方便地查看数据&#xff0c;需要根据当前的数据跳转到另外的界面中&#xff0c;比如查看明细等。本文演示 ALV 比较实用的功能&#xff1a;双击 ALV 单元格跳转到另外一个 ALV 中。 要实现的业务场景&…

Diffusers代码学习: 多个Adapter

T2I Adapter也是可组合的&#xff0c;允许您使用多个适配器对图像施加多个控制条件。例如&#xff0c;可以使用姿势贴图提供结构控制&#xff0c;使用深度贴图进行深度控制。这是由[MultiAdapter]类启用的。 让我们用姿势和深度适配器来调节文本到图像的模型。创建深度和姿势图…