`
helpbs
  • 浏览: 1163661 次
文章分类
社区版块
存档分类
最新评论

进程与信号(一)

 
阅读更多

第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系统调用来执行同样的任务。

分享到:
评论

相关推荐

    实验一 进程通信——管道和信号实验报告.doc

    如果在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。 要求:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即DEL键)...

    操作系统课程设计-信号通信与进程控制

    设计内容:信号通信与进程控制 主要包括如下几项: (l)进程的创建:编写一段程序,使用系统调用fork()创建两个或多个子进程。当此程序运行时,在系统中有一个父进程和其余为子进程活动。 (2)进程的控制:在程序中...

    进程信号的处理过程.docx

    进程信号的处理过程:(1)在目的进程中安装该信号,即设置如果目标进程捕获该信号时执行的操作代码。Linux采用sigal和sgation系统调用来完成。因信号是异步事件的典型应用,产生信号对进程而言是随机出现的,因此,...

    01 进程与信号(一).ppt

    进程和信号 进程和信号进程和信号进程和信号进程和信号进程和信号进程和信号进程和信号进程和信号进程和信号

    2.信号通信与进程控制

    2 信号通信与进程控制 &lt;任务&gt; l 进程的创建:编写一段程序 使用系统调用fork 创建两个或多个子进程 当此程序运行时 在系统中有一个父进程和其余为子进程活动 2 进程的控制:在程序中使用系统调用lockf 来给...

    进程间通信之信号 sinal ) 完整代码

    进程间通信之信号 sinal ) 唯一的异步通信方式 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) 六 信号量 ...

    利用信号进行进程间通信

    利用信号进行进程间通信:实现一个SIGINT信号的处理程序,注册该信号处理程序,创建一个子进程,父子进程都进入等待。

    进程同步——信号量机制

    关于信号量的文章,生产者消费者问题与读者写者问题---信号量机制,PV操作——进程同步的信号量问题,利用信号机制实现的 父子进程同步,嵌入式linux的学习笔记-进程间通信的信号与信号集(四)1)进程的同步与互斥 ...

    操作系统实验进程的软中断通信

    1[实验题目]  进程的软中断通信 2[实验目的]  (1)理解掌握软中断的概念和技术;... (2)若子进程向父进程发送信号,父进程接到信号后可以缺省操作、或忽视信号、或执行一函数,各是什么含义?

    基于Linux的实现进程的信号量互斥申请

    基于Linux的实现进程的信号量互斥申请 包括说明书 源代码 任务书 这事一个课程设计的最终答辩

    操作系统实验-信号量机制实现进程同步

    这是一个利用信号量机制实现进程同步的模拟程序,如你有更好的方法或觉的我的可以修改欢迎交流学习。

    操作系统实验-进程和进程通信

    要求在该程序中还要使用进程的睡眠、进程图象改换、父进程等待子进程终止、信号的设置与传送(包括信号处理程序)、子进程的终止等有关进程的系统调用。 分别利用UNIX的消息通信机制、共享内存机制(用信号灯实施...

    进程线程之间的同步生产者消费者信号量读者写者写者优先

    1。生产者消费者问题(信号量+mutex) 参考教材中的生产者消费者算法,创建5个进程,其中两个进程为生产者进程,...编写一个写者优先解决读者写者问题的程序,其中读者和写者均是多个进程,用信号量作为同步互斥机制。

    编写daemon进程,检测信号实现功能

    当检测到SIGHUP(远程挂断)信号时,该进程获得系统运行时间(读取/proc/uptime文件,该文件第一列为系统启动到现在的时间(以秒为单位),第二列为系统空闲的时间(以秒为单位)),将其追加写入日志文件。...

    3操作系统实验.doc

    实验一:信号机制 实验目的 1、了解什么是信号 2、熟悉LINUX系统中进程之间软中断通信的基本原理 实验内容 1、编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键...

    进程同步与互斥:System V 信号量示例代码

    进程同步与互斥:System V 信号量,相关使用教程链接如下: http://blog.csdn.net/tennysonsky/article/details/47811201

    操作系统=进程管理=实验报告

    1、编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录...

    linux下的进程管理演示(c语言)

    signal()让父进程捕捉用alarm函数设置时钟的时间段终止时产生的信号,当捕捉到该信号后,父进程使用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出子进程被杀死信息后终止,父进程等待两个子...

    (修改版)实现进程的软中断通信。要求:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断

    使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上的中断信号(即按DEL键);当捕捉到中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: ...

    进程和进程通信

    要求在该程序中还要使用进程的睡眠、进程图象改换、父进程等待子进程终止、信号的设置与传送(包括信号处理程序)、子进程的终止等有关进程的系统调用。 2. 分别利用UNIX的消息通信机制、共享内存机制(用信号灯...

Global site tag (gtag.js) - Google Analytics