系统调用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 字节的缓冲区,那么它实际上只转换为两个系统调用,即使你调用 fread
和 fwrite
数千次。因此,它看起来比直接使用 read/write()
要快得多。
但是,不是一次复制一个字节,您也可以在应用程序中使用足够大的缓冲区,尽可能少地调用 read/write
,以获得与直接系统调用。换句话说,并不是标准库 API 比系统调用快(这是不可能的,因为库在内部调用系统调用),只是调用 read/write
系统调用效率更高由于上下文切换开销而使用更大的缓冲区,标准库在编写时就考虑到了这一点。
假设我们有一个单核 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 字节的缓冲区,那么它实际上只转换为两个系统调用,即使你调用 fread
和 fwrite
数千次。因此,它看起来比直接使用 read/write()
要快得多。
但是,不是一次复制一个字节,您也可以在应用程序中使用足够大的缓冲区,尽可能少地调用 read/write
,以获得与直接系统调用。换句话说,并不是标准库 API 比系统调用快(这是不可能的,因为库在内部调用系统调用),只是调用 read/write
系统调用效率更高由于上下文切换开销而使用更大的缓冲区,标准库在编写时就考虑到了这一点。