系统服务 (daemons)

系统为了某些功能必须要提供一些服务 (不论是系统本身还是网络方面),这个服务就称为 service 。 但是 service 的提供总是需要程序的运行吧!否则如何运行呢?所以达成这个 service 的程序我们就称呼他为 daemon 啰! 举例来说,达成循环型例行性工作排程服务 (service) 的程序为 crond 这个 daemon 啦!本节节选自 鸟哥的 Linux 私房菜 -- 基础学习篇目录  第十八章、认识系统服务 (daemons)

daemon 的主要分类

如果依据 daemon 的启动与管理方式来区分,基本上,可以将 daemon 分为可独立启动的 stand alone , 与透过一支 super daemon 来统一管理的服务这两大类,这两类 daemon 的说明如下:

  • stand_alone:此 daemon 可以自行单独启动服务

就字面上的意思来说,stand alone 就是『独立的启动』的意思。这种类型的 daemon 可以自行启动而不必透过其他机制的管理; daemon 启动并加载到内存后就一直占用内存与系统资源。最大的优点就是:因为是一直存在内存内持续的提供服务, 因此对于发生客户端的要求时,stand alone 的 daemon 响应速度较快。常见的 stand alone daemon 有 WWW 的 daemon (httpd)、FTP 的 daemon (vsftpd) 等等。

  • super daemon: 一支特殊的 daemon 来统一管理

这一种服务的启动方式则是藉由一个统一的 daemon 来负责唤起服务!这个特殊的 daemon 就被称为 super daemon 。 早期的 super daemon 是 inetd 这一个,后来则被 xinetd 所取代了。这种机制比较有趣的地方在于, 当没有客户端的要求时,各项服务都是未启动的情况,等到有来自客户端的要求时, super daemon 才唤醒相对应的服务。当客户端的要求结束后,被唤醒的这个服务也会关闭并释放系统资源。

这种机制的好处是: (1)由于 super daemon 负责唤醒各项服务,因此 super daemon 可以具有安全控管的机制,就是类似网络防火墙的功能啦! (2)由于服务在客户端的联机结束后就关闭,因此不会一直占用系统资源。但是缺点是什么呢? 因为有客户端的联机才会唤醒该服务,而该服务加载到内存的时间需要考虑进去,因此服务的反应时间会比较慢一些啦!

服务与端口的对应

为了统一整个因特网的端口号对应服务的功能,好让所有的主机都能够使用相同的机制来提供服务与要求服务, 所以就有了『通讯协议』这玩意儿。也就是说,有些约定俗成的服务都放置在同一个埠号上面啦!举例来说, 网址列上面的 http 会让浏览器向 WWW 服务器的 80 埠号进行联机的要求!而 WWW 服务器也会将 httpd 这个软件激活在 port 80, 这样两者才能够达成联机的!

嗯!那么想一想,系统上面有没有什么配置可以让服务与埠号对应在一起呢?那就是 /etc/services 啦!

[root@www ~]# cat /etc/services
....(前面省略)....
ftp 21/tcp
ftp 21/udp fsp fspd
ssh 22/tcp # SSH Remote Login Protocol
ssh 22/udp # SSH Remote Login Protocol
....(中间省略)....
http 80/tcp www www-http # WorldWideWeb HTTP
http 80/udp www www-http # HyperText Transfer Protocol
....(底下省略)....
# 这个文件的内容是以底下的方式来编排的:
# <daemon name> <port/封包协议> <该服务的说明>

daemon 的启动脚本与启动方式

提供某个服务的 daemon 虽然只是一支程序而已,但是这支 daemon 的启动还是需要运行档、配置文件、运行环境等等, 举例来说,你可以查阅一下 httpd 这个程序 (man httpd) ,里面可谈到不少的选项与参数呢!此外,为了管理上面的方便, 所以通常 distribution 都会记录每一支 daemon 启动后所取得程序的 PID 在 /var/run/ 这个目录下呢! 还有还有,在启动这些服务之前,你可能也要自行处理一下 daemon 能够顺利运行的环境是否正确等等。鸟哥这里要讲的是, 要启动一支 daemon 考虑的事情很多,并非单纯运行一支程序就够了。

为了解决上面谈到的问题,因此通常 distribution 会给我们一个简单的 shell script 来进行启动的功能。 该 script 可以进行环境的侦测、配置文件的分析、PID 文件的放置,以及相关重要交换文件案的锁住 (lock) 动作, 你只要运行该 script ,上述的动作就一口气连续的进行,最终就能够顺利且简单的启动这个 daemon 啰!

那么这些 daemon 的启动脚本 (shell script) 放在哪里啊? 以及某些重要的配置文件又是放置到哪里?基本上是放在这些地方:

  • /etc/init.d/* :启动脚本放置处

    系统上几乎所有的服务启动脚本都放置在这里!事实上这是公认的目录,我们的 CentOS 实际上放置在 /etc/rc.d/init.d/ 啦! 不过还是有配置连结档到 /etc/init.d/ 的!既然这是公认的目录,因此建议您记忆这个目录即可!
  • /etc/sysconfig/* :各服务的初始化环境配置文件

    几乎所有的服务都会将初始化的一些选项配置写入到这个目录下,举例来说,登录档的 syslog 这支 daemon 的初始化配置就写入在 /etc/sysconfig/syslog 这里呢!而网络的配置则写在 /etc/sysconfig/network 这个文件中。 所以,这个目录内的文件也是挺重要的;
  • /etc/xinetd.conf, /etc/xinetd.d/* :super daemon 配置文件

    super daemon 的主要配置文件 (其实是默认值) 为 /etc/xinetd.conf ,不过我们上面就谈到了, super daemon 只是一个统一管理的机制,他所管理的其他 daemon 的配置则写在 /etc/xinetd.d/* 里头喔!
  • /etc/* :各服务各自的配置文件
  • /var/lib/* :各服务产生的数据库

    一些会产生数据的服务都会将他的数据写入到 /var/lib/ 目录中。举例来说,数据库管理系统 MySQL 的数据库默认就是写入 /var/lib/mysql/ 这个目录下啦!
  • /var/run/* :各服务的程序之 PID 记录处

    既然 daemon 是程序,所以当然也可以利用 kill 或 killall 来管理啦!不过为了担心管理时影响到其他的程序, 因此 daemon 通常会将自己的 PID 记录一份到 /var/run/ 当中!例如登录文件的 PID 就记录在 /var/run/syslogd.pid 这个文件中。如此一来, /etc/init.d/syslog 就能够简单的管理自己的程序啰。

Stand alone 的 /etc/init.d/* 启动

刚刚谈到了几乎系统上面所有服务的启动脚本都在 /etc/init.d/ 底下,这里面的脚本会去侦测环境、搜寻配置文件、 加载 distribution 提供的函数功能、判断环境是否可以运行此 daemon 等等,等到一切都侦测完毕且确定可以运行后, 再以 shell script 的 case....esac 语法来启动、关闭、 观察此 daemon 喔!我们可以简单的以 /etc/init.d/syslog 这个登录档启动脚本来进行说明:

[root@www ~]# /etc/init.d/syslog
用法: /etc/init.d/syslog {start|stop|status|restart|condrestart}
# 什么参数都不加的时候,系统会告诉你可以用的参数有哪些,如上所示。 范例一:观察 syslog 这个 daemon 目前的状态
[root@www ~]# /etc/init.d/syslog status
syslogd (pid 4264) 正在运行...
klogd (pid 4267) 正在运行...
# 代表 syslog 管理两个 daemon ,这两个 daemon 正在运行中啦! 范例二:重新让 syslog 读取一次配置文件
[root@www ~]# /etc/init.d/syslog restart
正在关闭核心记录器: [ 确定 ]
正在关闭系统记录器: [ 确定 ]
正在启动系统记录器: [ 确定 ]
正在启动核心记录器: [ 确定 ]
[root@www ~]# /etc/init.d/syslog status
syslogd (pid 4793) 正在运行...
klogd (pid 4796) 正在运行...
# 因为重新启动过,所以 PID 与第一次观察的值就不一样了!这样了解乎?

由于系统的环境都已经帮你制作妥当,所以利用 /etc/init.d/* 来启动、关闭与观察,就非常的简单!话虽如此, CentOS 还是有提供另外一支可以启动 stand alone 服务的脚本喔,那就是 service 这个程序。 其实 service 仅是一支 script 啦,他可以分析你下达的 service 后面的参数,然后根据你的参数再到 /etc/init.d/ 去取得正确的服务来 start 或 stop 哩!他的语法是这样的啦:

[root@www ~]# service [service name] (start|stop|restart|...)
[root@www ~]# service --status-all
选项与参数:
service name:亦即是需要启动的服务名称,需与 /etc/init.d/ 对应;
start|... :亦即是该服务要进行的工作。
--status-all:将系统所有的 stand alone 的服务状态通通列出来 范例三:重新启动 crond 这支 daemon :
[root@www ~]# service crond restart
[root@www ~]# /etc/init.d/crond restart
# 这两种方法随便你用哪一种来处理都可以!不过鸟哥比较喜欢使用 /etc/init.d/* 范例四:显示出目前系统上面所有服务的运行状态
[root@www ~]# service --status-all
acpid (pid 4536) 正在运行...
anacron 已停止
atd (pid 4694) 正在运行...
....(底下省略)....

Super daemon 的启动方式

其实 Super daemon 本身也是一支 stand alone 的服务,因为 super daemon 要管理后续的其他服务嘛,他当然自己要常驻在内存中啦!所以 Super daemon 自己启动的方式与 stand alone 是相同的! 但是他所管理的其他 daemon 就不是这样做啰!必须要在配置文件中配置为启动该 daemon 才行。配置文件就是 /etc/xinetd.d/* 的所有文件。那如何得知 super daemon 所管理的服务是否有启动呢?你可以这样做:

[root@www ~]# grep -i 'disable' /etc/xinetd.d/*
....(前面省略)....
/etc/xinetd.d/rsync: disable = yes
/etc/xinetd.d/tcpmux-server: disable = yes
/etc/xinetd.d/time-dgram: disable = yes
/etc/xinetd.d/time-stream: disable = yes

因为 disable 是『取消』的意思,因此如果『 disable = yes 』则代表取消此项服务的启动,如果是『 disable = no 』 才是有启动该服务啦!(Minimal版本内没有Super daemon 的软件,查看时没有任何东西)

关于super daemon 的配置文件请详见解析 super daemon 的配置文件

系统开启的服务

使用 netstat 这个网络状态观察命令来检查我们的 port 呢!甚至他也可以帮我们找到该 port 的程序呢 (PID)!

范例一:找出目前系统开启的『网络服务』有哪些?
[root@www ~]# netstat -tulp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 www.vbird.tsai:2208 *:* LISTEN 4575/hpiod
tcp 0 0 *:737 *:* LISTEN 4371/rpc.statd
tcp 0 0 *:sunrpc *:* LISTEN 4336/portmap
tcp 0 0 www.vbird.tsai:ipp *:* LISTEN 4606/cupsd
tcp 0 0 www.vbird.tsai:smtp *:* LISTEN 4638/sendmail: acce
tcp 0 0 *:ssh *:* LISTEN 4595/sshd
udp 0 0 *:filenet-tms *:* 4755/avahi-daemon:
....(底下省略)....
# 看一下上头, Local Address 的地方会出现主机名与服务名称的,要记得的是,
# 可以加上 -n 来显示 port number ,而服务名称与 port 对应则在 /etc/services 范例二:找出所有的有监听网络的服务 (包含 socket 状态):
[root@www ~]# netstat -lnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:2208 0.0.0.0:* LISTEN 4575/hpiod
....(中间省略)....
Active UNIX domain sockets (only servers)
Proto RefCnt Flags Type State I-Node PID/Program name Path
....(中间省略)....
unix 2 [ ACC ] STREAM LISTENING 10624 4701/xfs /tmp/.font-unix/fs7100
unix 2 [ ACC ] STREAM LISTENING 12824 5015/Xorg /tmp/.X11-unix/X0
unix 2 [ ACC ] STREAM LISTENING 12770 4932/gdm-binary /tmp/.gdm_socket
....(以下省略)....
# 仔细的瞧一瞧啊,除了原有的网络监听 port 之外,还会有 socket 显示在上面,
# 我们可以清楚的知道有哪些服务被启动呢! 范例三:观察所有的服务状态
[root@www ~]# service --status-all

配置启动后立即启动服务的方法

我们使用 netstat 仅能观察到目前已经启动的 daemon ,使用 service 这个命令或者是『 /etc/init.d/* start 』的方法则仅能在目前的环境下立即启动某个服务而已。 那么重新启动后呢?该服务是否还是继续的自动启动?这个时候我们就得要了解一下,到底我的 Linux 主机是怎么启动的呢?

  1. 打开计算机电源,开始读取 BIOS 并进行主机的自我测试;
  2. 透过 BIOS 取得第一个可启动装置,读取主要启动区 (MBR) 取得启动管理程序;
  3. 透过启动管理程序的配置,取得 kernel 并加载内存且侦测系统硬件;
  4. 核心主动呼叫 init 程序;
  5. init 程序开始运行系统初始化 (/etc/rc.d/rc.sysinit)
  6. 依据 init 的配置进行 daemon start (/etc/rc.d/rc[0-6].d/*)
  7. 加载本机配置 (/etc/rc.d/rc.local)

由上面的流程你可以看到系统服务在启动时就可以被启动的地方是在第六个步骤,而事实上第六个步骤就是以不同的运行等级呼叫不同的服务啦! 那么什么叫做运行等级呢?

我们在启动 Linux 系统时,可以进入不同的模式喔,这模式我们称为运行等级 (run level)。不同的运行等级有不同的功能与服务, 目前你先知道正常的运行等级有两个,一个是具有 X 窗口接口的 run level 5 ,另一个则是纯文本界面的 run level 3。

  • chkconfig: 管理系统服务默认启动启动与否
[root@www ~]# chkconfig --list [服务名称]
[root@www ~]# chkconfig [--level [0123456]] [服务名称] [on|off]
选项与参数:
--list :仅将目前的各项服务状态栏出来
--level:配置某个服务在该 level 下启动 (on) 或关闭 (off) 范例一:列出目前系统上面所有被 chkconfig 管理的服务
[root@www ~]# chkconfig --list |more
NetworkManager 0:off 1:off 2:off 3:off 4:off 5:off 6:off
acpid 0:off 1:off 2:off 3:on 4:on 5:on 6:off
....(中间省略)....
yum-updatesd 0:off 1:off 2:on 3:on 4:on 5:on 6:off xinetd based services: <==底下为 super daemon 所管理的服务
chargen-dgram: off
chargen-stream: off
....(底下省略)....
# 你可以发现上面的表格有分为两个区块,一个具有 1, 2, 3 等数字,一个则被 xinetd
# 管理。没错!从这里我们就能够发现服务有 stand alone 与 super daemon 之分。 范例二:显示出目前在 run level 3 为启动的服务
[root@www ~]# chkconfig --list | grep '3:on' 范例三:让 atd 这个服务在 run level 为 3, 4, 5 时启动:
[root@www ~]# chkconfig --level 345 atd on

chkconfig 是否很容易管理我们所需要的服务呢?真的很方便啦~ 你可以轻松的透过 chkconfig 来管理 super daemon 的服务喔!另外,你得要知道的是, chkconfig 仅是配置启动时默认会启动的服务而已, 所以该服务目前的状态如何是不知道的。我们举个底下的例子来说明好了:

范例四:先观察 httpd ,再观察默认有无启动,之后以 chkconfig 配置为默认启动
[root@www ~]# /etc/init.d/httpd status
httpd 已停止 <==根本就没有启动 [root@www ~]# chkconfig --list httpd
httpd 0:off 1:off 2:off 3:off 4:off 5:off 6:off
# 原因是默认并没有启动啊! [root@www ~]# chkconfig httpd on; chkconfig --list httpd
httpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
# 已经配置为『启动默认启动』了,再来观察看看到底该服务启动没? [root@www ~]# /etc/init.d/httpd status
httpd 已停止
# 哈!竟然还是没有启动喔!怎么会这样啊?

上面的范例四并没有启动 httpd 的原因很简单,因为我们并没有使用 /etc/init.d/httpd start 嘛!我们仅是配置启动时启动而已啊!那我们又没有重新启动,所以当然使用 chkconfig 并不会导致该服务立即被启动!也不会让该服务立即被关闭,而是只有在启动时才会被加载或取消加载而已喔。而既然 chkconfig 可以配置启动是否启动,那么我们能不能用来管理 super daemon 的启动与关闭呢?非常好!我们就来试看看底下的案例:

范例五:查阅 rsync 是否启动,若要将其关闭该如何处理?
[root@www ~]# /etc/init.d/rsync status
-bash: /etc/init.d/rsync: No such file or directory
# rsync 是 super daemon 管理的,所以当然不可以使用 stand alone 的启动方式来观察 [root@www ~]# netstat -tlup | grep rsync
tcp 0 0 192.168.201.110:rsync *:* LISTEN 4618/xinetd
tcp 0 0 www.vbird.tsai:rsync *:* LISTEN 4618/xinetd [root@www ~]# chkconfig --list rsync
rsync on <==默认启动呢!将它处理成默认不启动吧 [root@www ~]# chkconfig rsync off; chkconfig --list rsync
rsync off <==看吧!关闭了喔!现在来处理一下 super daemon 的东东! [root@www ~]# /etc/init.d/xinetd restart; netstat -tlup | grep rsync

最后一个命令你会发现原本 rsync 不见了!这样是否很轻易的就能够启动与关闭你的 super daemon 管理的服务呢!

  • chkconfig: 配置自己的系统服务
[root@www ~]# chkconfig [--add|--del] [服务名称]
选项与参数:
--add :添加一个服务名称给 chkconfig 来管理,该服务名称必须在 /etc/init.d/ 内
--del :删除一个给 chkconfig 管理的服务

如果我自己写了一个程序并且想要让该程序成为系统服务好让 chkconfig 来管理时, 可以怎么进行呢?只要将该服务加入 init 可以管理的 script 当中,亦即是 /etc/init.d/ 当中即可。 举个例子,我们在 /etc/init.d/ 里面创建一个 myvbird 文件,该文件仅是一个简单的服务范例,基本上,没有任何用途.... 对于该文件的必须性是这样的:

  • myvbird 将在 run level 3 及 5 启动;
  • myvbird 在 /etc/rc.d/rc[35].d 当中启动时,以 80 顺位启动,以 70 顺位结束。

关于所谓的顺位问题,我们会在第二十章介绍,这里你先看看即可。 你该如何进行呢?可以这样做:

[root@www ~]# vim /etc/init.d/myvbird
#!/bin/bash
# chkconfig: 35 80 70
# description: 没啥!只是用来作为练习之用的一个范例
echo "Nothing"

这个文件很好玩喔!你可以参考你自己系统上面的文件;基本上,比较重要的是第二行,他的语法是: 『 chkconfig: [runlevels] [启动顺位] [停止顺位] 』其中, runlevels 为不同的 run level 状态,启动顺位 (start number) 与 结束顺位 (stop number) 则是在 /etc/rc.d/rc[35].d 内创建以 S80myvbird 及 K70myvbird 为档名的配置方式!

[root@www ~]# chkconfig --list myvbird
service myvbird supports chkconfig, but is not referenced in any
runlevel (run 'chkconfig --add myvbird')
# 尚未加入 chkconfig 的管理机制中!所以需要再动点手脚 [root@www ~]# chkconfig --add myvbird; chkconfig --list myvbird
myvbird 0:off 1:off 2:off 3:on 4:off 5:on 6:off
# 看吧!加入了 chkconfig 的管理当中了!
# 很有趣吧!如果要将这些数据都删除的话,那么就下达这样的情况: [root@www ~]# chkconfig --del myvbird
[root@www ~]# rm /etc/init.d/myvbird

CentOS学习笔记--系统服务 (daemons)的更多相关文章

  1. CentOS学习笔记--SCSI 设备热插拔

    CentOS学习笔记--SCSI 设备热插拔 处于运行中的服务器,因业务要求也许不允许重启机器,而新添加的SCSI设备(主要是硬盘)如何实现热插拔呢? 首先需要查看一下设备: #cat /proc/s ...

  2. CentOS学习笔记--Tomcat安装

    Tomcat安装 通常情况下我们要配置Tomcat是很容易的一件事情,但是如果您要架设多用户多服务的Java虚拟主机就不那么容易了.其中最大的一个问题就是Tomcat执行权限.普通方式配置的Tomca ...

  3. CentOS学习笔记—启动、ROOT密码

    启动流程一览 简单来说,系统启动的经过可以汇整成底下的流程的: 加载 BIOS 的硬件资讯与进行自我测试,并依据配置取得第一个可启动的装置: 读取并运行第一个启动装置内 MBR 的 boot Load ...

  4. 【9-6】Centos学习笔记

    linux文件系统结构 常用技巧 快捷键启动终端 su命令,使用超级用户登陆 visudo :编辑用户权限 tar xf 文件名:解压文件 Vim编辑器 Tips yum包管理:Yum(全称为 Yel ...

  5. CentOS 学习笔记

    整理基础的CentOS常用命令 http://os.51cto.com/art/201003/190801.htm 在Hyper-V中的CentOS虚拟机中使用网络 http://blog.earth ...

  6. CentOS学习笔记--基本命令--文件与目录管理

    Linux基本命令--文件与目录管理 本节节选自鸟哥的 Linux 私房菜 -- 基础学习篇目录  第七章.Linux 文件与目录管理  ls(文件与目录的检视) ls命令就是list的缩写,ls可以 ...

  7. CentOS学习笔记--目录配置

      Linux目录配置 类Linux的目录看上去差不多,为什么? 以下内容节选自l 鸟哥的 Linux 私房菜 -- 基础学习篇目录  第六章.Linux 的文件权限与目录配置 3. Linux目录配 ...

  8. CentOS学习笔记--MySQL安装

    MySQL安装 Linux中使用最广泛的数据库就是MySQL,使用在线yum的方式安装的版本落后MySQL网站好几个小版本,本节亲自测试安装新版的MySQL. 测试机器环境: VMware Works ...

  9. CentOS学习笔记--时间

    时间 有装过Linux系统的人,可能都会有这样的经历,就是该机器安装windows系统时,时间正确,但是安装了linux系统后,尽管时区选择正确,也会发现系统时间不对.这是由于安装系统时采用了UTC, ...

随机推荐

  1. jsp自定义标签1

    1.编写一个实现tag接口的java类 package cn.itcast.web.tag; import java.io.IOException; import javax.servlet.http ...

  2. 在java中使用正则表达式注意的地方

    1. 对^与$的理解 通常我们会通过类似Matcher matcher = Pattern.compile(regex).matcher(string);的代码去拿到一个Matcher对象.这种情况下 ...

  3. [ActionScript 3.0] AS3.0 对象在一定范围随机显示不重叠

    import flash.geom.Rectangle; import flash.display.MovieClip; import flash.display.Sprite; var arr:Ar ...

  4. Oracle中的约束

    非空约束 NOT NULL 数据库表中的某一个列不能为空 唯一约束 UNIQUE 表中某一个列不允许重复 唯一约束所在列可以为NULL,但只能出现一次 代码: CREATE TABLE MEMBER ...

  5. Java SE 第二十二讲----接口interface

    1.接口:interface:接口的地位等同于class,接口中的所有方法都是抽象方法.在声明接口中的方法的时候,可以使用abstract关键字也可以不使用.通常情况下,都会省略掉abstract关键 ...

  6. sikuli

    1.sikuli和selenium集成问题,用java封装一个方法去操作web页面上的一些无法定位的控件  http://bbs.csdn.net/topics/390720479/ 2.关于Siku ...

  7. delphi SPCOMM 接收数据不完整!该如何解决

    SPCOMM 接收数据不完整!该如何解决   SPCOMM 接收数据不完整!我作了一个 读取地磅数据的程序,是用spcomm接收的! 总共有五台地磅,其他4台地磅数据读取都正常.但是有一台接收数据的时 ...

  8. php读取目录及子目录下所有文件名的方法

    本文实例讲述了php读取目录及子目录下所有文件名的方法,分享给大家供大家参考.具体实现方法如下: 一般来说php中读取目录下的文件名的方式确实不少,最简单的是scandir,具体代码如下: $dir= ...

  9. Arrays

    Arrays:用于操作数组对象的工具类,里面都是静态方法. asList方法:将数组转换成list集合. String[] arr = {"abc","kk", ...

  10. C# Windows Forms 事件处理顺序

    事件引发的顺序对某些Windows 窗体应用来说十分重要.当某些事件需要特别处理时(如重绘窗体的某些部分),必须知道事件在运行时的确切引发顺序.下面就应用程序和控件的生命周期中的几个重要阶段的事件顺序 ...