Linux - 基础IO

    xiaoxiao2023-11-13  144

    一.回顾c语言的接口

    1.向文件中写入数据(hello world)

    #include<stdio.h> #include<errno.h> #include<string.h> int main() { //FILE --->它是c语言中的一个文件指针(结构体) FILE * fp = fopen("log.txt","w"); if(!fp) { perror("fopen"); return 1; } const char* msg = "hello world\n"; int i = 0; for(; i < 10 ; ++i) { fwrite(msg,1,strlen(msg),fp); //第一个参数表示写入字符串的首地址,第二个参数是写入的基本单位是多少,第三个参数表示写入多少个 //第四个参数是给哪个文件写入。 } fclose(fp); }

    2.向显示器上打消息的方法 1)任何进程启动时,会默认打开三个输入输出流,stdin,stdout,stderr; 三种的io流的返回值都是FILE* 2)stdin :标准输入------》键盘 stdout:标准输出-------》显示器 stderr:标准错误--------》显示器 3)Linux下,一切皆文件。

    #include<stdio.h> int main() { const char * msg = "hello world\n"; fputs(msg,stdout); printf("%s\n",msg); fprintf(stdout , "%s\n" ,msg); }

    3.打开文件的方式 “r”:只能从文件中读数据,该文件必须先存在,否则打开失败 “w”:只能向文件写数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件 “a”:向文件增加新数据(不删除原有数据),若文件不存在则打开失败,打开时位置指针移到文件末尾 “r+”:可读/写数据,该文件必须先存在,否则打开失败 “w+”:可读/写数据,用该模式打开新建一个文件,先向该文件写数据,然后可读取该文件中的数据 “a+”:可读/写数据,原来的文件不被删去,位置指针移到文件末尾 fseek:操控文件的读写位置。 ftell:告诉当前写入的位置在哪里。 rewind:回到当前写入的最开始时。

    二.系统文件I/O

    1.操作文件的四个接口open,close,write,read, open

    int open(const char * pathname,int flags); //第一种通常打开已经存在的文件,flags就是告诉你打开文件想要干什么(读写); int open(const char * pathname,int flags, mode_t mode); //第二种如果文件不存在,则继续创建,mode告诉文件的权限是什么。 pathname: 要打开或创建的目标文件 flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。 参数: O_RDONLY: 只读打开 O_WRONLY: 只写打开 O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写 返回值: 成功:新打开的文件描述符 失败:-1

    write

    ssize_t write(int fd,const void * buf ,size_t count); //fd写入那个文件描述符,buf表示写入的缓冲区,写多少个字符。

    read

    ssize_t read(int fd,void * buf,size_t count) //fd读入的文件描述符,buf表示读到哪里,提供的缓冲区,第三个表示读多少个数据。 pathname:要打开或者要创建的目标文件。 flags:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量来进行“ | ” 运算,构成flags。 参数: O_RDONLY:只读打开。 O_WRONLY:只写打开。 O_RDWR:读,写打开。 这三个常量,必须指定一个,且只能指定一个。 O_CREAT:若文件不存在,则创建它,需要使用mode选项,用来指定新文件的访问权限。 O_APPEND:追加写 返回值: 成功:新打开的文件描述符。 失败: - 1; 向一个文件中写入数据 #include<stdio.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> int main() { umask(0);//将所写的文件权限清空 int fd = open("log.txt",O_WRONLY | O_CREAT,0644); if(fd < 0) { printf("open error"); return 0; } const char* msg = "hello world\n"; write(fd, msg , strlen(msg));//c++中不需要加1; close(fd); }

    三.文件描述符

    1. 概念0 & 1 & 2 Linux下的进程默认会打开三个文件描述符, 标准输入0, 标准输出1, 标准错误2, 0,1,2对应的物理设备一般是:键盘,显示器,显示器。 文件指针(FILE*)在文件描述符的上级;

    进程和文件的对应关系:一个进程可以打开多个文件。 而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件 重定向: 本来应该显示到显示器上的内容,当关闭显示器后,创建5个文件,那么5个文件的内容输出的话,全都输出到第一个文件中。 输出重定向的本质:先关闭1号文件,然后打开一个文件。 重定向只能把1号文件描述符内的文件内容重定向。 数据刷新方案缓冲方式: 1.无缓冲 2.行缓冲,显示器------》库函数 3.全缓冲,文件------》库函数

    int main() { const char * msg1 = "hello printf\n"; const char * msg2 = "hello fwrite\n"; const char * msg3 = "hello write\n"; printf(msg1); fwrite(msg2,1,strlen(msg2),stdout); write(1,msg3,strlen(msg3)); fork(); }

    显示器上一共打印了5个消息,printf和fwrite是多打印的。 多打印两个这种操作肯定是和fork()有关系的。 printf和fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。缓冲区的内容,并没有立即刷新出来,在调用fork()之后,进程退出,会统一刷新,写入文件中。fork()之后,创建子进程,代码共享,数据通过写时拷贝的方式各自独有一份。子进程就会有了同样的一份数据,即产生两份数据。 write时系统调用接口,没有缓冲区,库函数底层时系统调用。

    四.理解文件系统

    1.要把文件管好,就要把磁盘管好。 硬盘分为机械硬盘(HDD)和固态硬盘(SSD);

    当我们使用ls -l时除了看到文件名,还看到了文件元数据。 rwxr-xr-x. 1 root root 7493 “9月 13:15:56” a.out 属性-----硬链接数----文件所有者-----组----大小-----最后修改时间

    inode:表示文件的属性。 创建一个文件,就要有一个inode号;

    文件系统简单了解 Boot block--------Block group 0------------Block group n Boot block是操作系统的启动区,如果这块出现错误,磁盘就不能使用。 超级块----------------inode节点表--------------数据区 超级块中描述了inode的使用空间和剩余空间以及数据区的使用空间和剩余空间。

    inode bitmap:给inode节点表中分配格子。 block bitmap:给数据区分配格子。记录那个格子被占用,那个没有占用。 创建一个新文件主要的4个操作: 1.存储属性: 在内核中找到一个空闲的i节点,内核把文件信息记录到其中。 2.存储数据: 该文件需要储存几个磁盘块,就在内核中找到几个空闲的磁盘块,将内核缓冲区的内容复制上去。 3.记录分配情况 文件内容依次存放,内核在inode节点表中记录了上述块列表。 4.添加文件名到目录 新创建的文件名叫abc 内核将入口(263466,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性链接起来。 创建或者写文件: 在inode位图中找到一个空闲位图,将0置为1,并在对应的inode中写属性,其次将inode数据区中的空闲格子,由0置为1,并在数据区中写内容,最后将文件和映射关系放在目录中。 查文件 已知inode号为1234,其次在inode节点表中找1234,通过找到的inode位置找到数据区所在的位置,从第一个位置开始,读取文件大小个数据。 删除文件 找到文件的inode;inode位图清0,将数据位图请0;

    目录也是文件,维护好文件名和inode之间的映射关系。

    五.理解软硬链接

    文件名:link 硬链接: link-h 硬链接没有独立的inode; 我们看到,真正找到磁盘上文件的并不是文件名,而是inode。 在linux下可以让多个文件名对应于同一个inode; 就像是c++中的引用计数一样,当计数为0时,则删除文件。 软连接 link-s:里面放的是文件的路径。 软链接时通过名字引用另一个文件。 通过软连接可以直接从顶层目录指向底层的目录。

    面试题: 1.fopen和open的区别?

    fopen 系列是标准的C库函数;open系列是 POSIX 定义的,是UNIX系统里的system call。 也就是说,fopen系列更具有可移植性;而open系列只能用在 POSIX 的操作系统上。使用fopen 系列函数时要定义一个指代文件的对象,被称为“文件句柄”(file handler),是一个结构体;而open系列使用的是一个被称为“文件描述符” (file descriptor)的int型整数。fopen 系列是级别较高的I/O,读写时使用缓冲;而open系列相对低层,更接近操作系统,读写时没有缓冲。由于能更多地与操作系统打交道,open系列可以访问更改一些fopen系列无法访问的信息,如查看文件的读写权限。这些额外的功能通常因系统而异。使用fopen系列函数需要"#include <sdtio.h>";使用open系列函数需要"#include <fcntl.h>" ,链接时要之用libc(-lc)
    最新回复(0)