如何找到Linux内核系统调用的实现?


375

我试图mkdir通过查看内核源代码来理解一个函数的工作原理。这是一种尝试了解内核内部结构并在各种功能之间导航的尝试。我知道mkdir在中定义sys/stat.h。我找到了原型:

/* Create a new directory named PATH, with permission bits MODE.  */
extern int mkdir (__const char *__path, __mode_t __mode)
     __THROW __nonnull ((1));

现在,我需要查看在哪个C文件中实现此功能。从源目录,我尝试了

ack "int mkdir"

哪个显示

security/inode.c
103:static int mkdir(struct inode *dir, struct dentry *dentry, int mode)

tools/perf/util/util.c
4:int mkdir_p(char *path, mode_t mode)

tools/perf/util/util.h
259:int mkdir_p(char *path, mode_t mode);

但它们都不符合中的定义sys/stat.h

问题

  1. 实现哪个文件mkdir
  2. 使用上面的函数定义,如何找出实现的文件?内核在定义和实现方法时遵循什么模式?

注意:我正在使用内核2.6.36-rc1


2
顺便说一句,看看这个:voinici.ceata.org/~tct/resurse/utlk.pdf
汤姆·布里托

Answers:


386

系统调用不会像常规函数调用那样处理。它需要特殊的代码才能从用户空间过渡到内核空间,基本上是在调用站点上将一些内联汇编代码注入到您的程序中。“捕捉”系统调用的内核端代码也是底层的东西,您可能至少在一开始就不需要深入了解。

include/linux/syscalls.h内核源目录下,您可以找到以下内容:

asmlinkage long sys_mkdir(const char __user *pathname, int mode);

然后在中/usr/include/asm*/unistd.h,您会发现:

#define __NR_mkdir                              83
__SYSCALL(__NR_mkdir, sys_mkdir)

这段代码说的mkdir(2)是系统调用#83。也就是说,系统调用是通过数字调用的,而不是通过地址调用的,而不是通过自己的程序或链接到该程序的库中的正常函数调用的地址。我上面提到的内联汇编粘合代码使用它来进行从用户到内核空间的转换,并带上您的参数。

还有一点证据表明这里有些奇怪,那就是系统调用并不总是有严格的参数列表:open(2)例如,可以使用2个或3个参数。这意味着open(2)重载,C ++,而不是C的特征,但系统调用接口是C兼容。(这与C的varargs功能不同,后者允许单个函数采用可变数量的参数。)

要回答您的第一个问题,没有单个文件mkdir()存在。Linux支持许多不同的文件系统,并且每个文件系统都有自己的“ mkdir”操作实现。让内核将所有内容隐藏在单个系统调用之后的抽象层称为VFS。因此,您可能想开始fs/namei.c使用vfs_mkdir()。低级文件系统修改代码的实际实现在其他地方。例如ext4_mkdir(),定义了ext4实现fs/ext4/namei.c

关于第二个问题,是的,所有这些都有模式,但没有一条规则。实际上,您需要对内核的工作原理有一个相当广泛的了解,以便弄清楚应该在哪里寻找任何特定的系统调用。并非所有系统调用都涉及VFS,因此它们的内核端调用链并非都始于fs/namei.cmmap(2)例如,开始于mm/mmap.c,因为它是内核内存管理(“ mm”)子系统的一部分。

我建议您获得Bovet和Cesati撰写的“ 了解Linux内核 ” 的副本。


很好的答案。关于您提到的书,“了解Linux内核”有一点。我没有,但是从发布日期(2000年)和TOC(在oreilly站点)开始,我觉得这大约是2.2内核以及2.4内核的一些见解(但我错了)。我的问题是:有一本等效的书涵盖了2.6内核的内部原理?(甚至更好的覆盖2.2、2.4和2.6)?
DavAlPi

2
@DavAlPi:据我所知,Bovet&Cesati仍然是关于该主题的最佳单本书。当我需要用更多最新资料补充它时,我会在Documentation正在使用的内核的源树的子目录中进行挖掘。
沃伦·杨

1
实际上,open(2)是一个varargs函数。只有两种方法可以调用它,因此手册页以此方式进行记录,实际的原型包含...在其中,就像任何varargs函数一样。当然,这是在libc级别实现的。当不使用第三个参数时,它可以将0或垃圾值传递给内核ABI。
Random832 '17

“这是您不需要了解的东西”。如果在Stackexchange网络上找不到这种句子,那么世界将是一个更好的地方。
Petr

84

这可能无法直接回答您的问题,但是我发现strace尝试理解即使是最简单的shell命令所做的底层系统调用时,也确实很棒。例如

strace -o trace.txt mkdir mynewdir

系统mkdir mynewdir会将该命令调用转储到trace.txt中,以供您查看。


5
+1整洁的把戏!我以前没有用过
David Oneill

3
更好的是,使输出文件trace.strace并在VIM中打开。VIM将突出显示它,使阅读变得容易得多。
Marcin

55

阅读Linux内核源代码的一个好地方是Linux交叉引用(LXR) ¹。除了自由文本搜索结果外,搜索还返回类型匹配项(函数原型,变量声明等),因此它比单纯的grep更为方便(而且速度更快)。

LXR不会扩展预处理器定义。系统调用的名称在各处都被预处理器所破坏。但是,大多数(全部?)系统调用是使用SYSCALL_DEFINEx宏系列之一定义的。由于mkdir有两个参数,因此搜索将SYSCALL_DEFINE2(mkdir导致syscall声明mkdir

SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
{
    return sys_mkdirat(AT_FDCWD, pathname, mode);
}

好的,这sys_mkdirat意味着它是mkdiratsyscall,因此单击它只会导致您转到中的声明include/linux/syscalls.h,但是定义就在上面。

的主要工作mkdirat是调用vfs_mkdir(VFS是通用文件系统层)。单击该按钮将显示两个搜索结果:中的声明include/linux/fs.h和上面几行的定义。的主要工作vfs_mkdir是调用特定于文件系统的实现:dir->i_op->mkdir。要了解如何这个实现,你需要把个别文件系统的实施,并没有硬性的快速规则-它甚至可能是在内核树之外的模块。

¹ LXR是分度程序。有几个网站提供到LXR的界面,其已知版本集略有不同,Web界面也略有不同。它们往往来来往往,因此,如果您不习惯使用该工具,请在网络上搜索“ linux cross-reference”以查找其他工具。


那是一种资源。好答案。
稳定狗2013年

linux.no链接中的“内部服务器错误” 。
Fredrick Gauss

@FredrickGauss lxr.linux.no曾经是LXR最好的接口,但是它经常停机。现在我认为它已经一去不复返了。我替换了到另一个LXR接口的第一个链接。
吉尔斯

21

系统调用通常包装在SYSCALL_DEFINEx()宏中,这就是为什么简单的grep找不到它们的原因:

fs/namei.c:SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)

宏扩展后的最终函数名最终为sys_mkdir。该SYSCALL_DEFINEx()宏将像跟踪每个系统调用定义需要有代码样板的事情。


17

注意:.h文件未定义函数。它在该.h文件中声明,并在其他位置定义(实现)。这使编译器可以包含有关函数签名(原型)的信息,以允许对参数进行类型检查,并将返回类型与代码中的任何调用上下文匹配。

通常,C语言中的.h(头文件)用于声明函数和定义宏。

mkdir特别是系统调用。该系统调用周围可能有一个GNU libc包装器(实际上几乎可以肯定是)。真正的内核实现mkdir可以通过搜索内核源代码以及特别是系统调用来找到。

请注意,还将为每个文件系统实现某种目录创建代码。VFS(虚拟文件系统)层提供了系统调用层可以调用的通用API。每个文件系统都必须注册供VFS层调用的功能。这允许不同的文件系统实现它们自己的语义,以了解目录的结构(例如,如果使用某种哈希方法存储它们,以使搜索特定条目更为有效)。我之所以这样说是因为,如果您正在搜索Linux内核源代码树,则可能会跳过这些文件系统特定的目录创建功能。


8

您找到的所有实现都不匹配sys / stat.h中的原型。也许使用此头文件搜索include语句会更成功吗?


1
该实现(如sys / stat.h中所述)是userland和libc的业务。内核内部的东西(如何真正完成)是内核内部的业务。对于所有内核黑客,内部函数可以称为xyzzy并采用5个参数。libc的工作是接受userland调用,将其转换为所需的所有内核要求,然后将其交付并收集任何结果。
vonbrand

6

这里有一些非常不错的博客文章,描述了用于寻找底层内核源代码的各种技术。


12
请不要仅仅发布指向博客或论坛的链接,总结其内容,以便读者可以看到它们的内容,如果网站消失了,还可以保留一些东西。同样,您的第一个链接是关于libc的,对于这个问题,这是不合主题的。
吉尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.