文件IO_打开和关闭文件(附Linux-5.15.10内核源码分析)

目录

1.打开文件

1.1 函数原型介绍

1.1.1 open函数

1.1.2 creat函数

1.1.2 openat函数

1.2 内核源码分析

1.3 函数原型区别

2.关闭文件

2.1 函数原型介绍

2.1.1 close函数

2.2 内核源码实现


1.打开文件

1.1 函数原型介绍

1.1.1 open函数

#include 
#include 
#include 

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

函数简介:open函数用于打开一个文件,并返回一个文件描述符,应用程序可以通过文件描述符对文件进行读写等操作。open函数可以打开已经存在的文件,也可以创建一个新文件。

函数参数:

pathname:是文件的路径名。

flags:打开文件时的标志,可以设置为以下值之一或多个值的按位或:

  • O_RDONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读写打开
  • O_CREAT:如果文件不存在,则创建文件
  • O_TRUNC:如果文件存在且以写方式打开,则截断文件为零长度
  • O_APPEND:以追加方式打开文件

mode:只有在O_CREAT标志被设置时才有效,用于指定文件的访问权限,可以设置为以下值之一:

#define S_IRWXU 00700 //用户可读,可写,可执行
#define S_IRUSR 00400 //用户可读
#define S_IWUSR 00200 //用户可写
#define S_IXUSR 00100 //用户可执行

#define S_IRWXG 00070 //组可读,可写,可执行
#define S_IRGRP 00040 //组可读
#define S_IWGRP 00020 //组可写
#define S_IXGRP 00010 //组可执行

#define S_IRWXO 00007 //其他用户可读,可写,可执行
#define S_IROTH 00004 //其他用户可读
#define S_IWOTH 00002 //其他用户可写
#define S_IXOTH 00001 //其他用户可执行

mode可以通过八进制赋值,例如设置为0777,0777表示所有权限按位或。

需要注意的是mode需要与系统umask值共同作用才能得到最终的文件权限,umask作用去掉哪些权限,比如umask值为0022(八进制),标识去掉S_IWGRP和S_IWOTH权限。

文件最终权限计算方法为: mode &~ umask.

举例说明:

mode设置为0777,umask值为0022,文件最终权限为0755。

函数返回值:

成功:返回文件描述符,文件描述符为非负整数。

失败:返回-1,并设置errno。

示例代码

#define TEST_FILE "/tmp/test.txt"

int open_test() {
    int fd = open(TEST_FILE, O_RDWR | O_CREAT | O_TRUNC, 0777);
    if (fd == -1) {
        perror("open error");
        return -1;
    }
    return 0;
}

1.1.2 creat函数

#include 
#include 
#include 

int creat(const char *pathname, mode_t mode);

函数简介:creat函数用于创建并打开一个新文件,并返回一个文件描述符,如果文件已经存在,则会将其截断为0字节,应用程序可以通过文件描述符对文件进行读写等操作。

函数参数:

pathname:文件路径名。

mode:是新文件的权限(参考open函数)。

函数返回值:

成功:返回文件描述符,文件描述符为非负整数。

失败:返回-1,并设置errno。

示例代码:

int creat_test() {
    int fd = creat(TEST_FILE, 0644);
    if (fd == -1) {
        perror("creat error");
        return -1;
    }
    return 0;
}

1.1.2 openat函数

#include 
#include 
#include 

int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);

函数简介:openat函数用于在指定目录下打开文件并返回文件描述符。它与open函数类似,但可以指定相对路径来打开文件,而不需要提供完整的文件路径。

函数参数:

dirfd:已打开的文件目录描述符。

pathname:是指定文件名的字符串。

flags:打开文件时的标志(参考open函数)。

mode:是新文件的权限(参考open函数)。

openat函数dirfd和pathname参数组合关系:

  • 组合1:pathname为绝对路径时,按绝对路径方式打开文件,dirfd失效。
  • 组合2:pathname为相对路径时,由dirfd(文件目录)和pathname(相对路径)共同决定打开的文件名。
  • 组合3:pathname为相对路径时,如果dirfd设置为AT_FDCWD(当前工作目录),由dirfd(当前工作目录)和pathname(相对路径)共同决定打开的文件名。

函数返回值:

成功:返回文件描述符,文件描述符为非负整数。

失败:返回-1,并设置errno。

示例代码:

#define DIR_PATH "/tmp"
#define FILE "test.txt"
int openat_test() {
    int dirfd = open(DIR_PATH, O_RDONLY);
    if (dirfd == -1) {
        perror("open dir error");
        return -1;
    }

    int fd = openat(dirfd, FILE, O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("openat error");
        return -1;
    }
    return 0;
}

1.2 内核源码分析

文件IO_打开和关闭文件(附Linux-5.15.10内核源码分析)_第1张图片

图 1-1 open函数内核源码分析

open,creat,openat通过系统调用再调用一个共同的函数do_sys_open函数,do_sys_open函数有四个参数:dfd,path,flags,mode。这四个参数对应openat函数原型四个参数。

open函数没有dfd参数,所以在调用do_sys_open函数之前dfd设置为默认值AT_FDCWD(当前工作目录)。

creat函数没有dfd参数和flags参数,所以在调用do_sys_open函数之前dfd设置为默认值AT_FDCWD(当前工作目录),以及flags设置为默认值O_WRONLY | O_CREAT | O_TRUNC,creat因为flags默认值的关系,所以只能新建文件或者截断文件长度至0来打开文件。

openat函数需要传递四个参数至do_sys_open函数。

打开文件描述符系列函数源码调用路径如图,这里就不再赘述。

1.3 函数原型区别

 (1)open函数和creat函数区别

  • 区别1:open函数可以打开已存在的文件,也可以创建新文件,而creat函数只能创建新文件。如果文件已存在,creat函数会将其截断为零长度。
  • 区别2:open函数的标志参数可以更加灵活地指定文件的打开方式,包括只读、只写、读写、追加等选项。而creat函数只能以写方式打开文件,并且具有固定的标志(O_WRONLY | O_CREAT | O_TRUNC)。

(2)open函数和openat函数区别

  • 区别1:open函数接受一个文件路径名作为参数,该路径名可以是绝对路径或相对路径。 openat函数需要传递一个已打开的目录文件描述符(dirfd)和一个相对于该目录的路径名作为参数。这种方式可以更加灵活地控制文件的打开位置。
  • 区别2:目录解析方式: open函数的文件路径名会根据当前进程的工作目录进行解析。 openat函数的路径名是相对于提供的目录文件描述符(dirfd)进行解析。
  • 区别3:安全性考虑: open函数在解析文件路径时,依赖于进程的当前工作目录。这可能会导致安全性问题,特别是在多线程环境下,因为当前工作目录是共享的。 openat函数提供了更安全的方式,可以避免依赖于进程的当前工作目录,而是通过提供目录文件描述符来指定相对路径。

2.关闭文件

2.1 函数原型介绍

2.1.1 close函数

#include 

int close(int fd);

函数简介:close函数的作用是关闭文件描述符并释放相关资源。

函数参数:

fd:表示需要关闭的文件描述符。

函数返回值:

成功:返回0.

失败:返回-1,并设置errno,一些常见的错误码包括:

  • EBADF:无效的文件描述符,即文件描述符未打开或已经关闭。
  • EINTR:操作被信号中断。
  • EIO:I/O错误。

2.2 内核源码实现

文件IO_打开和关闭文件(附Linux-5.15.10内核源码分析)_第2张图片

图 2-1 close函数内核源码分析

 close函数通过系统调用sys_close函数进入内核空间,close通过fd找到struct file对象并清除和释放该对象,struct file的f_op成员会根据文件系统和文件类型不一样而设置不同的值,关闭文件时,也会调用f_op对应的一些具体函数清除资源,典型的情况如socket关闭,socket关闭需要完成三次握手,需要通过f_op->release函数实现。

你可能感兴趣的:(Linux环境编程,linux,c语言,物联网,ubuntu,开源)