Linux进程管理和文本编辑

    Linux是一个多用户、多任务的操作系统,这就意味着多个用户可以同时使用一个操作系统,而每个用户又可以同时运行多个命令。在这样的系统中,各种计算机资源(如文件、内存、CPU等)的分配和管理都以进程为单位。为了协调多个进程对这些共享资源的访问,操作系统要跟踪所有进程的活动,以及它们对系统资源的使用情况,实施对进程和资源的动态管理。

     进程和线程

    1.进程及其状态
    在多道程序工作环境下,各个程序是并发执行的,它们共享系统资源,共同决定这些资源的状态。彼此间相互制约、相互依赖,因而呈现出并发、动态及互相制约等新的特征。这样,用程序这个静态概念已不能如实反映程序活动的这些特征。为此,人们引进了进程(Process)这一新概念,来描述程序动态执行过程的性质。

    简单说来,进程就是程序的一次执行过程。它有着走走停停的活动规律。进程的动态性质是由其状态变化决定的。通常在操作系统中,进程至少要有三种基本状态,它们是运行态、就绪态和封锁态(或阻塞态)。

    运行状态是指当前进程已分配到CPU,它的程序正在处理器上执行时的状态。处于这种状态的进程个数不能大于CPU的数目。在一般单CPU机制中,任何时刻处于运行状态的进程至多有一个。

    就绪状态是指进程已具备运行条件,但因为其它进程正占用CPU,所以暂时不能运行而等待分配CPU的状态。一旦把CPU分给它,立即就可运行。在操作系统中,处于就绪状态的进程数目可以是多个。

    封锁状态是指进程因等待某种事件发生(例如等待某一输入、输出操作完成,等待其它进程发来的信号等)而暂时不能运行的状态。也就是说,处于封锁状态的进程尚不具备运行条件,即使CPU空闲,它也无法使用。这种状态有时也称为不可运行状态或挂起状态。系统中处于这种状态的进程可以是多个的。

    进程的状态可依据一定的条件和原因而变化,如图1所示。一个运行的进程可因某种条件未满足而放弃CPU,变为封锁状态;以后条件得到满足时,又变成就绪态;仅当CPU被释放时才从就绪态进程中挑选一个合适的进程去运行,被选中的进程从就绪态变为运行态。挑选进程、分配CPU这个工作是由进程调度程序完成的。

    另外,在Linux系统中,进程(Process)和任务(Task)是同一个意思。所以,在内核的代码中,这两个名词常常混用。

    2.Linux进程状态

    在Linux系统中,进程有以下几个状态:
    ◆运行态(TASK_RUNNING) 此时,进程正在运行(即系统的当前进程)或者准备运行(即就绪态)。
    ◆等待态 此时进程在等待一个事件的发生或某种系统资源。Linux系统分为两种等待进程:可中断的(TASK_INTERRUPTIBLE)和不可中断的(TASK_UNINTERRUPTIBLE)。可中断的等待进程可以被某一信号(Signal)中断;而不可中断的等待进程不受信号的打扰,将一直等待硬件状态的改变。
    ◆停止态(TASK_STOPPED) 进程被停止,通常是通过接收一个信号。正在被调试的进程可能处于停止状态。
    ◆僵死态(TASK_ZOMBIE) 由于某些原因被终止的进程,但是该进程的控制结构task_struct仍然保留着。

    图2示出Linux系统中进程状态的变化关系。


图1 进程状态及其变化


图2 Linux进程状态的变化


    3.进程的模式和类型
    在Linux系统中,进程的执行模式划分为用户模式和内核模式。如果当前运行的是用户程序、应用程序或者内核之外的系统程序,那么对应进程就在用户模式下运行;如果在用户程序执行过程中出现系统调用或者发生中断事件,就要运行操作系统(即核心)程序,进程模式就变成内核模式。在内核模式下运行的进程可以执行机器的特权指令;而且,此时该进程的运行不受用户的干预,即使是root用户也不能干预内核模式下进程的运行。

    按照进程的功能和运行的程序分类,进程可划分为两大类:一类是系统进程,只运行在内核模式,执行操作系统代码,完成一些管理性的工作,例如内存分配和进程切换;另外一类是用户进程,通常在用户模式中执行,并通过系统调用或在出现中断、异常时进入内核模式。

    用户进程既可以在用户模式下运行,也可以在内核模式下运行,如图3所示。


图3 用户进程的两种运行模式


    4.Linux线程
    线程是和进程紧密相关的概念。一般说来,Linux系统中的进程应具有一段可执行的程序、专用的系统堆栈空间、私有的“进程控制块”(即task_struct数据结构)和独立的存储空间。Linux系统中的线程只具备前三个组成部分,而缺少自己的存储空间。

    线程可以看作是进程中指令的不同执行路线。例如,在文字处理程序中,主线程负责用户的文字输入,而其它线程可以负责文字加工的一些任务。往往也把线程称作轻型进程。Linux系统支持内核空间的多线程,但它与大多数操作系统不同,后者单独定义线程,而Linux则把线程定义为进程的“执行上下文”。

     有关进程管理的命令

    Linux命令的执行是通过进程实现的。当在提示符之后输入一个命令或可执行文件的名字,一按回车键,这个命令就开始执行了。在操作系统中,为了执行这个命令,往往要创建相应的进程,通过进程的活动来完成一个预定的任务。其实,在Linux中,通常执行任何一个命令都会创建一个或多个进程。当进程完成了预期的目标自行终止时,该命令也就执行完了。不但用户可以创建进程,系统程序也可以创建进程。可以说,一个运行着的操作系统就是由许许多多的进程组成的。

    1.ps命令
    ps命令是查看进程状态的最常用的命令,它可以提供关于进程的许多信息。根据显示的信息可以确定哪个进程正在运行、哪个进程被挂起、遇到了哪些困难、进程已运行了多久、进程正在使用的资源、进程的相对优先级,以及进程的标识号(PID)。所有这些信息对用户都很有用,对于系统管理员来说更为重要。

    ps命令的一般格式是:ps [选项]

    ps的选项有几种类型。Unix 98的选项可以成组,并且在选项字母前必须有一个连字符(-);BSD的选项可以成组,并且在选项字母前没有连字符;而GNU的长选项在选项字母前有两个连字符。

    以下是ps命令常用的选项及其含义:
    -a 显示系统中与tty相关的(除会话组长之外)所有进程的信息。
    -e 显示所有进程的信息。
    -f 显示进程的所有信息。
    -l 以长格式显示进程信息。
    r 只显示正在运行的进程。
    u 显示面向用户的格式(包括用户名、CPU及内存使用情况等信息) 。
    x 显示所有非控制终端上的进程信息。
    --pid 显示由进程ID指定的进程的信息。
    --tty 显示指定终端上的进程的信息。

    ◆直接用ps命令可以列出每个与当前Shell有关的进程基本信息:

     
     $ ps
PID  TTY     TIME CMD
1542  pts/0    00:00:00 bash
1590  pts/0    00:00:00 ps


    上面代码中,各字段的含义如下:
    PID 进程标识号。
    TTY 该进程建立时所对应的终端,“?”表示该进程不占用终端。
    TIME 报告进程累计使用的CPU时间。注意,尽管觉得有些命令(如sh)已经运转了很长时间,但是它们真正使用CPU的时间往往很短。所以,该字段的值往往是00:00。
    CMD 执行进程的命令名。

    ◆利用选项-ef可以显示系统中所有进程的全面信息:

     
     $ ps  -ef
UID    PID  PPID  C   STIME TTY  TIME     CMD
root    1    0      0    20:42  ?     00:00:05   init
root    2    1      0    20:42  ?     00:00:00   [keventd]
root    3    1      0    20:42  ?     00:00:00   [kapmd]
root    4    1      0    20:42  ?     00:00:00   [ksoftirqd_CPU0]
……
mengqc 978  1     0    20:43   ?     00:00:00   kdeinit:Running…
……
mengqc 1541 978   0    20:44  ?       00:00:00   rxvt
mengqc 1542 1541  0    20:44  pts/0   00:00:00   bash
……
mengqc 1594 1542  0    21:39  pts/0   00:00:00   ps -ef


    上面各项的含义是:
    UID 进程属主的用户ID号。
    PID 进程ID号。
    PPID 父进程的ID号。
    C 进程最近使用CPU的估算。
    STIME 进程开始时间,以“小时:分:秒”的形式给出。
    TTY 该进程建立时所对应的终端,“?”表示该进程不占用终端。
    TIME 报告进程累计使用的CPU时间。注意,尽管觉得有些命令(如sh)已经运转了很长时间,但是它们真正使用CPU的时间往往很短。所以,该字段的值往往是0:00。
    CMD 是command(命令)的缩写,往往表示进程所对应的命令名。

    ◆利用下面的命令可以显示所有终端上所有用户的有关进程的所有信息:

     
     $ ps  -aux
USER  PID %CPU %MEM VSZ  RSS TTY STAT START TIME  COMMAND
root    1   0.1    0.1    1276  468  ?    S      20:42   0:05   init
root    2   0.0    0.0    0     0    ?    SW    20:42   0:00   [keventd]
root    3   0.0    0.0    0     0    ?    SW    20:42   0:00   [kapmd]
root    4   0.0    0.0    0     0    ?    SWN   20:42   0:00   [ksoftirqd_CPU0]
……
mengqc 978 0.0    3.1   20284  8156  ?    S      20:43    0:00   kdeinit:Running…
……
mengqc 1541 0.0   0.6   6288   1720  ?    R     20:44  0:00    rxvt
mengqc 1542 0.0  0.5   5588   1440 pts/0  S     20:44  0:00    bash
……
mengqc 1599  0.0  0.3   2676   792  pts/0  R     21:55  0:00    ps -aux


    上面列表中包含了一些新的项:
    USER 启动进程的用户。
    %CPU 运行该进程占用CPU的时间与该进程总的运行时间的比例。
    %MEM 该进程占用内存和总内存的比例。
    VSZ 虚拟内存的大小,以KB为单位。
    RSS 占用实际内存的大小,以KB为单位。
    STAT 表示进程的运行状态,包括以下几种代码:
    D 不可中断的睡眠;
    R 就绪(在可运行队列中);
    S 睡眠;
    T 被跟踪或停止;
    Z 终止(僵死)的进程。

    对于BSD格式,还包括以下代码:
    W 没有内存驻留页;
    < 高优先权的进程;
    N 低优先权的进程;
    L 有锁入内存的页面(用于实时任务或定制I/O任务);
    START 开始运行的时间。

    2.kill命令
    通常终止一个前台进程可以使用“Ctrl+C”组合键。但是,对于一个后台进程就须用kill命令来终止。kill命令是通过向进程发送指定的信号来结束相应进程。默认情况下,采用编号为15的TERM信号。TERM信号将终止所有不能捕获该信号的进程。对于那些可以捕获该信号的进程就要用编号为9的KILL信号,强行杀掉该进程。

    kill命令的一般格式是:

     
     kill  [-s  信号|-p ] [-a] 进程号...
kill  -l [信号]



    其中选项各选项的含义如下:
    -s 指定需要发送的信号,既可以是信号名(如KILL),也可以是对应信号的号码(如9) 。
    -p 指定kill命令只是显示进程的pid(进程标识号),并不真正发出结束信号。
    -l 显示信号名称列表,这也可以在/usr/include/linux/signal.h文件中找到。

    使用kill命令时应注意:
    (1)kill命令可以带信号号码选项,也可以不带。如果没有信号号码,kill命令就会发出终止信号(TERM)。这个信号可以杀掉没有捕获到该信号的进程,也可以用kill向进程发送特定的信号。例如:kill -2 1234

    它的效果等同于在前台运行PID为1234的进程的时候,按下“Ctrl+C”。但是普通用户只能使用不带signal参数的kill命令,或者最多使用-9信号。

    (2)kill可以带有进程ID号作为参数。当用kill向这些进程发送信号时,必须是这些进程的主人。如果试图撤销一个没有权限撤销的进程,或者撤销一个不存在的进程,就会得到一个错误信息。
    (3)可以向多个进程发信号,或者终止它们。
    (4)当kill成功地发送了信号,Shell会在屏幕上显示出进程的终止信息。有时这个信息不会马上显示,只有当按下回车键使Shell的命令提示符再次出现时才会显示出来。
    (5)信号使进程强行终止常会带来一些副作用,比如数据丢失或终端无法恢复到正常状态。发送信号时必须小心,只有在万不得已时才用kill信号(9),因为进程不能首先捕获它。

    要撤销所有的后台作业,可以键入“kill 0”。因为有些在后台运行的命令会启动多个进程,跟踪并找到所有要杀掉的进程的PID是件很麻烦的事。这时,使用“kill 0 ”来终止所有由当前Shell启动的进程是个有效的方法。

    用kill命令终止一个已经阻塞的进程,或者一个陷入死循环的进程,一般可以首先执行以下命令:
    $ find / -name core -print > /dev/null 2>&1&

    这是一条后台命令,执行时间较长。现在决定终止该进程。为此,运行ps命令来查看该进程对应的PID。例如,该进程对应的PID是1651,现在可用kill命令杀死这个进程:
    $ kill 1651

    再用ps命令查看进程状态时,就会发现find进程已经不存在了。

    3.sleep命令
    sleep命令的功能是使进程暂停执行一段时间。其一般格式是:
    sleep 时间值

    其中,“时间值”参数以秒为单位,即使进程暂停由时间值所指定的秒数。此命令大多用于Shell程序设计中,使两条命令执行之间停顿指定的时间。

    下面的命令行使进程先暂停100秒,然后查看用户mengqc是否在系统中:
    $ sleep 100; who | grep 'mengqc'

    信号(Signal,亦称作软中断)机制是在软件层次上对中断机制的一种模拟。异步进程可以通过彼此发送信号来实现简单通信。系统预先规定若干个不同类型的信号(如x86平台中Linux内核设置了32种信号,而现在的Linux和POSIX.4定义了64种信号),各表示发生了不同的事件,每个信号对应一个编号。运行进程当遇到相应事件或出现特定要求时(如进程终止或运行中出现某些错误—非法指令、地址越界等),就把一个信号写到相应进程task_struct结构的Signal位图(表示信号的整数)中。接收信号的进程在运行过程中要检测自身是否收到了信号,如果已收到信号,则转去执行预先规定好的信号处理程序。处理之后,再返回原先正在执行的程序。进程之间利用信号机制实现通信的过程如图4所示。

    这种处理方式与硬件中断的处理方式有不少相同之处,但是二者又是不同的。因为信号的设置、检测等都是软件实现的。信号处理机构是系统中围绕信号的产生、传送和处理而构成的一套机构。该机构通常包括三部分:信号的分类、产生和传送;对各种信号预先规定的处理方式;信号的检测和处理。

    1. 信号分类
    如上所述,信号分类随系统而变,可多可少。通常可分为进程终止、进程执行异常(如地址越界、写只读区、用户执行特权指令或硬件错误)、系统调用出错(如所用系统调用不存在、pipe文件有写者无读者等)、报警信号及与终端交互作用等。系统一般也给用户自己留出定义信号的编号。



    表1列出在x86平台上Linux内核定义的常用信号。

    2.进程对信号可采取的处理方式
    当发生上述事件后,系统可以产生信号,并向有关进程传送。进程彼此间也可用系统提供的系统调用(如kill( ) )发送信号。除了内核和超级用户外,并不是每个进程都可以向其它进程发送信号。普通进程只能向具有相同uid和gid的进程发送信号,或者向相同进程组中的其它进程发送信号。信号要记入相应进程的task_struct结构中Signal的适当位,以备接收进程检测和处理。

    进程接到信号后,在一定时机(如中断处理末尾)做相应处理,可采取以下处理方式:
    (1)忽略信号。进程可忽略收到的信号,但SIGKILL和SIGSTOP信号不能被忽略。
    (2)阻塞信号。进程可以选择对某些信号予以阻塞。
    (3)由进程处理该信号。用户在trap命令中可以指定处理信号的程序,从而进程本身可在系统中标明处理信号的处理程序的地址。当发出该信号时,就由标明的处理程序进行处理。
    (4)由系统进行默认处理。如上所述,系统内核对各种信号(除用户自定义之外)都规定了相应的处理程序。在默认情况下,信号就由内核处理,即执行内核预定的处理程序。

    每个进程的task_struct结构中都有一个指针sig,它指向一个signal_struct结构。该结构中有一个数组action[ ],其中的元素确定了当进程接收到一个信号时应执行什么操作。

    3.对信号的检测和处理流程
    对信号的检测和响应是在系统空间进行的。通常,进程检测信号的时机是:第一,从系统空间返回用户空间之前,即当前进程由于系统调用、中断或异常而进入系统空间以后,进行相应的处理工作。处理完后,要从系统空间中退出,在退出之前进行信号检测。第二,进程刚被唤醒的时候,即当前进程在内核中进入睡眠以后刚被唤醒,要检测有无信号,如存在信号就会提前返回到用户空间。

    信号的检测与处理的过程如图5所示。图中的①~⑤标出处理流程的顺序。从图中可以看出,信号的检测是在系统空间中进行,而对信号的处理却是在用户空间中执行。

    vi编辑器

    用户往往需要建立自己的文件,无论是一般文本文件、数据文件、数据库文件,还是程序源文件。建立和修改文本文件要利用编辑器。Linux系统上常用的文本编辑器有ed、 ex、edit、vi。按功能它们分为行编辑器(如ed、ex、edit)和屏幕编辑器(如vi)两类。vi是Visual Interface的简称,它汇集了行编辑和全屏幕编辑的特点,成为Unix/Linux系统中最常用的编辑器,几乎每个Unix/Linux系统都提供了vi。

    1. 进入vi
    只有进入vi编辑器之后才可以使用vi的命令;完成文本编辑以后,应退出vi,回到shell命令状态下。

    在系统提示符($)下输入命令vi和想要编辑(建立)的文件名,便可进入vi。例如,$ vi example.c

    如果example.c是一个新文件,则光标停在屏幕的左上角。在每一行开头都有一个“~”符号,表示空行。如果指定的文件已在系统中存在,那么输入上述形式的命令后,则在屏幕上显示出该文件的内容,光标停在左上角。在屏幕的最底行显示出一行信息,包括正在编辑的文件名,行数和字符个数,该行称作vi的状态行。

    2. 退出vi
    当编辑完文件,准备返回到shell状态时,要执行退出vi的命令。在vi的命令方式下有几种方法可以退出vi编辑器:
    : wq 把编辑缓冲区的内容写到你编辑的文件中,退出编辑器,回到Shell下。其操作过程是先键入冒号“:”,再键入命令wq。以下命令操作相同。
    : ZZ 仅当作过修改时才将缓冲区内容写到文件上。
    : x 与 :ZZ相同。
    :q! 强行退出vi。感叹号(!)告诉vi无条件退出,丢弃缓冲区内容。

    应该强调一下,当利用vi编辑器编辑文本时,所输入或修改的内容都存放在编辑缓冲区中,并没有存放在磁盘的文件中。如果没有使用写盘的命令而直接退出vi,那么,编辑缓冲区中的内容就被丢弃了,在此之前所作的编辑工作也就白费了。所以,当退出vi时,应想一想是否需要保存所编辑的内容,然后再执行合适的退出命令。

    3.vi的工作方式
    vi编辑器有三种工作方式:命令方式、输入方式和ex转义方式。通过相应的命令或操作,在这三种工作方式之间可以进行转换。如图6所示。

    当输入命令vi,进入vi编辑器时,就处于vi的命令方式。此时,从键盘上输入的任何字符都被当作编辑命令来解释,例如,a(append)表示附加命令,i(insert) 表示插入命令,x表示删除字符命令等。如果输入的字符不是vi的合法命令时,机器则发出“报警声”,光标不移动。另外,在命令方式下输入的字符(即vi命令)并不在屏幕上显示出来,例如输入“i” ,屏幕上并无什么变化,但通过执行i命令, 编辑器的工作方式却发生变化,由命令方式变为输入方式。

    通过输入vi的插入命令(i)、附加命令(a)、打开命令(o)、替换命令(s)、修改命令(c)或取代命令(r)可以从命令方式进入到输入方式。在输入方式下,从键盘上输入的所有字符都被插入到正在编辑的缓冲区中,被当作该文件的正文。所以,进入输入方式后输入的可见字符都在屏幕上显示出来,而编辑命令不再起作用,仅作为普通字母出现。

    由输入方式回到命令方式的办法是按下 键(通常在键盘的左上角)。如果已在命令方式下,那么按下 键就会发出“嘟嘟”声。为了确保想执行的vi命令是在命令方式下输入的,不妨多按几下 ,听到嘟声后再输入命令。

    vi和ex编辑器功能是相同的,二者主要区别是用户界面。在vi中,命令通常是单个键击,例如a、x、R等。而在ex中,命令是以 键结束的正文行。vi 有一个专门的“转义”命令,可访问很多面向行的ex命令。为使用ex转义方式,可输入一个冒号(:)。冒号作为ex命令提示符出现在状态行(通常在屏幕最下一行)。按下中断键(通常是)可终止正在执行的命令。多数文件管理命令都是在ex转义方式下执行的(例如读取文件,把编辑缓冲区的内容写到文件中)。转义命令执行后,自动回到命令方式。

    4.vi常用命令

    表2列出常用的vi编辑命令。


     
     表2  常用vi编辑命令
类别	命令	功               能

 	a	在该命令之后输入的字符都插入到光标之后,光标可在一行的任何位置。
插	A	在光标所在行的行尾添加文本。
入	i	在该命令之后输入的内容都插在光标位置之前,光标后的文本相应向右移动。
命	I	在光标所在行的行首插入新增文本,行首是该行的第一个非空白字符。
令	o	在光标所在行的下面新僖恍?随后输入的文本就插入在这一行上。
	O 	在光标所在行的上面新开辟一行,随后输入的文本就插入在这一行上。

    0	将光标移到当前行的第一个字符,不管它是否为空白符。
	^	将光标移到当前行的第一个非空白符(非制表符或非空格符)。
光	$	将光标移至当前行的行尾,停在最后一个字符上。
标	[行号]G	将光标移至由行号所指定的行的开头。如果没有给出行号,
          则光标移至该文件最后一行的开头。
移	[列号]|	将光标移至当前行指定的列上。如果没有指定列号,则移至当前行的第一列上。
动	w	将光标向前移至下一个词(以标点符号或空白符分开的字母数字串)的开头。
命	W	将光标向前移至下一个词(非空白字符串)的开头。
令	b	光标后退到先前一个词 (以标点符号或空白符分开的字母数字串)的开头。
	B	光标后退到先前一个词 (非空白字符串)的开头。
	e	将光标移至词尾(词是以标点符号或空白符分开的字母数字串)。
	E	将光标移至词尾(词是非空白字符串)。
	( )	(和)分别将光标移至上一个和下一个句子的开头。句子被定义为以句点(.)、
        问号(?)或感叹号(!)结尾、后随二个空格或一个换行的字符序列。
	H	将光标移至屏幕的左上角。如果H前面指定位移值n,
	    则光标移到距屏幕顶部(n-1)行的行首。
	M	将光标移至屏幕中间行的开头。
	L	将光标移至屏幕的最底行。

	x 	删除光标所在的字符,如果前面给出一个数值n(例如,5x),
	    则由光标所在字符开始向右删除n(5)个字符。
文	X	删除光标前面的那个字符。如果前面给出数值n,
本      则由光标之前的那个字符开始向左删除n个字符。
修	dd	删除光标所在的整行。
改	D	从光标位置开始删除到行尾。
命	d... d与光标移动命令(以...表示)组合而成的命令就从光标位置开
令	    始删到由光标移动限定的文本对象的末尾。
	u	如果插入后用u命令,就删除刚插入的正文;如果删除后用它,
	    就相当于又插入刚删除的正文。所有修改文本的命令都视为插入。
  	U	把当前行恢复成被编辑之前的状态,不管把光标移到该行后对它编辑了多少次。
	.	重复实现刚才的插入命令或删除命令。
	c	修改文本对象,并用新输入的文本代替老的文本。
        c后面紧随光标移动命令(限定删除文本的范围),之后是新
		输入的文本,最后输入<Esc>。            
	C	修改从光标位置到该行末尾的文本。它使用的一般方式是
	    C后面紧接新输入的文本,最后是<Esc>。
	cc	除影响到整行(不是行的一部分)外,其余作用与C命令相同。
	r	用随后输入的单个字符取代光标所在的字符。
	R	用随后输入的文本取代光标位及其右面的若干字符,
	    每输入一个字符就替代原有的一个字符。
	s 	用随后输入的的正文替换光标所在的字符。
	S	用新输入的正文替换当前行(整行)。

    系统初启

    当打开计算机电源以后,计算机就开始初启过程。初启过程的细节与机器的体系结构有关,但对所有的机器来说,初启的目的是共同的:将操作系统的副本读入内存中,建立正常的运行环境。对于Intel i386系列来说,引导过程分为硬件检测、加载引导程序、初始化内核和实现用户登录。

    1.硬件检测
    当PC启动时,首先CPU进入实模式,开始执行ROM-BIOS起始位置的代码。BIOS首先执行加电自检程序(POST),完成硬件启动,然后对系统中配置的硬件(如内存、硬盘及其它设备)进行诊断检测,确定各自在系统中存在,并且处于正常状态。自检工作要经历2~3分钟时间。自检工作完成后,按照预先在系统CMOS中设置的启动顺序,ROM-BIOS搜索软盘、硬盘,以及CD-ROM等设备的驱动器,读入系统引导区,通常都是磁盘上的第一个扇区,并将系统控制权交给引导装入程序。

    2.加载引导程序
    整个硬盘的第一个扇区是整个硬盘的引导扇区,加电后从这个扇区引导,所以它称作主引导记录块MBR。MBR中含有磁盘分区的数据和一段简短的程序,总共512字节。其中的程序并不直接引导操作系统,而是依据盘区划分的信息找到“活动”分区,再从活动分区中读入其引导扇区到内存,执行该引导扇区中的程序,再由该程序从硬盘中读入其它几个更为复杂的程序,并由它们加载操作系统的内核。

    引导扇区中的程序及其辅助程序(不包括LILO)采用汇编语言编写,共有三个汇编程序:
    (1)bootsect.S,Linux引导扇区的源代码,汇编后不能超过512字节。
    (2)setup.S,辅助程序的一部分。
    (3)video.S,另一部分辅助程序,用于引导过程中的屏幕显示。

    当BIOS引导一个系统时,总是把引导扇区读到内存的低端(即640KB的基本内存),然后由bootsect_helper子程序将低端内存的内核复制到高端内存上,并将低端内存复位。这样做就能满足Linux核心中驱动程序大量增长的需求,以便适应不同的系统配置。内核诚褚话愣际蔷顾醯模胍忌惹鸵几ㄖ绦虻挠诚衿唇釉谝黄穑莆诤说摹耙加诚瘛薄4笮〕?08KB的引导映像称为“大内核”,其文件名为bzImage。解压缩以后的内核映像总是放在地址为0x100000(1MB)的地方。

    内核加载完毕,系统跳转到setup.S的开始位置执行。setup.S是在实模式下运行,其主要功能是设置系统参数(包括内存、磁盘等,由BIOS返回)、检测和设置显示器及显示模式,并为进入保护模式做准备。最后,进入保护模式,并转入内核映像的头部开始执行,从而以后不再使用BIOS调用了。

    另外,也可以使用Linux引导程序LILO来引导操作系统,LILO也存放在引导扇区(或者放在MBR区,或者在某一磁盘分区的第一个扇区)中。LILO可以让用户从磁盘上的多个操作系统映像中选择引导哪一种操作系统。LILO在系统安装阶段建立一个操作系统映像在磁盘上存储位置的对照表。启动时,LILO利用该表引导BIOS装入指定的操作系统。实际上,除了从软盘上引导以外,Linux一般都是通过LILO引导的。

    3.系统初始化
    辅助程序setup为内核映像的执行做好了准备(包括解压缩)以后,就跳转到0x100000开始内核本身的执行,下面就是内核的初始化过程。初始化过程可以分为三个阶段:第一个阶段主要是CPU本身的初始化,例如页式映射的建立;第二阶段主要是系统中一些基础设施的初始化,例如内存管理和进程管理的建立和初始化;最后是对上层部分初始化,如根设备的安装和外部设备的初始化等。

    在初始化的第一阶段,首先设置内核页表,并启动页面映射机制。然后建立系统的第一个进程init_task。接着,初始化内核的bss(Block Started by Symbol)段,其中是一些全局变量或者静态变量;设置初始状态的中断向量表;把命令行和引导参数复制到内核中一个内容为全0的页面中。

    在第二阶段调用函数start_kernel( ),继续进行内核的初始化,甚至可以说这才真正开始内核初始化,这是在较高层次上的初始化。start_kernel( )就好像一般可执行程序中的主函数main( ),它初始化系统内核的各个部分,包括设置内存边界、初始化内存页面;设置各种入口地址,如异常事件处理程序入口、系统调用入口、调用门等;初始化IRQ中断处理机制;初始化信号机制;读取实时时间,重新设置时钟中断的中断服务程序入口等;初始化控制台、显示器;初始化核心模块结构;计算并初始化虚拟内存;对cache初始化;定义系统中最大进程数目;对IPC、磁盘配额等机制进行初始化;最后调用kernel_thread( )创建init内核线程,进行系统配置。init内核线程占用进程向量数组的第一项,由它来创建其它完成系统初始化工作的进程。

    在第三阶段,内核线程init首先锁定内核,然后调用过程来初始化外部设备及加载驱动程序(对外设的初始化包括PCI总线的初始化、网络初始化、一系列其他设备的初始化等);创建第二个内核线程keventd,由它充当“代理人”角色,使得某些函数在它的上下文中执行;设置“初始化代码段”和“初始化调度段”等;初始化文件系统,并加载它。

    当内核初始化工作完成后,系统中存在着五个运行实体:init线程为系统中的一个线程,它当前处于用户态,由它创建下述四个核心进程:kflushd核心线程、kupdate核心线程、kswapd核心线程和keventd核心线程。

    至此,系统初始化已完成。下面的工作就由用户态初始化进程/sbin/init完成系统运行的设置工作。例如,设置操作系统启动时缺省的执行级(通常是3-多用户模式,或者是5-X图形登录方式);激活交换分区、检查磁盘、加载硬件模块;执行相应的脚本文件,建立用户工作环境;显示登录界面及提示信息,接受用户登录。

    4.用户登录
    在用户态初始化阶段init程序在每个tty端口上创建一个进程,用来支持用户登录。每个进程都运行一个getty程序,它监测tty端口等待用户使用。一旦用户开始使用这个端口,getty进程就运行login程序,提示用户输入账号和口令信息。login程序接受用户输入的信息,然后用系统文件/etc/passwd校验用户信息。/etc/passwd中每一行都记录一个用户的信息,其格式如下:
    root:x:0:0:root:/root:/bin/bash

    其中各项之间以冒号隔开,各项依次表示用户名、加密口令(x表示口令密码存放在/etc/shadow文件中)、用户ID号、所属用户组号、主目录,以及登录时启动的Shell程序。

    如果是合法的用户,则输入正确的信息后可通过验证。login进程将用户的主目录作为当前目录,并执行指定的Shell。这样,用户就登录进入了系统,可以使用Shell交互地执行用户命令。当用户退出系统时,终止该用户的Shell进程。该终端的login进程开始等待下一次用户登录。

    通过上面六讲,我们讲解了有关Linux的基本知识,学习了一些走进Linux世界的基本常识。但是,要想深入、灵活地应用Linux,探索Linux的内部世界,让它成为我们的得力助手,还需要深入地学习。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值