Linux篇——文件操作与文件fd

1.当前路径

进程在启动的时候,自动记录自己启动时所在的路径就叫当前路径。

进程创建文件,如果只说明了文件名,那么操作系统是向当前路径进行创建。

1.当前路径怎么查看?

在/proc/路径下的对应进程pid的目录中,有一个cwd文件,指向了进程对应的当前工作目录。

2.如何改变当前路径?

使用系统调用

在这里插入图片描述

其中chdir函数的参数path是字符串,字符串的内容是指定的目录路径。

而fchdir的参数指定的是fd对应的文件目录,fd这个参数会在后面进行介绍。

2.C标准库的常用文件函数

FILE* fopen(const char* path,const char* mode);
int fclose(FILE* fp);
int fputs(const char* s,FILE* stream);
char* fgets(char* s,int size,FILE* stream);
size_t fread(void * ptr, size_t size, size_t nmemb, FILE* stream );
size_t fwrite(const void * ptr, size_t size, size_t nmemb, FILE* stream);

fopen:C语言封装的打开文件的函数,path是文件路径,mode是权限。

fclose:关闭文件,fp是文件对应的File指针

fputs:s是要输出的字符串,stream是要输出的文件对应的File指针,不会自动换行,换行需要自己加上\n

fgets:从stream对应的文件里读一个字符个数最多为size-1的字符串,遇到换行符会提前结束并在下一次读取的从下一行开始读,如果没读到\n不会自动换行,下次读的时候会继续接着上次读的结尾往后读

fread://二进制输入函数,从二进制文件中提取nmemb数量的大小为size的数据到ptr指向的空间里,返回值是实际读到的个数,<=nmemb

fwrite://二进制输出函数,ptr是要读取数据的地址,size是写入数据的大小(字节),nmemb是写几个size大小的数据,stream是写入文件对应的File*,输出的是二进制文件

3.什么是File

File是C语言封装的一个结构体,里面包含了文件的一些属性,其中就有我们之前提到的fd,还有哪些属性,这里不做描述。

4.文件描述符——fd

fd是操作系统管理文件所描述的一个整形变量。

操作系统要管理文件,必定要让文件先加载到内存,然后先描述,再组织,内核中要有描述对应文件的结构体——也就是struct file,和C语言的File不同。

struct file中最核心的数据可以分为3大类,一个是属性,一个是方法集,一个是缓冲区。

属性里面就有fd,而fd是干什么的呢,这就要和进程牵扯到一起,因为文件是由进程发起创建的。

进程的pcb(也就是task_struct)中有一个结构体指针struct file_struct* files,指向的结构体中有一个指针数组struct file* fd_array[],也就是存放了描述文件的结构体指针,指向了进程所打开文件的结构体,而fd就是该结构体在数组中的下标。

因此,显而易见,fd的作用就出来了,通过进程的指针struct file_struct* files找到该指针数组,再通过fd找到指定数组下标中的struct file,从而对文件进行操作。

文件描述符的分配规则

当进程打开时,操作系统默认会打开三个文件流

在这里插入图片描述

这三个文件流分别对应标准输入流、标准输出流、标准错误流,它们的File指向的File中都有对应的fd,因此,数组struct file fd_array[]的默认前三个下标0、1、2分别保存了这三个文件流对应的struct file*。

当进程打开文件时,会在struct file*数组中找到当前没有被使用的最小的下标,作为新的文件标识符。

如果在打开文件之前,把这个三个文件流关闭,那么就会自动分配到当前的最小下标。

5.管理文件的系统调用

C语言库函数的文件操作函数实际上都是封装了操作系统所给的系统调用,因此,弄懂了系统调用,任何语言的封装都能够明白。

在这里插入图片描述

1.open

OS提供了两种open系统调用,pathname也就是指定文件的文件路径,flags是打开文件选择的选项,mode是权限。

flags:

在C语言中,mode是可以是只读"r",只写"w",读写"rw"等等,这在底层中实际上就是选项的|,选项的|,相信很多人都一头雾水,选项还能|吗?

实际上,系统调用提供了很多选项

  1. O_RDONLY: 只读方式打开文件。
  2. O_WRONLY: 只写方式打开文件。
  3. O_RDWR: 读写方式打开文件。
  4. O_CREAT: 如果文件不存在则创建新文件。
  5. O_EXCL: 与 O_CREAT 同时使用,如果文件存在则报错。
  6. O_TRUNC: 如果文件存在且为写权限,则将其长度截断为0。
  7. O_APPEND: 在文件末尾追加数据而非覆盖原有内容。
  8. O_NONBLOCK: 非阻塞方式打开文件。
  9. O_SYNC: 同步写入数据,确保数据实际写入磁盘。
  10. O_DIRECTORY: 如果参数不是目录,则打开失败。
  11. O_NOFOLLOW: 如果参数是一个符号链接,则打开失败。
  12. O_DIRECT: 直接 I/O,绕过操作系统缓存。
  13. O_CLOEXEC: 执行exec调用时关闭文件描述符。
  14. O_TMPFILE: 创建一个匿名临时文件。

这些选项都是宏,一个int类型有32个bit位,通过bit位上的数字是否为1表示是否有这个选项,而这些选项都是一个bit位上是1,其余bit位上是0,因此|的时候,只有对应选项的带1的bit位是1,其余没选的都是0,这就达到了我们想要多个选项的效果,也就是数据结构里的位图。

mode:代表的是文件被创建时的读写权限,如果对文件的权限进行修改,也就是ugo(user、group、others)的读写执行权限,会受到umask掩码的影响,如果想修改umask,也可以调用系统调用。

在这里插入图片描述

2.close

在这里插入图片描述

关闭对应文件描述符fd的文件,也就是令struct file* fd_array[]对应下标fd指向空。

3.read

在这里插入图片描述

从文件描述符fd的文件中读取count个字节的数据到buf中。

4.write

在这里插入图片描述

向文件描述符fd的文件中写入buf中count(可能会低于count)个字节的数据。

6.操作系统怎么实现对不同的硬件进行读写

先描述再组织,每个不同的硬件,操作系统对应的读read、写write方法一定不相同。

通过创建对应硬件的struct file,有一个结构体指针变量struct file_operation*(方法集)指向的结构体中包含了对应文件的各种操作的函数指针,通过函数指针指向对应硬件的读写方法,转为了上层调用文件的读写方法,而文件的读写方法实际上就是方法集中所指向的不同硬件的读写方法,这样就可以屏蔽硬件之间的差异。

7.重定向的理解

当我们将stdout文件流关闭,即close(1),再打开一个文件时,根据分配规则,该文件会被分配到数组struct file* fd_array[]下标为1的位置,并printf函数打印内容,代码完成编译后,运行会发现,显示屏上并没有打印出内容,那么内容到了哪里呢?——实际上是打印到了文件中,实现了和重定向一样的功能。

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

在这里插入图片描述

所以,我们可以推测,重定向的实现,实际上是Shell对命令进行解释,然后再由底层对标准输出流进行关闭,而由于对文件操作要打开文件,就使得文件被分配到了标准输出流的下标上,输出只是对下标为1的文件进行输出,而并不关心下标为1的文件具体是什么。

  • 33
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Linux内核的文件操作函数可以帮助我们在内核态对系统中的文件进行读取、写入、打开、关闭等许多操作。下面介绍几个常用的文件操作函数及其使用案例。 1. open()函数 该函数用于打开文件,可以根据需要进行读写和创建等不同的操作。其函数原型如下: ``` #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); ``` flags参数用来指定打开文件时的模式,常用的模式包括: * O_RDONLY:只读模式 * O_WRONLY:只写模式 * O_RDWR:读写模式 * O_CREAT:文件不存在时创建文件 * O_APPEND:追加模式 mode参数用来指定创建文件时的权限,常用的权限包括: * S_IRUSR:用户具有读取权限 * S_IWUSR:用户具有写入权限 * S_IXUSR:用户具有执行权限 * S_IRGRP:组具有读取权限 * S_IWGRP:组具有写入权限 * S_IXGRP:组具有执行权限 * S_IROTH:其他用户具有读取权限 * S_IWOTH:其他用户具有写入权限 * S_IXOTH:其他用户具有执行权限 下面是一个使用open()函数打开并读取文件的例子: ``` #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { int fd, n; char buf[1024]; fd = open("test.txt", O_RDONLY); if (fd < 0) { perror("open"); exit(EXIT_FAILURE); } n = read(fd, buf, 1024); if (n < 0) { perror("read"); exit(EXIT_FAILURE); } printf("The content of test.txt is:\n%s", buf); close(fd); return 0; } ``` 2. read()函数 该函数用于从文件中读取数据。其函数原型如下: ``` #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); ``` fd参数是文件描述符,buf参数是读取数据的缓冲区,count参数是要读取的字节数。 下面是一个使用read()函数读取文件的例子: ``` #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { int fd, n; char buf[1024]; fd = open("test.txt", O_RDONLY); if (fd < 0) { perror("open"); exit(EXIT_FAILURE); } n = read(fd, buf, 1024); if (n < 0) { perror("read"); exit(EXIT_FAILURE); } printf("The content of test.txt is:\n%s", buf); close(fd); return 0; } ``` 3. write()函数 该函数用于将数据写入文件中。其函数原型如下: ``` #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); ``` fd参数是文件描述符,buf参数是要写入的数据的缓冲区,count参数是要写入的字节数。 下面是一个使用write()函数写入文件的例子: ``` #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { int fd, n; char buf[1024] = "This is a test for write() function.\n"; fd = open("test.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { perror("open"); exit(EXIT_FAILURE); } n = write(fd, buf, sizeof(buf)); if (n < 0) { perror("write"); exit(EXIT_FAILURE); } close(fd); return 0; } ``` 4. close()函数 该函数用于关闭文件。其函数原型如下: ``` #include <unistd.h> int close(int fd); ``` fd参数是文件描述符。 下面是一个使用close()函数关闭文件的例子: ``` #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { int fd, n; char buf[1024]; fd = open("test.txt", O_RDONLY); if (fd < 0) { perror("open"); exit(EXIT_FAILURE); } /* do something */ close(fd); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值