【Linux学习】文件系统 - 第三篇

news2024/11/16 16:00:56
🍑个人主页:Jupiter.
🚀 所属专栏:Linux从入门到进阶
欢迎大家点赞收藏评论😊

在这里插入图片描述

目录

  • `🦅重定向原理以及实现`
    • `🐱dup2系统调用实现重定向`。
      • `🎈dup2 系统调用`
      • `🍑在自定义shell中实现重定向`
  • `🌹FILE 与文件缓冲区`
  • `📕模拟实现C语言文件系统调用`


🦅重定向原理以及实现

  • 先看一个这样的代码:
#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。这种现象叫做输出重定向。常见的重定向有:>, >>, <

上述代码现象分析:

  • 因为我们关闭了fd=1,标准输出,所以当我们打开一个文件的时候,这个文件分配的fd=1;然后当我们printf打印内容到标准输出的时候,实际上是给标准输出文件里面写内容,但是这时候fd指向的文件已经改变了,所以就写到对应打开的文件中去了。

通过上面类似的的方法,也就可以实现输入重定向追加重定向。

重定向本质,如图:
在这里插入图片描述

🐱dup2系统调用实现重定向

🎈dup2 系统调用

在这里插入图片描述
在这里插入图片描述
翻译:dup2系统调用的功能是:将old文件fd拷贝给new文件的fd。

演示代码:

int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);
dup2(fd,1);   //1号文件描述符是标准输出                                                                                                                         
if(fd==-1)
{
     perror("open");
     return 1;
}
 printf("HHHHHHHHH\n");   //向标准输出里面写内容

运行结果与分析:

  • 结果:将printf里面的内容输出重定向到了log.txt文件中。
  • 分析:dup2(fd,1) 后,如下图所示,1本来指向的是显示器,3指向的新打开的文件,参数fd=3,dup2过后,1也指向的是新打开的文件,但是C语言接口printf是向显示器打印,也即是1号文件描述符,但是这里1号的指向改变了,所以再使用printf打印,就写入了到log.txt中了。

🍑在自定义shell中实现重定向

使用dup2实现一个重定向(>>,>,<),完善之前实现的自定以shell;

代码实现:

实现思路:


🌹FILE 与文件缓冲区

因为IO相关函数与系统调用接口对应,并且库函数是系统调用的封装,所以本质上,访问文件都是通过fd访
问的。所以C库当中的FILE结构体内部,必定封装了fd。

来段代码在研究一下:

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));
 
 	return 0;
 }

运行出结果:

  • hello printf
  • hello fwrite
  • hello write

但如果在main函数return前fork( )后, 我们发现结果变成了:

  • hello write
  • hello printf
  • hello fwrite
  • hello printf
  • hello fwrite

我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!

一般C库函数写入文件时是全缓冲的,而写入显示器行缓冲
printf fwrite 库函数自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据
的缓冲方式由行缓冲变成了全缓冲
而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
但是进程退出之后,会统一刷新,写入文件当中。
但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的
一份数据,随即产生两份数据。
write 没有变化,说明没有所谓的缓冲。

综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。
那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统
调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是
C,所以由C标准库提供。

如果有兴趣,可以看看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
};



📕模拟实现C语言文件系统调用

   #pragma once   
   
   #include<stdio.h>
   #define SIZE 1024
   
   #define NONE_FLUSH (1<<1)
   #define LINE_FLUSH (1<<2)    //行刷新
   #define FULL_FLUSH (1<<3)    //全刷新                                                         
                                                        
  typedef struct my_FILE
  {                                                        
     char buffer[SIZE];
     int pos;                           
     int cap;                       
     int flush_mode;                                   
      int fileno;                              
 }myFILE;          
                                                          
 void my_flush(myFILE* fp);           
 myFILE* my_fopen(const char* pathname,const char* mode);
 int my_fwrite(myFILE* fp,const char* s,int size);     
 void my_fclose(myFILE* fp);
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include"mystdio.h"
  4 #include<string.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 #include<unistd.h>
  9 
 10 
 11 myFILE* my_fopen(const char* pathname,const char* mode)
 12 {
 13     int fd = 0;
 14     umask(0);
 15     if(strcmp(mode,"r")==0)
 16     {
 17         fd = open(pathname,O_RDONLY);
 18     }
 19     else if(strcmp(mode,"w")==0)
 20     {
 21         fd = open(pathname,O_CREAT|O_WRONLY|O_TRUNC,0666);
 22     }                                                                                                                                      
 23     else if(strcmp(mode,"a")==0)
 24     {
 25         fd = open(pathname,O_CREAT|O_WRONLY|O_APPEND,0666);
 26     }
 27     else                                                                                                                                   
 28     {
 29         return NULL;
 30     }
 31 
 32     if(fd<0)
 33     {
 34         return NULL;
 35     }
 36     myFILE* fp = (myFILE*)malloc(sizeof(myFILE));
 37     if(fp==NULL) return NULL;
 38     fp->fileno=fd;
 39     fp->cap=SIZE;
 40     fp->flush_mode=LINE_FLUSH;
 41     fp->pos = 0;
 42 
 43     return fp;
 44 }
 46 void my_flush(myFILE* fp)
 47 {
 48     if(fp->pos==0) return ;
 49     write(fp->fileno,fp->buffer,fp->pos);
 50     fp->pos=0;
 51 }                                                                                                                                          
 52 int my_fwrite(myFILE* fp,const char* s,int size)
 53 {
 54     memcpy(fp->buffer+fp->pos,s,size);
 55     fp->pos+=size;
 56     if(fp->flush_mode==LINE_FLUSH && fp->buffer[fp->pos-1]=='\n')
 57     {
 58         my_flush(fp);
 59     }
 60     //下面可以判断全刷新等等
 61     if(fp->flush_mode==FULL_FLUSH && fp->pos == fp->cap)
 62     {
 63         my_flush(fp);
 64     }
 65     return size; 
 66 
 67 }
 68 void my_fclose(myFILE* fp)
 69 {
 70     my_flush(fp);
 71     close(fp->fileno);
 72     free(fp);
 73 }


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

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

相关文章

MM 10 -采购- 标准采购订单

思维导图 说明 采购订单页面介绍 抬头 支付、开票&#xff1a; 付款条件 文本&#xff1a; 机构数据&#xff1a; 其余sheet不常用 行项目 物料 数量 交货日期 价格 &#xff1a;信息记录带出来 工厂 行项目明细 物料 供应商物料号 供应商子范围&#xff1a; 批次&a…

PXE安装配置(rhel7)

实验需求&#xff1a; rhel7 虚拟机 所需软件安装&#xff1a; sudo yum install -y dhcp tftp-server httpd syslinux system-config-kickstart 启动&#xff1a; system-config-kickstart 制作ks文件 注意&#xff1a;File----Save保存一下 打开root下的ks.cfg在分区信…

python:将 NWPU_VHR-10 遥感目标检测数据集转换成 YOLO 格式

作者&#xff1a;CSDN _养乐多_ 本文将介绍将 NWPU_VHR-10 遥感目标检测数据集转换成 YOLO 格式的 python 脚本。 文章目录 一、数据集介绍1.1 数据集下载1.2 数据集介绍1.3 数据格式 二、格式转换三、完整代码 一、数据集介绍 1.1 数据集下载 https://opendatalab.com/Ope…

GRU门控循环单元【数学+图解】

文章目录 1、简介2、门控机制3、公式4、图解GRU4.1、重置门和更新门4.2、候选隐藏状态和隐藏状态⭐ 5、LSTM与GRU的对比6、应用7、训练技巧 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff…

MATLAB车道线检测系统

研究了汽车辅助驾驶系统中的计算机视觉问题 . 一方面 , 用小波变换对高速公路上的道路边缘进行检测 ; 另一方面 , 利用灰度以及几何特征实时跟踪和检测车道边缘 , 通过建立道路边缘模型 , 采用数据拟合的方法确定边界轨迹方程 , 从而估算出公路的延伸方向 , 实现汽车的自动防偏…

信号链直流误差

1 简介 在信号链中&#xff0c;可能会累积的误差有两类&#xff1a;直流和交流误差。直流或静态误差&#xff08;如增益和失调误差&#xff09;有助于了解信号链的精度或灵敏度。交流类误差也称为噪声和失真&#xff0c;限制着系统的性能和动态范围。这两类误差都需要了解&…

js中的ajax【Axios,XMLHttpRequest,Promise,async】回调函数地狱等问题

目录 前置知识 1.什么是异步请求&#xff1f; 2.什么是回调函数 3.如何查看网页的异步请求&#xff08;XHR&#xff09;&#xff1f; 4.什么是ajax jquery的ajax&#xff0c;xhr&#xff0c;axios关系 正文---几种请求之间的关系 axios Axios的诞生 Axios的介绍 定义…

同城交易小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商家管理&#xff0c;用户管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&#xff0…

2024年“华数杯”数学建模竞赛C题完整解析 | 代码与论文分享

C题 技术文档&#xff08;论文&#xff09;数据预处理问题一问题二问题三问题四 数据与代码问题一问题二问题三问题四 技术文档&#xff08;论文&#xff09; 29页&#xff0c;1w字&#xff0c;完整论文请看文章最后~ 数据预处理 &#xff08;1&#xff09;利用python对数据文件…

【安当产品应用案例100集】005-安当ASP实现Exchange双因素登录认证

Exchange双因素登录通过增加额外的安全验证层&#xff0c;可以有效提高企业邮箱系统的安全性&#xff0c;减少了数据泄露和账号被盗的风险&#xff0c;同时也符合了日益严格的安全合规要求。 其必要性主要体现在以下几个方面&#xff1a; 提高安全性&#xff1a;传统的用户名…

未授权访问漏洞系列详解⑤!

Kubernetes Api Server未授权访问漏洞 Kubernetes 的服务在正常启动后会开启两个端口:Localhost Port(默认8080)Secure Port(默认6443)。这两个端口都是提供 Api Server 服务的&#xff0c;一个可以直接通过Web 访问&#xff0c;另一个可以通过 kubectl 客户端进行调用。如果运…

html+css 实现hover上下边框生长

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

平安城市行业无人机解决方案(夜间作业场景应用)

夜间作业场景痛点&#xff1a; 夜间隐蔽作业需要权衡看清楚和隐蔽性 可见光相机夜间无法使用 “五合一”混合夜视传感器-H20N 夜间侦察巡逻 H20N星光级相机提升夜间作业覆盖效率 Zenmuse H20N变焦镜头夜景模式效果 Zenmuse H20N变焦镜头夜景模式效果 Zenmuse H20T变焦镜头夜…

【Android】四大组件(Activity、Service、Broadcast Receiver、Content Provider)、结构目录

文章目录 Android系统架构Android四大组件ActivityServiceBroadcast ReceiverContent Provider 两大视图主要结构目录 Android系统架构 https://blog.csdn.net/xzzteach/article/details/140904613 Android四大组件 Activity 一个 Activity 包含了用户能够看到的界面&#xff0…

判断Windows设备是否为翻新

网购设备难免踩坑&#xff0c;一定要学会查验&#xff0c;及时保存证据并投诉商家。 1、查验包装信息&#xff0c;是否有拆封痕迹 商家翻新处理的第一道工序就是换个完整的包装&#xff0c;一般不会把拆过的包装打包卖给买家&#xff0c;除非声明是二手。 2、查验序列号&#x…

干货-并发编程提高——线程的唤醒(七)

没有参数的wait()方法等价于wait(0),等价于永远等下去。虚假唤醒&#xff1a;一个线程也能在没有被通知&#xff0c;中断&#xff0c;或超时的情况下被唤醒。也即所谓的“虚假唤醒”。解决虚假唤醒的办法就是通过while循环来判断条件。 何为虚假唤醒 简单讲&#xff0c;要避免使…

磷酸铁锂与三元锂:谁会取得压倒性的胜利?

在电动车市场中&#xff0c;磷酸铁锂&#xff08;LFP&#xff09;和三元锂&#xff08;NCM/NCA&#xff09;电池是两种主要的锂离子电池技术。近年来&#xff0c;关于这两种电池的讨论愈发热烈。本文将深入探讨磷酸铁锂与三元锂的真正区别&#xff0c;以及它们在未来电动车市场…

AI8-文本检测

文本检测任务是找出图像或视频中的文字位置。不同于目标检测任务,目标检测不仅要解决定位问题,还要 解决目标分类问题。 文本在图像中的表现形式可以视为一种‘目标‘,通用的目标检测的方法也适用于文本检测,从任务本身上来 看: • 目标检测:给定图像或者视频,找出目…

什么是SQL注入?基于实例讲解

一、SQL注入的含义&#xff1a; SQL注入是一种常见的网络攻击&#xff0c;由于程序对输入数据的判断或者检验不严格&#xff0c;导致攻击者查询到了授权范围之外的数据&#xff0c;甚至还可以修改数据库中的数据&#xff0c;对数据库执行一些管理操作等&#xff0c;所以它的危…

【Material-UI】Button 中的点击事件处理(Handling clicks)详解

文章目录 一、点击事件处理基础1. 基本用法2. 事件处理器的传递 二、实际应用中的注意事项1. 事件处理逻辑的优化2. 避免过多的状态更新3. 使用合适的事件类型 三、关于文档中未提及的原生属性四、最佳实践1. 无障碍性2. 视觉反馈3. 防止重复点击 五、总结 在现代前端开发中&am…