Linux就这个范儿 第10章 生死与共的兄弟
就说Linux系统的开机。必须经过加载BIOS、读取MBR、Boot Loader、加载内核、启
动init进程并确定运行等级、执行初始化脚本、启动内核模块、执行对应运行等级的初始化
脚本、个性化设置、进入登录状态这十个步骤。怎么样?服气吧!如果觉得还不够直观,那
么看一下图10.1 Linux系统启动流程吧。
LVS的IPVS模块也是采用动态加载的方式
图10.1 Linux系统启动流程
那关机是不是会省点事儿呢?不是!Linux的关机会涉及4个命令和4个按键。其中的4个命令就是shutdown、halt、reboot和lnit,并且每个命令所执行的步骤还不尽相同。至于4个按键应该很少有人不知道,其中三个就是Ctrl+Alt+Del组合键,另外一个就是电源键。4个按键跟4个命令一样,执行的步骤也不尽相同。可见Linux关机的这点破事儿是要多乱有多乱,还斩不断。关机这事儿就跟死亡和交税一样,永远都避免不了。
所以,作为Linux,出来混这么久了,在这“生”、“死”关头,道儿上要是没几个好兄弟,早被人砍成七块八块的了。接下来要讲的就是那些与Linux生死与共的兄弟们的事儿了。有的伴随着“生”,有的伴随着“死”,有的“生”、“死”相伴……顺便提醒一下某位哲人曾经说过:“兄弟就是拿来利用的!”
10.1 死心眼儿的BIOS
记得很久以前的一本书上说:“至今的所有PC机都预装了比尔·盖茨编写的BASIC程序。”作为比尔粉丝的我肯定是不会放过要在自己的运行着Win95的电脑中找找看滴,却始终不得其钥匙(现在想想觉得那本书就不是给这个“世纪”的人写的)。后来在另外一本书上发现了一个名词“Basic Input Output System”,简称是BIOS,便以为这就是比尔的杰作。虽然让我找到了,却始终搞不清楚怎么用它来写程序。毕竟自己水平很凹,时间久了也就把这件事搁下了。直到高中毕业,时间突然多得花不完,在万般聊赖之时又翻到了“Basic Input Output System”这玩意。学过英语很多年的我瞬间被融化了:基本输入输出系统,跟BASIC没有半毛钱关系啊。没文化真可怕。
我想绝大多数人现在使用的电脑跟我当年用的在体系结构上也不会有什么太大变化。别跟我说你用的是苹果机,那玩意儿早就改intel体系了。如果不是,您还是当古董珍藏起来比较合适。所以当你按下电源按钮,就开始有奇迹发生!会有什么奇迹呢?来看看。
此时此刻,不管你内存有多么高端,里面内容都是乱的。别说没有Linux,连毛线都没有。不过很快就发生变化了。有一个特殊电路,它会“敲打”一下CPU的被称作reset的脚(别问我这只脚在那里,好几百只呢,我也搞不定!知道有这么回事儿就行了)。然后CPU就开始起变化了。一些寄存器,比如cs(从CPU外壳上是看不到滴)、eip凭空会出现一些固定的内容。cs足OxFFFF,eip是Ox0000。这就是告诉CPU从物理内存地址OxFFFFO处开始执行代码。那里有什么代码呢?就一条,让CPU跳转。告诉CPU:跳吧!跳吧!跳下去,你就会融化在BIOS里!
BIOS是我们电脑最开始执行的程序。它会读取CMOS中的信息来了解硬件情况,比如CPU的总线时钟、启动设备的搜索顺序、硬盘大小与型号、系统时间、即插即用设备、各设备的I/O地址、中断请求等。之后BIOS开始做开机自检,比如确定可用的内存(有时候还要测试一下)、确定时钟速度等。如果测试成功,会发出“畔”的一声告诉你没问题(有问题会狂叫)。接着开始对硬件进行初始化、设定即插即用设备、确定启动设备……最后从启动设备中读取MBR,并将控制权交给MBR中的程序。
BIOS这东西儿乎是所有PC的标配,历史甚为久远,不过由此上溯至1842年还是没必要的,追溯到1984年是没人站出来不同意的。怎么看也算是个老革命了吧!可是它却是个咧执的死心眼儿。甭管你是什么操作系统,它从来不买账,一定要从MBR做入口。所以作为操作系统,不跟MBR打好交道,就别指望能在电脑上过得好。这也导致了好多操作系统在这个地方争得不可开交,甚至斗得头破血流。不过死心眼也有它的好处,就是BIOS会始终默默无闻地坚守着它那没有繁花似锦却硝烟弥漫的岗位,心中默唱:我愿铺起,一条五彩的路。让操作系统去迎接黎明迎接欢乐。心甘情愿地做着雨花石。
10.2 小肚鸡肠的MBR(主引导记录)
MBR是Master Boot Record的缩写。中国人……哦不……应该是我们国家的人都叫它主引导记录。这是一个老革命(BIOS)认准了的主儿,地位自然不容小觑。不过这可是一个小肚鸡肠的主儿。
10.2.1 MBR的结构
好多人将Boot Sector(引导扇区)与MBR混淆在一起说。
严格上讲应该是Boot Sector 引导扇区是由MBR、DPT(Disk Partition Table,磁盘分区表)和BRID(Boot Record ID,引导记录标识)三部分组成的。
Boot Sector在硬盘上也就512字节,还不是MBR独占。知道有多小肚鸡肠了吧?
BIOS读MBR是不管什么DPT的,会将Boot Sector的全部内容读到内存中。读到什么位置呢?反正不管有多少爱可以胡来,BIOS是不会胡来的,总是会将它们读到地址是Ox7c00的内存位置,然后跳转到那里,将控制权交给MBR。这也就决定了MBR -定是在Boot Sector最前面,否则BIOS就将控制权交给DPT了。
BIOS-》启动设备-》boot sector引导扇区512字节(MBR446字节+DPT64字节+BRID2字节)-》grub-》内核
实际情况是,DPT要占用64个字节,Boot RecordID要占用2个字节,所以留给MBR的只有446个字节。
从个头上看是MBR>DPT>BRID的,实际上也是这么排队的。
这个Boot Sector就是硬盘(严格说是所有的可引导的存储介质)上的第一个扇区。可见BIOS的最后那么一抖,就是读在CMOS中被设为启动设备的第一个扇区,并将控制权交给它。BIOS虽然死心眼,可也不傻。不是在“第一扇区”的它都认,必须要骏明正身才行。BIOS要检查是否有BRID这东西,也就是Boot Sector的最后两个字节。看它是不是“Ox55AA”这样的内容。不是就玩死机,是就交权。至于交权后MBR都干了什么,BIOS还真挺放心的。
10.2.2磁盘分区表
到这取你呵能会问:刚才说磁盘分区表DPT只有64个了:节,那我的一块2T硬盘,分了10个区了,64个字节够用吗?够用,绝对够用。我还要告诉你,一个分区需要16个字节来描述。
估计你现在会在想,我小学数学是语文老师教的吧。其实还真是。我上小学时,班主任负责教语文和数学。但是这不影响我的算数成绩。因为硬盘分区表只能描述4个分区。至于你怎么分了10个区,那是个技巧问题。当然那不是你的技巧。为了说明这个问题,我们先看一个表来描述一下DPT表项的结构,见表10-1 DPT表项的结构。
表10-1 DPT表项的结构
偏移量 |
长度(字节) |
定 义 |
Ox00 |
1 |
分区状态:Ox00非活动分区;Ox80活动分区; 其他数值没啥意义 |
Ox01 |
1 |
分区起始磁头号 |
Ox02 |
2 |
分区起始扇区号,Ox02的0~5位 分区起始柱面号,Ox02的6~7位和Ox03的全部8位 |
Ox04 |
1 |
文件系统标识位,Ox83就是Linux分区 |
Ox05 |
1 |
分区结束磁头号 扩展分区 |
Ox06 |
2 |
分区结束扇区号,Ox06的O~5位 分区结束柱面号,Ox06的6~7位和Ox07的全部8位 |
Ox08 |
4 |
分区起始相对扇区号 |
OxOC |
4 |
分区总的扇区数 |
注意表10-1中的最后一行,分区的总扇区数是4个字节,32位。也就是说,最多能表示4294967295这么大昀数。既然一个扇区是512字节,那么一个分区的大小就被限制成4294967295x512=2T这么大。可以描述4个分区,顶天了就能支持8T大小的硬盘。再大就没辙了吗?有办法,使用GPT,叫GUID分区表(GUID PARTITION TABLE)。这玩意单个分区支持18EB。牛啊,那得多少块硬盘拼啊?我想,大部分人短期内见不到,我也就少废话了。反正Linux支持,遇到了自己问google吧。
我们还是回到10个分区怎么分出来的问题上。注意DPT表项的Ox04偏移量的位置,文件系统标识位。表上说明Ox83是Linux分区。还有一个标识是Ox05,表上没说,这里说明一下。这代表扩展分区。扩展分区也有类似DPT的东西,来描述逻辑分区,而且没有什么大小限制,可以分出任意多个逻辑分区。显然分10个是不算什么的。只是不管逻辑分区有多少,总和不能超过2T。
既然引出了扩展分区,又引出了逻辑分区,那么在DPT中剩下的三个叫啥?答:主分区。其实扩展分区也是主分区的一种,只是因为可以包含逻辑分区才这么命名的。而且扩展分区不能直接用,必须至少包含一个逻辑分区。而且Linux才不管是主分区还是什么逻辑分区,都能容身。不过Linux对待主分区和逻辑分区还是有差别的。所有sdal~sda4都分配给主分区,不管你有没有分配;逻辑分区的分区号都是5开始的。所以在Linux上看一下分区所对应的设备文件名,就能知道是主分区还是逻辑分区了。
10.2.3 MBR的功能
东拉西扯一直不着边,就没说到正题上。正题应该是MBR。这东西小肚鸡肠的就446个字节的心胸,而且也不是什么东西,就是一段空间。这么小的空间就别指望能利用它作出什么惊天地泣鬼神的事情了,当然病毒除外。正事儿就很简单,以它为跳板,找其他体量大的兄弟交权了事儿。
10.3和事佬GRUB(牛B的统一引导加载器)
BIOS死心眼,MBR小肚鸡肠。惹得操作系统们是打得你死我活、人仰马翻!事情到了这份田地,结果就有人看不下去了。于是请出了和事佬,几面调停,终于摆平了这场祸端。
10.3.1 Bootloader
当今公认的和事佬非GRUB莫属。GRUB是Grand Unified Bootloader的简称,翻译过来就是“牛B的统一引导加载器”。牛B是了不超的,因为牛B是要有真材实料的,没料的就是装13。几乎所有的Linux发行版都使用它。而且还能驱使一直仰着高贵的头的Solaris从10 1/06版开始也低下头采用GRUB作为它的引导加载器。在类UNIX这个世界里,GRUB是绝对的老大,很有领袖的范儿!自然是做和事佬的绝佳人选。
其实到底是牛B还是装13都是不重要的,因为那些都是形容词。重要的是名词,即BootloadeI-引导加载器,这是个什么玩意儿?Bootloader是在操作系统内核运行之前运行的一小段程序。它负责将操作系统内核装入内存,然后将电脑控制权交给操作系统。它还会搜集电脑的硬件信息、初始化硬件设备、安排内存布局,从而将电脑的软硬件环境整理得窗明几净、井井有条,为操作系统的内核布置了一张温暖的大床。
其实这并不是最重要的功能,因为这些BIOS都能干。由于死心眼的BIOS是始终坚定不移地去读取MBR并跳转。而小肚鸡肠的MBR也没什么能耐。哪个操作系统占了,这台电脑就是哪个操作系统说了算。生活在万恶的资本主义社会的电脑工程师们哪能允许这种*?所以制订了“多启动规范”这部“宪法”,允许用户*选择该由谁来管理他/她的电脑。很多Bootloader孰是这部“宪法”下的产物。
本来Linux也是拥有一颗*的心的,内核中早就备有具备这个能耐的代码的。本想与BIOS唱天仙配的,结果现在也只能隔GRUB遥相望了。况且某些内容一旦放入内核就很难改了(加入内核启动参数)。毕竟人的需求总是千奇百怪的,很难保证适合所有人的胃口。而且这个世界上,先知是没有的,问题总是要等到出现了才去想办法解决。所以,Linux内核自备的那些启动加载代码就先一边儿凉快儿去吧。
10.3.2 GRUB的功能
在早期的Linux发行版上,普遍采用的遵守多启动规范的Bootloader是LILO。据官方称LILO是Linux Loader的缩写。但是我怎么看都是“Logln LogOut”的缩写。你这一进一出的,吃伟哥也坚持不了多久啊!结果没几年,就被GRUB干掉了。
GRUB是个很牛B的Bootloader,主要表现在以下几点:
1. GRUB是动态可配置的。它在启动时读取配置信息,且允许启动时修改。
2. GRUB提供菜单供用户选择要启动的操作系统。通过向配置文件中添加相关信息,
GRUB可以控制150个甚至更多的菜单选项。在启动时可通过键盘的方向键,甚至
鼠标进行选择。
3.GRUB支持的操作系统非常广泛。它能够支持多种可执行格式。不但能够启动支持
多启动规范的操作系统,还能通过链式启动功能支持诸如Windows和OS/2之类的
不支持多启动规范的操作系统。不但支持所有Linux酌文件系统,还支持Windows
的FAT、NTFS,更支持LBA模式。甚至允许用户查看它所支持的文件系统里的文
件内容。
4. GRUB具有多种形式的用户界面。多数Linux发行版利用GRUB的图形界面,提
供带有漂亮背景的启动菜单。甚至还能支持鼠标操作。如果使用文本界面,还能利
用串口实现远程终端启动。
5. GRUB拥有丰富的控制台命令与用户交互。载入操作系统之前,在GRUB的交互
菜单界面下,按c键可以进入控制台。这个控制台与Linux的控制台很相似,提供类似shell的功能。通过命令,可以查看硬盘分区的细节,甚至修改分区设置。不但可以修改自己的配置信息,还能查看其所支持的文件系统中其他Bootloader的配置信息。即便在GRUB的配置文件丢失的时候,依然可以进入这个控制台,手工启动操作系统。
6. GRUB可以从镜像文件中启动操作系统。GRUB可以在它所支持的文件系统中找到
操作系统的镜像文件(Linux内核映像),并启动这个操作系统。甚至可以通过网络下载操作系统镜像,
因此它可以支持无盘工作站。
7. GRUB支持链式启动。所谓链式启动就是一个Bootloader可以启动另一个Bootloader。通过这种方式就可以肩动如DOS、Windows、OS/2等系统。GRUB4DOS
8. GRUB支持多种语言。这其中包括中文,因此可以设计非常友好的启动界面。
上面我只是罗列了一部分功能,显然是已经非常了不起了。我对GRUB的开发者们的敬佩之情真可谓是有如滔滔江水,连绵不绝。我非常怀疑,这个世界上是否有哪位牛人就直接拿GRUB当操作系统使用了。
10.3.3 GRUB的工作流程
目前GRUB所使用的GRUB的全称应该是GNU GRUB。也就是GNU组织山寨出来的。而且这个才是真正牛B的GRUB,之前说的功能都是它的。真的GRUB已经不知道去哪里凉快去了。可见山寨货不都是次品。现在的GRUB已经更新到第二代了(版本1.98)。第一代也很流行,所以也不能放过。
第一代的GRUB使用三段式引导启动stage1-》stage1.5-》stage2。
第一阶段是占MBR的坑,这是对付死心眼儿的BIOS的唯一方法。但是GRUB要发挥功能,在MBR的那点地盘上折腾不起来,只能叫个大的兄弟上。编译好的GRUB对应这个阶段会产生一个名为stage1的文件,大小一定是512。(gnu grub软件也要编译)
由于第一阶段太挫了,所以第二阶段只能放在紧接在MBR后面的固定位置,因为放别的地方会找不到。这里有大概30K左右的代码。主要是文件系统的驱动程序(/boot分区)。目的就是为了能够找到名为stage2的文件。这个文件的内容是GRUB的主功能代码。那既然MBR的代码是stagel,主功能代码是stage2,显然没有第二阶段啊!还真是.官方从来不叫第二阶段,这只是我的理解。对应这一阶段编译出来的文件名是stagel_5,所以官方说这是1.5阶段。设置这一阶段是为了stage2可以乱放。可是30K左右的代码并不能支持所有文件系统啊。所以这阶段的代码是不确定的。stage2放在什么文件系统上,这部分就是什么文件系统的驱动。
由于stage2受文件系统管理,所以在空问上基本上就没什么障碍了。那就:骏马,奔驰在辽阔的草原上吧!什么功能都能提供了。这就是我说的第三阶段,提供一切能够提供的功能。
第二代的GRUB是最近开始流行起来的。既然是二代了,肯定是进步不小的。最主要的是不用手工编写配置复杂的配置文件了,但是让玩Linux的不写配置文件那是不现实的事情,所以只能是极大地简化。简化到只需要提供几个参数,或者回答yes or no就行了。
其实所谓的第二代GRUB,官方开始时是不承认的,只是说的人多了也就这样了。普遍意义上认为的第二代是GRUB 1.98版以后的。不过这不影响人们对它的称呼,说GRUB2没人会觉得你2。
GRUB2改变了引导启动流程,同时在架构上也有非常大的变化。
GRUB2使用四个阶段。而且采用了比较霸道的方式,即必须提供IM酌空白空间,来放置它的核心代码。这IM的空间放在哪儿无所谓,但必须放在2T以内,否则找不到。所以有大硬盘的朋友要万分注意这个问题。
GRUB2的第一阶段与GRUBI相同,也是占MBR的坑。这阶段的代码改名了,叫boot.img。更贴切一些了。当然大小是亘古不变的512。它不去找什么stagel_5了。而是去那1M的空间中找diskboot.img。
这个diskboot.img就是第二阶段的代码,大小也是512。是GRUB核心文件core.img的第一个扇区。这阶段的代码就是继续将core.img文件余下的部分读到内存中,然后继续执行。
第三阶段比较有趣,开始加载内核模块。注意这不是Linux的内核模块,是GRUB的内核模块。这样设计的好处是core.img可以做得很小,可配置性和扩展性非常高。
第四阶段才是启动操作界面,供用户选择要启动的操作系统。
grub2 :boot.img512字节-》diskboot.img1M-》core.img
grub2:第一阶段(boot.img512字节)-》 第二阶段(diskboot.img1M )-》 第三阶段(core.img)-》第四阶段启动操作界面选择要启动的内核
grub1要三个阶段
grub2要四个阶段
说来说去,不管GRUB是一代还是二代,都脱离不了MBR。其实这个不是绝对的。完全没必要跟死心眼儿的BIOS较劲。GRUB的第一阶段代码是可以安装到别的地方的。不管是主分区还是逻辑分区,当然必须是第一个扇区,而且要求所安装的分区是活动分区(df查看/boot分区所在分区有* 活动分区)。微软实际上是一个很大公无私的企业。当年在设计DOS的分区管理程序时,并没有为我独尊的想法,即便到现在也还是个好人!被DOS或Windows管理的MBR中并没有与操作系统相关的引导程序。它只是扫描并读取随后的分区表,找到相应的活动分区,读取相应活动分区的第一个扇区的程序并交权。所以完全不用抱若MBR的大腿不放。而且将GRUB的第一阶段代码安装到其他地方,还可以防止有人利用DOS的fdisk /mbr命令的破坏。何乐而不为呢?
/boot/grub/device.map文件
cat /boot/grub/device.map
(hd0) /dev/hda
grub使用的设备名称和Linux的不太一样,这是一张映射表。
(hd0,2)对应/dev/hda3;(hd0,4)对应/dev/hda5
10.4唇齿相依的内核启动参数
终于要说到内核了,但是还要注意一点,在这个世界上,每一个运行着的Linux系统,都可以有一颗与众不同的内核。那是因为,每一个运行着的Linux系统,都可以有不同的内核启动参数组合。
10.4.1 什么是内核启动参数
学习代数的中学生们开始知道方程式是有参数的:电脑用久了的“小白领”们也会知道很多软件是有参数的;软件工程师们肯定知道很多函数是有参数的。这个世界怎么这么多参数?人们在这个世界极度缺少“先知”这种珍惜资源的情况下,只能用试错这种方法来解决遇到的问题。久而久之就发明了参数这个东西。所以参数是可以乱变的,错了就变,直到对了为止。既然Linux的开发者们跟我们是一个世界里的人,身在五行中就跳不出三界外,也无法保证Linux内核可以解决所有问题。所以让Linux内核支持启动参数,是最英明神武的决策。
所谓内核启动参数,就一定是在Linux内核启动的时候传给它的参数。否则就是内核运行时参数①了。其主要目的是,当内核无法识别某些硬件导敢不能设置硬件参数时,或者不希望内核更改某些参数值时,抑或希望内核表现某些特性时,让Linux内核具备人工干预的途径。这也是必须选择GRUB等Bootloader去启动Linux的具有绝对杀伤力的理由。因为直接让BIOS加载Linux内核将无法传递任何启动参数。
①Linux 是具备内核运行时参数的,我们后面会讲到。
10.4.2 内核启动参数的格式
Linux的内核启动参数是以空格分开的一个字符串列表,一般的格式如下:
name[=value_l][,value_2]…[,value一10]
“name”就是具体的参数名,内核根据这个名称来决定应该把参数值交给谁来处理。如果某个参数名找不到主儿,内核也不会轻易地把它丢掉,而是会传递给后面我们要讲解的init进程来处理。每个参数名所对应的值的数量是有限制的,最多10个,最少可以没有。但若你一定要跟这个数量上的限制较劲也不怕,可以重复使用同一个参数名来传递任意多个值①。
内核的参数名是大小写敏感的,比如mem和Mem是不同的参数。而且命名也是有规则可寻的。首先,参数只能由26个英文字母的大小写加上“.”和“”这些符号组成。其次,如果在参数名中出现“.”则表明这是传递给某一个内核可加载模块的,如果对应的模块没有加载,实际上是不起作用的。这个时候参数名的格式如下:
module_name. parameter_name
例如:acpi.debug_level=l这个启动参数是给acpi②模块传递debug_level参数,其值是1。
①比如:abc=0,1,2,3.4.5.6,7,8.9 abc=10,11,12.13.14.15,16.17,18,19。
②高级配置和电源管理接口( Advanced Configuration and Power Interface)。
内核配置文件 深度实践KVM P49
/boot/config-2.6.32-504.el6.x86_64
CONFIG_CRC7=m
CONFIG_LIBCRC32C=m
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=m
CONFIG_LZO_COMPRESS=m
CONFIG_LZO_DECOMPRESS=m
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_BZIP2=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_REED_SOLOMON=m
CONFIG_REED_SOLOMON_DEC16=y
CONFIG_TEXTSEARCH=y
CONFIG_TEXTSEARCH_KMP=m
CONFIG_TEXTSEARCH_BM=m
CONFIG_TEXTSEARCH_FSM=m
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
CONFIG_CHECK_SIGNATURE=y
CONFIG_CPUMASK_OFFSTACK=y
CONFIG_CPU_RMAP=y
CONFIG_NLATTR=y
CONFIG_AVERAGE=y
10.4.3常用的内核启动参数
最常见的内核启动参数有如下这么几个:
●init=……:设置init进程的位置。如果没有明确给出这个参数,内核会按照
/etc/init、/bin/init、/sbin/init、/bin/sh这个顺序来搜索,如果没找到,Linux就会宕机。
●root---…:该参数告诉内核启动时使用哪个磁盘分区作为根文件系统。比如可以
指定根文件系统为/dev/sda5:root=/dev/hda8。这个参数必须指定,否则Linux将无法正常工作,甚至因为找不到init进程而宕机。
● ro和rw:该参数告诉内核启动时如何挂接根文件系统。ro参数告诉内核以只读方
式挂接根文件系统,这样可以有效防止根文件系统被破坏。另外,如果在启动时需
要对根文件系统进行fsck,则必须以只读方式挂接。rw正好与ro相反,:允许读写
根文件系统的内容。如果不指定这个参数,则默认是ro。
● mem=……:限制或设定内核使用的内存数量。我就曾经遇到早期Linux版本无法
正确识别我p4电脑内存大小的情况,导致无法启动。使用mem参数指定正确内存
大小后问题解决。
●initrd=……:指定内核初始化用ramdisk的位置。这个功能非常有用,后面我们
会
● rhgb:这个参数只能在RetHat系的Linux发行版中看到。表示启动RedHat Graphics
Boot功能,就是让启动界面好看点。用图片或动画代替启动过程中显示的文本信息。
● enforcing=……:启动了SElinux导致进不了系统,这时候可以设置enforcing=0关闭SElinux来进入系统
● quiet: quiet参数是让内核采用静默方式肩动,即尽量少输出启动信息。
● nosmp和maxcpus=N:这两个参数只有在拥有多颗CPU的时候才起作用。nosmp
参数用来禁止开启支持多个CPU的功能。maxcpus则是限制最多支持的CPU个数。
现在人们都巴不得使用smp技术来提高运算速度,提供这种参数的人是不是蛋疼
呢?不要忘了存在就是道理这个重要的哲学观点。当Linux运行在虚拟机上的时候
经常会出现计时不准确的情况,这两个参数就能解决这个问题。
由于Linux的内核启动参数太多,举不胜举,有这么多也差不多够了。为啥提了10个却写了8条呢?中国人喜欢8这个数字嘛,我也喜欢!况且十全十美的东西是不存在的。
不知道大家注意没有,我所列举的这些内核启动参数都是必须是在内核启动之前就应该指定的,否则就会无法启动,或者得不到预想的效果。这不正好说明它们是内核启动参数而不是内核运行时参数了吗?正所诏唇齿相依,Linux内核是齿,则内核启动参数就是唇。如果内核启动参数设定不当,内核也很难正常工作。唇亡且有齿不寒之理?
至于这些参数放在什么地方、内核怎么处理、如何处理、何时处理等问题已经超出本书的范围,有兴趣的可以看看内核代码(我相信,如果你能完全掌握并融会贯通本书所涉及的内容,是有能力读懂绝大部分内核代码的)。内核源代码中附带的Documentation/kemel-
parameters.txt文件详细介绍了官方内核所提供的大部分内核启动参数。
10.5 自力更生的Linux内核 内核解压-》startup_32 -》start—kemel-》kernel init
BIOS把路已经铺稳了,GRUB借着MBR的地儿也把床给烘暖可以就枕了,内核启动参数全都弄准了。到了这个时候,Linux内核你再不自力更生的确有点对不起追你的粉了。接下来就是内核初始化的过程,看看它是怎么自立更生的吧。
10.5.1 内核引导协议
在这个时候Linux内核给Bootloader们两个选择。
第一是回到实模式①,让内核自己重新来过;
第二是Bootloader在保护模式②下直接交权给内核,内核继续接下来的工作。GRUB选择了后者。
不管Bootloader们作出了何种选择,都必须将内核需要的数据按照特定格式准备好,而且还专门制定了一个规范。这就是Linux的引导协议(Boot Protocol),有兴趣的可
以查阅内核源代码中附带的Documentation/x86/boot.txt文件。
①x86系列CPU的默认工作模式,16位字长,20位寻址,最高支持1MB内存。
②x86系列CPU的常用工作模式,32位字长,32位寻址,支持多任务,最高支持4GB内存。
包含模式:Linux就这个范儿P545 七种武器 ,实现虚拟内存技术主要有两种方案:段式内存和页式内存管理。这两种方案的目的只有一个,就是让多任务操作系统能够将
多个进程的地址空间保护起来,让它们互相隔离,使得它们不会互相打架。保护模式的叫法也来源于此。
10.5.2 内核为什么要压缩
不管怎样,内核想继续工作,都必须对自己进行解压缩。至于为什么要对内核进行压缩,这是一个由来已久的问题。由于最早的Linux是在x86平台上开发完成的,而这个平台的一个最大毛病就是开机时处于实模式下工作,最多只能访问1M内存,而且还有一大部分被BIOS占去了,所以就有了年长一点的人都了解的DOS 640 K①的问题。早期的Linux比较小巧,就几十K的样子,所以也不是个问题。而且那个时候大多数人都认为640K足够用了,也没人心这个问题。到后来Linux的功能越来越丰富,内核大小很快就超过了这个限制。但是再回头解决这个问题就有些来不及了。那怎么办呢?压缩吧!这个时候就有个zlmage这个内核文件格式,最大可以支持512K的压缩内核镜像。同时又引入了可加载模块技术,内核镜像可控制的很小,人们以为这下可以高枕无忧了。但好事不长。Linux为了与Windows等主流操作系统争夺市场,不得不继续丰富自身的功能。很快人们就发现即便利用可加载模块技术也很难将zlmage格式的内核镜像文件控制在512K之内,就又发明了bzlmage这个格式。其实这才足高枕无忧的开始。bzlmage格式的内核镜像要求Bootloader将它读到1MB以上的内存空间。而这个时候就要求Bootloader必须进入保护模式才能完成这个操作。因此也就有了前面所说的Bootloader们的两种选择。这个时候内核开发者们可没有大意,一直保持了内核镜像是压缩的。而且由于Linux的广泛应用,很多嵌入式设备也开始采用Linux作为操作系统,压缩过的内核镜像可以给这些嵌入式系统节省很多存储成本。所以,对Linux内核镜像进行压缩就被当作一个优良的传统给保留下来了。当然不是所有硬件体系的内核镜像都是压缩的,比如PowerPC②上就不用压缩。因为将Linux系统移植过去的时候人家本来就不存在640K这个问题,况且也很少有嵌入式系统应用这种架构的CPU。
①DOS留给用户的最多只有640K的用户空间,要想使用更多的内存,需要很多特别的技术。比如页而映射。将大于1MB的内存空间的一小块映射到640K以内。用完了再重新映射。
⑦由1BM设计的精简指令集架构的CPU,Mac电脑曾经使用过。当前我们最容易见到的是在各种游戏机上的应用。比如XBOX360、PS3和WII。
10.5.3 startup_32 它代表Linux的第一个进程——process 0的开始
内核自解压完毕之后将跳转到startup_32这个函数继续执行。这个函数是完全用汇编语言写的。内核源码中叫startup_32的函数有两个。第一个是在内核解压缩之前,调用内核白解压缩代码用的。这里要说的是第二个,是内核解压缩后的。比较有趣的是,这两个同名的函数都是用汇编写的,而且都在名为head。S的文件中定义。当然也是两个不同的文件,相同的只是名称和语言。不用担心会由冲突,导致内核乱执行。毕竟一个是压缩前的,一个是压缩后的。况且名称都是给人看的,电脑看的是地址。
这个startup_32对于整个Linux系统来说意义是非同小可的,它代表Linux的第一个进程——process 0的开始。这里,决定了Linux的内存布局;这里,Linux了解了自己将要与什么样的CPU在一起;这里,会把由BIOS提供和Bootloader们整理的资料放入内存的第一个分页里;这里,为以后要发生的中断请求做好准备①;这里,如果是64位系统,将把长模式②开启;这里,将跳往Linux内核的主函数-start kernel,开启Linux内核真正的生命之旅。
10.5.4 start kernel Linux内核的入口main函数:start—kemel
每一个C语言写的程序,都有入口函数叫main。Linux内核也有这样的函数,叫start—kemel。它就静静地躺在内核源代码的init/main.c这个文件中。其实这个函数跟大家想像的不太一样,它跟具体的硬件关系不大。大部分时间都是在为Linux内核准备必要的数据结构。比如:用于内存管理的那些数据结构,用于任务调度的那些数据结构,用于处理多任务同步的数据结构,用于处理中断的数据结构,用于文件系统管理的数据结构等。反正经过对这些数据结构的初始化,Linux此时已经具备一个操作系统应该具备的功能,各种子模块都可以良好运行。在这之后,start—kemel会创建一个名为kernel init的内核线程,然后进入自己的最终归宿-cpu_idle函数,进入无休止的循坏当中。
这个cpu_idle函数就是我们常见的CPU占用率经常是99%的进程。进程的pid是0。只要你看到它对CPU的占用率还在80%以上,就说明你的系统还很闲。虽然这只是一个死循环,而且优先级很低,但是功能不可小觑。它会一直调用CPU的idle指令,让CPU降温③且省电。
①x86系列CPU的中断描述符表在这里初始化,目前全部置0。
②最新的x86_64系列CPU的一种新的运行模式,相对于32位的保护模式,长模式是64位的,兼容32位程序。但是内存的寻址方式,AMD和Intel两家有不同见解。
③很多所谓给CPU降温的工具,其实就是强制执行idle指令。
④由于Linux的内核是一个整体,在它内部产生的多任务分支都叫内核线程。内核线程在被映射到用户空间时,很多都是我们通常所认识的进程。
kthreadd进程的pid是2,ppid是0,kthreadd是所有内核线程的祖先
ps axjf|grep kthreadd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
10.5.5 kernel init
如果你查看的是较老的内核源代码,或许找不到kemel init线程所对应的函数。没关系,这是因为较新的内核才这么叫。老的内核都叫init内核线程。不过在我看来新的称呼更加贴切。因为kemel—init内核线程实际上是Linux系统最为重要的init进程的内核部分。新的称呼更能体现它是在内核中这个特性。这个是Linux系统的第二个进程④,这也是init进程的pid -定是1的由来。而且在最新的内核代码中,由于必须在创建kemel init之前创建另外一个内核线程来做一些重要的事情,则不得不先创建kernel init,并把它锁起来。然后再创建新的内核线程,然后kemel_ init等到它执行完后解锁继续执行。如果不这么做,init进程的pid就会是2。那样的话,接下来所有的程序都可能会变的很2了。
Kemel_init内核进程比较重要的工作就是让Linux内核开始与外面进行沟通。比如:寻找用户指定的根文件系统(initramfs来做,先有根文件系统才有后续的加载硬件驱动程序)、给硬件加载驱动程序、初始化网络堆栈、在多CPU的系统中让其他CPU开始工作等。当一切都稳妥之后,kemel init会调用用户空间的第一个进程-init程序。这个时候,内核的初始化工作就完成了,Linux系统向用户空间开启了大门。
10.6 及时雨initramfs
历经千辛万苦的自力更生之后,Linux系统终于要与广大粉丝们见面了。等等!偏偏就在这个时候Linux也开始犯BIOS死心眼儿的毛病了。跟粉丝见面就一定要经init进程的介绍!如果内核找不到init进程肿么办捏?粉丝们,拜拜……
10.6.1 寻找init的问题
粉丝们一般会把init进程放在根文件系统中。如果Linux找不到根文件系统就没戏了。通常,可用的文件系统都会罗列在/etc/fstab文件中,所以mount命令可以找到它们。但是/etc/fstab既然是文件,就必须呆在文件系统中。这样,Linux找根文件系统的过程就成了鸡生蛋蛋生鸡的问题。为了解决它,内核的开发者们提供了“root=”这个内核启动参数,来指定哪个设备作为Linux的根文件系统。不过,这要是穿越到十几年前还是个不错的主意,也就是软盘或硬盘的差别。到如今可谓是技术百花齐放的年代。什么SCSI啊、SATA啊、RAID啊,五花八门举不胜举。而且位置都开始不固定了。比如U盘,一个电脑上好多USB扎,插哪儿了只有谁用谁知道,有的还带加密的。这你让Linux内核情何以堪啊?有人就建议,让内核带上全部驱动就行了。虽然是个馊主意,但总比没有强。可是这年头不但技术多,山寨的也多不是。某些山寨为了表示自己不山寨,还会采用特别的技术手段,比如改改某些参数什么的,并美其名日自主创新。这么折腾,Linux内核伤不起啊!好在及时雨initramfs的来到,给Linux和它的粉丝们撑起了一片广阔的天空,让山寨们折腾去吧!
10.6.2 较早的initrd
initramfs也算是个新人,不过也只能算是个相对的了。之前它有一个同行的老大哥,叫initrd。我在介绍内核启动参数的时候就提过一句。现在我们就来详细了解一下。
initramfs和initrd干的工作是一样的,只是机制不同罢了。initrd实际上就是在内存中开辟了一块模拟的磁盘空间。在Linux内核启动之后,先将这个模拟的磁盘当作根文件系统,并执行initrd当中的/linuxrc文件。linuxrc文件就是一个脚本程序,负责加载那些内核要访问的真正的根文件系统所必须的驱动和挂接真正的根文件系统。initrd对于Linux发行版和LiveCD是非带有好处的。比如作为发行版就要尽可能多的适应硬件环境。但是做一个超大内核显然是会被人喷的。这个时候就可以请initrd来帮忙。在系统安装阶段,检测用户的硬件特性并找到用于作为根文件系统的设备的驱动,并将这些放入initrd中。如果在安装过程中发现没有附带合适的驱动时,还能请求用户拿出设备驱动程序盘来安装。这样就基本不怕找不到init进程了。如果用户拿不出合适的驱动,还可以人性化地告诉用户:您的电脑不适合安装Linux。“科技以人为本”是亘古不能违反的真理!
10.6.3为什么选择initramfs
前面说过initramfs和initrd干的是一样的活,那要它来作甚?原因就在于机制的不同。initrd是一个ramdisk,ramdisk必须是某种文件系统,比如ext2。如果Linux要访问initrd,就必须带有相应的文件系统驱动。initramfs是一个使用gzip压缩的cpio打包文件。Linux内核会将它的内容装入一个tmpfs,这就不需要附带任何文件系统驱动就能工作。而且ramdisk大小必须固定,tmpfs却可以动态扩展。当然,这些都是一些小的技术差别。比较大的差别就是如何对待init进程这个问题。现在的Linux开发者们有了一个十分大胆的设想,就是尽早进入用户空间( Early Userspace),尽最大努力把所有能在用户空间完成的工作都不用内核去做。使用inirrd就必须在内核空间完成磁盘的挂接工作,这显然是可以在用户空间完成的。于是在继LVM、Udev等之后,开始动起了initrd的主意。于是就有了initramfs这玩意儿。
initramfs不再需要linuxrc这个文件了,取而代之的是直接启动init进程。initramfs由Bootloader准备好,告诉内核它在什么位置。内核知道了之后在kernel—init内核线程中也不挂接什么根文件系统,直接启动initramfs中的init进程。接下来去找根文件系统的活儿就都交给init进程。这下永远都不怕找不到init进程了。所以initramfs是个真正的及时雨,毕竟它比initrd要先走好几步呢!
或许很多人会问,那现在根分区中的init程序是干嘛的?这是个备份,如果没有initramfs,内核就会自己去挂接分区去找它。况且用户还需要使用init程序给init进程发送消息来切换运行级别呢!现在Linux依然提供“root=”和“initrd=”这两个内核启动参数。因为initrd并没有完全地退出历史舞台。
10.7 “生”、“死”永相伴的init进程
当Linux内核启动init进程之后,便“陷入”了人民群众的汪洋大海,开始了其真正的生命之旅。不过这个旅程并不孤单,永远有init进程与它相伴,一同去经历那六道轮回之苦。
10.7.1 名不副实
如果非要给Linux系统的初始化设定一个期限的语,我想是从开机到屏幕出现登录界面为止。既然Linux认准了init,而且从名字上一眼就能看出它就是用于初始化的,而且它也的确这么做了。init进程会根据用户设定的一个叫运行级别的指标来对Linux系统进行一系列的初始化过程。比如:启动对应运行级别的服务进程、配置网络、继续为某些硬件加载驱动、运行相关软件等。不过你要是认为init进程就干这些事情那就大错特错了。
init进程会接受用户的“CtrI+Alt+Del”三键大法,责令Linux系统重新启动;即便Linux系统已经运行于某个级别之上,用户还可以通过init切换到刖的运行级;当用户要关掉Linux系统,init进程会默默地将还没有来得及保存的文件保存好;如有用户希望某个程序需要一直运行respawn,可以告诉init进程,那就谁都kill不掉这个进程例如mingetty;init进程还会认养所有孤儿进程,如果某个程序要判断父进程是否死掉了,可以通过确认当前的父进程是否是init进程来判断。等等。所以init进程是名不副实的,它不单单是做初始化,几乎就是Linux系统的守护神。按照Linux的命名规范,我个人认为叫systemd是最合适不过的了。
要说systemd,还真有这么一个玩意儿,跟init进程做的事儿还差不多。而且在Linux这个无比*的世界中,init并不是某个特定程序的名称。它只是代表一个过程。只要一个程序,甚至是一组程序,能够完成这一过程,在Linux的眼里,它们都是init。而且还有“init=”这个内核启动参数,谁还会在意完成这个过程的程序叫什么名字呢?所以在不同的发行版中就有可能使用不同的程序来完成这个过程。比如:最古老的Slackware使用的是sysvinit;Ubuntu,RHEL和Fedora使用的是Upstart;而最新的Ubuntu和Fedora又开始使用systemd了。*的世界是一个混乱的世界,但这混乱中却充满了竞争。init这个名字掩盖了那背后各种实现策略的博弈,也让我们有所从有所不从。至于谁能在这混乱的竞争中胜出,或永久消亡,只能让历史去做个见证。而Linux要执行init过程的这个机制就像真理一样,从来没有改变过。
浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd
http://www.cnblogs.com/MYSQLZOUQI/p/5250336.html
10.7.2运行级别
运行级别(run level)这个称呼听起来确实很唬人,给人极其神秘又科技味儿十足的感觉。说白了就是mit用数字做的命令选项,而且mit本身也没定义这些数字的含义是什么。就像阿扁①在*内只是1020号罢了,谁在乎他是不是前“总统”呢?
运行级别这个概念源自System V②。这东西几乎是当前所有Unix和类Unix系统的祖宗了,当初给它设计的系统启动机制也被Linux所延续下来。所以对init的正确称呼就应该是System V init或SysV init。因为是在系统初始化时的行为,不同的运行级别会导致系统以不同的方式被初始化。这样的结果就导致了系统启动后对于用户会有不同的外在表现行为。这种设计不但被Linux这祥的类Unix系统所延续,Windows也借鉴了很多,比如经常会跑出来的“安全模式”。
运行级别大多数情况都是用数字来表示,从0~9共有10个。有些时候也用“S”来代
表某个运行级别。虽然是用数字来描述,但同一时刻一个Linux系统只能处于一个运行级别,
千万不要认为这是按顺序逐一执行的。
①陈水扁,前*地区最高*,于2010年11月ll日因贪腐案入狱。
②System V是众多UNIX操作系统版本中的一支,最初由AT&T开发,在1983年第一次发布。一共发行了4个主要版本1、2、3、4。System V Release 4,或者RVR4,是最成功的版本,成为一些UNIX共同特性的源头。
Linux系统一共定义了7个运行级别,分别是:
0 - halt,关闭系统,不能作为默认运行级别
1 - single user mode,单用户模式,类似安全模式,用于故障维护
2 - Multiuser, without NFS,与3类似,但是没有联网能力
3 - Full multiuser mode,全功能的多用户模式
4 - unused,保留,没有使用
5 – Xll,带有图形用户界面的全功能多用户模式
6 - reboot,重新启动,不能作为默认运行级别,否则会有灵异事件
注意,这个定义只是建议性的,不是所有Linux发行版都会遵照这个约定。0、l和6这三个运行级别在任何UNIX和类UNIX系统中都是一样的,Linux当然不能例外。其他运行级别会有差别,比如:Slackware的运行级别5是保留的,而4跟建议的5是相同的。绝大多数发行版中运行级别3的定义都是按照这个建议执行的,至少我没有见过其他情况。
System V的init是通过读取/etc/inittab这个文件来确认默认执行什么运行级别的。大多数Linux发行版所采用的init也是这么做的,但是systemd放弃了这个策略。使用systemd的发行版,就不会有这个文件。即使有,也只是起到了一今提示作用。
不同的运行级别会有不同的启动脚本程序和配置文件。在System V中,这些与运行级别相关的启动脚本和配置文件也是由inittab来指定的。比如我们打开使用sysvinit的Linux发行版的inittab文件,会看到类似这样的内容:
id : : initdefault : si : : sysinit : /etc/rc . d/rc . Sysinit ::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
::wait:/etc/rc.d/rc
ca : : ctrlaltdel : /sbin/shutdown -t3 -r now
pf : :powerfail:/sbin/shutdown -f -h + " Power Failure;System Shutting Down"
pr : :powerokwait:/sbin/shutdovm -c" Power Restored; Shutdown Cancelled"
: : respawn : /sbin/mingetty ttyl
: : respawn : /sbin/mingetty tty2
: : respawn : /sbin/mingetty tty3
: : respawn : /sbin/mingetty tty4
: : respawn : /sbin/mingetty tty5
: : respawn : /sbin/mingetty tty6
x:: respawn: /etc/Xll/prefdm -nodaemon
看起来挺变态的,其实格式是很严整的。任何一行都是按照:
id:runlevels:action选项:process 命令
这样一个固定的格式编写的。不过只给出格式还是让人很晕的,所以我解释一下:
● id是一个任意指定的四个字符以内的序列标号。在这个文件内必须是唯一的,所以
就当它是个索引好了。
● runlevels表示这一行适用于哪些或是哪个运行级别。如果忽略它,就表明适用于任
何运行级别。
● action表示在进入对应runlevels所指定的运行级别时,iit如何执行process字段的
命令的方式。具体的设定参考表10-2所示。
● process就是具体应该执行的程序了。这也表明在退出相应运行级别时要终止运行
的程序。
表10-2常见的action值
action值 |
说 明 |
respawn |
表示irut监视的进程,即使其结束也应诙被重新启动 |
wait |
init应该运行这个进程一次,并等待它结束才继续 |
once |
init仅运行这个进程一次 |
boot |
随系统启动运行,所有runlevels值对它没用 |
bootwait |
随系统启动运行,并且init霉它结束。行有runlevels无效 |
off |
没有任何意义 |
initdefault |
系统启动后的默认运行级别。出于进入相应的运行级别会激活对应级别的进程,所以对其指定process字段没有任何意义。如果inittab文件内不存在这一条记录,系统启动时会在控制台询问要进入的运行级别 |
sysinit |
系统启动时准备运行的命令 |
powerwait |
允许init在电源被切断时关闭系统。不过你得有UPS~和监视UPS并能通知init电源已被切断的软件 |
① 不间断电源。当断电后,UPS会利用自己的电池电脑共电。能够提供多久的供电能力取决于电池容量。我在购买第一台电脑时就配备了UPS,因此在还没有养成随时保存文件的好习惯的时候,UPS挽救了我好多重要的工作内容。玩游戏时也很给力
(续)
action值 |
说 明 |
powerfail |
同powerfail,但init不会等待正在运行的程序结束 |
powerokwait |
当电源监视软件报告“供电恢复”时,init要执行的操作 |
powerfailnow |
监测到UPS电源即将耗尽时,init要执行的操作 |
ctrlaltdel |
规定init在用户执行“CrtI+Alt+Del”三键人法时要执行的操作 |
当你看到这里的时候,我强烈建议你再回头看看我所给出的inittab文件的例子。注意init是不一定按照顺序来解析inittab文件的。优先级是:sysinit最高、boot和bootwait次之、其他的最低,有些还是事件触发的。
但是目前使用最多的Upstart甚本上很少碰inittab文件。它使用了一套伞新的配置脚本①来实现与inittab文件差不多的功能。这些配置脚本在/etc/init/目录下可以找到。当你翻阅这些配置脚本的时候,会发现与单独的inittab没什么特别本质的区别。只能说采用配置脚本将inittab模块化了。至于systemd,实在太过于激进了,就留你们自己去研究它吧。
10.7.3进入运行级别之前
不管谩定了何种运行级别,在init进程刚刚被肩动的时候,Linux系统还是不健全的,光秃秃的只有内核。为了让Linux系统能够被人使用,也为了Linux能够恰当地运行于某一运行级别之上,必须要进行一系列的初始化操作才行。这个初始化的过程通常会由一个脚本程序来完成。但并没有规定具体的文件名是什么。在我给出的inittab文件的例子中是/etc/rc.d/rc.sysinit脚本。这个脚本也是在Linux众多的发行版中最为常见的。
rc.syslmt这样的脚本都干了些什么呢?很难完全说清楚,不同发行版处理的方式有一些差别。所以我总结了一些共性罗列给大家。大体上都是要干这么儿件事的:
1. 确认主机名,主要是搞清楚自己是谁这个哲学问题
2. 挂接/proc和/sys等重要的特种文件系统
3. 设置控制台界面的默认字体
4. 输出版权信息,炫耀一下自己的出身
5. 启动udev,这时候才真正具备硬件管理的能力/sbin/start_udev
6. 初始化硬件,加载驱动,设置时钟
7. 设置键盘布局
8. 设置主机名,“我是谁”这个问题永远很重要
9. 设置ACPI
10.检测磁盘,执行fsck
11.挂接/etc/fstab中设置的其他文件系统,不包含/proc和NFS
12.初始化伪随机数生成器 /dev/urandom 系统刚启动噪音较少
13.清理/tmp和/var目录
14.启动交换空间 swap -a -e
15.初始化串口
16.将所有执行的操作写入/var/log/dmesg文件
实际上rc.syslnlt这样的脚本所执行的操作远不止这些,有很多发行版自己设计的特性操作。如果想看看自己的系统在这个过程中都执行了那些操作,more -下/var/log/dmesg文件就好了。
通过列举的这些项目可以很明显地看出,执行完这个脚本之后,Linux才长出头、五官、四肢和手脚,成为一个能够被人使用的操作系统,为接下来执行运行级别相关的脚本打下基础。
10.7.4进入运行级别
如果将进入运行级别之前所作的初始化认为是系统的通用初始化阶段,则进入运行级别
时还要进行针对运行级别的个性初始化。
个性初始化的目的就是为了启动不同的软件集,从而让不同运行级别能够有不同的外在
表现。从inittab文件的例子中可以发现/etc/rc.d/rc这个脚本是做这个事情的。不同的运行级别会有不同的数字参数传递给它,这样可以保证它在不同的运行级别上可以有不同的行为表现。
既然是个性化,就应该允许用户可配置。但是如果用户想自己针对某个运行级别进行个性化配置的时候,就必须修改rc这个脚本才行。显然门槛有点高,通用性也很差。那么这个时候就需要有一种方法,既可以配置,又没有高门槛,还能很通用就是一个好方法。SystemV就提供了这样一个好方法。Linux的设计者们也就懒得去想了,直接将它的方法继承下来。
这个方法是将个性初始化脚本在功能上进行了细粒度的划分,基本上一个程序对应一个初始化脚本,甚至一个程序会对应几个初始化脚本。将这些初始化脚本统一放到/etc/init.d或/etc/rc.d/init.d目录下。然后准备一些形如/etc/rc[N].d或/etc/rc.d/rc[N].d的目录,其中[N]与运行级别对应。在这些目录下建立到init.d目录下某些启动脚本的符号连接。建立了对哪些脚本的连接,就表明在进行个性初始化的时候要执行哪些连接。这样运行级别所需要启动的软件集也就被启动了。这些脚本谁去调用呢?交给rc这个脚本好了。让它根据给定的参数执行不同目录的所有脚本就行了。而且参数是数字的,正好可以和[N]对应,rc脚本可以很简单。
这样做的好处孰是:
● 如果用户想在某个级别中启动一个原本没有配置的软件,现在只需要建立一个连接
就好,门槛降低了很多。
● 如果某个程序想在某些运行级别下被自动启动,将自己的启动脚本放入init.d目录
下,然后在相应的rc[n].d目录下建立符号连接即可。甚至这个启动脚本都可以不
用放在init.d目录下,通用性问题得到很好的解决。
● 不同的运行级可能要启动某些共同的程序,这样用符号连接时就不必将启动脚本拷
来拷去了。不但有很好的复用性,还节省磁盘空间。
● rc脚本可以做得非常精巧简单,简单就代表稳定嘛!
为了能够更好地说明这种方法,我拿一个实际的例子来说。在我所使用的发行版中,
/etc/rc/rc3.d目录下有这些内容:
K01certmonger K75ntpdate Sllportreserve S25netfs
K01smartd K75quota_nld S12 rsyslog S26acpid
K020ddj obd K80sssd S13cpuspeed S26haldaemon
K10psacct K86cgred S13irqbalance S26udev-post
K10saslauthd K87restorecond S13 rpcbind S28autofs
K15httpd K89rdisc S14nfslock S55sshd
K20tomcat6 K95cgconfig S15mdmonitor S80postfix
K36mysqld S01sysstat S18rpcidmapd S82abrtd
K50netconsole S02lvm2-monitor S19rpcgssd S90crond
K60nfs S08ip6tables S20kdump S95atd
K69rpcsvcgssd S08iptables S22messagebus S99libvirt-guests
K73ypbind S10network S24avahi-daemon S99local
K74ntpd S11auditd S25cups
你会发现这些都是符号连接,大多数都是连接到/etc/rc/init.d目录下某个文件的。
不过这里所有符号连接的名称有点怪异,都是K??*或S??*这样的。其实这只是利用名称耍的花招。首先要明白的一个简单道理就是:开机启动的程序,关机的时候得关闭。当然,这个不是绝对的。因为有些程序启动之后很快就自己退出了,关机的时候不用理它。还有些开机时不用启动,但是关机的时候需要启动某些程序来收拾一下烂摊子。那么S就代表start(启动),K就代表kill(杀死或停止)。所以通过名称就能看出来哪些是开机的时候执行,哪些是关机的时候会执行。
那么后面的数字是干什么的呢?序号!就是程序启动或关闭需要一个茼后顺序。因为程序之间有时候会有一些“莫名其妙”的依赖关系,所以排好队很重要。为了能容许某些后来的程序可以加塞,序号都是跳着来的。
细心的人在查看自己系统的时候,会发现K和S开头的符号连接实际上连接的是同一个脚本程序。但是前面说过rc脚本只是执行它们,怎么处理启动和关闭呢?原来rc脚本会根据K或S来判断给这些启动脚本传递什么样的参数。如果以S开头,就传递start;如果以K开头就传递stop。守护进程不就是这样启动和停止的吗?
在很多发行版中,在rc2.d、rc3.d、rc4.d和rc5.d这四个目录中都会发现S99local这样一个符号连接。都会连接到一个rc.local文件上。这个文件并不在init.d目录下,一般会是/etc/rc.local或/etc/rc.d/rc.local。这个是专门为用户准备的一个设定开机自动启动程序的地方。
按照惯例,凡是能放入init.d目录的都应该是守护进程。所以对于非守护进程且还需要开机自动启动的程序,这里是一个非常好的设定地点。
大多数情况下rc.local是最后一个被执行的脚本。当它执行完毕后,系统就完成了全部
初始化任务,开始为用户提供服务了。
rc.local里面的执行顺序在6和7都是一样的,本身就是个脚本,肯定从上往下执行。
centos6和centos7唯一区别是6是串行的最后一个,而7是并行的,如果rc.local里启动的某个程序依赖了一些服务,那么可能导致这个程序起不来
解决办法有3个:
(1) 在rc.local最开始增加sleep多等待一会;
(2)手动修改rc-local.service,配置依赖关系;
(3)不要用rc.local
10.7.5转换运行级别
在一个运行中的Linux系统的运行级别并不是保持不变的,因为具有最高控制权的root
用户是可以随时切换运行级别的。当然,如果遇到一个不负责任的root,经常来切换运行级
别那将会是一件很可怕的事情。因为切换运行级别会导致当前正在运行的好多程序被关闭。
其实Linux系统的关机和重新启动就是靠切换运行级别实现的。运行级别O和6就是为
此而设置的。目前所提供的那些关机和重启命令都是基于这种运行级别假设的。这也是经历
了这么多年,一直没有人打这两个运行级别主意的主要原因。
作为root用户,切换运行级别很简单。执行init命令附带一个运行级别参数即可。例如:
# inti
切换到运行级别3。当然,init命令是很简单粗暴的,会立即执行切换操作。很多时候
这种行为会造成无法挽回的结果。所以Linux还提供了另外一个命令telinit(tell you init 告诉你进行运行级别切换),它带有一个一t参数可以指定多少秒之后去切换运行级别。例如:
#telinit -t
这条命令要求2分钟之后切换到运行级别3。显然给人以时间上的准备。在一个大型多
人使用的系统中,如果很多人不为管理员发出的切换运行级别警告所动,依然自以为是地做
着自己的工作。管理员完全可以利用telinit命令设定好在一段时间之后切换,然后就告诉那些正在使用系统的人:我已经做完了,留给你的时间就那么点儿,你自己看着办吧!显然这时候系统管理员不再会受到人情世故所恼了。
深入理解System V init和其引入的运行级别机制,就可以从容不迫地应对那些系统在启动和关闭时候所要解决的一切烦人的事情了。init进程不愧为能够与Linux生死与共的真朋挚友。
10.8幕后英雄们
当系统初始化完毕之后,Linux系统就可以给人使用了。但是让人如何使用却是一个仁
者见仁的问题了。Linux系统提供很多种方法,比如:提供一个类似DOS的文本界面让用户
通过各种命令来使用:或者提供一个图形界面,让用户只需要通过鼠标就能完成大部分工作;
甚至还可以让多个用户通过telnet或ssh连接进来,大家一起使用。这些使用方法都是与Linux同生共死的init来提供的吗?不是!提供这么多种使用方法的是一群幕后的英雄们。
10.8.1 终端
在开始介绍这些幕后英雄之前,需要先讲清楚一个概念,那就是——终端。
话说在电脑的远古时期,UNIX出现了。这是一个多用户、多任务的分时操作系统。当
时能够运行UNIX的电脑都很贵,人们在使用的时候都是使用一个廉价的设备连接到这台电
脑上共享使用的。UNIX会让使用者以为这个廉价设备就是那台昂贵的电脑。这个廉价设备
被称为终端。
Linux是一种类UNIX系统,它很自然地继承了UNIX通过终端来使用的特质。但是Linux
诞生的年代,电脑已经很便宜了。便宜到比当年的终端还便宜。显然Linux再要求人们用终
端去使用它就有点不知好歹了。但是没有终端义不像UNIX。怎么办呢?假装吧!假装键盘
就是终端的输入,假装显示器就是终端的输出。可是对别人说你什么都是假的,显然你再有
能力也没人信了。所以出于信誉第一的考虑,假终端被美其名日虚拟终端。自从Linux有了
虚拟终端,原来需要从终端获取的输入,键盘就搞定了;原来需要终端展现的输出,显示器
就搞定了。早期的UNIX程序全都能移植过来了。而且Linux为了充分展现这个假的一点不
比真的差,甚至比真还的好,一下假装了很多个。所以你孰发现可以通过CtrI+Alt+F[1 -6]组合键切换控制台。切换控制台就是在切换虚拟终端。
在Linux上,键盘的输入和屏幕的输出都是通过虚拟终端来完成的。一个虚拟终端被看
作是一个设备。在/dev目录下可以查看到这些设备所映射到的文件。所有以tty[0-9]*方式命名的文件就代表着虚拟终端。比如你在文本界面控制台直接登录系统,那么你当前使用的虚拟终端对应的设备应该是/dev/tty1。使用CtrI+Alt+F[1-6]组合键所切换的虚拟终端会分别与功能键对应,比如F2就对应着/dev/tty2。但是有两个比较特殊的虚拟终端设备,就是/det/tty和/dev/tty0。tty代表当前你正在使用的终端。tty0代表当前被激活的所有虚拟终端。好多时候这个很难区分,因为你使用的就是当前被激活的。当然要试验它们的差别还是有的,我后面会说。还有一个特殊的虚拟终端是/dev/console。这个代表的是“UNIX时代”本机的显示器和键盘。在“Linux时代”实际上很多时候代表的是/dev/tty1。在Linux上,所有tty设备都被称为进程控制终端,console被称为系统控制终端。进程和系统的差剐就是:所有程序输出的内容,都会输出到进程控制终端;所有内核输出的内容,都会输出到系统控制终端。
当前绝大多数Linux发行版默认会提供6个虚拟终端供用户使用。当然,虚拟终端的数
量是没有限制的。如果你足够狠,可以弄出几百个来,前提是你的键盘能支持
物理终端 /dev/console
虚拟终端 /dev/tty -》login程序
模拟终端 /dev/pts -》sshd程序
10.8.2 getty系列命令
根据Linux需要虚拟终端来处理绝大多数程序的输入输出这一事实,init在完成系统的
初始化之后,就必然要开启虚拟终端。那就让我们做一个小穿越,回到“10.7.2运行级别”
一节中给出的那个inittab文件的例子。其中就有关这方面的设定。那些所有process字段都带有“/sbin/mingetty”命令的条目就是干这个的。
mingetty命令是getty命令的一个变种。getty命令的变种还有好多,最常见的有agetty、
mingetty、fbgetty、fgetty和rungetty。有这些变种的出现就是因为getty命令本身非常复杂,而且伸缩能力有限。如果直接使用getty,Linux系统要提供图形用户界面就有可能遇到麻烦,比如程序无故退出、X Server死锁等问题。这些变种都很好地解决了这些问题。同时也简化了使用上的复杂度。就比如mingetty命令,只需要给定参数是ttyl就能够启动ttyl代表的虚拟终端。
getty系列命令不只是开启虚拟终端。它要干的一个比较重要的事情是调用login程序。
login程序比较简单,就是问你要用户名和密码。你输入正确的用户名和密码后,就可以开
始使用Linux了。
从inittab文件的例子中可以看到,mingetty命令被respawn所修饰。这表明init会坚守着它不会被kill掉。所以不管你采用何种方式,都无法越过输入用户名和密码的这道关卡。也正因为这个原因,才将getty系列命令交给init直接调用,而不是让rc脚本(还记得前面讲过吗?)去处理它。
细心的人会发现,当登入Linux系统后,使用“top”或“ps -ax”,甚至“ptree”命令都是找不到与所使用虚拟终端对应的mingetty的踪影的。这是为什么?原来getty系列命令在启动login程序时,是与login融为一体被login取代了,即使用相同的进程号①。那么init并不会认为getty已经退出了。而使用“ps”等命令查看进程时,看到的大多是程序本身的名字。所以能够看到login,却看不到getty。当kill掉login后,就会被init发现,重新将getty启动,让你输入用户名和密码。虽然我们看不到getty,但是这一切都是getty在起作用,也许这就是幕后英雄的悲剧吧。
使用exec系统调用就有这个效果
10.8.3 Display Manager
Linux的另一个幕后英雄是Display Manager。翻译过来就是显示管理器,可以简称DM。
用途就是提供图形界面的登录窗口。当前有好多种DM。比如:GNOME的GDM、KDE的KDM、独立的LightDM等。每种DM都有自己的特性,自然也就会得到不同人群的喜好。所以大多数Linux发行版会提供多种DM供用户选择。也正因为如此,仵inittab申往往是指
定一个脚本来调用DM们。
图形界面也要使用虚拟终端的。不过它一般不与文本界面争虚拟终端的资源。大多数Linux发行版将tty7分配给图形界面。这样也就使得即使开启了图形界面登录,也可以通过
CtrI+Alt+F[1-6]来切换到文本界面,保持整体风格的一致。要切换回图形界面就按CtrI+Alt+F7即可。不过也有例外,就是有一些发行版将图形界面分配给ttyl。这些发行版的考虑是认为用户开启图形界面,就应该是一直使用,那么就应该将默认的虚拟终端提供给用户。
图形界面使用哪一个虚拟终端,由DM说了算。只是大多数DM不像getty那些变种那
么简单。规定它使用哪个虚拟终端一般需要用配置文件来指定。由于DM的种类繁多,我就
不列举了。想了解的话可以去问google。
DM与getty相同的地方就是都被init给respawn了。这样谁都杀不死它,所以也就不能跳过输入用户名和密码的关卡。而且在登录之后,DM也与getty -样,消失了。
文本界面 6个tty
图形界面 6个tty+1个图形tty
10.8.4伪终端
在通过网络连接,使用telnet或ssh多人共享一个Linux系统时,就不再使用虚拟终端
了。但是由于传统程序们都是要使用虚拟终端的,所以就只能“假上加假”了。既然不能告
诉人们虚拟终端是假的,自然不能提假上加假这事儿。但人类的智慧是无穷的,语言更是丰
富多彩的。所以就发明了伪终端这个名词。
不管是不是假上加假,它好歹也是个设备。所以在/dev/曰录下也是有名号的。不过这假
的就是假的,名号非常混乱。而且很多时候会不经意地发现多出来几个,一眨眼又少了几个。
连踪影都难确定。不过要识别它还是有办法的,就是它们的名称都是以“pty”开头的。更
有热心的Linux发行版,将它们统一放到/dev/pts曰录下,只要在这里的就一定都是伪终端。
Linux系统对伪终端的控制是比较随意的,init也不去管它们。那怎么保障安全,让每
个通过网络登录的用户都必须输入用户名和密码呢?这个是交给普通程序来处理的。比如使
用ssh来登录,那么相应的ssh服务器程序-sshd要负责向系统申请伪终端资源,并要求用户输入用户名和密码。所以如果sshd不安全,使用sshd的Linux系统也就不安全。幸好sshd的表现还不错。它的哥哥telnet就没有这么幸运,由于安全性的问题基本上快要退出历史舞台了。
不管怎么样,伪终端都算是一个幕后英雄。因为人们很少关心它。但是它却解决了人们
通过网络共享使用Linux的问题。即便是假上加假,也同样能够赢得人们对它的喝彩。
ps aux|grep pts
sshd :root@pts/0 #pts目录下文件名为0的文件
10.8.5配置文件
对于Linux这种高度可配置的操作系统来说,在任何时候配置文件都是功不可没的。尤
其是那些涉及系统初始化的配置文件们,决定了系统未来的命运走向,地位更不容忽视。比
较遗憾的是:各大发行版都在执行着各自的设计策略,我无法提取出具备共性的东西。这个
地方只能留给你们自己去研究。
无论如何,这些配置文件们都是幕后英雄。甚至连名字都无法让人们去牢记。但是每一
介Linux的使用者都会因为它们的存在而受益匪浅。能够掌握某个常用发行版的配置文件,
才能真正驯服这个系统,让它对你再无秘密可言。
10.9结束语
我们现在再来回顾一下Linux系统从开机到关机这个过程中所出现的那些角色。死心眼
的BIOS、小肚鸡肠的MBR、和事佬GRUB、唇齿相依的内核启动参数、自力更生的Linux内核、及时雨initramfs、生死永相伴的init,以及那些鲜为人知的幕后英雄们。这些都是与Linux生死与共的兄弟。那兄弟到底是来干什么的?答:兄弟是拿来利用的
centos5使用sysvinit
centos6使用ubuntu的UpStart
centos7使用systemd
在 RedHat 公司的 RHEL5 中(RHEL6 已经使用 upstart 了),rc.sysinit 主要完成以下这些工作。设置定义在/etc/sysctl.conf 中的内核参数
# Apply sysctl settings, including files in /etc/sysctl.d
apply_sysctl() {
sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
for file in /etc/sysctl.d/* ; do
is_ignored_file "$file" && continue
test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
done
}
cat /boot/grub/grub.conf
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/mapper/VolGroup-lv_root
# initrd /initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-504.8.1.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-504.8.1.el6.x86_64 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=VolGroup/lv_swap SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=VolGroup/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM
initrd /initramfs-2.6.32-504.8.1.el6.x86_64.img
title CentOS (2.6.32-358.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-358.el6.x86_64 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=VolGroup/lv_swap SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=VolGroup/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
initrd /initramfs-2.6.32-358.el6.x86_64.img
# pwd
/boot
# ll
总用量 48200
-rw-r--r--. 1 root root 104081 2月 22 2013 config-2.6.32-358.el6.x86_64
-rw-r--r-- 1 root root 106312 1月 29 2015 config-2.6.32-504.8.1.el6.x86_64
drwxr-xr-x. 3 root root 1024 1月 26 2015 efi
drwxr-xr-x. 2 root root 1024 12月 4 03:06 grub
-rw-r--r--. 1 root root 16218612 1月 26 2015 initramfs-2.6.32-358.el6.x86_64.img
-rw------- 1 root root 19364509 3月 10 2015 initramfs-2.6.32-504.8.1.el6.x86_64.img
drwx------. 2 root root 12288 1月 26 2015 lost+found
-rw-r--r--. 1 root root 185734 2月 22 2013 symvers-2.6.32-358.el6.x86_64.gz
-rw-r--r-- 1 root root 200245 1月 29 2015 symvers-2.6.32-504.8.1.el6.x86_64.gz
-rw-r--r--. 1 root root 2407466 2月 22 2013 System.map-2.6.32-358.el6.x86_64
-rw-r--r-- 1 root root 2544888 1月 29 2015 System.map-2.6.32-504.8.1.el6.x86_64
-rwxr-xr-x. 1 root root 4043888 2月 22 2013 vmlinuz-2.6.32-358.el6.x86_64
-rwxr-xr-x 1 root root 4153008 1月 29 2015 vmlinuz-2.6.32-504.8.1.el6.x86_64
http://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=401882005&idx=2&sn=ef58771798e24992a355441e311489b4&scene=0#wechat_redirect
除此之外,还需要一个Linux启动内核的映像文件。在 RedHat linux 6.0光盘的 images/目录下有这个文件——vmlinuz。
/boot/vmlinuz-2.6.32-504.el6.x86_64
cat /etc/inittab
http://www.ibm.com/developerworks/cn/linux/1407_liuming_init2/
为什么/etc/inittab文件的内容这么少,书上的/etc/inittab文件的内容都没有,是因为书上是按照centos5来说的,centos6使用ubuntu的UpStart,而UpStart没有运行级别的概念,他只是模拟运行级别,所以/etc/inittab文件只保留了一句话
id:3:initdefault:
# inittab is only used by upstart for the default runlevel.
#
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# System initialization is started by /etc/init/rcS.conf
#
# Individual runlevels are started by /etc/init/rc.conf
#
# Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
#
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# with configuration in /etc/sysconfig/init.
#
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
#
# Default runlevel. The runlevels used are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:3:initdefault:
cat rc.sysinit
#!/bin/bash
#
# /etc/rc.d/rc.sysinit - run once at boot time
#
# Taken in part from Miquel van Smoorenburg's bcheckrc.
#
HOSTNAME=$(/bin/hostname)
set -m
if [ -f /etc/sysconfig/network ]; then
. /etc/sysconfig/network
fi
if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; then
HOSTNAME=localhost
fi
if [ ! -e /proc/mounts ]; then
mount -n -t proc /proc /proc
mount -n -t sysfs /sys /sys >/dev/null 2>&1
fi
if [ ! -d /proc/bus/usb ]; then
modprobe usbcore >/dev/null 2>&1 && mount -n -t usbfs /proc/bus/usb /proc/bus/usb
else
mount -n -t usbfs /proc/bus/usb /proc/bus/usb
fi
#remount /dev/shm to set attributes from fstab #669700
mount -n -o remount /dev/shm >/dev/null 2>&1
#remount /proc to set attributes from fstab #984003
mount -n -o remount /proc >/dev/null 2>&1
. /etc/init.d/functions
PLYMOUTH=
[ -x /bin/plymouth ] && PLYMOUTH=yes
# Check SELinux status
SELINUX_STATE=
if [ -e "/selinux/enforce" ] && [ "$(cat /proc/self/attr/current)" != "kernel" ]; then
if [ -r "/selinux/enforce" ] ; then
SELINUX_STATE=$(cat "/selinux/enforce")
else
# assume enforcing if you can't read it
SELINUX_STATE=1
fi
fi
if [ -n "$SELINUX_STATE" -a -x /sbin/restorecon ] && __fgrep " /dev " /proc/mounts >/dev/null 2>&1 ; then
/sbin/restorecon -R -F /dev 2>/dev/null
fi
disable_selinux() {
echo $"*** Warning -- SELinux is active"
echo $"*** Disabling security enforcement for system recovery."
echo $"*** Run 'setenforce 1' to reenable."
echo "0" > "/selinux/enforce"
}
relabel_selinux() {
# if /sbin/init is not labeled correctly this process is running in the
# wrong context, so a reboot will be required after relabel
AUTORELABEL=
. /etc/selinux/config
echo "0" > /selinux/enforce
[ -n "$PLYMOUTH" ] && plymouth --hide-splash
if [ "$AUTORELABEL" = "0" ]; then
echo
echo $"*** Warning -- SELinux ${SELINUXTYPE} policy relabel is required. "
echo $"*** /etc/selinux/config indicates you want to manually fix labeling"
echo $"*** problems. Dropping you to a shell; the system will reboot"
echo $"*** when you leave the shell."
start rcS-emergency
else
echo
echo $"*** Warning -- SELinux ${SELINUXTYPE} policy relabel is required."
echo $"*** Relabeling could take a very long time, depending on file"
echo $"*** system size and speed of hard drives."
/sbin/fixfiles -F restore > /dev/null 2>&1
fi
rm -f /.autorelabel
echo $"Unmounting file systems"
umount -a
mount -n -o remount,ro /
echo $"Automatic reboot in progress."
reboot -f
}
# Print a text banner.
echo -en $"\t\tWelcome to "
read -r system_release < /etc/system-release
if [[ "$system_release" == *"Red Hat"* ]]; then
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;31m"
echo -en "Red Hat"
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
PRODUCT=$(sed "s/Red Hat \(.*\) release.*/\1/" /etc/system-release)
echo " $PRODUCT"
elif [[ "$system_release" == *Fedora* ]]; then
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;34m"
echo -en "Fedora"
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
PRODUCT=$(sed "s/Fedora \(.*\) \?release.*/\1/" /etc/system-release)
echo " $PRODUCT"
elif [[ "$system_release" =~ "CentOS" ]]; then
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;36m"
echo -en "CentOS"
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
PRODUCT=$(sed "s/CentOS \(.*\) \?release.*/\1/" /etc/system-release)
echo " $PRODUCT"
else
PRODUCT=$(sed "s/ release.*//g" /etc/system-release)
echo "$PRODUCT"
fi
# Only read this once.
cmdline=$(cat /proc/cmdline)
# Initialize hardware
if [ -f /proc/sys/kernel/modprobe ]; then
if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then
sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1
else
# We used to set this to NULL, but that causes 'failed to exec' messages"
sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1
fi
fi
touch /dev/.in_sysinit >/dev/null 2>&1
# Set default affinity
if [ -x /bin/taskset ]; then
if strstr "$cmdline" default_affinity= ; then
for arg in $cmdline ; do
if [ "${arg##default_affinity=}" != "${arg}" ]; then
/bin/taskset -p ${arg##default_affinity=} 1
fi
done
fi
fi
nashpid=$(pidof nash 2>/dev/null)
[ -n "$nashpid" ] && kill $nashpid >/dev/null 2>&1
unset nashpid
apply_sysctl
/sbin/start_udev
# Load other user-defined modules
for file in /etc/sysconfig/modules/*.modules ; do
[ -x $file ] && $file
done
# Load modules (for backward compatibility with VARs)
if [ -f /etc/rc.modules ]; then
/etc/rc.modules
fi
mount -n /dev/pts >/dev/null 2>&1
[ -n "$SELINUX_STATE" ] && restorecon -F /dev/pts >/dev/null 2>&1
# Configure kernel parameters
update_boot_stage RCkernelparam
apply_sysctl
# Set the hostname.
update_boot_stage RChostname
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
[ -n "${NISDOMAIN}" ] && domainname ${NISDOMAIN}
# Sync waiting for storage.
{ rmmod scsi_wait_scan ; modprobe scsi_wait_scan ; rmmod scsi_wait_scan ; } >/dev/null 2>&1
# Device mapper & related initialization
if ! __fgrep "device-mapper" /proc/devices >/dev/null 2>&1 ; then
modprobe dm-mod >/dev/null 2>&1
fi
if [ -f /etc/crypttab ]; then
init_crypto 0
fi
if ! strstr "$cmdline" nompath && [ -f /etc/multipath.conf -a \
-x /sbin/multipath ]; then
modprobe dm-multipath > /dev/null 2>&1
/sbin/multipath -v 0
if [ -x /sbin/kpartx ]; then
/sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p p" >/dev/null
fi
fi
if ! strstr "$cmdline" nodmraid && [ -x /sbin/dmraid ]; then
modprobe dm-mirror >/dev/null 2>&1
dmraidsets=$(LC_ALL=C /sbin/dmraid -s -c -i)
if [ "$?" = "0" ]; then
for dmname in $dmraidsets; do
if [[ "$dmname" == isw_* ]] && \
! strstr "$cmdline" noiswmd; then
continue
fi
/sbin/dmraid -ay -i --rm_partitions -p "$dmname" >/dev/null 2>&1
/sbin/kpartx -a -p p "/dev/mapper/$dmname"
done
fi
fi
# Start any MD RAID arrays that haven't been started yet
[ -r /proc/mdstat -a -r /dev/md/md-device-map ] && /sbin/mdadm -IRs
if [ -x /sbin/lvm ]; then
if [ ! -f /.nolvm ] && ! strstr "$cmdline" nolvm ; then
action $"Setting up Logical Volume Management:" /sbin/lvm vgchange -a ay --sysinit --ignoreskippedcluster
else
echo $"Logical Volume Management disabled at boot."
fi
fi
if [ -f /etc/crypttab ]; then
init_crypto 0
fi
if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then
fastboot=yes
fi
if [ -f /fsckoptions ]; then
fsckoptions=$(cat /fsckoptions)
fi
if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then
fsckoptions="-f $fsckoptions"
elif [ -f /.autofsck ]; then
[ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck
if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then
AUTOFSCK_OPT="$AUTOFSCK_OPT -f"
fi
if [ -n "$AUTOFSCK_SINGLEUSER" ]; then
[ -n "$PLYMOUTH" ] && plymouth --hide-splash
echo
echo $"*** Warning -- the system did not shut down cleanly. "
echo $"*** Dropping you to a shell; the system will continue"
echo $"*** when you leave the shell."
[ -n "$SELINUX_STATE" ] && echo "0" > /selinux/enforce
start rcS-emergency
[ -n "$SELINUX_STATE" ] && echo "1" > /selinux/enforce
[ -n "$PLYMOUTH" ] && plymouth --show-splash
fi
fsckoptions="$AUTOFSCK_OPT $fsckoptions"
fi
if [ "$BOOTUP" = "color" ]; then
fsckoptions="-C $fsckoptions"
else
fsckoptions="-V $fsckoptions"
fi
READONLY=
if [ -f /etc/sysconfig/readonly-root ]; then
. /etc/sysconfig/readonly-root
fi
if strstr "$cmdline" readonlyroot ; then
READONLY=yes
[ -z "$RW_MOUNT" ] && RW_MOUNT=/var/lib/stateless/writable
[ -z "$STATE_MOUNT" ] && STATE_MOUNT=/var/lib/stateless/state
fi
if strstr "$cmdline" noreadonlyroot ; then
READONLY=no
fi
if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then
mount_empty() {
if [ -e "$1" ]; then
echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null
mount -n --bind "$RW_MOUNT$1" "$1"
fi
}
mount_dirs() {
if [ -e "$1" ]; then
mkdir -p "$RW_MOUNT$1"
find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null
mount -n --bind "$RW_MOUNT$1" "$1"
fi
}
mount_files() {
if [ -e "$1" ]; then
cp -a --parents "$1" "$RW_MOUNT"
mount -n --bind "$RW_MOUNT$1" "$1"
fi
}
# Common mount options for scratch space regardless of
# type of backing store
mountopts=
# Scan partitions for local scratch storage
rw_mount_dev=$(blkid -t LABEL="$RW_LABEL" -l -o device)
# First try to mount scratch storage from /etc/fstab, then any
# partition with the proper label. If either succeeds, be sure
# to wipe the scratch storage clean. If both fail, then mount
# scratch storage via tmpfs.
if mount $mountopts "$RW_MOUNT" > /dev/null 2>&1 ; then
rm -rf "$RW_MOUNT" > /dev/null 2>&1
elif [ x$rw_mount_dev != x ] && mount $rw_mount_dev $mountopts "$RW_MOUNT" > /dev/null 2>&1; then
rm -rf "$RW_MOUNT" > /dev/null 2>&1
else
mount -n -t tmpfs $RW_OPTIONS $mountopts none "$RW_MOUNT"
fi
for file in /etc/rwtab /etc/rwtab.d/* /dev/.initramfs/rwtab ; do
is_ignored_file "$file" && continue
[ -f $file ] && cat $file | while read type path ; do
case "$type" in
empty)
mount_empty $path
;;
files)
mount_files $path
;;
dirs)
mount_dirs $path
;;
*)
;;
esac
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
done
done
# Use any state passed by initramfs
[ -d /dev/.initramfs/state ] && cp -a /dev/.initramfs/state/* $RW_MOUNT
# In theory there should be no more than one network interface active
# this early in the boot process -- the one we're booting from.
# Use the network address to set the hostname of the client. This
# must be done even if we have local storage.
ipaddr=
if [ "$HOSTNAME" = "localhost" -o "$HOSTNAME" = "localhost.localdomain" ]; then
ipaddr=$(ip addr show to 0.0.0.0/0 scope global | awk '/[[:space:]]inet / { print gensub("/.*","","g",$2) }')
for ip in $ipaddr ; do
HOSTNAME=
eval $(ipcalc -h $ip 2>/dev/null)
[ -n "$HOSTNAME" ] && { hostname ${HOSTNAME} ; break; }
done
fi
# Clients with read-only root filesystems may be provided with a
# place where they can place minimal amounts of persistent
# state. SSH keys or puppet certificates for example.
#
# Ideally we'll use puppet to manage the state directory and to
# create the bind mounts. However, until that's all ready this
# is sufficient to build a working system.
# First try to mount persistent data from /etc/fstab, then any
# partition with the proper label, then fallback to NFS
state_mount_dev=$(blkid -t LABEL="$STATE_LABEL" -l -o device)
if mount $mountopts $STATE_OPTIONS "$STATE_MOUNT" > /dev/null 2>&1 ; then
/bin/true
elif [ x$state_mount_dev != x ] && mount $state_mount_dev $mountopts "$STATE_MOUNT" > /dev/null 2>&1; then
/bin/true
elif [ ! -z "$CLIENTSTATE" ]; then
# No local storage was found. Make a final attempt to find
# state on an NFS server.
mount -t nfs $CLIENTSTATE/$HOSTNAME $STATE_MOUNT -o rw,nolock
fi
if [ -w "$STATE_MOUNT" ]; then
mount_state() {
if [ -e "$1" ]; then
[ ! -e "$STATE_MOUNT$1" ] && cp -a --parents "$1" "$STATE_MOUNT"
mount -n --bind "$STATE_MOUNT$1" "$1"
fi
}
for file in /etc/statetab /etc/statetab.d/* ; do
is_ignored_file "$file" && continue
[ ! -f "$file" ] && continue
if [ -f "$STATE_MOUNT/$file" ] ; then
mount -n --bind "$STATE_MOUNT/$file" "$file"
fi
for path in $(grep -v "^#" "$file" 2>/dev/null); do
mount_state "$path"
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
done
done
if [ -f "$STATE_MOUNT/files" ] ; then
for path in $(grep -v "^#" "$STATE_MOUNT/files" 2>/dev/null); do
mount_state "$path"
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
done
fi
fi
if mount | grep -q /var/lib/nfs/rpc_pipefs ; then
mount -t rpc_pipefs sunrpc /var/lib/nfs/rpc_pipefs && service rpcidmapd restart
fi
fi
if [[ " $fsckoptions" != *" -y"* ]]; then
fsckoptions="-a $fsckoptions"
fi
_RUN_QUOTACHECK=0
if [ -f /forcequotacheck ] || strstr "$cmdline" forcequotacheck ; then
_RUN_QUOTACHECK=1
fi
if [ -z "$fastboot" -a "$READONLY" != "yes" ]; then
STRING=$"Checking filesystems"
echo $STRING
fsck -T -t noopts=_netdev -A $fsckoptions
rc=$?
if [ "$rc" -eq "0" ]; then
success "$STRING"
echo
elif [ "$rc" -eq "1" ]; then
passed "$STRING"
echo
elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then
echo $"Unmounting file systems"
umount -a
mount -n -o remount,ro /
echo $"Automatic reboot in progress."
reboot -f
fi
# A return of 4 or higher means there were serious problems.
if [ $rc -gt 1 ]; then
[ -n "$PLYMOUTH" ] && plymouth --hide-splash
failure "$STRING"
echo
echo
echo $"*** An error occurred during the file system check."
echo $"*** Dropping you to a shell; the system will reboot"
echo $"*** when you leave the shell."
str=$"(Repair filesystem)"
PS1="$str \# # "; export PS1
[ "$SELINUX_STATE" = "1" ] && disable_selinux
start rcS-emergency
echo $"Unmounting file systems"
umount -a
mount -n -o remount,ro /
echo $"Automatic reboot in progress."
reboot -f
elif [ "$rc" -eq "1" ]; then
_RUN_QUOTACHECK=1
fi
fi
remount_needed() {
local state oldifs
[ "$READONLY" = "yes" ] && return 1
state=$(LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts)
oldifs=$IFS
IFS=","
for opt in $state ; do
if [ "$opt" = "rw" ]; then
IFS=$oldifs
return 1
fi
done
IFS=$oldifs
return 0
}
# Remount the root filesystem read-write.
update_boot_stage RCmountfs
if remount_needed ; then
action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /
fi
# Clean up SELinux labels
if [ -n "$SELINUX_STATE" ]; then
restorecon /etc/mtab /etc/ld.so.cache /etc/blkid/blkid.tab /etc/resolv.conf >/dev/null 2>&1
fi
# If relabeling, relabel mount points.
if [ -n "$SELINUX_STATE" -a "$READONLY" != "yes" ]; then
if [ -f /.autorelabel ] || strstr "$cmdline" autorelabel ; then
restorecon $(awk '!/^#/ && $4 !~ /noauto/ && $2 ~ /^\// { print $2 }' /etc/fstab) >/dev/null 2>&1
fi
fi
if [ "$READONLY" != "yes" ] ; then
# Clear mtab
(> /etc/mtab) &> /dev/null
# Remove stale backups
rm -f /etc/mtab~ /etc/mtab~~
# Enter mounted filesystems into /etc/mtab
mount -f /
mount -f /proc >/dev/null 2>&1
mount -f /sys >/dev/null 2>&1
mount -f /dev/pts >/dev/null 2>&1
mount -f /dev/shm >/dev/null 2>&1
mount -f /proc/bus/usb >/dev/null 2>&1
fi
# Mount all other filesystems (except for NFS and /proc, which is already
# mounted). Contrary to standard usage,
# filesystems are NOT unmounted in single user mode.
# The 'no' applies to all listed filesystem types. See mount(8).
if [ "$READONLY" != "yes" ] ; then
action $"Mounting local filesystems: " mount -a -t nonfs,nfs4,smbfs,ncpfs,cifs,gfs,gfs2,glusterfs -O no_netdev
else
action $"Mounting local filesystems: " mount -a -n -t nonfs,nfs4,smbfs,ncpfs,cifs,gfs,gfs2i,glusterfs -O no_netdev
fi
# Update quotas if necessary
if [ X"$_RUN_QUOTACHECK" = X1 -a -x /sbin/quotacheck ]; then
action $"Checking local filesystem quotas: " /sbin/quotacheck -anug
fi
if [ -x /sbin/quotaon ]; then
action $"Enabling local filesystem quotas: " /sbin/quotaon -aug
fi
# Check to see if a full relabel is needed
if [ -n "$SELINUX_STATE" -a "$READONLY" != "yes" ]; then
if [ -f /.autorelabel ] || strstr "$cmdline" autorelabel ; then
relabel_selinux
fi
else
if [ -d /etc/selinux -a "$READONLY" != "yes" ]; then
[ -f /.autorelabel ] || touch /.autorelabel
fi
fi
# Initialize pseudo-random number generator
if [ -f "/var/lib/random-seed" ]; then
cat /var/lib/random-seed > /dev/urandom
else
[ "$READONLY" != "yes" ] && touch /var/lib/random-seed
fi
if [ "$READONLY" != "yes" ]; then
chmod 600 /var/lib/random-seed
dd if=/dev/urandom of=/var/lib/random-seed count=1 bs=4096 2>/dev/null
fi
if [ -f /etc/crypttab ]; then
init_crypto 1
fi
# Configure machine if necessary.
if [ -f /.unconfigured ]; then
if [ -x /bin/plymouth ]; then
/bin/plymouth quit
fi
if [ -x /usr/bin/system-config-keyboard ]; then
/usr/bin/system-config-keyboard
fi
if [ -x /usr/bin/passwd ]; then
/usr/bin/passwd root
fi
if [ -x /usr/sbin/system-config-network-tui ]; then
/usr/sbin/system-config-network-tui
fi
if [ -x /usr/sbin/timeconfig ]; then
/usr/sbin/timeconfig
fi
if [ -x /usr/sbin/authconfig-tui ]; then
/usr/sbin/authconfig-tui --nostart
fi
if [ -x /usr/sbin/ntsysv ]; then
/usr/sbin/ntsysv --level 35
fi
# Reread in network configuration data.
if [ -f /etc/sysconfig/network ]; then
. /etc/sysconfig/network
# Reset the hostname.
action $"Resetting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
fi
rm -f /.unconfigured
fi
# Clean out /.
rm -f /fastboot /fsckoptions /forcefsck /.autofsck /forcequotacheck /halt \
/poweroff /.suspended &> /dev/null
# Do we need (w|u)tmpx files? We don't set them up, but the sysadmin might...
_NEED_XFILES=
[ -f /var/run/utmpx -o -f /var/log/wtmpx ] && _NEED_XFILES=1
# Clean up /var.
rm -rf /var/lock/cvs/* /var/run/screen/*
find /var/lock /var/run ! -type d -exec rm -f {} \;
rm -f /var/lib/rpm/__db* &> /dev/null
rm -f /var/gdm/.gdmfifo &> /dev/null
[ "$PROMPT" != no ] && plymouth watch-keystroke --command "touch /var/run/confirm" --keys=Ii &
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
> /var/run/utmpx
touch /var/log/wtmpx
chgrp utmp /var/run/utmpx /var/log/wtmpx
chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
[ -n "$SELINUX_STATE" ] && restorecon /var/run/utmp* /var/log/wtmp* >/dev/null 2>&1
# Clean up various /tmp bits
[ -n "$SELINUX_STATE" ] && restorecon /tmp
rm -f /tmp/.X*-lock /tmp/.lock.* /tmp/.gdm_socket /tmp/.s.PGSQL.*
rm -rf /tmp/.X*-unix /tmp/.ICE-unix /tmp/.font-unix /tmp/hsperfdata_* \
/tmp/kde-* /tmp/ksocket-* /tmp/mc-* /tmp/mcop-* /tmp/orbit-* \
/tmp/scrollkeeper-* /tmp/ssh-* \
/dev/.in_sysinit
# Make ICE directory
mkdir -m 1777 -p /tmp/.ICE-unix >/dev/null 2>&1
chown root:root /tmp/.ICE-unix
[ -n "$SELINUX_STATE" ] && restorecon /tmp/.ICE-unix >/dev/null 2>&1
# Start up swapping.
update_boot_stage RCswap
action $"Enabling /etc/fstab swaps: " swapon -a -e
if [ "$AUTOSWAP" = "yes" ]; then
curswap=$(awk '/^\/dev/ { print $1 }' /proc/swaps | while read x; do get_numeric_dev dec $x ; echo -n " "; done)
swappartitions=$(blkid -t TYPE=swap -o device)
if [ x"$swappartitions" != x ]; then
for partition in $swappartitions ; do
[ ! -e $partition ] && continue
majmin=$(get_numeric_dev dec $partition)
echo $curswap | grep -qw "$majmin" || action $"Enabling local swap partitions: " swapon $partition
done
fi
fi
# Set up binfmt_misc
/bin/mount -t binfmt_misc none /proc/sys/fs/binfmt_misc > /dev/null 2>&1
# Boot time profiles. Yes, this should be somewhere else.
if [ -x /usr/sbin/system-config-network-cmd ]; then
if strstr "$cmdline" netprofile= ; then
for arg in $cmdline ; do
if [ "${arg##netprofile=}" != "${arg}" ]; then
/usr/sbin/system-config-network-cmd --profile ${arg##netprofile=}
fi
done
fi
fi
# Now that we have all of our basic modules loaded and the kernel going,
# let's dump the syslog ring somewhere so we can find it later
[ -f /var/log/dmesg ] && mv -f /var/log/dmesg /var/log/dmesg.old
dmesg -s 131072 > /var/log/dmesg
# create the crash indicator flag to warn on crashes, offer fsck with timeout
touch /.autofsck &> /dev/null
[ "$PROMPT" != no ] && plymouth --ignore-keystroke=Ii
if strstr "$cmdline" confirm ; then
touch /var/run/confirm
fi
# Let rhgb know that we're leaving rc.sysinit
if [ -x /bin/plymouth ]; then
/bin/plymouth --sysinit
fi
cat /etc/init.d/functions
# -*-Shell-script-*-
#
# functions This file contains functions to be used by most or all
# shell scripts in the /etc/init.d directory.
#
TEXTDOMAIN=initscripts
# Make sure umask is sane
umask 022
# Set up a default search path.
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
export PATH
# Get a sane screen width
[ -z "${COLUMNS:-}" ] && COLUMNS=80
[ -z "${CONSOLETYPE:-}" ] && CONSOLETYPE="$(/sbin/consoletype)"
if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" -a -z "${LANGSH_SOURCED:-}" ] ; then
. /etc/profile.d/lang.sh 2>/dev/null
# avoid propagating LANGSH_SOURCED any further
unset LANGSH_SOURCED
fi
# Read in our configuration
if [ -z "${BOOTUP:-}" ]; then
if [ -f /etc/sysconfig/init ]; then
. /etc/sysconfig/init
else
# This all seem confusing? Look in /etc/sysconfig/init,
# or in /usr/doc/initscripts-*/sysconfig.txt
BOOTUP=color
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \\033[0;39m"
LOGLEVEL=1
fi
if [ "$CONSOLETYPE" = "serial" ]; then
BOOTUP=serial
MOVE_TO_COL=
SETCOLOR_SUCCESS=
SETCOLOR_FAILURE=
SETCOLOR_WARNING=
SETCOLOR_NORMAL=
fi
fi
# Interpret escape sequences in an fstab entry
fstab_decode_str() {
fstab-decode echo "$1"
}
# Check if any of $pid (could be plural) are running
checkpid() {
local i
for i in $* ; do
[ -d "/proc/$i" ] && return 0
done
return 1
}
__readlink() {
ls -bl "$@" 2>/dev/null| awk '{ print $NF }'
}
__fgrep() {
s=$1
f=$2
while read line; do
if strstr "$line" "$s"; then
echo $line
return 0
fi
done < $f
return 1
}
# __umount_loop awk_program fstab_file first_msg retry_msg retry_umount_args
# awk_program should process fstab_file and return a list of fstab-encoded
# paths; it doesn't have to handle comments in fstab_file.
__umount_loop() {
local remaining sig=
local retry=3 count
remaining=$(LC_ALL=C awk "/^#/ {next} $1" "$2" | sort -r)
while [ -n "$remaining" -a "$retry" -gt 0 ]; do
if [ "$retry" -eq 3 ]; then
action "$3" fstab-decode umount $remaining
else
action "$4" fstab-decode umount $5 $remaining
fi
count=4
remaining=$(LC_ALL=C awk "/^#/ {next} $1" "$2" | sort -r)
while [ "$count" -gt 0 ]; do
[ -z "$remaining" ] && break
count=$(($count-1))
usleep 500000
remaining=$(LC_ALL=C awk "/^#/ {next} $1" "$2" | sort -r)
done
[ -z "$remaining" ] && break
kill $sig $(fstab-decode /sbin/fuser -m $remaining 2>/dev/null | sed -e "s/\b$$\b//g") > /dev/null
sleep 3
retry=$(($retry -1))
sig=-9
done
}
# Similar to __umount loop above, specialized for loopback devices
__umount_loopback_loop() {
local remaining devremaining sig=
local retry=3
remaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $2}' /proc/mounts)
devremaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $1}' /proc/mounts)
while [ -n "$remaining" -a "$retry" -gt 0 ]; do
if [ "$retry" -eq 3 ]; then
action $"Unmounting loopback filesystems: " \
fstab-decode umount $remaining
else
action $"Unmounting loopback filesystems (retry):" \
fstab-decode umount $remaining
fi
for dev in $devremaining ; do
losetup $dev > /dev/null 2>&1 && \
action $"Detaching loopback device $dev: " \
losetup -d $dev
done
remaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $2}' /proc/mounts)
devremaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $1}' /proc/mounts)
[ -z "$remaining" ] && break
fstab-decode /sbin/fuser -k -m $sig $remaining >/dev/null
sleep 3
retry=$(($retry -1))
sig=-9
done
}
# __proc_pids {program} [pidfile]
# Set $pid to pids from /var/run* for {program}. $pid should be declared
# local in the caller.
# Returns LSB exit code for the 'status' action.
__pids_var_run() {
local base=${1##*/}
local pid_file=${2:-/var/run/$base.pid}
local pid_dir=$(/usr/bin/dirname $pid_file)
local binary=$3
[ -d "$pid_dir" -a ! -r "$pid_dir" ] && return 4
pid=
if [ -f "$pid_file" ] ; then
local line p
[ ! -r "$pid_file" ] && return 4 # "user had insufficient privilege"
while : ; do
read line
[ -z "$line" ] && break
for p in $line ; do
if [ -z "${p//[0-9]/}" -a -d "/proc/$p" ] ; then
if [ -n "$binary" ] ; then
local b=$(readlink /proc/$p/exe | sed -e 's/\s*(deleted)$//')
[ "$b" != "$binary" ] && continue
fi
pid="$pid $p"
fi
done
done < "$pid_file"
if [ -n "$pid" ]; then
return 0
fi
return 1 # "Program is dead and /var/run pid file exists"
fi
return 3 # "Program is not running"
}
# Output PIDs of matching processes, found using pidof
__pids_pidof() {
pidof -c -o $$ -o $PPID -o %PPID -x "$1" || \
pidof -c -o $$ -o $PPID -o %PPID -x "${1##*/}"
}
# A function to start a program.
daemon() {
# Test syntax.
local gotbase= force= nicelevel corelimit
local pid base= user= nice= bg= pid_file=
local cgroup=
nicelevel=0
while [ "$1" != "${1##[-+]}" ]; do
case $1 in
'') echo $"$0: Usage: daemon [+/-nicelevel] {program}"
return 1;;
--check)
base=$2
gotbase="yes"
shift 2
;;
--check=?*)
base=${1#--check=}
gotbase="yes"
shift
;;
--user)
user=$2
shift 2
;;
--user=?*)
user=${1#--user=}
shift
;;
--pidfile)
pid_file=$2
shift 2
;;
--pidfile=?*)
pid_file=${1#--pidfile=}
shift
;;
--force)
force="force"
shift
;;
[-+][0-9]*)
nice="nice -n $1"
shift
;;
*) echo $"$0: Usage: daemon [+/-nicelevel] {program}"
return 1;;
esac
done
# Save basename.
[ -z "$gotbase" ] && base=${1##*/}
# See if it's already running. Look *only* at the pid file.
__pids_var_run "$base" "$pid_file"
[ -n "$pid" -a -z "$force" ] && return
# make sure it doesn't core dump anywhere unless requested
corelimit="ulimit -S -c ${DAEMON_COREFILE_LIMIT:-0}"
# if they set NICELEVEL in /etc/sysconfig/foo, honor it
[ -n "${NICELEVEL:-}" ] && nice="nice -n $NICELEVEL"
# if they set CGROUP_DAEMON in /etc/sysconfig/foo, honor it
if [ -n "${CGROUP_DAEMON}" ]; then
if [ ! -x /bin/cgexec ]; then
echo -n "Cgroups not installed"; warning
echo
else
cgroup="/bin/cgexec";
for i in $CGROUP_DAEMON; do
cgroup="$cgroup -g $i";
done
fi
fi
# Echo daemon
[ "${BOOTUP:-}" = "verbose" -a -z "${LSB:-}" ] && echo -n " $base"
# And start it up.
if [ -z "$user" ]; then
$cgroup $nice /bin/bash -c "$corelimit >/dev/null 2>&1 ; $*"
else
$cgroup $nice runuser -s /bin/bash $user -c "$corelimit >/dev/null 2>&1 ; $*"
fi
[ "$?" -eq 0 ] && success $"$base startup" || failure $"$base startup"
}
# A function to stop a program.
killproc() {
local RC killlevel= base pid pid_file= delay try binary=
RC=0; delay=3; try=0
# Test syntax.
if [ "$#" -eq 0 ]; then
echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
return 1
fi
if [ "$1" = "-p" ]; then
pid_file=$2
shift 2
fi
if [ "$1" = "-b" ]; then
if [ -z $pid_file ]; then
echo $"-b option can be used only with -p"
echo $"Usage: killproc -p pidfile -b binary program"
return 1
fi
binary=$2
shift 2
fi
if [ "$1" = "-d" ]; then
delay=$(echo $2 | awk -v RS=' ' -v IGNORECASE=1 '{if($1!~/^[0-9.]+[smhd]?$/) exit 1;d=$1~/s$|^[0-9.]*$/?1:$1~/m$/?60:$1~/h$/?60*60:$1~/d$/?24*60*60:-1;if(d==-1) exit 1;delay+=d*$1} END {printf("%d",delay+0.5)}')
if [ "$?" -eq 1 ]; then
echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
return 1
fi
shift 2
fi
# check for second arg to be kill level
[ -n "${2:-}" ] && killlevel=$2
# Save basename.
base=${1##*/}
# Find pid.
__pids_var_run "$1" "$pid_file" "$binary"
RC=$?
if [ -z "$pid" ]; then
if [ -z "$pid_file" ]; then
pid="$(__pids_pidof "$1")"
else
[ "$RC" = "4" ] && { failure $"$base shutdown" ; return $RC ;}
fi
fi
# Kill it.
if [ -n "$pid" ] ; then
[ "$BOOTUP" = "verbose" -a -z "${LSB:-}" ] && echo -n "$base "
if [ -z "$killlevel" ] ; then
if checkpid $pid 2>&1; then
# TERM first, then KILL if not dead
kill -TERM $pid >/dev/null 2>&1
usleep 100000
if checkpid $pid ; then
try=0
while [ $try -lt $delay ] ; do
checkpid $pid || break
sleep 1
let try+=1
done
if checkpid $pid ; then
kill -KILL $pid >/dev/null 2>&1
usleep 100000
fi
fi
fi
checkpid $pid
RC=$?
[ "$RC" -eq 0 ] && failure $"$base shutdown" || success $"$base shutdown"
RC=$((! $RC))
# use specified level only
else
if checkpid $pid; then
kill $killlevel $pid >/dev/null 2>&1
RC=$?
[ "$RC" -eq 0 ] && success $"$base $killlevel" || failure $"$base $killlevel"
elif [ -n "${LSB:-}" ]; then
RC=7 # Program is not running
fi
fi
else
if [ -n "${LSB:-}" -a -n "$killlevel" ]; then
RC=7 # Program is not running
else
failure $"$base shutdown"
RC=0
fi
fi
# Remove pid file if any.
if [ -z "$killlevel" ]; then
rm -f "${pid_file:-/var/run/$base.pid}"
fi
return $RC
}
# A function to find the pid of a program. Looks *only* at the pidfile
pidfileofproc() {
local pid
# Test syntax.
if [ "$#" = 0 ] ; then
echo $"Usage: pidfileofproc {program}"
return 1
fi
__pids_var_run "$1"
[ -n "$pid" ] && echo $pid
return 0
}
# A function to find the pid of a program.
pidofproc() {
local RC pid pid_file=
# Test syntax.
if [ "$#" = 0 ]; then
echo $"Usage: pidofproc [-p pidfile] {program}"
return 1
fi
if [ "$1" = "-p" ]; then
pid_file=$2
shift 2
fi
fail_code=3 # "Program is not running"
# First try "/var/run/*.pid" files
__pids_var_run "$1" "$pid_file"
RC=$?
if [ -n "$pid" ]; then
echo $pid
return 0
fi
[ -n "$pid_file" ] && return $RC
__pids_pidof "$1" || return $RC
}
status() {
local base pid lock_file= pid_file= binary=
# Test syntax.
if [ "$#" = 0 ] ; then
echo $"Usage: status [-p pidfile] {program}"
return 1
fi
if [ "$1" = "-p" ]; then
pid_file=$2
shift 2
fi
if [ "$1" = "-l" ]; then
lock_file=$2
shift 2
fi
if [ "$1" = "-b" ]; then
if [ -z $pid_file ]; then
echo $"-b option can be used only with -p"
echo $"Usage: status -p pidfile -b binary program"
return 1
fi
binary=$2
shift 2
fi
base=${1##*/}
# First try "pidof"
__pids_var_run "$1" "$pid_file" "$binary"
RC=$?
if [ -z "$pid_file" -a -z "$pid" ]; then
pid="$(__pids_pidof "$1")"
fi
if [ -n "$pid" ]; then
echo $"${base} (pid $pid) is running..."
return 0
fi
case "$RC" in
0)
echo $"${base} (pid $pid) is running..."
return 0
;;
1)
echo $"${base} dead but pid file exists"
return 1
;;
4)
echo $"${base} status unknown due to insufficient privileges."
return 4
;;
esac
if [ -z "${lock_file}" ]; then
lock_file=${base}
fi
# See if /var/lock/subsys/${lock_file} exists
if [ -f /var/lock/subsys/${lock_file} ]; then
echo $"${base} dead but subsys locked"
return 2
fi
echo $"${base} is stopped"
return 3
}
echo_success() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
echo -n $" OK "
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 0
}
echo_failure() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
echo -n $"FAILED"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
echo_passed() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo -n $"PASSED"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
echo_warning() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo -n $"WARNING"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
# Inform the graphical boot of our current state
update_boot_stage() {
if [ -x /bin/plymouth ]; then
/bin/plymouth --update="$1"
fi
return 0
}
# Log that something succeeded
success() {
[ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_success
return 0
}
# Log that something failed
failure() {
local rc=$?
[ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_failure
[ -x /bin/plymouth ] && /bin/plymouth --details
return $rc
}
# Log that something passed, but may have had errors. Useful for fsck
passed() {
local rc=$?
[ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_passed
return $rc
}
# Log a warning
warning() {
local rc=$?
[ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_warning
return $rc
}
# Run some action. Log its output.
action() {
local STRING rc
STRING=$1
echo -n "$STRING "
shift
"$@" && success $"$STRING" || failure $"$STRING"
rc=$?
echo
return $rc
}
# returns OK if $1 contains $2
strstr() {
[ "${1#*$2*}" = "$1" ] && return 1
return 0
}
# Confirm whether we really want to run this service
confirm() {
[ -x /bin/plymouth ] && /bin/plymouth --hide-splash
while : ; do
echo -n $"Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "
read answer
if strstr $"yY" "$answer" || [ "$answer" = "" ] ; then
return 0
elif strstr $"cC" "$answer" ; then
rm -f /var/run/confirm
[ -x /bin/plymouth ] && /bin/plymouth --show-splash
return 2
elif strstr $"nN" "$answer" ; then
return 1
fi
done
}
# resolve a device node to its major:minor numbers in decimal or hex
get_numeric_dev() {
(
fmt="%d:%d"
if [ "$1" == "hex" ]; then
fmt="%x:%x"
fi
ls -lH "$2" | awk '{ sub(/,/, "", $5); printf("'"$fmt"'", $5, $6); }'
) 2>/dev/null
}
# Check whether file $1 is a backup or rpm-generated file and should be ignored
is_ignored_file() {
case "$1" in
*~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
return 0
;;
esac
return 1
}
# Evaluate shvar-style booleans
is_true() {
case "$1" in
[tT] | [yY] | [yY][eE][sS] | [tT][rR][uU][eE])
return 0
;;
esac
return 1
}
# Evaluate shvar-style booleans
is_false() {
case "$1" in
[fF] | [nN] | [nN][oO] | [fF][aA][lL][sS][eE])
return 0
;;
esac
return 1
}
# Apply sysctl settings, including files in /etc/sysctl.d
apply_sysctl() {
sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
for file in /etc/sysctl.d/* ; do
is_ignored_file "$file" && continue
test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
done
}
key_is_random() {
[ "$1" = "/dev/urandom" -o "$1" = "/dev/hw_random" \
-o "$1" = "/dev/random" ]
}
find_crypto_mount_point() {
local fs_spec fs_file fs_vfstype remaining_fields
local fs
while read fs_spec fs_file remaining_fields; do
if [ "$fs_spec" = "/dev/mapper/$1" ]; then
echo $fs_file
break;
fi
done < /etc/fstab
}
# Because of a chicken/egg problem, init_crypto must be run twice. /var may be
# encrypted but /var/lib/random-seed is needed to initialize swap.
init_crypto() {
local have_random dst src key opt mode owner params makeswap skip arg opt
local param value rc ret mke2fs mdir prompt mount_point
ret=0
have_random=$1
while read dst src key opt; do
[ -z "$dst" -o "${dst#\#}" != "$dst" ] && continue
[ -b "/dev/mapper/$dst" ] && continue;
if [ "$have_random" = 0 ] && key_is_random "$key"; then
continue
fi
if [ -n "$key" -a "x$key" != "xnone" ]; then
if test -e "$key" ; then
owner=$(ls -l $key | (read a b owner rest; echo $owner))
if ! key_is_random "$key"; then
mode=$(ls -l "$key" | cut -c 5-10)
if [ "$mode" != "------" ]; then
echo $"INSECURE MODE FOR $key"
fi
fi
if [ "$owner" != root ]; then
echo $"INSECURE OWNER FOR $key"
fi
else
echo $"Key file for $dst not found, skipping"
ret=1
continue
fi
else
key=""
fi
params=""
makeswap=""
mke2fs=""
skip=""
# Parse the src field for UUID= and convert to real device names
if [ "${src%%=*}" == "UUID" ]; then
src=$(/sbin/blkid -t "$src" -l -o device)
elif [ "${src/^\/dev\/disk\/by-uuid\/}" != "$src" ]; then
src=$(__readlink $src)
fi
# Is it a block device?
[ -b "$src" ] || continue
# Is it already a device mapper slave? (this is gross)
devesc=${src##/dev/}
devesc=${devesc//\//!}
for d in /sys/block/dm-*/slaves ; do
[ -e $d/$devesc ] && continue 2
done
# Parse the options field, convert to cryptsetup parameters and
# contruct the command line
while [ -n "$opt" ]; do
arg=${opt%%,*}
opt=${opt##$arg}
opt=${opt##,}
param=${arg%%=*}
value=${arg##$param=}
case "$param" in
cipher)
params="$params -c $value"
if [ -z "$value" ]; then
echo $"$dst: no value for cipher option, skipping"
skip="yes"
fi
;;
size)
params="$params -s $value"
if [ -z "$value" ]; then
echo $"$dst: no value for size option, skipping"
skip="yes"
fi
;;
hash)
params="$params -h $value"
if [ -z "$value" ]; then
echo $"$dst: no value for hash option, skipping"
skip="yes"
fi
;;
verify)
params="$params -y"
;;
swap)
makeswap=yes
;;
tmp)
mke2fs=yes
esac
done
if [ "$skip" = "yes" ]; then
ret=1
continue
fi
if [ -z "$makeswap" ] && cryptsetup isLuks "$src" 2>/dev/null ; then
if key_is_random "$key"; then
echo $"$dst: LUKS requires non-random key, skipping"
ret=1
continue
fi
if [ -n "$params" ]; then
echo "$dst: options are invalid for LUKS partitions," \
"ignoring them"
fi
if [ -n "$key" ]; then
/sbin/cryptsetup -d $key luksOpen "$src" "$dst" <&1 2>/dev/null && success || failure
rc=$?
else
mount_point="$(find_crypto_mount_point $dst)"
[ -n "$mount_point" ] || mount_point=${src##*/}
prompt=$(printf $"%s is password protected" "$mount_point")
plymouth ask-for-password --prompt "$prompt" --command="/sbin/cryptsetup luksOpen -T1 $src $dst" <&1
rc=$?
fi
else
[ -z "$key" ] && plymouth --hide-splash
/sbin/cryptsetup $params ${key:+-d $key} create "$dst" "$src" <&1 2>/dev/null && success || failure
rc=$?
[ -z "$key" ] && plymouth --show-splash
fi
if [ $rc -ne 0 ]; then
ret=1
continue
fi
if [ -b "/dev/mapper/$dst" ]; then
if [ "$makeswap" = "yes" ]; then
mkswap "/dev/mapper/$dst" 2>/dev/null >/dev/null
fi
if [ "$mke2fs" = "yes" ]; then
if mke2fs "/dev/mapper/$dst" 2>/dev/null >/dev/null \
&& mdir=$(mktemp -d /tmp/mountXXXXXX); then
mount "/dev/mapper/$dst" "$mdir" && chmod 1777 "$mdir"
umount "$mdir"
rmdir "$mdir"
fi
fi
fi
done < /etc/crypttab
return $ret
}
# A sed expression to filter out the files that is_ignored_file recognizes
__sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d'
cat /etc/rc.d/rc
#! /bin/bash
#
# rc This file is responsible for starting/stopping
# services when the runlevel changes.
#
# Original Author:
# Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#
set -m
# check a file to be a correct runlevel script
check_runlevel ()
{
# Check if the file exists at all.
[ -x "$1" ] || return 1
is_ignored_file "$1" && return 1
return 0
}
# Now find out what the current and what the previous runlevel are.
argv1="$1"
set $(/sbin/runlevel)
runlevel=$2
previous=$1
export runlevel previous
. /etc/init.d/functions
export CONSOLETYPE
do_confirm="no"
if [ -f /var/run/confirm ]; then
do_confirm="yes"
fi
UPSTART=
[ -x /sbin/initctl ] && UPSTART=yes
# See if we want to be in user confirmation mode
if [ "$previous" = "N" ]; then
if [ "$do_confirm" = "yes" ]; then
echo $"Entering interactive startup"
else
echo $"Entering non-interactive startup"
fi
fi
# Get first argument. Set new runlevel to this argument.
[ -n "$argv1" ] && runlevel="$argv1"
# Is there an rc directory for this new runlevel?
[ -d /etc/rc$runlevel.d ] || exit 0
# Set language, vc settings once to avoid doing it for every init script
# through functions
if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
. /etc/profile.d/lang.sh 2>/dev/null
export LANGSH_SOURCED=1
fi
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
# Check if the subsystem is already up.
subsys=${i#/etc/rc$runlevel.d/K??}
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] || continue
check_runlevel "$i" || continue
# Bring the subsystem down.
[ -n "$UPSTART" ] && initctl emit --quiet stopping JOB=$subsys
$i stop
[ -n "$UPSTART" ] && initctl emit --quiet stopped JOB=$subsys
done
还可以使用 initctl 的 emit 命令从命令行发送一个事件。
#initctl emit <event>
这一般是用于 UpStart 本身的排错。
http://www.ibm.com/developerworks/cn/linux/1407_liuming_init2/
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
# Check if the subsystem is already up.
subsys=${i#/etc/rc$runlevel.d/S??}
[ -f /var/lock/subsys/$subsys ] && continue
[ -f /var/lock/subsys/$subsys.init ] && continue
check_runlevel "$i" || continue
# If we're in confirmation mode, get user confirmation
if [ "$do_confirm" = "yes" ]; then
confirm $subsys
rc=$?
if [ "$rc" = "1" ]; then
continue
elif [ "$rc" = "2" ]; then
do_confirm="no"
fi
fi
update_boot_stage "$subsys"
# Bring the subsystem up.
[ -n "$UPSTART" ] && initctl emit --quiet starting JOB=$subsys
if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
export LC_ALL=C
exec $i start
fi
$i start
[ -n "$UPSTART" ] && initctl emit --quiet started JOB=$subsys
done
[ "$do_confirm" = "yes" ] && rm -f /var/run/confirm
exit 0
[root@steven ~]# cat /sbin/service
#!/bin/sh . /etc/init.d/functions VERSION="$(basename $0) ver. 0.91"
USAGE="Usage: $(basename $0) < option > | --status-all | \
[ service_name [ command | --full-restart ] ]"
SERVICE=
SERVICEDIR="/etc/init.d"
OPTIONS= if [ $# -eq ]; then
echo "${USAGE}" >&
exit
fi cd /
while [ $# -gt ]; do
case "${1}" in
--help | -h | --h* )
echo "${USAGE}" >&
exit
;;
--version | -V )
echo "${VERSION}" >&
exit
;;
*)
if [ -z "${SERVICE}" -a $# -eq -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
fi
;;
esac
done
exit
elif [ $# -eq -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
exit $?
fi
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done if [ -f "${SERVICEDIR}/${SERVICE}" ]; then
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" ${OPTIONS} #env -i:开始一个新的空的环境;
else
echo $"${SERVICE}: unrecognized service" >&
exit
fi
在一个新的空的环境中执行指令
$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/steven/bin
echo $TERM
xterm
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
env命令
来自: http://man.linuxde.net/env
Shell内建命令 env命令用于显示系统中已存在的环境变量,以及在定义的环境中执行指令。该命令只使用"-"作为参数选项时,隐藏了选项"-i"的功能。若没有设置任何选项和参数时,则直接显示当前的环境变量。 如果使用env命令在新环境中执行指令时,会因为没有定义环境变量"PATH"而提示错误信息"such file or directory"。此时,用户可以重新定义一个新的"PATH"或者使用绝对路径。
选项
-i:开始一个新的空的环境;
-u<变量名>:从当前环境中删除指定的变量。
参数
变量定义:定义在新的环境中变量,定义多个变量定义用空格隔开。格式为“变量名=值”;
指定:指定要执行的指令和参数。
实例
[root@localhost ~]# env hostname=LinServ-1 TERM=linux SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=192.168.2.111 2705 22 SSH_TTY=/dev/pts/0 USER=root LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35: mail=/var/spool/mail/root PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin INPUTRC=/etc/inputrc pwd=/root LANG=zh_CN.UTF-8 SHLVL=1 HOME=/root logname=root SSH_CONNECTION=192.168.2.111 2705 192.168.2.2 22 LESSOPEN=|/usr/bin/lesspipe.sh %s G_BROKEN_FILENAMES=1 _=/bin/env
[root@steven pts]# pwd
/dev/pts
[root@steven pts]# ll
crw--w---- 1 root tty 136, 0 1月 18 19:50 0
c--------- 1 root root 5, 2 1月 18 05:29 ptmx
查看当前shell在哪个终端下运行
[root@steven pts]# tty
/dev/pts/0
# ll #每开一个pts 模拟终端,/dev/pts/目录下就会多一个终端文件,从0开始 0,1,2,3,4。。。。。
总用量 0
crw--w---- 1 root tty 136, 0 1月 18 19:52 0
crw--w---- 1 root tty 136, 1 1月 18 19:52 1
c--------- 1 root root 5, 2 1月 18 05:29 ptmx