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


    文件描述符:

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

    目录

    0.复习文件操作(C语言)

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

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

    0.1.2 复习a选项

    0.1.3 复习w选项

    0.1.4 复习读操作

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

    1.1 见一见系统接口

    1.1.1 open

    1.1.2 close

    1.1.3 write


    0.复习文件操作(C语言)

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

    1. #include
    2. int main()
    3. {
    4. FILE *fp = fopen("log.txt","w");//写入
    5. if(fp == NULL)
    6. {
    7. perror("fopen");
    8. return 1;
    9. }
    10. const char* msg = "hello file";
    11. int cnt = 1;
    12. while(cnt < 20)
    13. {
    14. fprintf(fp,"%s:%d\n",msg,cnt++);
    15. }
    16. fclose(fp);
    17. return 0;
    18. }

    运行结果:

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

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

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

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. FILE *fp = fopen("log.txt","w");//写入
    7. if(fp == NULL)
    8. {
    9. perror("fopen");
    10. return 1;
    11. }
    12. printf("pid:%d\n",getpid());//获取当前进程的pid
    13. while(1)
    14. {
    15. sleep(1);
    16. }
    17. const char* msg = "hello file";
    18. int cnt = 1;
    19. while(cnt < 20)
    20. {
    21. fprintf(fp,"%s:%d\n",msg,cnt++);
    22. }
    23. fclose(fp);
    24. return 0;
    25. }

    使用指令查看

    ls /proc/pid值 -l
    

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

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

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

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. chdir("/home/Lxy");//更改当前进程的工作路径
    7. FILE *fp = fopen("log.txt","w");//写入
    8. if(fp == NULL)
    9. {
    10. perror("fopen");
    11. return 1;
    12. }
    13. printf("pid:%d\n",getpid());
    14. while(1)
    15. {
    16. sleep(1);
    17. }
    18. const char* msg = "hello file";
    19. int cnt = 1;
    20. while(cnt < 20)
    21. {
    22. fprintf(fp,"%s:%d\n",msg,cnt++);
    23. }
    24. fclose(fp);
    25. return 0;
    26. }

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

    ls /proc/pid值 -l
    

    0.1.2 复习a选项

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

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. FILE *fp = fopen("log.txt","a");//写入
    7. if(fp == NULL)
    8. {
    9. perror("fopen");
    10. return 1;
    11. }
    12. const char* msg = "hello file";
    13. int cnt = 1;
    14. while(cnt <= 5)
    15. {
    16. fprintf(fp,"%s:%d\n",msg,cnt++);
    17. }
    18. fclose(fp);
    19. return 0;
    20. }

    0.1.3 复习w选项

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

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. FILE *fp = fopen("log.txt","w");//写入
    7. if(fp == NULL)
    8. {
    9. perror("fopen");
    10. return 1;
    11. }
    12. fclose(fp);
    13. return 0;
    14. }

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

    0.1.4 复习读操作

    fgets

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

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. //chdir("/home/Lxy");//更改当前进程的工作路径
    7. FILE *fp = fopen("log.txt","r");//写入
    8. if(fp == NULL)
    9. {
    10. perror("fopen");
    11. return 1;
    12. }
    13. char buffer[64];
    14. while(fgets(buffer,sizeof(buffer),fp) != NULL)
    15. {
    16. printf("echo:%s",buffer);
    17. }
    18. fclose(fp);
    19. return 0;
    20. }

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

    1. #include
    2. #include
    3. #include
    4. int main(int argc,char* argv[])
    5. {
    6. if(argc != 2)
    7. {
    8. printf("Usage:%s filename\n",argv[0]);
    9. return 1;
    10. }
    11. FILE * fp = fopen(argv[1],"r");
    12. if(fp == NULL)
    13. {
    14. perror("fopen");
    15. return 1;
    16. }
    17. char buffer[64];
    18. while(fgets(buffer,sizeof(buffer),fp) != NULL)
    19. {
    20. printf("%s",buffer);
    21. }
    22. fclose(fp);
    23. return 0;
    24. }

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

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

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

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

    那么这里有两个问题?

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

    那么为什么要做封装?

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

    1.1 见一见系统接口

    1.1.1 open

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

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

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

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

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

    第一个参数介绍:

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

    第二个参数介绍:

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

    1. #include
    2. #define PRINT_A 0x1 // 0000 00001
    3. #define PRINT_B 0x2 // 0000 00010
    4. #define PRINT_C 0x4 // 0000 00100
    5. #define PRINT_D 0x8 // 0000 01000
    6. #define PRINT_DFL 0x0
    7. void Show(int flags)
    8. {
    9. if(flags & PRINT_A) printf("Hello A\n");
    10. if(flags & PRINT_B) printf("Hello B\n");
    11. if(flags & PRINT_C) printf("Hello C\n");
    12. if(flags & PRINT_D) printf("Hello D\n");
    13. if(flags == PRINT_DFL) printf("hello Default\n");
    14. }
    15. int main()
    16. {
    17. printf("hello Default\n");
    18. Show(PRINT_DFL);
    19. printf("Hello A\n");
    20. Show(PRINT_A);
    21. printf("Hello B\n");
    22. Show(PRINT_B);
    23. printf("PRINT_A和PRINT_B\n");
    24. Show(PRINT_A | PRINT_B);
    25. printf("PRINT_C和PRINT_D\n");
    26. Show(PRINT_C | PRINT_D);
    27. printf("PRINT_A和PRINT_B和PRINT_C和PRINT_D\n");
    28. Show(PRINT_A | PRINT_B | PRINT_C | PRINT_D);
    29. return 0;
    30. }

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main()
    7. {
    8. int fd = open("log.txt",O_WRONLY | O_CREAT);
    9. if(fd < 0 )
    10. {
    11. perror("open");
    12. return 1;
    13. }
    14. printf("fd: %d\n",fd);
    15. return 0;
    16. }

    第三个参数:

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

    1. int main()
    2. {
    3. int fd = open("log.txt",O_WRONLY | O_CREAT,0666);
    4. if(fd < 0 )
    5. {
    6. perror("open");
    7. return 1;
    8. }
    9. printf("fd: %d\n",fd);
    10. return 0;
    11. }

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

    1.1.2 close

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

    1.1.3 write

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

    第一个参数(int fd):fd,特定的文件描述符。也就是向那个文件写。

    第二个参数(const void* buf):写入缓冲区的起始地址

    第三个参数(count):写入缓冲区的大小

    我们也用C语言来练一练

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main()
    8. {
    9. umask(0);
    10. //打开文件
    11. int fd = open("log.txt",O_WRONLY | O_CREAT,0666);
    12. if(fd < 0 )
    13. {
    14. perror("open");
    15. return 1;
    16. }
    17. printf("fd: %d\n",fd);
    18. //对文件操作
    19. int cnt = 0;
    20. const char *str = "hello file\n";
    21. while(cnt<5)
    22. {
    23. write(fd,str,strlen(str));
    24. cnt++;
    25. }
    26. //关闭文件
    27. close(fd);
    28. return 0;
    29. }

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

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

    (本篇完)

  • 相关阅读:
    2023辽宁石油化工大学计算机考研信息汇总
    Excel 数据透视表教程大全之 02 添加字段、设置数据格式应用货币模式、按值进行排序(教程含样本数据)
    现货白银MACD实战分析例子
    博莱克威奇适时发布电子书,阐释澳大利亚采矿业超越可再生能源的脱碳之道
    不同数据类型在单片机内存中占多少字节?
    U-Boot与kernel之间的参数传递机制完全解析
    cesium影像推送
    设计模式概述
    XPath的使用
    SPI技术实现对比Java SPI、Spring SPI、Dubbo SPI
  • 原文地址:https://blog.csdn.net/qq_58325487/article/details/127695284