Linux20 : IO

🔥个人主页: Milestone-里程碑

❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>

<<Git>><<MySQL>>

🌟心向往之行必能至

目录

一:理解文件

[1-1 狭义理解](#1-1 狭义理解)

[1-2 ⼴义理解](#1-2 ⼴义理解)

[1-3 ⽂件操作的归类认知](#1-3 ⽂件操作的归类认知)

[1-4 系统⻆度](#1-4 系统⻆度)

[1.5 回顾c的文件操作](#1.5 回顾c的文件操作)

二.系统文件I/O

[2.1 接口介绍](#2.1 接口介绍)

[2.2 open使用](#2.2 open使用)

[2.3.1 创建并写入](#2.3.1 创建并写入)

[2.3.2 写入并追加写入](#2.3.2 写入并追加写入)

[2.3.3 追加写入并读取](#2.3.3 追加写入并读取)

[2.3 open返回值及fd](#2.3 open返回值及fd)

[2.3.1 解析为何结果不同](#2.3.1 解析为何结果不同)

[2.4 重定向](#2.4 重定向)

[2.5 dup2--系统调用](#2.5 dup2--系统调用)

[2.6 完善前面自定义shell的重定向](#2.6 完善前面自定义shell的重定向)


一:理解文件

1-1 狭义理解

• ⽂件在磁盘⾥
• 磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的
• 磁盘是外设(即是输出设备也是输⼊设备)
• 磁盘上的⽂件 本质是对⽂件的所有操作,都是对外设的输⼊和输出 简称 IO

1-2 ⼴义理解

• Linux 下⼀切皆⽂件(键盘、显⽰器、⽹卡、磁盘...... 这些都是抽象化的过程)(后⾯会讲如何去理解)

1-3 ⽂件操作的归类认知

• 对于 0KB 的空⽂件是占⽤磁盘空间的
• ⽂件是⽂件属性(元数据)和⽂件内容的集合(⽂件 = 属性(元数据)+ 内容)
• 所有的⽂件操作本质是⽂件内容操作和⽂件属性操作

1-4 系统⻆度

• 对⽂件的操作本质是进程对⽂件的操作
• 磁盘的管理者是操作系统
• ⽂件的读写本质不是通过 C 语⾔ / C++ 的库函数来操作的(这些库函数只是为⽤⼾提供⽅便),⽽是通过⽂件相关的系统调⽤接⼝来实现的

至于c中与文件相关的接口,我们不再重复

访问文件,需要打开文件,而打开文件是进程操作的

本质是:进程对文件的操作

1.5 回顾c的文件操作

我们学过,系统会默认打开三个文件
分别是
stdin:标准输入 键盘文件
stdout:标准输出 显示器文件
stderr:标准错误 显示器文件

二.系统文件I/O

我们前面学过打开文件有fopen,其实系统才是打开的最底层方式,不过学习之前,我们先雪伊西传递位

这下面的传递位,与位图的思想类似

bash 复制代码
#include<stdio.h>                                                                                                                                                                                                                
  2 #define ONE 0001
  3 #define TWO 0010
  4 #define THREE 0011
  5 void func(int flag)
  6 {
  7   if(flag&ONE)
  8     printf("flag has ONE\n");
  9   if(flag&TWO)
 10     printf("flag has TWO\n");
 11   if(flag&THREE)
 12     printf("flag has THREE\n");
 13 }
 14 int main()
 15 {
 16   func(ONE);
 17   func(ONE|TWO);
 18   func(ONE|TWO|THREE);
 19   return 0;
 20 }
~
bash 复制代码
flag has ONE
flag has THREE
flag has ONE
flag has TWO
flag has THREE
flag has ONE
flag has TWO
flag has THREE

2.1 接口介绍

write read close lseek ,类⽐C⽂件相关接⼝。

bash 复制代码
#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

2.2 open使用

2.3.1 创建并写入

bash 复制代码
1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9   int fd=open("log.txt",O_CREAT | O_WRONLY,0666);
 10   if(fd<0) return 1;
 11   printf("fd:%d\n",fd);
 12   char buf[]="hello linux\n";                                                                                                                                                                                                    
 13   write(fd,buf,strlen(buf));
 14   close(fd);
 15   return 0;
 16 }

结果:

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ make
gcc -o file file.c
[lcb@hcss-ecs-1cde 7]$ ./file
fd:3
[lcb@hcss-ecs-1cde 7]$ cat log.txt
hello linux

同样如果我们再对log.txt写入,没有修改flag,会覆盖写入

2.3.2 写入并追加写入

注意:APPEND不可单独使用

bash 复制代码
1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9   int fd=open("log.txt",O_WRONLY | O_APPEND,0666);
 10   if(fd<0) return 1;
 11   printf("fd:%d\n",fd);
 12   char buf[]="hello linux\n";
 13   write(fd,buf,strlen(buf));
 14   close(fd);                                                                                                                                                                                                                     
 15   return 0;
 16 }

结果

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ ./file
fd:3
[lcb@hcss-ecs-1cde 7]$ ./file
fd:3
[lcb@hcss-ecs-1cde 7]$ cat log.txt
hello linux
hello linux
hello linux

2.3.3 追加写入并读取

read调用

  • 在非阻塞模式下,数据尚未准备好,则返回错误代码EAGAIN或EWOULDBLOCK。
  • 文件指针已到达文件末尾,此时返回0。
  • 出现错误,返回-1。
bash 复制代码
 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9   int fd=open("log.txt", O_RDWR,0666);
 10   if(fd<0) return 1;
 11   printf("fd:%d\n",fd);
 12  lseek(fd,0,SEEK_SET);
 13  char buf[1024]={0};
 14  int read_len=read(fd,buf,sizeof(buf)-1);
 15  if(read_len>0)
 16    printf("log.txt:%s\n",buf);
 17  else if(read_len == 0)                                                                                                                                                                                                          
 18    printf("log.txt为空\n");
 19  else{
 20    perror("read error");
 21  }
 22   close(fd);
 23   return 0;
 24 }
bash 复制代码
[lcb@hcss-ecs-1cde 7]$ ./file
fd:3
log.txt:hello linux
hello linux
hello linux

2.3 open返回值及fd

上面我们发现,每次打开文件的返回值fd都是3 ,那么是每个文件都是3吗

bash 复制代码
#include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9   int fd1=open("log1.txt",O_CREAT| O_RDWR,0666);
 10   int fd2=open("log2.txt",O_CREAT| O_RDWR,0666);
 11   int fd3=open("log3.txt",O_CREAT| O_RDWR,0666);
 12   int fd4=open("log4.txt",O_CREAT| O_RDWR,0666);
 13   printf("fd1:%d\n",fd1);
 14   printf("fd2:%d\n",fd2);
 15   printf("fd3:%d\n",fd3);
 16   printf("fd4:%d\n",fd4);                                                                                                                                                                                                        
 17   close(fd1);
 18   close(fd2);
 19   close(fd3);
 20   close(fd4);
 21 //if(fd<0) return 1;
 22 //printf("fd:%d\n",fd);
 23 //seek(fd,0,SEEK_SET);
 24 //har buf[1024]={0};
 25 //nt read_len=read(fd,buf,sizeof(buf)-1);
 26 //f(read_len>0)
 27 // printf("log.txt:%s\n",buf);
 28 //lse if(read_len == 0)
 29 // printf("log.txt为空\n");
 30 //lse{
 31 // perror("read error");
 32 //
 33 //close(fd);
 34   return 0;
 35 }

open的返回值会是从0开始未被使用的最小值

把0 1 2依次关闭试试

• Linux进程默认情况下会有3个缺省打开的⽂件描述符,分别是标准输⼊0, 标准输出1, 标准错误2.
• 0,1,2对应的物理设备⼀般是:键盘,显⽰器,显⽰器

bash 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9   close(0);
 10   int fd =open("log.txt",O_RDWR |O_TRUNC,0666);                                                                                                                                                                                           
 11   printf("fd:%d\n",fd);
 12   //int fd1=open("log1.txt",O_CREAT| O_RDWR,0666);
 13   //int fd2=open("log2.txt",O_CREAT| O_RDWR,0666);
 14   //int fd3=open("log3.txt",O_CREAT| O_RDWR,0666);
 15   //int fd4=open("log4.txt",O_CREAT| O_RDWR,0666);
 16   //printf("fd1:%d\n",fd1);
 17  //rintf("fd2:%d\n",fd2);
 18  //rintf("fd3:%d\n",fd3);
 19  //rintf("fd4:%d\n",fd4);
 20  //lose(fd1);
 21  //lose(fd2);
 22  //lose(fd3);
 23  // close(fd1);
 24 //if(fd<0) return 1;
 25 //printf("fd:%d\n",fd);
 26 //seek(fd,0,SEEK_SET);
 27 //har buf[1024]={0};
 28 //nt read_len=read(fd,buf,sizeof(buf)-1);
 29 //f(read_len>0)
 30 // printf("log.txt:%s\n",buf);
 31 //lse if(read_len == 0)
 32 // printf("log.txt为空\n");
 33 //lse{
 34 // perror("read error");
 35 //
 36 //close(fd);
 37   return 0;
 38 }

0

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ ./file
fd:0

1,但不关闭close,原因后面缓存区再说

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ ./file

2

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ ./file
fd:0

2.3.1 解析为何结果不同

其实0 1 2的文件 ,系统帮我们默认打开了

0---标准输入流

1---标准输出流

2---标准错误流

那么结果就很明显了,printf fprintf等等c的接口,都是依赖stdout的

关闭了,标准输出流关闭了

而后面再把1的给了log.txt(后面再提)

那么如果我们关闭1,再对log.txt写入呢

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ cat log.txt
hello linux

发现没有写在显示器文件上,而是写在了log.txt的文件上


其实看上面的0 1 2 3 4....,这不就是数组下标吗

刚开始的1指向标准输出,close后,再打开,fd的返回值是从最小未使用的开始,所以就成了1指向log.txt,而printf的打印结果也就去log.txt文件了

2.4 重定向

我们可以发现,上面关闭1,再打开写入,改变了写入的目标文件,这不就像我们说到的重定向吗

所以重定向的本质

改变指针指向的文件

2.5 dup2--系统调用

上面虽然实现了重定向,但我们发现每次打开再写入,有点麻烦,那么系统给我们提供了这么一个接口

那么问,我们要将1这个位置的指针从标准输出变为fd,参数位置应该如何

答案:fd 放在第一个,1放第二个,因为fd覆盖1,那么log.txt这个文件就被fd与1同时指向了

bash 复制代码
 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9  // close(1);
 10   int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
 11  dup2(fd,1);
 12   char buf[]="hello linux\n";
 13   printf("fd:%d\n",fd);
 14   printf("%s\n",buf);
 15   close(fd);                                                                                                                                                                                                                     
 16   //int fd1=open("log1.txt",O_CREAT| O_RDWR,0666);
 17   //int fd2=open("log2.txt",O_CREAT| O_RDWR,0666);
 18   //int fd3=open("log3.txt",O_CREAT| O_RDWR,0666);
 19   //int fd4=open("log4.txt",O_CREAT| O_RDWR,0666);
 20   //printf("fd1:%d\n",fd1);
 21  //rintf("fd2:%d\n",fd2);
 22  //rintf("fd3:%d\n",fd3);
 23  //rintf("fd4:%d\n",fd4);
 24  //lose(fd1);
 25  //lose(fd2);
 26  //lose(fd3);
 27  // close(fd1);
 28 //if(fd<0) return 1;
 29 //printf("fd:%d\n",fd);
 30 //seek(fd,0,SEEK_SET);
 31 //har buf[1024]={0};
 32 //nt read_len=read(fd,buf,sizeof(buf)-1);
 33 //f(read_len>0)
 34 // printf("log.txt:%s\n",buf);
 35 //lse if(read_len == 0)
 36 // printf("log.txt为空\n");
 37 //lse{
 38 // perror("read error");
 39 //
 40 //close(fd);
 41   return 0;
 42 }

结果也确实如此,log.txt即被3指向,也被1指向

bash 复制代码
[lcb@hcss-ecs-1cde 7]$ cat log.txt
fd:3
hello linux

2.6 完善前面自定义shell的重定向

进程替换,不影响重定向结果

先定义好redir对应的读取权限,再定义一个filename来获取有重定向的文件名字

都是全局变量

bash 复制代码
     
   31 #define NONE_REDIR 0
   32 #define INPUT_REDIR 1
   33 #define OUTPUT_REDIR 2       
   34 #define APPEND_REDIR 3
   35 int redir = NONE_REDIR;                                             
   36 std::string filename;    
bash 复制代码
//
  125   int  Docommad()
  126   {
  127     pid_t id =fork();
  128     if(id==0)
  129     {
  130       //child
  131       if(redir==INPUT_REDIR)
  132       {
E>133         int fd=open(filename.c_str(),O_RODNLY,0666)
  134         if(fd<0) exit(1);
  135           dup2(fd,1);
  136         close(fd);
  137       }
  138 
  139       if(redir==OUTPUT_REDIR)
  140       {
E>141         int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666)
  142           if(fd<0) exit(1);
  143           dup2(fd,1);
  144         close(fd);
  145       }
  146       if(redir==APPEND_REDIR)
  147       {
E>148         int fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
  149         if(fd<0) exit(1);
  150         dup2(fd,1);
  151         close(fd);                                                                                                                                                                                                             
  152       }
  153     }
  154       else{
  155 
  156       execvp(argv[0],argv);
  157       }
  158       exit(1);
  159     }
  160     //father
  161     int status=0;
E>162    pid_t ret = waitpid(id,&status,0);
E>163    if(ret>0)
  164    {
  165      lastcode=WEXITSTATUS(status);
  166    }
E>167     return 0;
E>168   }

找出重定向符号后面的文件名

bash 复制代码
void trimspace(char mad[],int&end)//引用是为了这里改变是改变整体的end,不是改变副本,为后面的dup2使用end使用
  259 {
  260   while(mad[end])
  261   {
  262     ++end;
  263   }
  264 }
  265 void RedirCheck(char mad[])
  266 {
  267   int begin=0;
  268   int end=strlen(mad)-1;
  269   //从后往前找
  270   while(end>begin)
  271   {
  272     if(mad[end]=='<')
  273     {
  274       mad[end++]='0';//分割符
  275       // < 左边的空格不用排查,因为用不到,只需排查右边的
  276       trimspace(mad,end);
  277       redir=INPUT_REDIR;
  278       filename=mad+end;
  279       break;
  280     }
  281     else if(mad[end]=='>')
  282     {
  283      // >>
  284       if(mad[end-1]=='>')
  285       {
  286         mad[end-1]=0;
  287         redir=APPEND_REDIR;
  288       }
  289       else{
  290         // >
  291         redir=OUTPUT_REDIR;
  292       }
  293       trimspace(mad,++end);
  294       filename=mad+end;
  295     }
  296   }
  297 }
相关推荐
A尘埃2 小时前
物流公司配送路径动态优化(Q-Learning算法)
算法
郝学胜-神的一滴2 小时前
深入浅出:使用Linux系统函数构建高性能TCP服务器
linux·服务器·开发语言·网络·c++·tcp/ip·程序人生
天若有情6732 小时前
【自研实战】轻量级ASCII字符串加密算法:从设计到落地(防查岗神器版)
网络·c++·算法·安全·数据安全·加密
承渊政道2 小时前
Linux系统学习【Linux系统的进度条实现、版本控制器git和调试器gdb介绍】
linux·开发语言·笔记·git·学习·gitee
技术路上的探险家2 小时前
Ubuntu下Docker与NVIDIA Container Toolkit完整安装教程(含国内源适配)
linux·ubuntu·docker
代码AC不AC2 小时前
【Linux】深入理解缓冲区
linux·缓冲区·标准错误
DeeplyMind2 小时前
第七章:数据结构大比拼
数据结构·计算机科学·少儿编程·少儿科技读物
元亓亓亓2 小时前
考研408--数据结构--day8--遍历序列&线索二叉树
数据结构·考研·408·线索二叉树
Doro再努力2 小时前
【Linux操作系统12】Git版本控制与GDB调试:从入门到实践
linux·运维·服务器·git·vim