基础IO ——文件描述符

目录

初知:

1.文件是什么? 文件是 文件内容 加 文件属性

2.所以对文件操作就衍生为 对文件内容操作 和 对文件属性操作。

3.打开文件这个操作,系统都做了什么?

4.是不是所有文件都处于打开的状态?        不是,没有打开的文件都在磁盘上静静地存储着,等着被打开。

5.所以从系统的角度,我们可以将文件分为 打开的文件(内存文件) 和 磁盘文件。

6.通常我们打开文件,访问文件,关闭文件等相关操作是谁来做的?

7.这个文件所在的路径在哪里?

 8.打开文件fopen 有几个选项“r, w,r+,w+,a,a+”,我们来了解下。

系统文件接口

1.open

①细节:

②open的返回值

 ③进程与文件

 ④文件描述符的分配规则

2.close

3.write

4.read


初知:

1.文件是什么? 文件是 文件内容 加 文件属性

        我创建一个新的文件,是不是不用占磁盘空间因为我没写内容啊,并不是的,文件属性(大小、文件名、创建时间等)也要占用磁盘空间。

2.所以对文件操作就衍生为 对文件内容操作 和 对文件属性操作。

3.打开文件这个操作,系统都做了什么?

        将磁盘上的文件内容或属性加载到内存中,由CPU来进行访问。

4.是不是所有文件都处于打开的状态?
        不是,没有打开的文件都在磁盘上静静地存储着,等着被打开。

5.所以从系统的角度,我们可以将文件分为 打开的文件(内存文件) 和 磁盘文件。

6.通常我们打开文件,访问文件,关闭文件等相关操作是谁来做的?

        我们在C语言中调用 fread 、fwrite、fclose函数->变成一串串代码->编译为可执行程序->运行程序,只有程序跑起来了,成为进程,才会真正对文件操作。所以真正对文件操作的就是进程。

我们切实去使用C语言操作一下文件。

         这就是C语言中对文件的写入操作。

7.这个文件所在的路径在哪里?

        文件就在当前路径下,当前路径就是进程所在的工作路径。

我们试验一下:

在写入文件之前,获取此进程的pid,通过ls /proc/pid -l去查看进程信息。proc是内存级文件系统可以查看进程信息。

这里的cwd是,current working directory 即是当前工作目录。我们获得了当前进程的工作路径。这时我们的文件也在当前路径下。

 我们用chdir修改下,当前进程所在的工作路径,看看文件最后在哪?

用ls /proc 查看 ,这时的进程的工作路径在/home/Yewei

 

 而文件如我们所料,在/home/Yewei路径下。

 8.打开文件fopen 有几个选项“r, w,r+,w+,a,a+”,我们来了解下。

r+和w+的功能都是可以读和写 

w:只写,如果这个文件没有,就创建一个文件。如果这个文件有内容,就清空,从文件开头开始写入。

r:只读,不能写。

a:写入在文件的结尾,如果文件不存在,则创建该文件。

a+:读 加 写在文件结尾,如果文件不存在,则创建该文件。

系统文件接口

我们都知道c语言对文件的操作都是封装自操作系统的提供的对文件操作的接口,我们下面了解下这些接口。

1.open

        这是最重要的一个接口。

        第一个参数是接受文件的名字,第二个参数flags是用来接受标记位。什么是标记位,我的理解是这个函数有多个选项执行不同的功能,传参数来选择执行哪个功能。不像其他的函数,传1或0等这里要传,

                               只读                        只写           读加写             追加                 建造

        这些都是宏,为什么要传他们,我传1、2、3、4等不好吗?那我们要传30个参数怎么办,太麻烦了。而这里使用宏就可以传一个整形就行,为什么?

        系统传递标记位,是用位图结构来进行实现的。也就是说,这些宏对应的是位图中的一个数。例如:

         传给系统就是一个整数,而且能清楚表达我们想做的,简单易懂。

写段代码体验一下:

  1 #include<stdio.h>                                                                                                                                                    
  2 
  3 #define PRIT_I 0x1
  4 #define PRIT_L 0x2
  5 #define PRIT_V 0x4
  6 #define PRIT_U 0x8
  7 
  8 void fuc(int flags)
  9 {
 10   if(flags & PRIT_I)
 11     printf("I ");
 12   if(flags & PRIT_L)
 13     printf("L ");
 14   if(flags & PRIT_V)
 15     printf("V ");
 16   if(flags & PRIT_U)
 17     printf("U");
 18   
 19 }
 20 
 21 int main()
 22 {
 23   fuc(PRIT_I);
 24   fuc(PRIT_L);
 25   fuc(PRIT_V);
 26   fuc(PRIT_U);
 27   printf("\n");
 28   fuc(PRIT_I);
 29   printf("\n");
 30   fuc(PRIT_I | PRIT_L);
 31   printf("\n");
 32   fuc(PRIT_I | PRIT_L | PRIT_V);
 33   printf("\n");
 34   fuc(PRIT_I | PRIT_L | PRIT_V | PRIT_U);
 35   printf("\n");
 36 
 37   return 0;
 38 }

运行:

         很是方便。知道了标记位,我们来小用下open。

  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("fight.txt", O_WRONLY | O_CREAT);
  9   if(fd < 0)
 10   {
 11     perror("open");//打印错误信息
 12     return 1;
 13   }
 14 
 15   printf("fd : %d \n", fd);                                                                                                                                          
 16 
 17 
 18   return 0;
 19 }

                这里的返回值为什么是3呢?我们下文来讲 。

        为什么创建的这个文件,权限是乱的,-wSr...是什么?还记得open也有函数重载么,我们用第二个open,因为创建一个从没有过的文件,要设置它初始的权限。

int fd = open("fight.txt", O_WRONLY | O_CREAT, 0666);

 运行结果:

       

我们明明想要0666,也就是权限为-rw-rw-rw-,现在为什么是-rw-rw-r--,那是跟我们系统的权限掩码有关,此时我们系统的权限掩码是0002,以致如此。

①细节:

c语言中fopen函数底层调用的是系统的open,fopen函数传入w会清空文件再写入或者创建新文件然后写入,底层给open第二个参数传入的是:

 fopen函数传入a,底层给open第二个参数传入的是:

        这样看来系统接口好麻烦,还是封装过后的函数好用。

②open的返回值

        为什么是3呢?0 1 2 呢?其他呢?

  int fda = open("fight1.txt", O_WRONLY | O_CREAT, 0666);
  int fdb = open("fight2.txt", O_WRONLY | O_CREAT, 0666);
  int fdc = open("fight3.txt", O_WRONLY | O_CREAT, 0666);
  int fdd = open("fight4.txt", O_WRONLY | O_CREAT, 0666);
  int fde = open("fight5.txt", O_WRONLY | O_CREAT, 0666);

  printf("fda : %d \n", fda);
  printf("fdb : %d \n", fdb);
  printf("fdc : %d \n", fdc);
  printf("fdd : %d \n", fdd);
  printf("fde : %d \n", fde);

结果:

        很怪啊,0 1 2 都没有,3以后递增,为什么?且听我细细道来。

文件描述符中的                                                                        C语言中的默认文件流

0: 标准输入                                                                            extern FILE * stdin

1: 标准输出                                                                            extern FILE * stdout   

2: 标准错误                                                                            extern FILE * stdin

        这俩如出一辙,我们都知道,C语言中的库函数都是封装自系统的接口。

        

         这三个std和012有什么关系呢?一步一步来。我们知道:

        

        那FILE是什么?

        它是C库中的结构体,它其中封装了很多个成员。回到系统接口,对文件操作而言,系统接口只认文件描述符fd(file descriptor)。这些stdin、stdout、stderr都是文件,要去打开这些文件必定要使用fd,所以FILE这个结构体必须要封装fd(文件描述符),这样这三个文件才可以去使用封装过后的fd,来调用系统接口。

例如:

struct FILE
{
    //封装fd的细节
}

int main()
{
    FILE * stdin;
    FILE * stdout;
    FIEL * stderr;
}

试验一下,在系统接口read中传入0 1 2 ,是否是对应的stdin stdout stderr

 ③进程与文件

        这里的0 1  2  3  ... 好像跟数组下标有关,这里为什么要这么设计呢?看下去.

        在系统运行中,有可能有大量的文件被打开,所以系统要建立数据结构来进行对这些文件的管理,从数据结构的角度理解,就如同这样:

 然后每个file就可以连接起来,以数据结构的角度来看,当然系统内核有自己的实现方式.

        1个进程打开n个文件,在内核中进程如何映射到对应的文件的?

        struct task_struct 中有struct files_struct *fs指针,这个指针再指向struct files_struct,这个结构中有一个指针数组struct file*fd_arr[],里面存在着struct file*的指针。

图示:

        这些都是内核中实现的。我们来实际模拟一下:

 

 ④文件描述符的分配规则

        从头遍历指针数组struct file* fd_arr[],将没有使用的下标且是没有使用中最小的,分配给新文件。

我们关闭0,分配给新文件的文件描述符就是0:

  close(0);
  int fd = open("Go.txt",O_WRONLY | O_CREAT,0666);
  printf("fd: %d\n",fd);
  close(fd);

 结果:

 

2.close

         有开必有关,只需将open时的返回值传入即可。

3.write

 代码:

  const char *str = "You gonna break my heart\n";
  write(fd, str, strlen(str));

结果: 

4.read

         从文件中读取内容。这里的返回值ssize_t 是有符号整数。count是你所要读取的内容所占字节大小。

        感谢观看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值