linux、系统调用 do_execv 与 execv?

linux, systemcalls do_execv vs execv?

引用我的演讲:

Note the clear borderline between user space and kernel space. User programs cannot include kernel headers in their code and cannot call kernel functions directly. In other words, your program can’t simply call the sys_read() service function to read a file from the disk. Similarly, kernel code does not call user-space functions like printf(), does not include user-space header like <stdio.h> or , and does not link against user-space libraries like libc. The only gate to kernel mode (and OS services) that’s the user can use is the syscall instruction as described above.

  1. “用户程序不能包含内核头文件”所以当我在我的 C 程序中编写时 getpid() 是这个 user-space 函数吗?

  2. 当我在终端输入 getpid 时,是否相同(使用-space 函数)?

  3. 我无法访问我系统中的 linux 头文件 /home/user/linux-4.15 ,所以怎么说用户 space 无法访问内核 space?

  4. 给出下图:

我打开了一些 linux 文件 (init/main.c) 并看到:

static int run_init_process(const char *init_filename)
{
      argv_init[0] = init_filename;
      return do_execve(getname_kernel(init_filename),....
}

这个do_execve在哪里声明的?该图像仅显示 execvsys_execv...有什么区别?

"User programs cannot include kernel headers" So when I write in my C program getpid() is this user-space function?

是的。它是 libc 中调用系统调用的瘦包装器。但是根据体系结构和 libc 实现,用户空间中可能会有一些簿记(例如缓存结果以供将来调用)。

对于许多简单的系统调用,glibc 使用预处理器宏生成这些包装器。在我的系统上,对 getpid 的用户空间调用转到文件 sysdeps/unix/syscall-template.S:

0x00007ffff7ea6244  59  in ../sysdeps/unix/syscall-template.S
(gdb) disassemble 
Dump of assembler code for function getpid:
   0x00007ffff7ea6240 <+0>: endbr64 
=> 0x00007ffff7ea6244 <+4>: mov    [=10=]x27,%eax
   0x00007ffff7ea6249 <+9>: syscall 
   0x00007ffff7ea624b <+11>:    retq   
End of assembler dump.

这只是将系统调用号放入寄存器并执行 syscall 指令。

我们使用这个包装器的原因是为了避免了解不同体系结构和内核的系统调用机制和编号的细节。这使得我们的程序更 portable。我们link反对的libc知道0x27在这个系统上是getpid,应该写成%eax等等


syscall指令执行时,处理器切换到内核态,从arch/x86/entry/entry_64.S, where entry_SYSCALL_64 calls do_syscall_64 which is in arch/x86/entry/common.c开始执行:

        regs->ax = sys_call_table[nr](regs);

您可以看到它调用了 sys_call_table 的索引 nr 处的函数。 table 由符号列表 (sys_something) 填充,其中每个符号都由一个宏定义:SYSCALL_DEFINEn 其中 n 是参数的数量。由于getpid不是参数,所以在kernel/sys.c中定义为SYSCALL_DEFINE0(getpid)

/**
 * sys_getpid - return the thread group id of the current process
 *
 * Note, despite the name, this returns the tgid not the pid.  The tgid and
 * the pid are identical unless CLONE_THREAD was specified on clone() in
 * which case the tgid is the same in all threads of the same group.
 *
 * This is SMP safe as current->tgid does not change.
 */
SYSCALL_DEFINE0(getpid)
{
    return task_tgid_vnr(current);
}

What about when I type getpid in terminal is it the same (use-space function)?

我不知道终端命令 getpid,但如果有的话,它应该是一个 executable 二进制文件(或脚本),它最终会调用一个系统调用,或者libc 系统调用的包装器。因为,内核维护任务和进程 ID,用户空间代码无法访问内核内存。


I can't access linux header files in my system /home/user/linux-4.15 , so how it's said user space can't access kernel space?

您是说您可以访问头文件吗?当然,您可以访问整个源代码。但是即使你在你的程序中包含这些头文件,并编译,并以某种方式 link 它们与你的内核代码,这并不意味着你可以 运行 它们在内核模式下。

除非您使用可加载内核模块。事实上,您需要内核头文件来编译内核模块。然后,您可以 请求 内核在内核模式下加载和执行这些模块。但是你需要调用另一个系统调用(init_module)来实现它。


where is this do_execve declared? the image shows only execv and sys_execv... and what's the difference?

这里是系统调用的定义execve:

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}

类似于getpidexecve是用一个SYSCALL_DEFINEn(这次是三个参数)宏定义的,它生成sys_execve符号。在内部,内核调用 do_execve。如果搜索文件的其余部分,您会发现 do_execve 本身就是 do_execveat_common 的包装器。经过一些检查和初始化,调用 bprm_execve,调用 exec_binprm,依此类推。


Can you elaborate what's the difference between do_execve and sys_execve?

差别不大。除了,sys_execve 符号由 SYSCALL_DEFINE3 宏定义,并且旨在由特定于体系结构的系统调用机制调用,这可能不同于常规 C 函数(例如 asmlinkage)。 do_execve 是一个普通的 C 函数。在这种情况下,它不是从任何其他 C 代码调用的,但它是可能的。然而,直接从内核代码内部调用 sys_execve 是不正确的。