Linux内核源代码分析——第5章 系 统 调 用

  1. 云栖社区>
  2. 博客>
  3. 正文

Linux内核源代码分析——第5章 系 统 调 用

xumaojun 2012-02-07 16:40:27 浏览573
展开阅读全文
3. sys_reboot
29298:内核中其他地方可能都没有sys_reboot的实现方法先进。其原因可以理解为:根据调
用的名字我们就可以知道,reboot系统调用可以用来重新启动机器。根据所提供的参数,它
还能够挂起机器,关闭电源,允许或者禁止使用Ctrl+Alt+Del组合键来重启机器。如果你要
使用这个函数编写代码,需要特别注意它上面的注释标题的警告:首先同步磁盘,否则磁盘
缓冲区中的数据可能会丢失。
由于它可能为系统引发的潜在后果,sys_reboot需要几个特殊参数,这一点马上就会讨论。
29305:如果调用者不具有CAP_SYS_BOOT(14096行)权能(capability),系统就会返回EP
ERM错误。权能在第7章中会详细讨论。现在,简单地说就是:权能是检测用户是否具有特定
权限的方法。
29309:在这里,这种偏执的思想充分发挥了作用。syst_reboot根据从16002到16005行定义
的特殊数字检测参数magic1和magic2。这种思想是:如果sys_reboot在某种程度上是被偶然
调用的,那么就不太可能再从由magic1和magic2组成的小集合中同时提取值。注意,这并不
意味着这是一个防止粗心的安全措施。
顺便说一下,这些特殊数字并不是随机选取的。第一个参数的关系是十分明显的,它是“感
受死亡(feel dead)”的双关语。后面的三个参数要用十六进制才能了解它们全部的意思:
它们分别是0x28121969,0x5121996,0x16041998。这似乎代表Linus的妻子(或者就是Linu
s自己)和他两个女儿的生日。由此推论,当Linus和他的妻子养育了更多儿女的时候,重启
动需要的特殊参数可能在某种程度上会增加。不过我想在他们用尽32位可能空间之前,他的
妻子就会制止他的行为了。
29315:请求内核锁,这样能保证这段代码在某一时间只能由一个处理器执行。使用lock_ke
rnel/unlock_kernel“函数对”所保护起来的任何其他代码对其他CPU都同样是不可访问的。
在单处理器的机器中,这只是一个no-op(不处理任何事情);而详细讨论它在多处理器上的
作用则是第10章的内容。
29317:在LINUX_REBOOT_CMD_RESTART的情况中,sys_reboot调用一系列基于reboot_notifi
er_list的函数来通知它们系统正在重新启动。正常情况下,这些函数都是操作系统关闭时需
要清除的模块的一部分。这个列表函数似乎并不在内核中的其他地方使用—至少在标准内核
发行版本中是这样,也许此外的其他模块可能使用这个列表。不管怎样,这个列表的存在可
以方便其他人使用。
LINUX_REBOOT_CMD_RESTART和其他cmd识别出的值从16023行开始通过#define进行宏定义。这
些值并没有潜在的意义,选用它们的简单原因是它们一般不会发生意外,并且相互之间各不
相同。(有趣的是,LINUX_REBOOT_CMD _OFF是零,这是在意外情况下最不可能出现的一个值
。但是,由于LINUX_REBOOT_CMD_OFF简单地禁止用户使用Ctrl+Alt+Del重新启动机器,它就
是一种“安全”的意外了。)
29321:打印警告信息以后,sys_reboot调用machine_restart(2185行)重启机器。正如从
2298行中所看到的一样,machine_restart函数从来不会返回。但是不管怎样,对于machine
_restart的调用后面都跟着一个break语句。
这仅仅是经典的良好的编程风格吗?的确如此,但是却又不仅仅如此。文件kernel/sys.c的
代码是属于体系结构无关部分的。但是machine_restart,它显然是体系结构所特有的,属于
代码的体系结构特有的部分(arch/i386/kernel/process.c)。因而对于不同的移植版本也
有所不同。我们并不清楚以后内核的每个移植版本的实现都不会返回—例如,它可能调度底
层硬件重启但是本身要仍然持续运行几分钟,这就需要首先从函数中返回。或者更为确切的
说法是,由于某些特定的原因,系统可能并不总是能够重启;或许某些软件所控制的硬件根
本就不能重启。在这种平台上,machine_restart就应该可以返回,因此体系结构无关的代码
应该对这种可能性有所准备。
针对这个问题,正式的发行版本中都至少包含一个退出端口,使machine_ restart函数可以
从这个端口返回:m68k端口。不同的基于m68k的机器支持的代码也各不相同,由于本书主要
是针对x86的,我不希望花费过多的时间来解析所有的细节。但是这的确是可能的(在其他情
况下,machine_restart简单进入一个无限循环—既不重新启动机器,也不返回。但是这里我
们担心的是需要返回的情况)。
因此,我们毕竟是需要break的。前面看起来只是简单的习惯甚至是偏执的思想在这里为了内
核的移植性已经变成必须的了。
29324:接下来的两种情况分别允许和禁止臭名昭著的Ctrl+Alt+Del组合键(这三个组合键也
被称为“Vulcan神经收缩(Vulcan nerve pinch)”,“黑客之手(hacker’s claw)”,
“三指之礼(three-fingered salute)”,我最喜欢后面这个)。这些只是简单地设置全局
C_A_D标志(在29160行定义,在29378行检测)。
29332:这种情况和LINUX_REBOOT_CMD_RESTART类似,但只是暂停系统而不是将其重新启动。
两者之间的一个区别是它调用machine_halt(2304行)—这是x86上的一条no-op指令,但是
在其他平台上却要完成关闭系统的实际工作—而不是调用machine_restart。并且它会把mac
hine_halt不能暂停的机器转入低功耗模式运行。它使用do_exit(23267行)杀死内核本身。
29340:到现在为止,这已经是一种比较熟悉的模式了。这里,sys_reboot关闭机器电源,除
了为可以使用软件自行关闭电源的系统调用machine_power_off(2307行)之外,其他的应该
和暂停机器情况完全相同。
29348:LINUX_REBOOT_CMD_RESTART2的情况是已建立主题的一个变种。它接收命令,将其作
为ASCII字符串传递,该字符串说明了机器应该如何关闭。字符串不会由sys_reboot本身来解
释,而是使用machine_restart函数来解释;因而这种模式的意义,如果有的话,就是这些代
码是平台相关的(我使用“如果有”的原因是启动机器—特别是在x86中—一般只有一种方法
,因此其他的信息都可以被machine_restart忽略)。
29365:调用者传递了一个无法识别的命令。sys_reboot不做任何处理,仅仅返回一个错误
因此,即使由magic1和magic2传递给sys_reboot正确的magic数值,它也无须处理任何内容。
29369:一个可识别的命令被传递给sys_reboot。如果流程执行到这里,它可能就是两个设置
C_A_D的命令之一,因为其他情况通常都是停止或者重新启动机器。在任何情况下,sys_reb
oot都简单把内核解锁并返回0以表示成功。
4. sys_sysinfo
24142:一个只能返回一个整型值的系统调用。如果需要返回更多的信息,我们只需要使用类
似于在系统调用中传递多于四个参数时所使用的技巧就可以了:我们通过一个指向结构的指
针将结果返回。收集系统资源使用情况的sysinfo系统调用就是这种函数的一个样例。
24144:分配并清空一个struct sysinfo结构(15004行)以暂时存储返回值。sys_sysinfo可
以把结构中的每个域都独立地拷贝出来,但是这样会速度很慢、很不方便,而且必然不容易
阅读。
24148:禁止中断。这在第6章中会有详细的介绍;作为目前来说,我们只要说明这种模式在
使用的过程中能够确保sys_sysinfo正在使用的值不会改变就足够了。
24149:struct sysinfo结构的uptime域用来指明系统已经启动并运行了的秒数。这个值是使
用jiffies(26146行)和HZ来计算的。jiffies计算了系统运行过程中时钟的滴答次数;HZ是
系统相关的一个参数,它十分简单,就是每秒内部时钟滴答的次数。
24151:数组avenrun(27116行)记录了运行队列的平均长度—也就是等待CPU的平均进程数
—在最后的1秒钟、5秒钟和15秒钟。calc_load(27135行)周期性地重复计算它的值。由于
内核中是要严格禁止浮点数运算的,所以只能通过计算变化的次数这一修正值来计算。
24155:同样记录系统中当前运行的进程数。
24158:si_meminfo(07635行)写入这个结构中的内存相关成员,si_swapinfo(38544行)
写入与虚拟内存相关的部分。
24161:现在整个结构都已经全部填充了。sys_sysinfo试图将其拷贝回用户空间,如果失败
就返回EFAULT错误,如果成功就返回0。
  
 
linux内核reboot2006
 
讲述如何实现linux内核reboot

 linux/kernel/sys.c:

asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg)
这里提供系统调用reboot。可以通过lib引用完成。

case LINUX_REBOOT_CMD_RESTART:
  notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
  printk(KERN_EMERG "Restarting system.\n");
  machine_restart(NULL);
  break;

linux/arch/arm/kernel/process.c

void machine_restart(char * __unused)
{
 /*
  * Clean and disable cache, and turn off interrupts
  */
 cpu_proc_fin();

 /*
  * Tell the mm system that we are going to reboot -
  * we may need it to insert some 1:1 mappings so that
  * soft boot works.
  */
 setup_mm_for_reboot(reboot_mode);

 /*
  * Now call the architecture specific reboot code.
  */
 arch_reset(reboot_mode);

 /*
  * Whoops - the architecture was unable to reboot.
  * Tell the user!
  */
 mdelay(1000);
 printk("Reboot failed -- System halted\n");
 while (1);
}

linux/include/asm-arm/cpu-multi32.h:

extern const struct processor arm7_processor_functions;
#define cpu_proc_fin()    processor._proc_fin()

linux/arch/arm/mm/proc-arm6,7.S:

/*
 * Purpose : Function pointers used to access above functions - all calls
 *      come through these
 */
  .type arm7_processor_functions, #object
ENTRY(arm7_processor_functions)
  .word cpu_arm7_data_abort
  .word cpu_arm7_check_bugs
  .word cpu_arm7_proc_init
  .word cpu_arm7_proc_fin
  .word cpu_arm7_reset
  .word cpu_arm7_do_idle

  /* cache */
  .word cpu_arm7_cache_clean_invalidate_all
  .word cpu_arm7_cache_clean_invalidate_range
  .word cpu_arm7_flush_ram_page

  /* dcache */
  .word cpu_arm7_dcache_invalidate_range
  .word cpu_arm7_dcache_clean_range
  .word cpu_arm7_dcache_clean_page
  .word cpu_arm7_dcache_clean_entry

  /* icache */
  .word cpu_arm7_icache_invalidate_range
  .word cpu_arm7_icache_invalidate_page

  /* tlb */
  .word cpu_arm7_tlb_invalidate_all
  .word cpu_arm7_tlb_invalidate_range
  .word cpu_arm7_tlb_invalidate_page

  /* pgtable */
  .word cpu_arm7_set_pgd
  .word cpu_arm7_set_pmd
  .word cpu_arm7_set_pte
  .size arm7_processor_functions, . - arm7_processor_functions

linux/include/asm-arm/arch-sa1100/system.h:

static inline void arch_reset(char mode)
{
 if (mode == 's') {
  /* Jump into ROM at address 0 */
  cpu_reset(0);
 } else {
  /* Use on-chip reset capability */
  RSRR = RSRR_SWR;
 }
}

 linux/include/asm-arm/cpu-multi32.h:

#define cpu_reset(addr)    processor.reset(addr)

 linux/arch/arm/mm/proc-arm6,7.S:

ENTRY(cpu_arm7_reset)
  mov r1, #0
  mcr p15, 0, r1, c7, c0, 0  @ flush cache
  mcr p15, 0, r1, c5, c0, 0  @ flush TLB
  mov r1, #0x30
  mcr p15, 0, r1, c1, c0, 0  @ turn off MMU etc
  mov pc, r0

 
 
 
 

网友评论

登录后评论
0/500
评论
xumaojun
+ 关注