• Linux 基础IO


    1.在系统角度理解文件

    文件=内容+属性

    1.1文件的所有操作:1.对内容操作  2.对属性操作;

    C/C++程序,会默认打开三个文件流:标准输入、标准输出、标准错误;

    Linux下一切皆文件

    键盘、显示器可以被看作文件,但是我从来没有打开过键盘和显示器,依旧能够进行scanf、fgets、printf、cout………(映照了C/C++程序,会默认打开三个文件流:标准输入、标准输出、标准错误;)

    标准输入(stdin  默认设备键盘)、标准输出(stdout、默认设备显示器)、标准错误(stderr、默认设备显示器);

    2. 文件在磁盘(硬件)上放着,我们访问文件,先写代码->编译->exe->运行->访问文件:本质是谁在访问文件呢    答:进程在访问文件(进程是需要接口的)

    2.1需要向硬件写入,只有操作系统有权利;普通用户也想写入,就必须让OS提供文件类的系统接口(系统接口比较难,所以语言上对这些接口做了封装,为了让接口更好的使用;导致了不同的语言,有了不同语言级别的文件访问接口(都不一样)但是,封装的系统接口是一样的)

    2.2跨平台问题:基本上所有的语言都是跨平台的;如果语言不提供对文件的系统接口的封装,是不是所有的访问文件的操作,都必须直接使用OS的接口;答:是的

    一旦使用系统接口,编写所谓的文件代码,就无法在其他平台上直接运行了,是不具备跨平台性的;语言级别上把所有平台的代码,都实现一遍,再条件编译,动态裁剪;来实现跨平台;

    3.显示器是硬件吗?为什么往显示器上打印,不觉得奇怪?

    其实,本质上也是一种写入,和往磁盘写入文件没有本质区别,只不过能直观看到罢了

    4.文件:站在系统的角度,能够被input读取,或者output写出的设备叫做文件;

    5.复习C语言所学的文件操作

    5.1当每一个进程运行起来的时候,每个进程都会记录自己所处的工作路径;

    5.2当前路径是怎样形成的:进程会使用cwd(current work directory)在底层做一个拼接,拼接想要创建(或打开的)文件名,形成当前路径

    1. #include
    2. #include
    3. int main(int argc,char* argv[]) //如果此时把这个文件改成mycat,就变成了一个命令行参数,打印其中的内容;
    4. {
    5. if (argc != 2)
    6. {
    7. printf("argv error!\n");
    8. return 1;
    9. }
    10. //FILE* fp = fopen("log.txt", "w"); //"w" 先把文件清空,再写入;
    11. 第一个参数可以带路径,不带路径就是当前路径 //第二个参数为要进行的方式“r”"w" "a"等
    12. //if (fp == NULL)
    13. //{
    14. // perror("fopen");//当返回失败时,打印函数的错误信息;
    15. // return 2;
    16. //}
    17. 进行操作;
    18. //const char* s1 = "hello fwrite\n";//要不要把\0写入(要不要+1) 答:不加1,\0结尾是c语言的规定,文件是不需要遵守的
    19. // //文件要保存的是有效数据 /0只是标定结尾的标识符,不算有效数据;不是字符串有效的内容
    20. //fwrite(s1,strlen(s1),1, fp);
    21. //const char* s2 = "hello fprintf\n";
    22. //fprintf(fp,"%s", s2);
    23. //const char* s3 = "hello fputs\n";
    24. //fputs(s3, fp);
    25. //fclose(fp);
    26. //FILE* fp = fopen("log.txt", "a"); //append 追加;不断往里面追加内容;
    27. //if (fp == NULL)
    28. //{
    29. // perror("fopen");
    30. // return 1;
    31. //}
    32. 进行操作;
    33. //const char* s1 = "hello fwrite\n";
    34. //fwrite(s1, strlen(s1), 1, fp);
    35. //const char* s2 = "hello fprintf\n";
    36. //fprintf(fp, "%s", s2);
    37. //const char* s3 = "hello fputs\n";
    38. //fputs(s3, fp);
    39. //fclose(fp);
    40. FILE* fp = fopen("argv[1]", "r"); //读
    41. if (fp == NULL)
    42. {
    43. perror("fopen");
    44. return 1;
    45. }
    46. //按行读取;
    47. char line[64];
    48. //fgets 是C语言提供的接口,s是string的缩写,会自动在字符结尾添加\0
    49. while (fgets(line, sizeof line - 1, fp) != NULL)
    50. {
    51. //printf("%s\n", line);
    52. fprintf(stdout, "%s", line); //stdout是啥;
    53. }
    54. fclose(fp);
    55. return 0;
    56. }

    5.3复习C语言

    fopen 以w方式打开文件,默认先清空文件(注意,在fwrite之前,就清空了)

    fopen 以a方式打开文件,追加,不断向文件中新增内容;

    演示了文件读和写的一般操作;

    5.4 三个标准输入,输出流是什么? 答:标准输入对应的设备是键盘;标准输出对应的设备是显示器;标准错误对应的设备是显示器;

    6.学习系统接口

    6.1 open\close \read \write

    6.2引子:

    1. #include<stdio.h>
    2. //#include<unistd.h>
    3. #include<string.h>
    4. #define ONE 0x1 //0000 0001
    5. #define TWO 0X2 //0000 0010
    6. #define THREE 0x4 //0000 0100
    7. void show(int flags)
    8. {
    9. if(flags & ONE) printf("hello one\n");
    10. if(flags & TWO) printf("hello two\n");
    11. if(flags & THREE) printf("hello three\n");
    12. }
    13. 使用类似位图的方式,来解决此问题
    14. int main()
    15. {
    16. show(ONE);
    17. show(TWO);
    18. show(ONE | TWO);
    19. show(ONE | TWO | THREE);
    20. return 0;
    21. }

    6.3 open函数

    1. int open(const char *pathname, int flags);
    2. 由此flags的内部,为啥是各种宏定义就说明白了
    3. 返回值是int类型——返回的是 file descriptor;失败了返回-1

    file descriptor  ——文件描述符;

    这几个常量被定义在头文件fcntl.h中

    fcntl 是 "file control" 的缩写。它是由 "file"(文件)和 "control"(控制)两个单词组合而成的。

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

    在介绍这几个常量之前,要先介绍一下open函数,因为这几个常量是用在open函数里面的

    其中 pathname 是要打开的文件的路径名,

    flags 用于指定打开文件的方式和行为,上面这几个常量就是用在这里的

    mode 是一个 mode_t 类型的参数,用于指定创建新文件时的权限

    介绍这几个常量:
    O_CREAT:在文件打开过程中创建新文件
    O_RDONLY:以只读方式打开文件。
    O_WRONLY:以只写方式打开文件。
    O_RDWR:以读写方式打开文件。
    O_APPEND:在文件末尾追加数据,而不是覆盖现有内容。
    O_TRUNC:如果文件已经存在,将其里面内容全部删干净。
    O_EXCL:与 O_CREAT 一起使用时,如果文件已经存在,则 open() 调用将失败。
    O_SYNC:使文件写操作变为同步写入,即将数据立即写入磁盘。
    O_NONBLOCK:以非阻塞方式打开文件,即使无法立即进行读写操作也不会被阻塞。

    //在应用层看到的一个很简单的动作,在系统接口层面甚至是OS层面,可能要做非常多的动作;   

    1. #include<sys/types.h>
    2. #include<sys/stat.h>
    3. #include<fcntl.h>
    4. #include<stdio.h>
    5. #include<string.h>
    6. int main()
    7. {
    8. umask(0); //设置系统权限;
    9. //int fd = open("log.txt", O_WRONLY|O_CREAT,666); //三个参数,专门用来创建的
    10. //如果文件已经存在,那就只打开就行;
    11. int fd = open("log.txt", O_RDONLY);
    12. if (fd < 0)
    13. {
    14. perror("open");
    15. return 1;
    16. }
    17. //打开成功
    18. printf("open sucess,fd:%d\n", fd);
    19. //const char* s = "hello write\n";
    20. //write(fd, s, strlen(s));
    21. char buffer[64];
    22. memset(buffer, '\0', sizeof(buffer));
    23. read(fd, buffer, sizeof(buffer));
    24. printf("%s", buffer);
    25. close(fd);
    26. return 0;
    27. }

    7.分析系统接口的细节,引入fd(文件描述符) ——理论为主;

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main()
    7. {
    8. int fd1 = open("log1.txt", O_WRONLY | O_CREAT, 666);
    9. printf("open sucess fd: %d\n", fd);
    10. int fd2 = open("log2.txt", O_WRONLY | O_CREAT, 666);
    11. printf("open sucess fd: %d\n", fd);
    12. int fd3 = open("log3.txt", O_WRONLY | O_CREAT, 666);
    13. printf("open sucess fd: %d\n", fd);
    14. int fd4 = open("log4.txt", O_WRONLY | O_CREAT, 666);
    15. printf("open sucess fd: %d\n", fd);
    16. close(fd1);
    17. close(fd2);
    18. close(fd3);
    19. close(fd4);
    20. //验证三个标准流
    21. fprintf(stdout, "hello stdout\n"); //输出
    22. const char*s= "hello 1\n";
    23. write(1, s, strlen(s));
    24. int a = 10;
    25. fscanf(stdin, "%d", &a);
    26. printf("%d\n", a);
    27. char input[16];
    28. ssize_t s = read(0, input, sizeof(input));
    29. if (s > 0)
    30. {
    31. input[s] = '\0';
    32. printf("%s\n", input);
    33. }
    34. //证明
    35. printf("stdin:%d\n", stdin->_fileno);
    36. printf("stdout:%d\n", stdout->_fileno);
    37. printf("stderr:%d\n", stderr->_fileno);
    38. return 0;
    39. }

      以上代码运行后,会产生几个问题?

    7.1  0,1,2    去了哪里  答:C语言默认打开三个流  stdin(0)、stdout(1)、stderr(2)——默认是FILE*类型;—>内部绝对有fd—>怎样证明?

    FILE 是一个结构体类型;C语言设计提供的结构体;结构体一般内部会有各种成员

    C文件 库函数内部,一定会调用系统调用;——在系统角度认FILE?还是fd?  答:系统只认fd!所以FILE结构体里,必定封装了fd;

    7.2 为什么是这样的数据      

    8.1进程要访问文件,必须要打开文件;一个进程可以打开多个文件;一般而言,进程:打开的文件 = 1:n;文件要被访问,前提是加载到内存中,才能被访问!如果是多个进程都打开自己的文件,系统中就会存在大量的被打开的文件!所以OS必须会把如此之多的文件管理起来(先描述,在组织;在内核中,如何看待打开的文件?OS内部要为了管理每一个被打开的文件,会构建 struct file结构体对象,充当一个被打开的文件;结构体中包含了一个被打开文件的几乎所有内容(不仅仅包含属性);)如果有很多,再用双链表组织起来;

    8.2进程和文件的对应关系  答:PCB中有一个struct file* array[32] 指针数组,他指向了结构体struct file结构体对象;利用了哈希索引的原理,进程找文件变成了 PCB找数组,数组找结构体struct file对象,再进行操作;(Linux中PCB(task_struct进程控制块里存在一个结构体指针    struct files_struct* files 里面的变量有一个指针数组,指向了struct file结构体对象(那个文件)))

    8.3 fd在内核层面上,本质上就是一个数组下标;可以看源代码来证明;

    9.未来,如果是多平台跑,肯定是要使用语言级别的文件调用;但是学习系统级别的调用,可以帮助我们更好的理解这个语言级别的调用接口,更多的是为网络调用打好基础;因为网络调用时需要系统级别的文件调用;C语言/C++是没有网络接口的;

    10文件:磁盘文件(没有被打开)内存文件(被进程在内存中打开)

  • 相关阅读:
    网页自适应方案
    SpringBoot知识点整理
    stm32f4xx-时钟系统
    Docker与VM虚拟机的区别以及Docker的特点
    基于JavaWEB+MySQL的学生成绩综合管理系统
    【业务架构】什么是价值实现——转型、项目和领导力
    如何让 WPF 程序更好地适配 UI 自动化
    第三十八章 构建数据库应用程序 - 处理表单提交请求
    OSPF虚拟链路以及选路
    (附源码)ssmJavaEE无人机数据管理系统 毕业设计 111022
  • 原文地址:https://blog.csdn.net/qincjun/article/details/139565586