我想学习Linux内核的工作方式。我已经使用Linux十年了,但是我仍然不太了解内核的功能。在那里,我发现:
Linux内核源代码并非完全不可能理解
内核编程不仅适用于向导,也适用于我!
系统编程真的很有趣
我可以编写玩具内核模块,很有趣!
最令人惊讶的是,所有这些东西都是有用的。
我根本没有做过底层编程-我在大学里写了一些C语言,否则一直在做Web开发和机器学习。但是事实证明,我新获得的操作系统知识帮助我更轻松地解决了常规编程任务。
现在,我还觉得如果要使用Survivor:修复内核的USB驱动程序中的错误,我有机会不会立即被踢出孤岛。
这都是关于Linux的,但是很多相同的概念也适用于OSX。我们将讨论
什么是内核?
为什么要花时间去学习这些东西?
根据您自己的意愿,有一些可以更好地理解Linux内核的策略:
追踪所有事物!
阅读一些内核代码!
写一个有趣的内核模块!
写一个操作系统!
尝试Eudyptula挑战
做实习。
简单来说:
内核是一堆知道如何与硬件交互的代码。
Linux主要是用C编写的,并带有一些汇编语言。假设您在浏览器中转到http://google.com。这需要输入,通过网络发送数据,分配一些内存,以及可能写入一些缓存文件。您的内核具有以下代码
每次按下按键时都会解释按键
讲TCP / IP协议,用于通过网络将信息发送给Google
与您的硬盘驱动器通信以向其写入字节
了解文件系统的实现方式(硬盘驱动器上的字节甚至意味着什么?!)
为可能正在运行的所有不同进程提供CPU时间
与您的图形卡对话以显示页面
跟踪已分配的所有内存
还有更多。所有这些代码始终在您使用计算机的同时运行!
一次处理很多事情!在本文的其余部分中,我想让您理解的唯一概念是* 系统调用。系统调用是内核的API,您编写的常规程序可以使用系统调用与计算机的硬件进行交互。一些系统调用示例:
open
打开文件
sendto
并recvfrom
发送和接收网络数据
write
写入磁盘
chmod
更改文件的权限
brk
并sbrk
分配内存
因此,当您open()
在Python中调用函数时,在堆栈的某个位置最终使用了open
系统调用。
这就是您现在需要了解的所有内核!这是一堆一直在您的计算机上运行的C代码,您可以使用系统调用与之交互。
有一些明显的原因:这真的很有趣!并非所有人都知道!说您编写了一个有趣的内核模块真是太酷了!
但是还有一个更严重的原因:了解操作系统和程序之间的接口将使您成为一个更好的程序员。让我们看看如何!
想象一下,您正在编写一个Python程序,它是要从文件中读取一些数据/user/bork/awesome.txt
。但这不起作用!
一个非常基本的问题是:您的程序是否甚至打开了正确的文件?您可以开始使用常规调试技术进行调查(打印出一些东西!使用调试器!)。但是令人惊奇的是,在Linux上,打开文件的唯一方法是使用 open
系统调用。您可以open
使用称为strace的工具获得所有这些调用的列表(以及因此打开了程序的每个文件)。
让我们做一个简单的例子!假设我想知道Chrome打开了哪些文件!
$ strace -e open google-chrome[... lots of output omitted ...]open("/home/bork/.config/google-chrome/Consent To Send Stats", O_RDONLY) = 36open("/proc/meminfo", O_RDONLY|O_CLOEXEC) = 36open("/etc/opt/chrome/policies/managed/lastpass_policy.json", O_RDONLY) = 36
这是一个非常强大的工具,可以用来观察程序的行为,如果我们不了解有关系统调用的一些基础知识就不会有这种行为。我使用strace来:
看看我的文件认为我的节目是开就是它的 真正开放(系统调用:read
)
找出日志文件我行为不端的不良记录程序被写入(虽然我也可以使用lsof
)(系统调用:write
)
监视我的程序正在通过网络发送什么数据(系统调用:sendto
和recvfrom
)
找出每一个我的程序打开网络连接时(系统调用:socket
)
我非常喜欢strace,所以我对strace进行了闪电般的演讲: 使用strace监视您的程序。
/proc
/proc
可让您恢复已删除的文件,并且是一个出色的编程工具,很好地说明了如何更好地了解操作系统。
它是如何做到的?假设我们已经编写了一个程序 smile.c,并且正在运行它。但是后来我们不小心删除了二进制文件!
该进程的PID现在为8604
。我可以在以下位置找到该过程的可执行文件/proc/8604/exe
:
/proc/8604/exe -> /home/bork/work/talks/2014-09-strangeloop/smile (deleted)
是(deleted)
,但我们仍然可以看一下! cat /proc/8604/exe > recovered_smile
将恢复我们的可执行文件。哇。
关于中的流程,还有很多其他真正有用的信息/proc
。(例如他们打开了哪些文件–试试ls -l/proc/<pid>/fd
)
您可以通过找到更多信息man proc
。
ftrace与strace完全不同。strace跟踪系统调用,ftrace跟踪内核函数。
老实说,我还没有机会这样做,但这确实很酷,所以我在告诉您。想象一下,您在使用TCP时遇到了一些问题,并且看到大量的TCP重新传输。ftrace可以为您提供有关每次内核中的TCP重新传输功能每次调用的信息!
要了解如何实际执行此操作,请阅读Brendan Gregg的文章 Linux ftrace TCP Retransmit Tracing。
在Linux Weekly News上似乎也有关于ftrace的文章 !
我梦见有一天实际上会对此进行调查:)
您的CPU具有大量不同级别的缓存(L1!L2!),它们可能会对性能产生重大影响。perf
是一个很好的工具,可以告诉你
多久使用一次不同的缓存(有多少次L1缓存未命中?)
您的程序使用了多少CPU周期(!!)
分析信息(每个功能花费了多少时间?)
以及其他许多非常有用的性能信息。
更好地了解操作系统非常有用,即使您编写Python,也可以使您成为一名更好的程序员。我已经找到了strace
和 最有用的高级编程工具/proc
。据我所知,ftrace和perf对较低级别的编程最有用。还有tcpdump
和lsof
和 netstat
和各种事情,我不会进入这里。
现在,您可以确信,花更多时间来学习有关Linux的知识是值得的。让我们讨论一些更好地了解Linux的策略!
我strace
之前简短地提到过。strace
从字面上看,这是我在宇宙中最喜欢的程序。更好地了解您的内核正在执行的操作的一种好方法是–选择一个您很了解的简单程序(如ls
),然后strace
在其上运行。
这将向您显示程序与内核进行通信的时间。我曾经从蒙特利尔乘坐火车到纽约,经过 13小时的火车旅行,然后 穿过killall,这真的很有趣。让我们尝试ls
!
我跑去strace -o out ls
将输出保存到文件中。strace将输出一个完整的 CRAP。事实证明,启动程序非常复杂,在这种情况下,大多数系统调用都与此相关。有很多
打开库: open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC)
将这些库放入内存: mmap(NULL, 2126312, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7faf507fc000
还有很多其他我不太了解的事情。在寻找乐趣时,我的主要策略是一开始就忽略所有的废话,而只专注于我所理解的。事实证明,ls
这不需要做很多事情!
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3getdents(3, /* 5 entries */, 32768) = 136getdents(3, /* 0 entries */, 32768) = 0close(3)= 0fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 12), ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf5104a000write(1, "giraffeoutpenguin\n", 22) = 22close(1)= 0munmap(0x7faf5104a000, 4096)= 0close(2)= 0exit_group(0) = ?
这太棒了!这是它需要做的:
打开当前目录: openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)
获取该目录的内容:getdents(3, /* 5 entries */, 32768) = 136
。看起来是136个字节的东西!
关闭目录: close(3)
将文件写到标准输出: write(1, "giraffe out penguin\n", 22) = 22
关闭一堆东西进行清理。
那真的很简单,我们已经学会了一个新的系统调用!中间的那个mmap?不知道那是什么。但这完全没问题!应力是最好的。
所以!在随机过程上运行strace并查找文档,以查找您不认识的系统调用,这是学习大量知识的简便方法!
好的,让我们想象一下,我们已经对getdents
(列出目录内容的系统调用)产生了兴趣,并且我们想更好地了解它的实际作用。有一个名为livegrep的出色工具,可让您搜索内核代码。是尼尔森·埃尔哈格(Nelson Elhage)的出色表现。
因此,让我们用它来查找的源getdents
,该源列出了目录中的所有条目!我使用livegrep搜索它,并找到 了源。
在211行,它调用iterate_dir
。因此,让我们来看看吧!老实说,这段代码对我来说毫无意义(也许res = file->f_op->iterate(file, ctx)
是在目录上迭代的东西?)。
但是我们可以看一下很干净!
如果您想了解当前的Linux内核开发,《 Linux周刊新闻》是一个很好的资源。例如,这是有关btrfs文件系统的有趣文章 !
内核模块听起来令人生畏,但实际上它们确实是平易近人的!从根本上说,所有内核模块都是
init
加载模块时运行的功能
cleanup
卸载模块时运行的功能
您可以使用加载内核模块,insmod
并使用卸载它们rmmod
。这是一个有效的“ Hello world”内核模块!
#include <linux/module.h>// included for all kernel modules#include <linux/kernel.h>// included for KERN_INFO#include <linux/init.h>// included for __init and __exit macrosstatic int __init hello_init(void){ printk(KERN_INFO "WOW I AM A KERNEL HACKERl!!!\n"); return 0;// Non-zero return means that the module couldn't be loaded.}static void __exit hello_cleanup(void){ printk(KERN_INFO "I am dead.\n");}module_init(hello_init);module_exit(hello_cleanup);
您可以在这里看到评论非常丰富的源代码: rickroll.c。基本上,加载时需要做的是
找到系统调用表(事实证明这并不简单!)
禁用写保护,以便实际上允许我们对其进行修改(!!)
保存旧的,open
以便我们可以将其放回原处
open
用我们自己的rickroll_open
系统调用替换系统调用
而已!
以下是相关代码:
sys_call_table = find_sys_call_table();DISABLE_WRITE_PROTECTION;original_sys_open = (void *) sys_call_table[__NR_open];sys_call_table[__NR_open] = (unsigned long *) rickroll_open;ENABLE_WRITE_PROTECTION;printk(KERN_INFO "Never gonna give you up!\n");
该rickroll_open
功能也很容易理解。这是它的草图,尽管我省略了一些您应该完全阅读的重要实现细节:rickroll.c
static char *rickroll_filename = "/home/bork/media/music/Rick Astley - Never Gonna Give You Up.mp3";asmlinkage long rickroll_open(const char __user *filename, int flags, umode_t mode) { if(strcmp(filename + len - 4, ".mp3")) { /* Just pass through to the real sys_open if the extension isn't .mp3 */ return (*original_sys_open)(filename, flags, mode); } else { /* Otherwise we're going to hijack the open */ fd = (*original_sys_open)(rickroll_filename, flags, mode); return fd; } }
听起来真是不可接近!而从头开始编写一个全功能的操作系统是每吨工作。但是,关于操作系统的伟大之处在于,您无需具备完整的功能!
我写了一个小的操作系统 ,基本上只有一个键盘驱动程序。除了我以外,不为任何人编译。这是三个星期的工作,我学到了很多。有一个非常出色的Wiki,其中包含许多有关制作操作系统的信息。
如果您对无限有趣的内核模块恶作剧没有太多想法可以与您的朋友一起玩(我当然没有!), Eudyptula Challenge专为帮助您入门而设计,并且难度逐步加大。第一个是只编写一个“ hello world”内核模块,这非常简单!
如果您真的很认真地考虑所有这些,那么我知道一些程序:
Google Summer of Code,面向学生
GNOME妇女宣传计划
GNOME妇女外展计划(OPW)是一项很棒的计划,为希望为Linux内核做出贡献的妇女提供指导和为期3个月的有薪实习。 来自OPW实习生和校友的1000多个补丁已被接受到内核中。
在应用程序中,您向内核提交了一个简单的补丁程序(!!),并且该文件已被很好地记录下来。尽管您确实需要了解一些C,但是您不必成为专家。
您现在可以申请!本轮申请的截止日期为2014年10月31日,您可以在内核OPW网站上找到更多信息 。