Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2050504
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类: LINUX

2009-10-24 00:43:25

进程与信号(一)
第11章 进程与信号

进程与信号构成了Linux操作环境的基础部分。他们控制了几乎所有由Linux与其他的类Unix计算机系统所执行的活动。理解Linux与Unix如何管理进程将会使得系统程序员,程序编写者,或是系统管理处于一个有利的位置。

在这一章,我们将会了解在Linux环境中进程中如何被处理的以及如何确定在指定的时刻计算机正在做什么。我们同时也会了解如何在我们自己的程序中启动与停止其他的进程,如何使得进程发送与接收消息,以及如何避免僵尸进程。具体而言,我们将会了解下列内容:

进程结构,类型与调度
使用不同的方法启动一个新进程
父进程,子进程以及僵尸进程
什么是信号以及如何使用信号

什么是进程

Unix规范第2版,以及之前的第1版,将进程定义为"一个地址空间,在这个地址空间中运行一个或是多个线程,以及这些线程所需要的系统资源"。我们将会在第12章来了解线程。就目前而言,我们只是将进程看作一个正在运行的程序。

一个多任务操作系统例如Linux会允许多个程序同时运行。每一个正在运行的程序实例构成一个进程。对于一个窗口系统,例如X Window系统,更是如此。与Windows类似,X提供了一个图形用户接口允许多个程序同时运行。每一个程序都可以显示一个或是多个窗口。

作为一个多用户系统,Linux允许多个用户同时访问系统。每一个用户会同时运行多个程序,或是同一个程序的多个实例。系统本身运行其他的程序来管理系统资源并且控制用户访问。

正如我们在第4章所看到的,一个程序,或进程,是由程序代码,数据,变量(占用系统内存),打开的文件(文件描述符),以及环境组成。通常,Linux系统会在进程之间共享代码与系统库,从而每次在内存中只有一份代码拷贝。

进程结构

下面我们来看一下一个进程在操作系统中是如何安排的。如果两个用户,neil与rick,同时运行grep程序在不同的文件中查找不同的字符串,所运行的进程如下图11-1所示。

如果我们运行ps命令,所得到的输出结果如下所示:

$ ps -af
UID     PID PPID C STIME TTY  TIME     CMD
rick    101 96   0 18:24 tty2 00:00:00 grep pid_t /usr/include/sys/*.h
neil    102 92   0 18:24 tty4 00:00:00 grep XOPEN /usr/include/features.h

每一个进程都被分配了一个唯一的数字,称为进程标识或是PID。这通常是2到32768之间的一个正数。当一个进程被启动时,队列中下一个未被使用的数字就会被选中,而数据由2重新开始,从而他们可以循环。数字1通常是为特殊进程init所保留的,init进程管理其他的进程。我们会在稍后讨论init进程。在这里我们可以看到由neil与rick所启动的两个进程已经被标识为101与102。

将会由grep命令所执行的程序代码存储在一个磁盘文件中。通常,Linux进程并不会改写用于保存程序代码的内存区域,所以代码是以只读方式装入内存的。我们可以在上图中看到,因为这个区域不可以被改写,所以他可以被安全的共享。

系统库也可以被共享。所以,例如在内存中就可以只需要一份printf拷贝,尽管会有多个程序调用他。这更为复杂,但是与Windows中动态链接库的工作方式相似。

正如我们在前面的图中所看到的,另一个好处就是包含可执行程序grep的磁盘文件更小,因为他并不包含共享库代码。这对于单个程序而言并不是明显,但是却在整个操作系统上为标准C库节省的大量的空间。

当然,并不是一个程序所需要的所有内容都可以共享。例如,每一个进程所使用的不同变量。在这个例子中,我们可以看到搜索字符串是作为一个进程数据空间的变量s传递给grep命令的。这些是单独的,并且通常不能被其他的进程所读取。两个grep命令中所用的文件也是不同的,进程拥有其自己的用户文件访问的文件描述符集合。

另外,进程拥有其自己的堆栈空间,用于函数中的局部变量以及函数调用与返回的控制。同时他也拥有其自己的环境空间,包含为进程使用所建立的环境变量,正如我们在第4章了解putenv与getenv时所看到的。一个进程必须同时维护其自己的程序计数,在其执行中执行到哪里,哪一个是执行线程等的记录。在下一章,我们将会看到当我们使用线程时,进程可以有多个执行线程。

在许多Linux系统以及一些Unix系统上,在/proc目录中有一个特殊的文件集合。特殊之处就在于他们并不是真正的文件,而是允许我们在进程运行时查看进程的内部,就如同他们是目录中的文件一样。我们已经在第3章简要的了解了/proc文件系统。

最后,与Unix类似,因为Linux有一个虚拟内存系统可以将代码与数据换出到一个磁盘区域,可以管理更多的进程,而不仅是只适合物理内存。

进程表

Linux进程表是一个描述当装载入的所有进程的数据结构,例如,他们的PID,状态以及命令字符串,由ps所输出的信息等。操作系统使用他们的PID来管理进程,而他们被用作进程表中的索引。进程表是大小限制的,所以一个系统支持的进程数目也是有限制的。早期的Unix系统有支持256进程的限制。更为现代的实现已经放宽了这个限制,而所支持的进程数目只受形成一个进程表项的可用内存的限制。

查看进程

ps命令可以显示我们正在运行的进程,另一个用户正在运行的进程,或是系统上的所有进程。如下面的例子输出:

$ ps -af
UID      PID PPID C STIME TTY      TIME CMD
root     433  425 0 18:12 tty1 00:00:00 [bash]
rick     445  426 0 18:12 tty2 00:00:00 -bash
rick     456  427 0 18:12 tty3 00:00:00 [bash]
root 467 433 0 18:12  tty1  00:00:00 sh /usr/X11R6/bin/startx
root 474 467 0 18:12  tty1  00:00:00 xinit /etc/X11/xinit/xinitrc --
root 478 474 0 18:12  tty1  00:00:00 /usr/bin/gnome-session
root 487   1 0 18:12  tty1  00:00:00 gnome-smproxy --sm-client-id def
root 493   1 0 18:12  tty1  00:00:01 [enlightenment]
root 506   1 0 18:12  tty1  00:00:03 panel --sm-client-id default8
root 508   1 0 18:12  tty1  00:00:00 xscreensaver -no-splash -timeout
root 510   1 0 18:12  tty1  00:00:01 gmc --sm-client-id default10
root 512   1 0 18:12  tty1  00:00:01 gnome-help-browser --sm-client-i
root 649 445 0 18:24  tty2  00:00:00 su
root 653 649 0 18:24  tty2  00:00:00 bash
neil 655 428 0 18:24  tty4  00:00:00 -bash
root 713   1 2 18:27  tty1  00:00:00 gnome-terminal
root 715 713 0 18:28  tty1  00:00:00 gnome-pty-helper
root 717 716 13 18:28 pts/0 00:00:01 emacs
root 718 653 0 18:28  tty2  00:00:00 ps –af

这个输出显示了许多进程的信息,包括Linux系统上运行在X下的Emacs编辑器所调用的进程。例如,TTY列显示了由哪一个终端启动,TIME列表给出到目前为止所用的CPU时间,而CMD列表显示启动这个进程所用的命令。下面我们来仔细的看一下其中的一些。

neil 655 428 0 18:24 tty4 00:00:00 –bash

初始登陆是在4号虚拟控制台上执行的。这只是这个机器的一个控制台。所运行的shell程序是Linux默认的bash。

root 467 433 0 18:12 tty1 00:00:00 sh /usr/X11R6/bin/startx

X Window系统是由命令startx来启动的。这是一个启动X服务器的shell脚本并且运行一个初始化的X程序。

root 717 716 13 18:28 pts/0 00:00:01 emacs

这个进程代表在一个X窗口中运行的Emacs。他是由窗口管理器为了响应一个新窗口请求而启动的。一个伪终端,pts/0,赋给这个shell用于读取与写入。

root 512 1 0 18:12 tty1 00:00:01 gnome-help-browser --sm-client-i

这是由窗口管理器所启动的GNOME帮助浏览器。

默认情况下,ps程序只显示那些与一个终端,一个控制台,一个串行线,或是一个伪终端相连的进程。其他进程的运行并不需要与一个终端上的用户相关联。这些通常是Linux用来管理共享资源的系统进程。我们可以使用ps命令的-a选项来查看所有这样的进程,并且使用-f选项可以查看全部信息。

系统进程


下面是Linux系统上所运行的其他一些进程。输出已经进行简化处理。

$ ps -ax
  PID TTY   STAT TIME COMMAND
    1 ?     S    0:05 init
    2 ?     SW   0:00 [keventd]
    3 ?     SW   0:00 [kapmd]
    4 ?     SWN  0:00 [ksoftirqd_CPU0]
    5 ?     SW   0:00 [kswapd]
    6 ?     SW   0:00 [bdflush]
    7 ?     SW   0:00 [kupdated]
    8 ?     SW   0:00 [kinoded]
   10 ?     SW   0:00 [mdrecoveryd]
   75 ?     SW<  0:00 [lvm-mpd]
  503 ?     S    0:00 /sbin/syslogd -a /var/lib/dhcp/dev/log
  506 ?     S    0:00 /sbin/klogd -c 1 -2
  542 ?     SW   0:00 [khubd]
  614 ?     S    0:00 /sbin/portmap
  653 ?     S    0:00 /usr/sbin/sshd
  730 ?     S    0:00 /sbin/dhcpcd -H -D -N -Y -t 999999 -h beast eth0
  744 ?     S    0:00 /usr/sbin/cupsd
 1004 ?     S    0:00 /usr/lib/postfix/master
 1021 ?     S    0:00 pickup -l -t fifo -u
 1022 ?     S    0:00 qmgr -l -t fifo -u
 1037 ?     S    0:00 /usr/sbin/atd
 1055 ?     S    0:00 /usr/sbin/cron
 1071 ?     S    0:00 /usr/sbin/nscd
 1084 ?     S    0:00 /usr/sbin/nscd
 1094 tty1  S    0:00 /sbin/mingetty --noclear tty1
 1095 tty2  S    0:00 /sbin/mingetty tty2
 1096 tty3  S    0:00 /sbin/mingetty tty3
 1097 tty4  S    0:00 /sbin/mingetty tty4
 1098 tty5  S    0:00 /sbin/mingetty tty5
 1099 tty6  S    0:00 /sbin/mingetty tty6
 1102 ?     S    0:00 /usr/X11R6/bin/xdm
 1106 ?     S    0:02 /usr/X11R6/bin/X :0 vt07 -auth /var/lib/xdm/authdir/a
 1108 ?     S    0:00 -:0
 1124 ?     S    0:00 /usr/X11R6/bin/xconsole -notify -nostdin -verbose -ex
 1155 ?     S    0:00 -192.168.0.25:0
 1168 ?     S    0:00 /bin/sh /usr/X11R6/bin/kde
 1259 pts/2 S    0:00 /bin/bash
 1262 pts/1 S    0:00 /bin/bash
 1273 pts/2 S    0:00 su -
 1274 pts/2 S    0:00 -bash
 1313 pts/1 S    0:00 emacs
 1321 ?     S    0:02 kdeinit: khelpcenter
 1329 ?     S    0:02 kdeinit: konqueror --silent
 1357 pts/2 R    0:00 ps -ax

在这里我们可以看到一个非常重要的进程。

1 ? S 0:05 init

通常而言,每一个进程都是由另一个被称之为其父进程的进程来启动的。被启动的进程被称之为子进程。当Linux启动时,他运行init程序,他是最重要的祖先进程而其进程号为1。如果我们喜欢,可以将其称之为操作系统进程管理器,并且是所有进程的父进程。我们稍后将要看到的其他系统进程都是由init进程或是由init进程所启动的其他进程来启动的。

这样的一个例子就是登陆过程。init为我们用来登陆的每一个串行终端或是调制解调器中的拨号启动一个getty程序。如下所示的ps的输出:

1095 tty2 S 0:00 /sbin/mingetty tty2

getty进程会在终端等待激活,向用户提示熟悉的登陆提示,然后将控制权传递给登陆程序,后者会设置用户环境,并且最后启动一个shell。当用户shell退出时,init就会启动另一个getty进程。

我们可以看到启动新进程并且等待他们完成的能力是系统的基础。我们在本章的后面将会看到如何在我们自己的程序中使用fork,exec以及wait系统调用来执行同样的任务。
阅读(794) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~