操作系统——文件fd

C语言文件读写

写文件

  1 #include <stdio.h>                                                                                                                                    
  2 #include <string.h>
  3 
  4 
  5 int main()
  6 {
  7     //w:文件不存在会自动创建,先清空文件,再写入
  8     //a:we\\文件不存在自动创建,会在文件的末尾继续写入
  9     //FILE* p = fopen("log.txt","w");
 10     FILE* p = fopen("log.txt","a");
 11     const char* inf = "welcome to read\n";
 12     fwrite(inf,strlen(inf),1,p);
 13     //fprintf(p,"%s",inf);
 14     fclose(p);
 15     return 0;
 16 }

未指定路径会在默认在当前路径,为什么?

进程属性cwd(current work directory)会有路径的信息,如果我们修改这个属性,文件就会创建到修改后的路径下:

当前路径下并没有创建文件

 到我们修改的路径下面发现了我们写入的文件 

读文件

    1 #include <stdio.h>
    2 #include <string.h>
    3 #include <unistd.h>
    4 
    5 
    6 int main()
    7 {
    8     char* msage[100];
    9     FILE* p = fopen("log.txt","r");
   10     fread(msage,1,sizeof(msage),p);
   11     printf("%s\n", msage);                                           
   12     fclose(p);
   13     return 0;
   14 }

系统操作文件的接口

每一种语言可以对文件进行读写,它们的库函数都是对系统调用的封装。主要有这几个接口

open

参数:

pathname是文件路径,如果只有文件名默认当前路径

flags是可以指定函数功能的参数,主要有这几个宏

O_RDONLY: 只读打开

O_WRONLY: 只写打开

O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写 

 mode指定新建文件的权限

返回值:

成功:新打开的文件描述符

失败:-1


flag的传递类似于位图,是一种比特位的标志位传参,比如:

  1 #include <stdio.h>
  2 #define fun1 1
  3 #define fun2 1<<1
  4 #define fun3 1<<2
  5 #define fun4 1<<3
  6 
  7 void fun(int n)
  8 {
  9     if(n&1) printf("fun1\n");
 10     if(n&2) printf("fun2\n");
 11     if(n&4) printf("fun3\n");
 12     if(n&8) printf("fun4\n");
 13 
 14 }
 15 int main()
 16 {
 17     fun(fun1|fun3);
 18     return 0;                                                          
 19 }

write 

read 

close 

使用样例:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 
  8 int main()
  9 {
 10     int fd = open("log.txt", O_RDWR|O_CREAT|O_APPEND,0666);
 11     if(fd < 0)
 12     {
 13         return -1;
 14     }
 15     const char* inf = "writing success!!!\n";
 16     write(fd,inf,strlen(inf));
 17     char buf[1024];
 18     int fd_r = open("log.txt",O_RDONLY);
 19     while(1)
 20     {
 21         ssize_t ret = read(fd_r, buf, strlen(inf));
 22         if(ret > 0)
 23             printf("%s",buf);                                          
 24         else
 25             break;
 26     }
 27     close(fd_r);
 28     close(fd);
 29     return 0;
 30 }

 原理

文件被打开时,操作系统会为文件创建结构体。

当进程要访问文件,进程的PCB有文件描述符表指针,会从该表中寻找空元素,指向文件结构体

进程启动,操作系统都会为我们打开三个文件,stdin,stdout,stderr.可以验证一下:

文件重定向

文件描述符的分配规则

stderr对应的描述符编号是2,将它关闭后再打开文件,新文件的描述符编号是2.这说明了文件描述符的分配规则是:在files_struct中寻找最小的未被使用的下标,作为新文件的描述符

重定向

 

 将stdout关闭后,新建的文件流的文件描述符就是1,当我们再次向stdout输入时,就会将内容放到文件里。这就是输出重定向。

重定向系统调用接口——dup2

 

newfd是oldfd的拷贝,所以这个接口是用oldfd替换newfd,特别容易混淆 

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 
  8 int main()
  9 {
 10     int fd = open("log.txt",O_WRONLY|O_TRUNC|O_CREAT, 0666);
 11     dup2(fd,2);
 12     printf("%d\n", fd);
 13     const char* message = "writing!!!!\n";
 14     ssize_t ret = write(2,message, strlen(message));
 15     if(ret < 0)
 16     {
 17         perror("write fail\n");
 18     }
 19     return 0;
 20 } 

重新理解,一切皆文件

 硬件驱动会提供对应的操作接口,操作系统会在文件结构体创建的时候初始化。文件结构体存储在文件描述符表中,进程对硬件的使用对上层的角度来看就是对文件的操作,所以说一切皆文件。

文件缓冲区

示例

 

看这段代码的结果:

为什么只有系统调用接口write可以输出呢?这个答案与文件缓冲区有很大的关系。 

原理

每种编程语言都有自己的文件操作接口,以c语言为例:

fprintf() printf fwrite等等c接口,在操作完成后都会将数据放到c语言自己维护的缓冲区中,只有在一定条件下(后面说)才会刷新缓冲区到操作系统的缓冲区,最后由操作系统传给硬件设备,例如磁盘,显示器等。

所以刚才的代码里最后一行,直接将stdout关闭,直接将操作系统缓冲区刷新到硬件设备,而c语言自己的缓冲区由于之前未刷新到操作系统缓冲区,关闭直接就找不到操作系统的缓冲区了,更别提将数据传输到硬件设备上了。

缓冲区什么时候刷新?

缓冲区的刷新分为三种:

无缓冲——直接刷新

行缓冲——直到碰到\n才刷新(显示器)

全缓冲——缓冲区满了才刷新(写文件)

当进程退出时也会刷新缓冲区。

为什么要有缓冲区?

提高效率,将多个数据放到缓冲区一起传输会比单个数据多次传输效率高。就好像快递都是一车一车运送的,而不是一个包裹一个包裹运送的。

配合格式化,当我们向显示器打印一个数字时,例如,printf("%d," 10),最终要显示的其实是两个字符

再次理解缓冲区

为什么重定向输出到文件,c语言文件接口输出的内容被写了两遍?

fork创建子进程,子进程会以写时拷贝的方式创建,缓冲区也不例外(每个进程都有缓冲区), 在进程结束后,每个进程都会刷新到文件中,只有系统调用write写的内容在操作系统缓冲区中,只有一份。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值