LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1432|回复: 10

通过并行化 Linux 系统服务来提高引导速度

[复制链接]
发表于 2005-1-5 22:04:52 | 显示全部楼层 |阅读模式
今天无意间看到一篇使系统服务并行运行加快系统启动速度的文章,于是
尝试了下,感觉办法很不错,同时让我对lfs的系统引导脚本有了更深的理解
总觉得系统启动太慢的兄弟们不妨研究研究尝试下。
原文在:
http://www-900.ibm.com/developerWorks/cn/linux/l-boot/index.shtml
03年11月的了,不知道有没有人已经在sir发过,刚才搜索下好象没找到。
因为要修改系统启动的脚本,很可能让系统启动不正常,所以最好有双linux系统,即使修改错误了,还可以从另一个系统引导修复。
如果很不幸,没有的话,可以先学下这招,紧急救护系统也有用 :p
紧急救护模式:
[PHP]
grub菜单中选择linux,按e,e,进入编辑模式,kernel......那一行最后加上
init=/bin/bash,这样引导系统可以得到一个bash shell
这样进入的系统会是read-only的,首先先使用fsck检查系统
fsck -a /dev/hdaX  处理根分区
fsck -R -A -a   处理其他分区
如果系统在上次重启,是正常重启的,文件系统是正常卸载的,可以不用fsck检查直接把系统挂载成read-write的
mount / -o remount,rw  
mount /proc  
swapon -a    打开所有的交换分区
在修复好系统后将系统重新挂载成ro的,就可以安全重启了。
mount / -o remount,ro
[/PHP]
下面简单的说一下linux系统的启动过程
如果你想更了解系统的启动,这有篇文章非常好。
http://www.faqs.org/docs/Linux-H ... h-Prompt-HOWTO.html
当grub加载内核后,/sbin/init首先从/etc/inittab取得运行级别
id:3:initdefault: 这里启动级别是3--文本模式
si::sysinit:/etc/rc.d/init.d/rc sysinit
这一行代表在任何运行级别都去执行/etc/rc.d/init.d/rc
sysinit 是传入的参数,并且这是init执行的第一个脚本,做系统的最基本的初始化,比如挂载proc系统拉,检查根分区拉,开启swap拉。等等
但是在不同的发行版可能不同,比如在Mandrakelinux中是:si::sysinit:/etc/rc.d/rc.sysinit
init执行的则是/etc/rc.d/rc.sysinit
经过了系统的初始化后,init开始读取run-level中的脚本,这些脚本位于
/etc/rc.d/rcX.d目录下。X是当前的运行级别.这里是系统启动的服务,比如network,alsa,httpd....等
l3:3:wait:/etc/rc.d/rc 3
:3:表示这是在运行级别3中执行的脚本,wait表示init会等待这些脚本都执行
完再继续运行,不知道如果改为once会怎么样,还没有实验.... :ask
另外有的发行版会在执行rcX.d之后去执行/etc/rc.d/rc.local
最后就看到可爱的login拉
在/etc/rc.d/rcX.d中的脚本都是以SXX+服务名或者KXX+服务名组成的,并且都是到/etc/rc.d/init.d中相应脚本的符号链接,有的发行版是/etc/init.d
其中XX是0-9的数字,数字越小,则启动的时间越早。
以S开头的表示系统启动时传递start参数的服务,就是开启拉。K开头的就是
传递stop参数拉。kill bill...
/etc/rc.d/rc3.d:
S10sysklogd@   S20network@  S25random@ S30httpd S40alsa@  S85numlock@
可以看出,我的系统进入rc3.d时首先启动的服务是sysklogd,最后是numlock
很明显,httpd服务必须要在network之后运行,不然没有网络哪来的web服务?想知道系统是怎么识别这些数字作为顺序的吧,俺也是今天才知道的。。
原来是用ls -v 列出它们,挨个放血
逐一的去执行它们,这样就会使系统启动的速度很慢,服务越多越明显
现在希望做的就是,让那些相互之间没有依赖关系的服务可以同时开启,而不是逐一的执行,这样系统启动的速度就会加快很多了。
而相互有依赖关系的服务,告诉make这些服务的依赖关系,然后让make去解决
再使用make -j 参数使服务可以并行启动。
在makefile中写入服务之间的相依赖关系
httpd      :      network
表示httpd依赖network,这样在network启动完毕,则可以启动httpd
而其他不相依赖的服务可以同时进行,这样系统启动的速度就快多拉 :2cool
原理就是这样,下面是我的实际做法:
首先是make 的配置文件:
/etc/rc.d/runlevel.mk

  1. ########################################################################
  2. # Description : Gnu Makefile to control the services in the specified
  3. #               runlevel. It will run the required services, and log
  4. #               the output of the services to the file
  5. #               /var/log/initd.start (for service startup) and
  6. #               /var/log/initd.stop (for service shutdown).
  7. #
  8. #               This controlling program is designed to be invoked by
  9. #               the "/etc/rc.d/rc" script.
  10. #
  11. # Author      : [email]jameshunt@uk.ibm.com[/email]
  12. #
  13. # Notes       :
  14. #
  15. # - Run as,
  16. #
  17. #    make [-n] -j -f runlevel.mk \
  18. #        RUNLEVEL={0|1|2|3|4|5|6} \
  19. #        JOB={start|stop|restart|status}
  20. #
  21. # - $(JOB) is not validated - that is left to the service program.
  22. # - $(RUNLEVEL) is not validated - that is left to the calling program
  23. #   (usually /etc/rc.d/rc).
  24. # - It wouldn't take too much effort to auto-generate this Makefile.
  25. #
  26. ########################################################################

  27. # passed as a parameter
  28. RUNLEVEL =

  29. # passed as a parameter (start, stop, status, etc)
  30. JOB =

  31. # set to a value to enable debug output
  32. DEBUG =

  33. ########################################################################
  34. # START CONFIGURATION

  35. # system commands used by this facility
  36. CAT               = /bin/cat
  37. RM                = /bin/rm
  38. ECHO              = /bin/echo
  39. DATE              = /bin/date

  40. # Directory containing scripts/programs to run.
  41. INITD_DIR        := /etc/rc.d/init.d #这里要修改成自己系统的所有服
  42. #务脚本存放目录,有些发行版是/etc/init.d 这个目录必须正确

  43. # Directory into which a lock file is created when a service starts.
  44. # (Note that the lock file is created by the service).
  45. SUBSYS_FILE_DIR  := /var/lock/subsys #这个目录自己创建

  46. # Used to create temporary files, before collating them all into
  47. # $(FINAL_OUTPUT_FILE).
  48. TMP_DIR          := /tmp

  49. TMPFILE_PREFIX   := .runlevel
  50. TMP_FILE          = $(TMP_DIR)/$(TMPFILE_PREFIX).$(JOB).$@

  51. # File that contains all output of programs/scripts run.
  52. FINAL_OUTPUT_FILE = /var/log/initd.$(JOB)

  53. # List of *all* services.
  54. #
  55. # (Important Note: if you don't include a service in this list,
  56. # it won't get run!)
  57. #这里写上所有的需要启动的服务,这些服务的名字必须要与/etc/rc.d/init.d
  58. #中的名字一致的。
  59. ALL               = \
  60.                     sysklogd          \
  61.                     network       \
  62.                     httpd         \
  63.                     random        \
  64.                     alsa          \
  65.                     numlock       \

  66. # END CONFIGURATION
  67. ########################################################################

  68. # Check command-line parameters
  69. ifndef RUNLEVEL
  70. $(error must specify RUNLEVEL, so I know what to run)
  71. endif

  72. ifndef JOB
  73. $(error must specify JOB, so I know what to do)
  74. endif

  75. default: $(ALL) create_final_output_file
  76. ifneq ($(DEBUG),)
  77.         @$(ECHO) "RUNLEVEL=$(RUNLEVEL)"
  78.         @$(ECHO) "JOB=$(JOB)"
  79.         @$(ECHO) "FINAL_OUTPUT_FILE=$(FINAL_OUTPUT_FILE)"
  80.         @$(ECHO) "TMP_FILE=$(TMP_FILE)"
  81.         @$(ECHO) "ALL=|$(ALL)|"
  82.         #@$(ECHO)
  83.         #@$(ECHO) "ALL (less local)=|$(filter-out local,$(ALL))|"
  84.         #@$(ECHO)
  85.         #@$(ECHO) "ALL (less kudzu)=|$(filter-out kudzu,$(ALL))|"
  86.         #@$(ECHO)
  87. endif

  88. ##############################################################
  89. # Generic rule to control a service.
  90. #
  91. # Note that we capture all output to a file.

  92. $(ALL)                        : $(SUBSYS_FILE_DIR)/$@
  93.                                                         @$(ECHO) "Begin "$(JOB) $@" at `$(DATE)`"  >  $(TMP_FILE)
  94.                                                         @$(INITD_DIR)/$@ $(JOB)                      >> $(TMP_FILE) 2>&1
  95.                                                         @$(ECHO) "End "$(JOB) $@" at `$(DATE)`"    >> $(TMP_FILE)

  96. ##############################################################
  97. # List of services that have dependencies.
  98. #
  99. # (Note: It is not necessary to list services that have no
  100. # dependencies).

  101. # Include the relevant dependencies. If you intend to use this facility,
  102. # you must provide 2 makefiles / runlevel, one for starting the services
  103. # in the runlevel, and one for stopping the services in the runlevel.
  104. #
  105. # WARNING: If make attempts to include a file that does not exist, it will
  106. # exit. This could cause your system to boot in an unfamiliar way.
  107. include /etc/rc.d/start3.mk #这里是服务依赖关系的记录文件
  108. #
  109. # Lastly, merge all the service output files into a single file.
  110. # Note that the order of the service output in the merged file is not
  111. # chronological.
  112. create_final_output_file        :
  113.                                                                                                                 $(CAT) $(TMP_DIR)/$(TMPFILE_PREFIX).$(JOB).* \
  114.                                                                                                                                 > $(FINAL_OUTPUT_FILE)
  115.                                                                                                                 $(RM) -f $(TMP_DIR)/$(TMPFILE_PREFIX).$(JOB).*

  116. # EOF
复制代码


/etc/rc.d/start3.mk #由上面的runlevel.mk决定位置名称
httpd                                                                                :        network
其中的空格必须是用TAB键打出来的
最后修改/etc/rc.d/init.d/rc使其不再运行/etc/rc.d/rcX.d中的脚本,而去执行
/etc/rc.d/runlevel.mk,实现服务的并行启动

/etc/rc.d/init.d/rc :

  1. #!/bin/sh
  2. # Begin $rc_base/init.d/rc - Main Run Level Control Script

  3. # Based on rc script from LFS-3.1 and earlier.
  4. # Rewritten by Gerard Beekmans  - [email]gerard@linuxfromscratch.org[/email]

  5. . /etc/sysconfig/rc
  6. . $rc_functions

  7. # This sets a few default terminal options.
  8. stty sane

  9. # These 3 signals will not cause our script to exit
  10. trap "" INT QUIT TSTP

  11. [ "$1" != "" ] && runlevel=$1

  12. if [ "$runlevel" = "" ]
  13. then
  14.         echo "Usage: $0 <runlevel>" >&2
  15.         exit 1
  16. fi

  17. previous=$PREVLEVEL
  18. [ "$previous" = "" ] && previous=N

  19. if [ ! -d $rc_base/rc$runlevel.d ]
  20. then
  21.         echo "$rc_base/rc$runlevel.d does not exist"
  22.         exit 1
  23. fi

  24. # Attempt to stop all service started by previous runlevel,
  25. # and killed in this runlevel
  26. if [ "$previous" != "N" ]
  27. then
  28.         for i in $(ls -v $rc_base/rc$runlevel.d/K* 2> /dev/null)
  29.         do

  30.                 check_script_status

  31.                 suffix=${i#$rc_base/rc$runlevel.d/K[0-9][0-9]}
  32.                 prev_start=$rc_base/rc$previous.d/S[0-9][0-9]$suffix
  33.                 sysinit_start=$rc_base/rcsysinit.d/S[0-9][0-9]$suffix

  34.                 if [ "$runlevel" != "0" ] && [ "$runlevel" != "6" ]
  35.                 then
  36.                         if [ ! -f $prev_start ] && [ ! -f $sysinit_start ]
  37.                         then
  38.                                 echo -n -e $WARNING
  39.                                 echo "$i can't be executed because it was"
  40.                                 echo "not started in the previous runlevel ($previous)"
  41.                                 echo -n -e $NORMAL
  42.                                 continue
  43.                         fi
  44.                 fi
  45.                 $i stop
  46.                 error_value=$?

  47.                 if [ "$error_value" != "0" ]
  48.                 then
  49.                         print_error_msg
  50.                 fi
  51.         done
  52. fi
  53. ################# 由于我都是从文本模式登陆,所以加上判断句,如果
  54. ##runlevel为3的话,就去执行runlevel.mk,而不逐一执行这些脚本
  55. if [ "$runlevel" = 3 ]
  56. then
  57.                   
  58.      make -j -f /etc/rc.d/runlevel.mk RUNLEVEL=3 JOB=start
  59.      # -j 表示以并行的方式去启动在runlevel.mk中定义的服务     
  60.      # -f 指定MakeFile为runlevel.mk,否则make只会去尝试去运行
  61.      #当前目录下的MakeFile.RUNLEVEL=3在这里没有任何意义,因为
  62.      #我没有使用$runlevel可以在任何run-level下并行服务
  63.      #
  64.      #JOB=start表示去start /etc/rc.d/rc3.d中的服务

  65. else

  66. #如果启动级别不是3的话,仍然按照正常去引导系统.
  67. #这样就不用写stop的脚本,我只想让系统启动的更快些 :p
  68. #因为lfs的启动脚本比较怪,它不象其他的发行版使用/etc/rc.d/rc.sysinit
  69. #作为系统的初始化,而是把那些过程分开成几个脚本运行
  70. #si::sysinit:/etc/rc.d/init.d/rc sysinit 传入的sysinit参数去执行
  71. #/etc/rc.d/rcsysinit.d目录下的脚本,这样我就不能轻易的使用$runlevel
  72. #去使在任何运行级别都可以并行服务了,它会传进来个sysinit @_@
  73. #
  74. for i in $( ls -v $rc_base/rc$runlevel.d/S* 2> /dev/null)

  75. do

  76.         if [ "$previous" != "N" ]
  77.         then
  78.                 suffix=${i#$rc_base/rc$runlevel.d/S[0-9][0-9]}
  79.                 stop=$rc_base/rc$runlevel.d/K[0-9][0-9]$suffix
  80.                 prev_start=$rc_base/rc$previous.d/S[0-9][0-9]$suffix

  81.                 [ -f $prev_start ] && [ ! -f $stop ] && continue
  82.         fi

  83.         check_script_status

  84.         case $runlevel in
  85.                 0|6) $i stop        ;;
  86.                 *)   $i start        ;;
  87.         esac
  88.         error_value=$?

  89.         if [ "$error_value" != "0" ]
  90.         then
  91.                 print_error_msg
  92.         fi
  93. done
  94. fi
  95. # End $rc_base/init.d/rc
复制代码

另外要修改rc脚本时,要注意语法。修改后用sh -n rc 测试有没有语法错误
并且这个rc位置也是由/etc/inittab决定的
如果MDK中rc就是在/etc/rc.d下。
l3:3:wait:/etc/rc.d/rc 3
另外哪位大侠知道这个变量是在哪里定义的阿?找了半天也没找到。
previous=$PREVLEVEL

一点小笔记,希望对你有帮助
 楼主| 发表于 2005-1-6 17:25:56 | 显示全部楼层
previous=$PREVLEVEL没人知道这个变量是怎么来的吗?我看了MDK的配置文件,也有这么一个,不知道哪里得到的呢?
大侠帮帮忙阿。。。。。不要让我又多一悬帖吧。。。。。。:thank
发表于 2005-1-17 12:51:19 | 显示全部楼层
中间有些语言表达不达意,知道楼主是高手,但是编程习惯就是这样?注释前也这里少个#那里少个#的?

准备尝试一下,还在看... ...
另:你就加了一个 httpd : network的依赖,就能实现只需5秒就到登陆界面了?
回复 支持 反对

使用道具 举报

发表于 2005-1-17 13:07:37 | 显示全部楼层
如果我没记错的话,FC和Mandrakelinux正在做这方面的研究。还有人画出了启动进程的对比图。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-1-17 13:08:35 | 显示全部楼层
不好意思,第一次写,没什么经验,写的不好,我没什么编程经验,以后会注意的。
你可以看到,我启动的服务比较少,没有什么费时的,并且感觉lfs要比其他的发行版
快一些。
上面有原文的链接,写的比较好。
稍微有点困难的就是找出服务的相依赖,由启动顺序可以得到一些信息。
btw 我不是高手,菜鸟一个。
回复 支持 反对

使用道具 举报

发表于 2005-1-17 15:43:13 | 显示全部楼层
原文看了呀,但不知道如何操作,看了你的,有点眉目,知道了一些如何操作的。
但现在还是不敢尝试。 :)

在我看了楼主一系列的回帖及主题帖后,反正认为你比我厉害好多,就是俺心目中的高手了! :)
回复 支持 反对

使用道具 举报

发表于 2005-1-17 16:04:47 | 显示全部楼层
previous=$PREVLEVEL
这个定义好象是在debian里看到过。
回复 支持 反对

使用道具 举报

发表于 2005-1-17 21:41:33 | 显示全部楼层
呵呵,gentoo的rc已经有了并行化系统服务的选项,用不着自己去研究引导脚本。
回复 支持 反对

使用道具 举报

发表于 2005-1-17 23:01:04 | 显示全部楼层
我上次弄这个东西,弄到启动不了了,
改启动的服务没有起来,不敢弄了.
危险,早先,没看到楼主的恢复方法,只好重新安装,郁闷了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-1-17 23:12:12 | 显示全部楼层
这个恢复办法很好的。
这样根本就不存在读取系统任何一个脚本,根本不做系统的初始化。
不象单用户模式还需要做系统的基本初始化才能使用。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表