当前位置 : 主页 > 操作系统 > centos >

[ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口

来源:互联网 收集:自由互联 发布时间:2023-02-04
文件描述符: 文件 = 文件内容 + 文件属性。 (文件属性也是数据--即便你创建一个空文件,也要占据磁盘空间) 文件操作 = 文件内容的操作 + 文件属性的操作。(有可能在操作的过程

文件描述符:

  • 文件 = 文件内容 + 文件属性。 (文件属性也是数据-->即便你创建一个空文件,也要占据磁盘空间)
  • 文件操作 = 文件内容的操作 + 文件属性的操作。(有可能在操作的过程中,即改变内容,有改变属性)
  • 所谓的"打开"文件,究竟在干什么?将文件的属性或内容加载到内存汇总! -- 冯诺依曼体系决定
  • 是不是所有的文件,都会处于被打开的状态?绝对不是!没有被打开的文件,在哪儿里?只在磁盘上存储着!
  • 打开的文件(内存文件)和磁盘文件
  • 通常我们打开文件、访问文件、关闭文件。是谁在进行相关操作?fopen,fclose,fread,fwrite....... ---》 代码 --》程序 --》 当我们的文件程序运行起来的时候,才会执行对应的代码,然后才是真正的对文件进行相关的操作。
  • 进程和打开文件的关系
  • 0.复习文件操作(C语言)

    0.1由一段C语言文件操作产出几个问题

    #include <stdio.h>int main(){ FILE *fp = fopen("log.txt","w");//写入 if(fp == NULL) { perror("fopen"); return 1; } const char* msg = "hello file"; int cnt = 1; while(cnt < 20) { fprintf(fp,"%s:%d\n",msg,cnt++); } fclose(fp); return 0;}

    运行结果:

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径

    这是我们写的一段最简单的C语言代码,这段代码可以产出几个问题:

    0.1.1 log.txt生成时没有带路径,默认这个文件会在哪里形成呢?

    我们都知道是在当前路径。那么什么是当前路径呢?当前路径是源代码所在的路径吗?其实这种说法是一种感性的认识,其实当前路径是进程所在的路径。为了验证这一结论。我们使用命令来查看一下。

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ FILE *fp = fopen("log.txt","w");//写入 if(fp == NULL) { perror("fopen"); return 1; } printf("pid:%d\n",getpid());//获取当前进程的pid while(1) { sleep(1); } const char* msg = "hello file"; int cnt = 1; while(cnt < 20) { fprintf(fp,"%s:%d\n",msg,cnt++); } fclose(fp); return 0;}

    使用指令查看

    ls /proc/pid值 -l

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_02

    我们发现有一个cwd(current working directory)--当前工作路径

    因此我们验证了:当前路径是当前进程所处的工作路径。

    此时我们如果对当前工作路径进行修改,我们仍然可以得到想要的结果(此时我们更改当前的工作路径到/home/Lxy)

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ chdir("/home/Lxy");//更改当前进程的工作路径 FILE *fp = fopen("log.txt","w");//写入 if(fp == NULL) { perror("fopen"); return 1; } printf("pid:%d\n",getpid()); while(1) { sleep(1); } const char* msg = "hello file"; int cnt = 1; while(cnt < 20) { fprintf(fp,"%s:%d\n",msg,cnt++); } fclose(fp); return 0;}

    我们继续使用指令来进行查看

    ls /proc/pid值 -l

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_03

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_04

    0.1.2 复习a选项

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_05

    a 选项是一个写入操作,写入到文件的结尾。也就是追加操作。我们赶紧有C语言来看看效果

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ FILE *fp = fopen("log.txt","a");//写入 if(fp == NULL) { perror("fopen"); return 1; } const char* msg = "hello file"; int cnt = 1; while(cnt <= 5) { fprintf(fp,"%s:%d\n",msg,cnt++); } fclose(fp); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_06

    0.1.3 复习w选项

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_07

    我们看到w操作解释的第一句话是Truncate file to zero length or create text file for writing. (将文件截断为零长度或创建文本文件进行写入).意思就是如果文件不存在则创建之;如果文件已经存在则从头开始写入。我们首先验证一下存在不写会有什么结果。

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ FILE *fp = fopen("log.txt","w");//写入 if(fp == NULL) { perror("fopen"); return 1; } fclose(fp); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_08

    我们看到了文件被清空了。因此我们得到当我们以'w'方式打开文件,准备写入时,其实文件已经被清空了。这是w和a的一个最大的区别!

    0.1.4 复习读操作

    fgets

    fgets是从特定的文件流(FILE * stream)中读取特定的数据(char *s),大小是size.返回值成功了就是读取的起始地址。我们也用C语言来实现一下

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_09

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_10

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ //chdir("/home/Lxy");//更改当前进程的工作路径 FILE *fp = fopen("log.txt","r");//写入 if(fp == NULL) { perror("fopen"); return 1; } char buffer[64]; while(fgets(buffer,sizeof(buffer),fp) != NULL) { printf("echo:%s",buffer); } fclose(fp); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_11

    我们根据读操作写一个小程序玩一玩,我们的想法是./myfile filename 可以打印出文件的内容

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc,char* argv[]){ if(argc != 2) { printf("Usage:%s filename\n",argv[0]); return 1; } FILE * fp = fopen(argv[1],"r"); if(fp == NULL) { perror("fopen"); return 1; } char buffer[64]; while(fgets(buffer,sizeof(buffer),fp) != NULL) { printf("%s",buffer); } fclose(fp); return 0;}

    我们来一起看看吧~我们发现我们所写的和系统提供的cat指令大差不大啦~(感兴趣的小伙伴自己也尝试一下吧)

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_12

    至此,我们文件基本操作(C语言)就复习到这里~

    1.认识文件相关系统调用接口

    我们回归理论:当我们像文件写入的时候,最终是不是像磁盘写入?肯定是,那么操作系统是硬件,只有操作系统有资格像硬件写入。那么能绕开操作系统吗?答案是不能。所有的上层访问文件的操作都必须贯穿操作系统。操作系统是如何被上层使用的呢?答案肯定是使用操作系统提供的相关系统调用!

    那么这里有两个问题?

  • 如何理解printf? ---- 封装了系统调用接口
  • 我们怎么从来没见过系统调用接口呢? ---- 所有的语言都对系统接口做了封装
  • 那么为什么要做封装?

  • 原生系统接口,使用成本比价高(后期我们会看并且使用系统接口)
  • 直接使用原生系统接口,语言不具备跨平台性。因此操作系统不同,操作系统所提供的原生接口不同。那么封装是如何解决跨平台问题的呢?--- 穷举所有的底层接口+条件编译 (根据具体对象调用对应的系统调用接口) 比如你用的是Linux系统,C语言就会把win,MacOs等接口关闭,只露出Linux的接口。上层用户用的都是一个函数,但是其实底层做了封装。
  • 1.1 见一见系统接口

    1.1.1 open

    open是文件系统接口最重要的接口,没有之一。因此我们先来看看这个接口。

    第一个参数(const char* pathname):带路径的文件名

    第二个参数(flags):打开文件传递的选项(下面会重点介绍)

    第三个参数(mode):设置权限

    返回值:int , -1表示出现错误 (C语言FILE*)

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_13

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_14

    第一个参数介绍:

    带路径的文件名,如果只写文件名默认在当前路径下创建。

    第二个参数介绍:

    flags标志位有很多选项,其中这些选项都是宏。其中系统传递标记位时,是用位图结构来进行传递的。因此每一个宏标记只需要有一个比特位是1,并且有其他宏对应的值不能重叠。在这里我们讲自己写一个接口来验证一下

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_15

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_16

    #include <stdio.h>#define PRINT_A 0x1 // 0000 00001#define PRINT_B 0x2 // 0000 00010#define PRINT_C 0x4 // 0000 00100#define PRINT_D 0x8 // 0000 01000#define PRINT_DFL 0x0void Show(int flags){ if(flags & PRINT_A) printf("Hello A\n"); if(flags & PRINT_B) printf("Hello B\n"); if(flags & PRINT_C) printf("Hello C\n"); if(flags & PRINT_D) printf("Hello D\n"); if(flags == PRINT_DFL) printf("hello Default\n");}int main(){ printf("hello Default\n"); Show(PRINT_DFL); printf("Hello A\n"); Show(PRINT_A); printf("Hello B\n"); Show(PRINT_B); printf("PRINT_A和PRINT_B\n"); Show(PRINT_A | PRINT_B); printf("PRINT_C和PRINT_D\n"); Show(PRINT_C | PRINT_D); printf("PRINT_A和PRINT_B和PRINT_C和PRINT_D\n"); Show(PRINT_A | PRINT_B | PRINT_C | PRINT_D); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_17

    通过这个例子我们就更好的理解了open的第二个参数,介绍这两个参数后,我们就可以使用open系统函数了,我们也用C语言来实现一下

    #include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){ int fd = open("log.txt",O_WRONLY | O_CREAT); if(fd < 0 ) { perror("open"); return 1; } printf("fd: %d\n",fd); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_18

    第三个参数:

    通过打印值我们发现fd返回值确实是一个int型。而且我们发现log.txt文件的权限部分是一对乱码,原因是,当我们新建一个文件的时候,我们要设置文件的权限。因此我们得出一个结论,如果我们要创建一个不存在的文件,不能使用两个参数的open接口,而是要使用带有权限参数的open接口。我们重新创建一下log.txt,并设置权限位0666

    int main(){ int fd = open("log.txt",O_WRONLY | O_CREAT,0666); if(fd < 0 ) { perror("open"); return 1; } printf("fd: %d\n",fd); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_19

    此时我们发现权限比刚才的正常多了,确实不在是乱码现象了。但是0664,而不是0666。这是因为权限掩码是umask。默认的umask是0002.所以如果我们就想要权限是0666.我们手动设置umask为0即可。我们再来看看

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_系统调用_20

    1.1.2 close

    我们把文件打开了,总得关闭文件吧。因此我们来看看关闭文件的系统接口close.因此这个接口非常简单好用。

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_21

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_22

    1.1.3 write

    我们把文件打开和关闭了解了之后,接下来我们要对文件进行操作了。第一个我们要了解的是write。像文件内写多西。

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_23

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_文件操作_24

    第一个参数(int fd):

    fd,特定的文件描述符。也就是向那个文件写。

    第二个参数(const void* buf):

    写入缓冲区的起始地址

    第三个参数(count):

    写入缓冲区的大小

    我们也用C语言来练一练

    #include <stdio.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){ umask(0); //打开文件 int fd = open("log.txt",O_WRONLY | O_CREAT,0666); if(fd < 0 ) { perror("open"); return 1; } printf("fd: %d\n",fd); //对文件操作 int cnt = 0; const char *str = "hello file\n"; while(cnt<5) { write(fd,str,strlen(str)); cnt++; } //关闭文件 close(fd); return 0;}

    [ Linux ] 复习C文件IO相关操作及认识文件相关系统调用接口_当前路径_25

    我们看到成功的向log.txt文件写入了5条hello file

    至此我们已经见过了最基本的系统接口,后续我们还要对系统调用接口详细介绍使用

    (本篇完)

    上一篇:【MySQL高级】MySql中常用工具及Mysql 日志
    下一篇:没有了
    网友评论