系统调用return的值是如何传回给用户进程的?

How is system call return value passed back to user process?

假设我们有一个单核 cpu 运行

int filedesc = open("foo.txt", O_RDONLY);

filedesc是用户进程中的变量,当open开始执行时cpu获取上下文切换并运行内核进程,return的值如何open 被传递给 filedesc?

此外,与

相比
FILE *file = fopen("foo.txt", "r");
由于缓冲,

read/file 和 fopen 快得多,但在幕后它调用 open,我想知道在这种情况下 open 是否仍然检索一个字节又一个?如果是这样的话,每个字节都会有上下文切换开销,因为 fopen 缓冲区在用户进程中,系统调用 return 值在我的第一个问题中来回传递,为什么它运行得更快?提前致谢!

“由于缓冲,fopen 比 [然后 fopen] 快得多,但在引擎盖下它调用打开...”
一般来说,根据定义,如果 function1() 实现包括调用 function2(),则直接调用 function2(),并且如果使用与 function1() 调用时相同的选项集,将始终有一个更短的执行时间。如果您在 fopen()open() 中看到相反的情况,那么它建议您直接调用 open() 时使用的选项集必须与在 [=16] 中调用时使用的选项集不同=].但是内部 do_sys_open() 的实现具有相同数量的参数 open(),因此不可能存在速度差异。你应该质疑你的 bench-marking 技巧。

关于 return 值如何 return 发送给用户...
Linux 系统调用是使用 open()SYSCALL_DEFINEn. The following example implementation 的变体定义的,说明了这一点,并表明在函数 do_sys_open() 的封装中,其中一个参数包括 const char __user *macro 和函数中,允许它跟踪发起呼叫的用户:

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
    struct open_flags op;
    int fd = build_open_flags(flags, mode, &op);
    struct filename *tmp;

    if (fd)
        return fd;

    tmp = getname(filename);
    if (IS_ERR(tmp))
        return PTR_ERR(tmp);

    fd = get_unused_fd_flags(flags);
    if (fd >= 0) {
        struct file *f = do_filp_open(dfd, tmp, &op);
        if (IS_ERR(f)) {
            put_unused_fd(fd);
            fd = PTR_ERR(f);
        } else {
            fsnotify_open(f);
            fd_install(fd, f);
        }
    }
    putname(tmp);
    return fd;
}

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
    if (force_o_largefile())
        flags |= O_LARGEFILE;

    return do_sys_open(AT_FDCWD, filename, flags, mode);
}

我猜你在这里有点困惑。当你说 fopen() 更快时,你的实际意思是 fread()fwrite()read()write() 快。许多实现可能都是如此,因为 C 标准库在 userspace 中使用缓冲,而大多数 POSIX 实现不在 userspace 中使用缓冲。但是,他们可能会在内核 space.

中使用缓冲

假设您正在复制一个 1kb 的文件。如果您一次一个字节执行此操作,使用 read() 从文件中获取一个字节并使用 write() 将其复制到另一个字节,您最终会调用相应的系统调用 1024 次。每一次,都有一个从 user-space 到内核 space 的上下文切换。另一方面,如果你使用一个 C 库实现,比如在内部使用一个 512 字节的缓冲区,那么它实际上只转换为两个系统调用,即使你调用 freadfwrite 数千次。因此,它看起来比直接使用 read/write() 要快得多。

但是,不是一次复制一个字节,您也可以在应用程序中使用足够大的缓冲区,尽可能少地调用 read/write,以获得与直接系统调用。换句话说,并不是标准库 API 比系统调用快(这是不可能的,因为库在内部调用系统调用),只是调用 read/write 系统调用效率更高由于上下文切换开销而使用更大的缓冲区,标准库在编写时就考虑到了这一点。