代码之家  ›  专栏  ›  技术社区  ›  merlin2011

对cgroup任务的写失败是确定的非持久性的吗?

  •  0
  • merlin2011  · 技术社区  · 6 年前

    考虑下面的程序。

    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    
    void
    setup() {
        system("mkdir /sys/fs/cgroup/cpuset/TestingCpuset");
        system("echo 0,1 > /sys/fs/cgroup/cpuset/TestingCpuset/cpuset.cpus");
        system("echo 0 > /sys/fs/cgroup/cpuset/TestingCpuset/cpuset.mems");
    }
    
    int
    main() {
        setup();
        // Picked to be the pid of a ordinary thread or process on the currently
        // running system.
        const char* validPid = "30100";
        const char* invalidPid = "2";
        const char* taskPath = "/sys/fs/cgroup/cpuset/TestingCpuset/tasks";
        int fd = open(taskPath, O_WRONLY);
        if (fd < 0) {
            fprintf(stderr, "Failed to open %s; errno %d: %s\n", taskPath, errno,
                    strerror(errno));
        }
        int retVal = write(fd, invalidPid, strlen(invalidPid));
        if (retVal < 0) {
            fprintf(stderr, "Invalid write of %s to fd %d; errno %d: %s\n",
                    invalidPid, fd, errno, strerror(errno));
        }
    
        retVal = write(fd, validPid, strlen(validPid));
        if (retVal < 0) {
            fprintf(stderr, "Invalid write of %s to fd %d; errno %d: %s\n",
                    validPid, fd, errno, strerror(errno));
        }
    }
    

    这个程序的输出(在 sudo )是:

    Invalid write of 2 to fd 3; errno 22: Invalid argument
    

    请注意 后续写入不会失败 ;第一次写入失败 没有引起下一篇文章的失败。

    这种缺乏故障持久性的情况是否具有确定性和可靠性?

    我看过写人页面,但它没有说明失败的持久性。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Nominal Animal    6 年前

    在Linux中,没有通常与文件描述符相关联的错误状态。请参阅下面的链接。

    但是,在我们继续之前,请不要使用 < 0 . 如果发生错误(对于 open() write() ,返回值为 -1 . 如果 写入() 成功,返回写入的字符数。即使对于sysfs写入,您也应该检查它。只有一个文件系统/内核bug(或bug“family”),其中 read() / 写入() 返回的负值不是 -1个 (实际上并没有指出错误,而是一个包装的无符号整数成功值,用于对普通文件进行非常大的写入),因此,内核现在将所有读/写限制为略小于2 GiB。如果每个人都用 <零 ,我们根本不会抓到。

    在我看来,最好稍微多疑一点,抓住意外的错误,而不是假设并可能默默地丢失数据。


    这种缺乏故障持久性的情况是否具有确定性和可靠性?

    对于下的内核伪文件 /sys/ ,答案是肯定的:每个写操作都被视为一个单独的操作。以前对同一描述符的读取或写入不会影响当前写入的结果。

    写入sysfs伪文件只需调用伪文件表示的可调参数的store()方法;请参阅 fs/sysfs/file.c:sysfs_kf_bin_write() . 根本没有状态记录。

    (我们可以讨论一个可调函数是否可以记录以前的赋值尝试,并在此基础上改变它的行为,但是假设Linus Torvalds不会故意让这种事情发生。) “飞” 一点也不。)


    一般来说,Linux内核不会在文件描述中存储任何错误状态。如果我们看看 fs/read_write.c:write() (寻找 SYSCALL_DEFINE3(write, ),我们可以看到 写入() 当前内核中的syscall调用 ksys_write() ,它验证描述符是否有效(返回 EBADF 否则出错),并调用 vfs_write() . (应注意,如果成功,则使用 file_pos_write() ;文件位置没有原子更新。因此,Linux中对同一文件描述符的多线程并发写入应该使用 pwrite() pwritev() 而不是 写入() ,以避免比赛窗口wrt。文件位置更新)。

    不管怎样, vfs_write() 做一些错误检查( EBADF公司 , EINVAL , EFAULT )还有簿记和电话 __vfs_write() ,它是一个包装函数,调用适当的特定于文件系统的函数 file->fop->write() file->fop->write_iter() .

    (我们也可以看看 fs/file_table.c 关于Linux内核如何管理其内部文件描述符表(每个用户空间进程), include/linux/fdtable.h:struct fdtable 对于描述符表本身,在 include/linux/fs.h:struct file Linux文件描述的定义。这些结构中根本没有与“错误状态”相关的成员。但是,注意 f_op 成员 struct file struct file_operations 结构,它包含与此特定打开文件相关的基本文件操作的每个文件系统处理程序(请参见 include/linux/fs.h:struct file_operations ).)

    (注意,在Linux中,syscalls返回一个整数。对于错误条件,此整数包含 消极的 错误号。考虑零值和正值 成功 . C库维护 errno 完全在用户空间。如果你使用 syscall() ,您需要检测错误条件并有选择地维护 厄尔诺 你自己需要的。所以,当你看到一个内核系统调用返回 -EINVAL ,表示返回错误 英瓦尔 到用户空间。C库负责将其转换为 -1个 具有 errno == EINVAL .)

    同样,描述符中不会记录任何错误状态,并且每个操作都是单独发生的,与以前的操作没有关联(除了文件位置,在编写本文时,文件位置本身没有原子更新)。一些文件系统 能够 理论上,要跟踪操作,并保持与描述符相关联的内部错误状态,但是,再次声明,除非这是其他实现所遵循的文件系统的一个有案可稽的特性,否则Linux内核开发人员不太可能真的允许这样的事情发生。


    重要的是要认识到Linux内核开发人员必须遵循两个关键原则(因为Linus强制这样做):公共内核接口(syscalls,/proc和/sys pseudofiles)在内核版本之间是稳定和兼容的(请参见 this LKML message )理智的实践胜过理论,即使有某种标准要求。例如,请参见 Torvalds' Wikiquotes ,或他在 Linux Kernel mailing list (马克信息镜; lkml.org here ).

    我之所以相信他的意见,正如他自己所说, “因为他们知道他们不必” . 我(试着)自己做,这就是为什么这个答案希望包含足够的参考,以便您可以验证自己。

        2
  •  0
  •   codegrep_admin    6 年前

    写系统调用可能会失败,原因有很多——状态上没有任何持久性。