当前位置: 首页> 科技> 数码 > 【Linux学习】文件系统 - 第三篇

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

时间:2025/9/17 13:22:34来源:https://blog.csdn.net/2301_77509762/article/details/140880119 浏览次数:0次
🍑个人主页: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 0int _blksize;
#elseint _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 }

关键字:【Linux学习】文件系统 - 第三篇

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: