• 【虚拟文件系统】文件系统 API 解读(1)


             Linux 是近年来发展起来的一种新型的操作系统,其最重要的特征之一就是支持多种文件系统,使其更加灵活,从而与许多其它的操作系统共存。Linux支持ext,ext2,xia,minix,umsdos,msdes,fat32 ,ntfs,proc,stub,ncp,hpfs,affs 以及 ufs 等多种文件系统。为了实现这一目的,Linux 对所有的文件系统采用统一的文件界面,用户通过文件的操作界面来实现对不同文件系统的操作。对于用户来说,我们不要去关心不同文件系统的具体操作过程,而只是对一个虚拟的文件操作界面来进行操作,这个操作界面就是 Linux 的虚拟文件系统(VFS ) 。

            本文讲述的虚拟文件系统 API 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解文件系统操作。


    open(2)

    open()

    creat()

    openat()    遵循 POSIX.1-2008

    openat2(2) 遵循 Linux 标准

    1.库

    标准 c 库,libc, -lc

    2.头文件

    3.接口定义

    1. int open(const char *pathname, int flags);
    2. int open(const char *pathname, int flags, mode_t mode);
    3. int creat(const char *pathname, mode_t mode);
    4. int openat(int dirfd, const char *pathname, int flags);
    5. int openat(int dirfd, const char *pathname, int flags, mode_t mode);
    6. /* Documented separately, in openat2(2): */
    7. int openat2(int dirfd, const char *pathname,
    8. const struct open_how *how, size_t size);

            openat() 依赖的宏:

            glibc 2.10 后,_POSIX_C_SOURCE >= 200809L

            glibc 2.10 前,_ATFILE_SOURCE 

    4.接口描述

           open() 系统调用用来打开 pathname 指定的文件。如果指定的文件不存在,open() 有可能会创建该文件(flags 标记中指定了 O_CREAT)。

            open() 的返回值是一个文件描述符,它是一个指向进程打开文件描述符符表入口的小非负数。文件描述符会在接下来的各种操作(read(2)、write(2)、lseek(2)、fcntl(2) 等)种用来指向这个打开的文件。返回的文件描述符是当前进程中没有打开的描述符号码最小的那个。

            默认情况下,文件描述符被设置为在 execve(2) 前后保持打开(fcntl(2) 中描述的 FD_CLOEXEC 标记初始被设置为禁止),不过可以通过下面的 O_CLOEXEC 标记来修改这个设置。文件偏移值被设置为文件的开始(参考 lseek(2))。

            open() 调用会创建一个新的打开文件描述(open file description),系统打开文件表中的条目(entry)。打开文件描述记录文件的偏移以及文件状态标记(参考下面)。一个文件描述符是一个打开文件描述的索引,这个索引不会受到 pathname 删除或者指向另一个文件影响,更多关于打开文件描述参考后面注意章节。

            flags 参数必须包含下面访问模式中的一种:O_RDONLY、O_WRONLY、O_RDWR。这些请求以只读、只写或者读写方式打开相应文件。

            此外,可以在 flags 里指定文件创建标记或者文件状态标记,每个标记进行位或。文件创建标记包括:O_CLOEXEC、O_CREAT、O_DIRECTORY、O_EXCL、O_NOCTTY、O_NOFOLOOW、O_TMPFILE 和 O_TRUNC。文件状态标记包括下面列表中所有其他标记。这两组标记的区别是创建标记影响打开操作本身的语义,而文件状态标记影响接下来所有 I/O 操作。文件状态标记能够通过 fcntl(2) 得到或者修改(一些情况下)。

            所有文件创建标记和文件状态标记如下:

            O_APPEND

            文件以追加模式打开。在每次 write(2) 前,文件偏移都是被指定到文件的末尾的,和 lseek(2) 一样。文件偏移的修改以及写操作以单原子步骤进行。

            在 NFS 文件系统中,如果多个进程向同一个文件通过 O_APPEND 追加数据,那么有可能会导致文件破坏。因为 NFS 不支持向文件追加数据,所以内核就需要模拟,而这种模拟只有在存在数据竞争情况下才能实现。

            O_ASYNC

            开启信号驱动的 I/O 操作:当文件描述符输入

    输出数据可用时生成一个信号(默认是 SIGIO,但是可以通过 fcntl(2) 修改)。这个特性只在终端、伪终端、套接字以及管道、FIFO (Linux 2.6 后)有效。参考 fcntl(2) 查阅更多内容,也可以查看后面 BUGS 章节。

            O_CLOEXEC(Linux 2.6.23 后)

            使能新文件描述符的执行关闭标记(close-on-exec),设置这个标记省去了通过 fcntl(2) F_SETFD 操作来设置 FD_CLOEXEC 标记。

            多线程场景下这个标记是很常用的,比如一个线程通过 fcntl(2) F_SETFD 设置 FD_CLOEXEC 标记而另一个线程打开了文件描述符并且通过 fork(2) 和 execve(2) 传教子进程,这就可能会存在竞争问题。根据执行的不同顺序,新打开的文件描述符可能会无意的泄露给 fork(2) 创建的子进程。

            O_CREAT

            如果 pathname 不存在,以常规文件创建它。

            新文件的所有者(用户 ID)是进程的有效用户 ID。

            新文件的用户组 ID 是进程的有效组 ID (System V 语义)或者父目录的组 ID(BSD 语义)。Linux 上主要依赖父目录的设置组 ID 模式位是否被设置:如果设置了,那么就适用 BSD 语义,否则适用于 System V 语义。对于一些文件系统,行为依赖于 mount(8) 中描述的 bsdgroups 和 sysvgroups 的挂载选项

            mode 参数指定应用在新创建文件上的文件模式位。如果 O_CREAT 和 O_TMPFILE 都没有指定,那么 mode 参数会被忽略。对于 这两个选项必须提供 mode 参数,如果没有提供,那么就会使用栈上对应的随机值用作模式(很危险了)。

            有效模式通过进程的 umask 修改:在没有 ACL 访问控制表的情况下,创建文件的模式为 mode&~umask。

            下面这些符号常量可以用于 mode:

            S_IRWXU        00700 用户(文件拥有者)具有读写以及执行权限

            S_IRUSR        00400 用户具有读权限

            S_IWUSR       00200 用户具有写权限

            S_IXUSR        00100 用户具有执行权限

            S_IRWXG       00070 用户组具有读写和执行权限

            S_IRGRP        00040 用户组具有读权限

            S_IWGRP       00020 用户组具有写权限

            S_IXGRP        00010 用户组具有执行权限

            S_IRWXO       00007 其他人具有读写执行权限

            S_IROTH        00004 其他人具有读权限

            S_IWOTH       00002 其他人具有写权限

            S_IXOTH        00001 其他人具有执行权限

            根据 POSIX 要求,mode 中如果指定其他位,那么行为是未定义的。Linux 上也可以设置下面这些位:

            S_ISUID        0004000 设置用户 ID 位

            S_ISGID        0002000 设置用户组 ID (参考 inode(7))

            S_ISVTX       0001000 黏贴位(参考 inode(7))

    5.返回值

           成功时,open()、openat()、creat() 会返回一个新的文件描述符(一个非负数)。错误时返回 -1,并设置 errno 提示相应的错误。 

    6.注意

           在 Linux 上,O_NOBLOCK 标记有时也有一些特殊用途,比如指向打开一个文件而不会对其读写。比如,可以使用这个来打开一个设备的文件描述符,后面会使用 ioctl(2) 来操作设备。

    7.代码示例

    1. // C program to illustrate
    2. // open system call
    3. #include
    4. #include
    5. #include
    6. #include
    7. extern int errno;
    8. int main()
    9. {
    10. // if file does not have in directory
    11. // then file foo.txt is created.
    12. int fd = open("foo.txt", O_RDONLY | O_CREAT);
    13. printf("fd = %d\n", fd);
    14. if (fd == -1) {
    15. // print which type of error have in a code
    16. printf("Error Number % d\n", errno);
    17. // print program detail "Success or failure"
    18. perror("Program");
    19. }
    20. return 0;
    21. }
  • 相关阅读:
    如何快速通过CMMI认证?
    手把手带你学python自动化测试(九)——Python 的 unittest 框架
    小白终于解决了在学习Go中不知道Makefile是什么的难题
    重庆自考本科难考吗?怎样降低难度?
    从单个/两个向量构建一组正交基底
    2022 年-Q2
    是什么让 NFT 项目成为“蓝筹”?
    MyBatis实现动态SQL更新
    简单软光栅实现
    VTK 基础入门 ( 一 ) 相机设置
  • 原文地址:https://blog.csdn.net/BillyThe/article/details/133885284