Linux—基础IO

C语言文件操作函数汇总

下面是C语言的文件操作函数的接口:
在这里插入图片描述

对文件进行写入操作:

  1 #include<stdio.h>
  2 
  3 int main()
  4 {
  5   FILE* fp =fopen("t.txt","w");
  6   if(fp == NULL)
  7   {
  8     perror("fopen fail\n");
  9     return 1;
 10   }
 11 
 12   fputs("亚索\n",fp);
 13   fclose(fp);
 14   return 0;                                                                                                         
 15 }

cat 看一下就已经写入成功了。
在这里插入图片描述

对文件读取操作:

  1 #include<stdio.h>
  2 
  3 int main()
  4 {
  5   FILE* fp =fopen("t.txt","r");
  6   if(fp == NULL)
  7   {
  8     perror("fopen fail\n");
  9     return 1;
 10   }
 11  char buffer[64];
 12  fgets(buffer,sizeof(buffer),fp);
 13  printf("%s",buffer);                                                                                               
 14   fclose(fp);
 15   return 0;
 16 }

运行程序:刚刚写入的就读取并打印在显示器上了。
在这里插入图片描述
C的文件操作就简单使用一下,后面会补上关于文件操作的。

默认打开的3个输入输出流

进程在运行的时候都会打开默认打开3个输入输出流,分别是标准输入流,标准输出流,标准错误流,对应到C语言中就是stdin,stdout,stderr,stdin对应的设备是键盘,stdout和stderr对应的设备是显示器。
查看man手册我们课题看到这3个都是FILE*
在这里插入图片描述
所以当C程序被运行起来,OS就会打开这3个输入输出流,我们就可以调用printf类似的函数对显示器和键盘进行对用的输入输出的操作了。

系统IO

操作文件除了语言提供的接口,我们还可以使用系统调用接口。上面的C语言中的文件接口函数叫库函数。在前面的讲系统时画的一张图:

在这里插入图片描述
C的库函数就是对系统调用进行封装,方便二次开发。

open函数

open的函数原型:

int open(const char *pathname, int flags, mode_t mode);

open的头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

open函数的第一个参数

pathname:要打开或创建的目标文件

当pathname以路径的方式给出,则但需要创建文件时在pathname路径下进行创建
若是以文件名方式给出,则但需要创建文件时,默认在当前路径。

open函数的第二个参数

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构flags。

常用的选项:

O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写

打开文件时可以传入多个参数,可以用“ | ”隔开。
例如:用只写的方式打开文件当文件不存在时

O_WRONLY | O_CREAT

open函数的第三个参数

mode:表示创建文件的默认权限

但实际上创建出来的文件还会受到umask的影响,实际创建的文件权限为:mode&(~umask),umask的默认值一般是0002.若想不受影响,则需要在创建文件前将umask的值设为0

umask(0)

open函数的返回值

在这里插入图片描述
我们来打印看看文件描述符:

  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 
  6 
  7 int main()
  8 {
  9 
 10   umask(0);
 11   int fd1 = open("g.txt1",O_RDONLY | O_CREAT,0666);
 12   int fd2 = open("g.txt2",O_RDONLY | O_CREAT,0666);
 13   int fd3 = open("g.txt3",O_RDONLY | O_CREAT,0666);
 14   int fd4 = open("g.txt4",O_RDONLY | O_CREAT,0666);
 15   printf("fd1:%d\n",fd1);
 16   printf("fd2:%d\n",fd2);
 17   printf("fd3:%d\n",fd3);
 18   printf("fd4:%d\n",fd4);                                                                                           
 19   return 0;
 20 }

在这里插入图片描述
如果打开一个不存在的文件呢?

  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 
  6 int main()
  7 {
  8   int fd = open("x.txt",O_RDONLY);                                                                                  
  9   printf("fd:%d\n",fd);
 10 
 11   return 0;
 12 }

运行直接是返回-1.
在这里插入图片描述

文件描述符

文件是由进程运行时打开的,一个进程可以打开多个文件。在系统中可能存在大量的进程,那系统中也存在很多文件,为了区分已经打开的文件属于哪个特定的进程,所以还要建立进程和文件的对应关系。

在这里插入图片描述

当文件加载到内存时,系统会创建struct flie,并用双链表连接起来,同时还有file_struct,而file_struct中放的是结构体指针数组,每个每个进程都有一个指针file*,指向file_struct.Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2. 0,1,2对应的物理设备一般是:键盘,显示器,显示器,从3开始指向新的文件。所以本质上文件描述符就是该数组的下标。因此,只要拿着文件描述符就可以找到对应的文件。

文件描述符的规则

  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include<unistd.h>
  6 
  7 int main()
  8 {
  9   int fd = open("t.txt",O_RDONLY);
 10   if(fd < 0)
 11   {
 12     perror("open");
 13     return 1;
 14   }
 15 
 16   printf("fd:%d\n",fd);
 17  close(fd);                                                                                                         
 18   return 0;
 19 }

在这里插入图片描述

先关闭0

close(0)

在这里插入图片描述

//关闭2试试
close(2);

在这里插入图片描述

综上可以得出结果:文件描述符的规则在file_struct数组中找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

先来一段代码:

  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include<unistd.h>
  6 
  7 int main()
  8 {
  9   close(1);
 10   int fd = open("p.txt",O_WRONLY|O_CREAT,00644);
 11   if(fd < 0)
 12   {
 13     perror("open");
 14     return 1;
 15   }
 16 
 17   printf("this is linux!\n");
 18   fflush(stdout);
 19                                                                                                                     
 20   close(fd);
 21   return 0; 
 22 }  

我们关掉1,本来时打印在显示器上的却在文件中,这就是重定向。
在这里插入图片描述

重定向的本质

在这里插入图片描述
关闭1时,1就不再指向标准输出了而是指向了新的文件,此时往显示器上输出就会在新的文件中。

dup2函数

在这里插入图片描述
我们直接把3的内容拷贝到2中,也就完成了重定向。Linux提供的系统调用就是dup2这个函数。
dup2函数:

int dup2(int oldfd, int newfd);
  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include<unistd.h>
  6 
  7 int main()
  8 {
  9   int fd = open("y.txt",O_WRONLY|O_CREAT,00644);                                                                    
 10   if(fd < 0)
 11   {
 12     perror("open");
 13     return 1;
 14   }
 15   close(1);
 16   dup2(fd,1);
 17 
 18   printf("this is linux!\n");
 19   fprintf(stdout,"hello fprintf\n");
 20 
 21   close(fd);
 22   return 0;
 23 }

在这里插入图片描述
数据被输入到了y.txt中了,完成了重定向

FILE

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

  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 int main()
  8 {
  9   const char *msg0="hello printf\n";
 10   const char *msg1="hello fwrite\n";
 11   const char *msg2="hello write\n";
 12     printf("%s", msg0);
 13     fwrite(msg1, strlen(msg0), 1, stdout);                                                                          
 14     write(1, msg2, strlen(msg2));
 15     fork();
 16     return 0;
 17 }

程序运行的结果:
在这里插入图片描述

对程序进行重定向

在这里插入图片描述
我们发现只有系统调用的write打印了1遍,printf,fwrite都输出了2遍。一般C库函数写入文件是全缓冲,写入显示器是行缓冲
重定向影响了缓冲的方式,重定向到普通文件时,数据的缓冲方式由全缓冲变成了行缓冲。我们放入到缓冲区的数据不会被立即刷新,甚至fork之后。
到进程退出后会统一刷新,写入文件中,但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,就产生两份数据。write没有变化就说明没有缓冲。

注意:
这里说的缓冲区都是用户级缓冲区。这个缓冲区是有C语言库函数提供的

感兴趣的可以研究FILE的结构体。
本篇文章暂时到这里就结束了。由于博主水平有限,如有错误,还请指出,万分感谢!!!
在这里插入图片描述

评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_End丶断弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值