入门Linux Kernel就是看的Robert Love所著的《linux kernel development》和 《linux system programming》,感觉真的是深入浅出娓娓道来。这几天在Quora上看到大神对 Linux Kernel 的一些回答,也是深有启发,特此将一些有意思的问题翻译过来。
(1)什么是“SMP 安全”以及为什么linux kernel是SMP安全的?
答:SMP安全的意思是代码是线程安全的,也就是说代码在多线程并发访问时也会正确执行。非SMP安全的代码可能导致线程敌对(thread hostile)或线程竞争(racey),也就是说多线程可能在code内竞争,导致bug。
Linux内核是SMP安全的,因为它是高度线程化的,而且可以工作在多处理器的环境下。换句话说,它必须是SMP安全的。
(2)如何查看系统在系统调用(system call)上所用的时间比例?
答:top和vmstat是查看此信息的最简单方法。“sy”那一列所表示的就是CPU在内核里的开销,也就包括了系统调用的开销。对于很多工作而言,这个数据非常接近系统调用所占用的时间,因为非系统调用不会占用内核太久的时间。
对于单独的进程而言,可以使用time命令来查看运行时间信息。比如time df,就会告诉你执行df命令所占用的全部运行时间,用户空间运行时间,内核空间运行时间。
(3)copy_to_user如何工作?
答:copy_to_user是linux内核定义的函数(定义在<asm/uaccess.h>中),用来把数据从内核空间拷贝到用户空间。函数具体的实现取决于系统的架构,但是一般都是如下形式:
#include <asm/uaccess.h>
int copy_to_user(void *dst, const void *src, unsigned int size);
如果成功的话,copy_to_user会从内核地址src拷贝size大小的字节到用户空间地址dst。copy_to_user返回没有被拷贝的字节数,因此当所有数据全部拷贝成功的时候,函数返回0.
为什么不直接调用memcpy()呢?有两个原因。第一,内核可以往任意地址写入数据。但是用户程序不可以。copy_to_user需要检查dst,保证该地址是可以访问的以及可以被当前进程写入。第二,随着系统架构不同,我们不能简单地把数据从内核拷贝到用户空间。我们可能需要先做一些特别的准备工作,比如刷新cache,或者一些其它的特殊操作。
让我们看看copy_to_user在大家最熟知的x86架构上做了什么。首先,copy_to_user调用access_ok来检查dst是否可以被写入(检查类型为VERIFY_WRITE)。如果access_ok返回非0值,那么copy_to_user就开始进行拷贝了。对于486和486以前的x86处理器而言,由于页表随时可能变化,所以目的地址所在页必须在内存中。对于486以后的x86处理器,WPbit。最后,copy_to_user通过__copy_to_user_ll来拷贝内存,拷贝的最终实现方式是一个优化版本的memcpy。
让我们看一个例子。这是linux内核3.6里面定义的gethostname系统调用。
-
int gethostname(char*name,int len) { int i, errno; struct new_utsname*u; if(len<0) return-EINVAL; down_read(&uts_sem); u= utsname(); i=1+ strlen(u->nodename); if(i> len) i= len; errno=0; if(copy_to_user(name, u->nodename, i)) errno=-EFAULT; up_read(&uts_sem); return errno; }