Robert Love 在 quora 上关于 Linux Kernel 的问答摘录之四

(16)Linux中的initramfs程序是什么?

initramfs是initial RAM filesystem的缩写。它是Linux初始文件系统的一份cpio存档。这份存档将在内核初始化完成后,init进程运行前,加载到内存当中去。在真正的root文件系统被加载前,Linux内核将加载initramfs,将它当做一个初始的root文件系统。这个初始的root文件系统包含了加载真正root文件系统和初始化用户系统的文件----其中最重要的部分就是内核模块。

Linux内核是模块化的,也就是说它支持动态的加载和卸载目标文件。内核的很多功能都可以被编译成一个模块,比如对一个文件系统的支持功能或者访问一个磁盘所需要的驱动。这样的话Linux系统只用把最基本的功能编译到内核镜像中去,把其他功能和硬件驱动编译成模块,让用户根据需要来动态地加载和卸载。

但是如果root文件系统是在一个需要加载某个内核模块才能访问的文件系统或者磁盘上呢?我们就没有办法boot了。因为我们需要加载这个模块来访问磁盘,但是这个模块却又在这个磁盘上,这就成了一个鸡生蛋蛋生鸡的问题。initramfs可以让内核通过一个特殊的RAM文件系统来访问这些模块,从而解决了这个问题。一般来讲,initramfs包含了文件系统模块和磁盘驱动模块,用户可以通过这些模块去加载自己的root文件系统。


(17)什么是无滴答(tickless)内核?

为了理解tickless内核,我们首先需要了解一个内核通常是怎么工作的。操作系统的很多功能都需要周期性的调用----比如更新系统时间,减少当前运行进程的时间片,终止定时器,等等。为了实现这些周期性的操作,系统提供了一个叫做可编程中断定时器(PIT)的硬件,也叫作系统定时器或者定时器。定时器允许操作系统以一定的时间粒度----比如每10ms一次----来调用中断。内核可以注册一个定时器中断处理程序来响应这个定时器中断,然后进行前文提到的各种周期操作。由于定时器中断是以一个已知的固定周期----比如说10ms----触发,因此定时器中断处理程序的调用就意味着10ms过去了。每一次定时器的中断触发就叫做一个滴答(tick)。

这种模型有两个主要的缺点。第一是定时器的中断周期大小需要做一个权衡。如果中断频率太低,那么系统维护的时间颗粒度就不是最优的。比如你的定时器中断每10ms到来一次,由于系统依靠定时器中断来追踪进程的时间片,因此你的进程调度器的精度上限就是10ms。相反,如果中断频率太高,那么就会浪费很多处理器的时间用来处理一些不必要的定时器中断。

第二个需要考虑的问题是电量的消耗。移动设备是通过很激进的电源管理策略来保障自己的运行时间的。定时器可能在1s内唤醒设备100次或者1000次,这样会很快耗尽电池电量,因为设备永远不会进入真正的idle状态。

现在我们来到tickless内核。在这种内核中,没有固定的定时器滴答周期。相反,内核根据他的下一个事件来调度下一个定时器滴答。这种内核应该叫做“动态滴答”内核更合适一点。如果当前运行的程序还有18ms的时间片剩余,那么内核可以动态的将定时器中断设定为18ms后触发。这样系统就消除了固定定时器周期带来的问题:当我们需要高精度时,就把中断频率提高,反之则不提高。另外,当系统处于idle状态时,我们就没有必要周期性的调度定时器了。这样系统可以真正的进入idle状态,极大地提高电池续航时间。

这种内核的缺点就是它的复杂性。基于tick的系统比tickless系统简单很多。身为一个程序员,如果你知道定时器中断的频率是固定的而且你可以依靠这个中断来实现自己的周期响应动作,那么开发内核代码就轻松多了。而在像Linux这种支持tickless操作的系统中,不管你用或不用这个特性,你都或多或少需要付出一些代价。启用一个tickless系统的主要代价就是动态滴答的计算过程和定时器的调度成本。相对绝大多数的系统负载来说,这些成本是可以忽略不计的。


(18)为什么在已经有了内核缓存机制的情况下还需要标准I/O库提供的缓存机制?

因为标准I/O库提供的用户态缓存和内核缓存是不同的。

用户态缓存机制通过实现用户空间的缓冲区来优化进程性能。这个缓存减少了系统调用的次数,还可以根据媒体介质的块大小(block size)来调整I/O操作,实现对齐(align)。

内核缓存就完全是另外一个故事了。它有两种形式----读缓存和写缓存----都是用来优化整体的系统性能。读缓存利用空间局部性和时间局部性来减少I/O操作的次数。写缓存可以推迟I/O操作,还可以对I/O操纵进行合并和排序。


(19)当访问一个内存地址时,Linux内核如何知道这个页面是mmaped的?

当你第一次访问一个mmaped的页面时,页错误处理函数就会运行。在大多数结构体系上,这个函数会去调用一个体系相关的叫做do_page_fault()的函数。这个函数会检查一些基本的问题:这是不是内核内存?如果不是,我们是不是处在中断处理函数当中?如果不是,内核会去查找当前进程地址空间里包含此内存地址的vm_area_struct结构体。

vm_area_struct结构体描述了拥有相同页错误处理机制----比如换页写入一个mmaped的文件----的进程地址空间里的内存区域。内核把这些内存区域叫做虚拟内存区域或者VMA。如果包含当前出错内存地址的VMA存在并且通过了访问验证,内核就会再去调用handle_mm_fault()来对请求的页面内容进行换页。handle_mm_fault()会从vm_area_struct结构体来获得mmaped文件的信息,从而换页写入所需数据。

简单来说,一个页错误会产生,然后页错误处理函数会运行。这个内存地址所在的vm_area_struct结构体描述了此内存地址是否对应一个mmaped页面。


(20)为什么当终端进程结束后,从终端启动的Linux进程也会结束?

你说的很对,孤儿进程的父进程会被重新指定成init进程。

但是当控制台终端结束时,shell会向控制台所在进程组内的所有进程发送信号。进程收到SIGHUP信号的默认响应动作是终止进程。原因很简单,这个进程所在的终端结束了,绝大多数情况下(除非该进程是没有依附任何tty的daemon进程),它都会希望自己终止掉。

如果你想避免这种行为,你可以使用nohup命令来让一个新创建的进程忽略SIGHUP信号,或者用setsid命令来让这个进程与tty分离,使之成为一个daemon进程。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值