Linux下的 进程

一、进程的概念

打开系统的资源管理器,选择“process”选项卡,可以直接观察当前系统中运行的进程。

进程是一个由其中运行着的一个或者多个线程的地址空间以及这些线程所需要的系统资源组成的集合体。

进程是执行程序的过程,是程序地一个具体实现。同一个程序可以执行多次,每次都可以在内存中开辟独立的空间来装载,从而产生多个进程。

一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以任何时刻内存中只有代码的一份拷贝

二、进程的种类

Linux操作系统包括3种不同类型的进程,每种进程都有自己的特点和属性。

1〉交互进程:由一个Shell启动的进程,交互进程既可以在前台运行,也可以在后台运行。

2〉批处理进程:这种进程和终端没有联系,是一个进程序列。

3〉监控进程:也称守护进程,Linux系统启动是启动的进程,并在后台运行。

三、进程查看命令1、 who

该命令主要用于查看当前在线上的用户情况。

1〉这个命令用途:

a. 确定某用户是否在线,已确定能否建立即时通讯。

b.系统管理员希望监视每个登录的用户此时此刻的所作所为,也要使用who命令。

2〉常用语法格式:

who[imqsuwHT][--count] [--idle] [--heading] [--help] [--message] [--mesg] [--version][--writable] [file] [am i]

a. 所有的选项都是可选的,也就是说可以单独使用who命令。

不使用任何选项时,who命令将显示以下三项内容:

loginname:登录用户名;

terminalline:使用终端设备;

logintime:登录到系统的时间。

[例1.1.1] 查看登录到系统的用户情况

$who

roottty1 Mar 17 13:49

foxytty2Mar 17 13:49

roottty3 Mar 17 13:49

bbsttyp0 Mar 17 13:49 (river.net)

可以看到,现在系统一共有四个用户。第一列是登录用户的帐号;第二列是登录所使用的终端;第三列是登录时间;第四列是用户从什么地方登录的网络地址,这里是域名,也可以是IP地址。

b. 如果给出的是两个非选项参数,“am i”,即该命令格式为:“whoam i”,

那么who命令将只显示运行who程序的用户的用户名、登录终端和登录时间。

“-m” 和“whoam i”的作用一样,显示运行该程序的用户名。即“who -m”与“whoam i”作用相同。

[例1.1.2] “whoam i”格式命令的结果:

233.river.net!roottty1 Mar 17 13:49

可见只显示出了运行该who命令的用户情况,当然这时候不存在空闲时间。

c. -q,–count 只显示用户的登录帐号和登录用户的数量,该选项优先级高于其他任何选项。

命令:who -q(也可以who –count)

此时及时使用who –m–q –count也不会只显示自身用户的详细信息,因为 –q选项的优先级较高。

d. -s 标准显示,显示USER,LINR,LOGIN_TIME,可以省略。主要是用于和其他版本的who命令兼容。

e. -i,-u,–idle 在登录时间后面显示该用户最后一次对系统进行操作至今的时间,也就是常说的“发呆”时间。

命令:who-u(-i,–idle在以后的版本中将会被删除,使用-u代替即可)

f. -H,–heading 显示一行列标题。常用的标题如表4-2所示。

命令:who –H(或者who –heading)

表4-2who命令输出常用标题

标题

说明

USER

用户登录帐号

LINE

用户登录使用终端

LOGIN-TIME

用户登录时间

IDLE

用户空闲时间,即未进行操作的时间

PID

用户登录shell的进程ID

FROM

用户网络地址

[例1.1.3] 查看登录用户的详细情况,键入:

$who-uH

显示如下:

USERLINE LOGIN-TIME IDLE FROM

roottty1 Mar 17 13:49 .

foxytty2Mar 17 13:49 00:01

roottty3 Mar 17 13:49 00:01

bbsttyp0 Mar 17 13:49 00:01 (river.net)

这样一目了然。其中-u选项指定显示用户空闲时间,所以可以看到多了一项IDLE。第一个root用户的IDLE项是一个“.”,这就说明该用户在前1秒仍然是活动的,而其他用户后面都有一个时间,称为空闲时间。

g. -w,-T–mesg,–message,–writable选项一样,在登录帐号(USER)后面显示一个字符来表示用户的信息状态:

+:允许写信息;

-:不允许写信息;

?:不能找到终端设备。

h. –help 在标准输出上显示帮助信息。

i. –version 在标准输出上显示版本信息。

who命令应用起来非常简单,可以比较准确地掌握用户的情况,所以使用非常广泛。

2、 w

该命令也用于显示登录到系统的用户情况,是who命令的一个增强版。

与who不同的是,它不但可以显示有谁登录到系统,还可以显示出这些用户当前正在进行的工作,并且统计数据相对who命令来说更加详细和科学。

w命令的显示项目按以下顺序排列:当前时间,系统启动到现在的时间,登录用户的数目,系统在最近1秒、5秒和15秒的平均负载。然后是每个用户的各项数据,项目显示顺序如下:登录帐号、终端名称、远程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行。

其中JCPU时间指的是和该终端(tty)连接的所有进程占用的时间。这个时间里并不包括过去的后台作业时间,但却包括当前正在运行的后台作业所占用的时间。而PCPU时间则是指当前进程(即在WHAT项中显示的进程)所占用的时间。

[例1.2.1] 显示当前登录到系统的用户的详细情况

$ w

2:50pmup 2 min, 4 users, load average:0.22,0.16,0.06

USER TTYFROM LOGIN@ IDLE JCPU PCPU WHAT

roottty1 2:49pm 0:00s 0.56s 0.10s w

foxytty2 2:49pm 1:09 0.42s 0.42s bash

roottty3 2:49pm 46.00s 0.67s 0.25s telnet bbs3

bbsttyp0 river.net 2:49pm 45.00s 0.49s 0.49s bbs h river.net

下面介绍该命令的具体用法和参数。

语法格式如下:

w [-h] [-u] [-s] [-f][-V] [user]

下面对参数进行说明:

-h 不显示标题。

[例1.2.2] 不显示标题

$ w -h

roottty1 2:49pm 0:00s 0.56s 0.10s w

foxytty2 2:49pm 1:09 0.42s 0.42s bash

roottty3 2:49pm 46.00s 0.67s 0.25s telnet bbs3

bbsttyp0 river.net 2:49pm 45.00s 0.49s 0.49s bbs h river.net

-u 当列出当前进程和CPU时间时忽略用户名。这主要是用于执行su命令后的情况。-s 使用短模式。不显示登录时间、JCPU和PCPU时间。

[例1.2.3] 不显示LOGIN,JCPU,PCPU

$ w -s

2:50pmup 2 min, 4 users, load average:0.22,0.16,0.06

USER TTYFROM LOGIN@ IDLE JCPU PCPU WHAT

roottty1 0:00s w

foxytty2 1:09 bash

roottty3 46.00s telnet bbs3

bbsttyp0 river.net 45.00s bbs h river.net

-f 切换显示FROM项,也就是远程主机名项。原本不显示的变为显示,原本显示的变为不显示。-V 显示版本信息。(命令形式:w -v)

User 只显示指定用户的相关情况。(命令形式:w USER)

[例2.4] 显示root用户的相关情况

$ w root

2:50pmup 2 min, 4 users, load average:0.22,0.16,0.06

USER TTYFROM LOGIN@ IDLE JCPU PCPU WHAT

root tty1 0:00s w

3、 ps

用来查看当前进程的情况,是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。

Ps[]

ps命令最常用的还是用于监控后台进程的工作情况,因为后台进程是不和屏幕键盘这些标准输入/输出设备进行通信的,所以如果需要检测其情况,便可以使用ps命令了。

该命令语法格式如下:

ps [-e] [-f] [-h][-l][-w]

ps:

-e 显示所有进程。

-f 全格式。

h 不显示标题。

-l 长格式。

-w 宽输出。

a 显示终端上的所有进程,包括其他用户的进程。

r 只显示正在运行的进程。

x 显示没有控制终端的进程。

O[+|-] k1 [,[+|-] k2 [,…]] 根据SHORT KEYS、k1、k2中快捷键指定的多级排序顺序显示进程列表。对于ps的不同格式都存在着默认的顺序指定。这些默认顺序可以被用户的指定所覆盖。其中“+”字符是可选的,“-”字符是倒转指定键的方向。

pids 只列出指定进程的情况。各进程ID之间使用逗号分隔。该进程列表必须在命令行参数的最后一个选项后面紧接着给出,中间不能插入空格。比如:ps-f1,4,5。

以下介绍长命令行选项,这些选项都使用“–”开头:

–sort X[+|-] key [,[+|-] key [,…]] 从SORT KEYS段中选一个多字母键。“+”字符是可选的,因为默认的方向就是按数字升序或者词典渌承颉1热纾?ps-jax -sort=uid,-ppid,+pid。

–help 显示帮助信息。

–version 显示该命令的版本信息。

在前面的选项说明中提到了排序键,接下来对排序键作进一步说明。需要注意的是排序中使用的值是ps使用的内部值,并非仅用于某些输出格式的伪值。排序键列表见表4-3。

排序键列表 短格式 长格式 说明 ccmd可执行的简单名称Ccmdline完整命令行fflgs长模式标志gpgrp进程的组IDGtpgid控制tty进程组IDjcutime累计用户时间Jcstime累计系统时间kutime用户时间Kstime系统时间mmin_flt次要页错误的数量Mmaj_flt主要页错误的数量ncmin_flt累计次要页错误Ncmaj_flt累计主要页错误osession对话IDppid进程IDPppid父进程IDrrss驻留大小Rresident驻留页ssize内存大小(千字节)Sshare共享页的数量tttytty次要设备号Tstart_time进程启动时间UuidUIDuuser用户名vvsize总的虚拟内存数量(字节)ypriority内核调度优先级

常用ps命令参数

前面两节介绍的参数可能让读者觉得有些可怕,实际上这是一个非常容易使用的命令,一般的用户只需掌握一些最常用的命令参数就可以了。 最常用的三个参数是u、a、x,下面将通过例子来说明其具体用法。

[例1.3.1]以root身份登录系统,查看当前进程状况

$ ps

PID TTY TIME COMMAND

5800 ttyp0 00:00:00 bash

5835 ttyp0 00:00:00 ps

可以看到,显示的项目共分为四项,依次为PID(进程ID)、TTY(终端名称)、TIME(进程执行时间)、COMMAND(该进程的命令行输入)。

可以使用u选项来查看进程所有者及其他一些详细信息,如下所示:

$ ps u

USER PID %CPU %MEM USZ RSS TTY STAT START TIMECOMMAND

test 5800 0.0 0.4 1892 1040 ttyp0 S Nov27 0:00-bash

test 5836 0.0 0.3 2528 856 ttyp0 R Nov27 0:00 ps u

在bash进程前面有条横线,意味着该进程便是用户的登录shell,所以对于一个登录用户来说带短横线的进程只有一个。还可以看到%CPU、%MEM两个选项,前者指该进程占用的CPU时间和总时间的百分比;后者指该进程占用的内存和总内存的百分比。

在这种情况下看到了所有控制终端的进程;但是对于其他那些没有控制终端的进程还是没有观察到,所以这时就需要使用x选项。使用x选项可以观察到所有的进程情况。

[例1.3.2]下面是使用x选项的例子:

$ ps x

PID TTY STAT TIME COMMAND

5800 ttyp0 S 0:00 -bash

5813 ttyp1 S 0:00 -bash

5921 ttyp0 S 0:00 man ps

5922 ttyp0 S 0:00 sh -c /usr/bin/gunzip -c/var/catman/cat1/ps.1.gz | /

5923 ttyp0 S 0:00 /usr/bin/gunzip -c/var/catman/cat1/ps.1.gz

5924 ttyp0 S 0:00 /usr/bin/less -is

5941 ttyp1 R 0:00 ps x

可以发现突然一下子就多出了那么多的进程。这些多出来的进程就是没有控制终端的进程。 前面看到的所有进程都是test用户自己的。其实还有许多其他用户在使用着系统,自然也就对应着其他的很多进程。如果想对这些进程有所了解,可以使用a选项来查看当前系统所有用户的所有进程。经常使用的是aux组合选项,这可以显示最详细的进程情况。

[例1.3.3]

$ ps aux

USER PID %CPU %MEM VSZ RSS TTY STAT START TIMECOMMAND

root 1 0.0 0.0 1136 64 ? S Nov25 0:02 init [3]

root 2 0.0 0.0 0 0 ? SW Nov250:00 [kflushd]

root 3 0.0 0.0 0 0 ? SW Nov250:03 [kupdate]

root 4 0.0 0.0 0 0 ? SW Nov250:00 [kpiod]

root 5 0.0 0.0 0 0 ? SW Nov250:00 [kswapd]

root 163 0.0 0.1 1628 332 ? S Nov25 0:02 sshd

root 173 0.0 0.0 1324 200 ? S Nov25 0:00 syslogd

root 181 0.0 0.0 1420 0 ? SW Nov25 0:00 [klogd]

daemon 191 0.0 0.1 1160 312 ? S Nov25 0:00 /usr/sbin/atd

root 201 0.0 0.1 1348 492 ? S Nov25 0:00 crond

root 212 0.0 0.0 1292 68 ? S Nov25 0:00 inetd

……

在显示的最前面是其他用户的进程情况,可以看到有root、daemon等用户以及他们所启动的进程。 在上面的例子中,介绍了ps命令最常见的一些选项和选项组合,用户可以根据自己的需要选用。

4、 top

top命令和ps命令的基本作用是相同的,显示系统当前的进程和其他状况;

但是top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态。如果在前台执行该命令,它将独占前台,直到用户终止该程序为止。该命令可以按CPU使用,内存使用和执行时间对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定。

显示的各项目为:

uptime 该项显示的是系统启动时间、已经运行的时间和三个平均负载值(最近1秒,5秒,15秒的负载值)。

processes 自最近一次刷新以来的运行进程总数。当然这些进程被分为正在运行的,休眠的,停止的等很多种类。进程和状态显示可以通过交互命令t来实现。

CPU states 显示用户模式,系统模式,优先级进程(只有优先级为负的列入考虑)和闲置等各种情况所占用CPU时间的百分比。优先级进程所消耗的时间也被列入到用户和系统的时间中,所以总的百分比将大于100%。

Mem 内存使用情况统计,其中包括总的可用内存,空闲内存,已用内存,共享内存和缓存所占内存的情况。

Swap 交换空间统计,其中包括总的交换空间,可用交换空间,已用交换空间。

PID 每个进程的ID。

PPID每个进程的父进程ID。

UID 每个进程所有者的UID 。

USER 每个进程所有者的用户名。

PRI 每个进程的优先级别。

NI 该进程的优先级值。

SIZE 该进程的代码大小加上数据大小再加上堆栈空间大小的总数。单位是KB。

TSIZE 该进程的代码大小。对于内核进程这是一个很奇怪的值。

DSIZE 数据和堆栈的大小。

TRS 文本驻留大小。

D 被标记为“不干净”的页项目。

LIB 使用的库页的大小。对于ELF进程没有作用。

RSS 该进程占用的物理内存的总数量,单位是KB。

SHARE 该进程使用共享内存的数量。

STAT 该进程的状态。

其中S代表休眠状态;

D代表不可中断的休眠状态;

R代表运行状态;

Z代表僵死状态;

T代表停止或跟踪状态。

TIME 该进程自启动以来所占用的总CPU时间。如果进入的是累计模式,那么该时间还包括这个进程子进程所占用的时间。且标题会变成CTIME。

%CPU 该进程自最近一次刷新以来所占用的CPU时间和总时间的百分比。

%MEM 该进程占用的物理内存占总内存的百分比。

COMMAND 该进程的命令名称,如果一行显示不下,则会进行截取。内存中的进程会有一个完整的命令行。

[例 1.4.1] 键入top命令查看系统状况

$ top

1:55pmup 7 min, 4user, loadaverage:0.07,0.09,0.06

29 processes:28 sleeping, 1 running, 0 zombie, 0stopped

CPU states: 4.5% user, 3.6% system, 0.0%nice, 91.9%idle

Mem: 38916K av, 18564K used,20352K free,11660K shrd, 1220Kbuff

Swap: 33228K av, 0K used,33228K free,11820K cached

PID USER PRI NI SIZE RSS SHARE STAT LIB %CPU %MEMTIME COMMAND

363 root 14 0 708 708 552 R 0 8.1 1.8 0:00 top

1 root 0 0 404 404 344 S 0 0.0 1.0 0:03 init

2 root 0 0 0 0 0 SW 0 0.0 0.0 0:00 kflushd

3 root -12 -12 0 0 0 SW< 0 0.0 0.0 0:00 kswapd

4 root 0 0 0 0 0 SW 0 0.0 0.0 0:00 md_thread

5 root 0 0 0 0 0 SW 0 0.0 0.0 0:00 md_thread

312 root 1 0 636 636 488 S 0 0.0 1.6 0:00 telnet

285 root 6 0 1140 1140 804 S 0 0.0 2.9 0.00 bash

286 root 0 0 1048 1048 792 S 0 0.0 2.6 0.00 bash

25 root 0 0 364 364 312 S 0 0.0 0.9 0.00 kerneld

153 root 0 0 456 456 372 S 0 0.0 1.1 0.00 syslogd

160 root 0 0 552 552 344 S 0 0.0 1.4 0.00 klogd

169 daemon 0 0 416 416 340 S 0 0.0 1.0 0.00 atd

178 root 2 0 496 496 412 S 0 0.0 1.2 0.00 crond

187 bin 0 0 352 352 284 S 0 0.0 0.9 0.00 portmap

232 root 0 0 500 500 412 S 0 0.0 1.2 0.00 rpc.mountd

206 root 0 0 412 412 344 S 0 0.0 1.0 0.00 inetd

215 root 0 0 436 436 360 S 0 0.0 1.1 0.00 icmplog

第一行的项目依次为当前时间、系统启动时间、当前系统登录用户数目、平均负载。第二行为进程情况,依次为进程总数、休眠进程数、运行进程数、僵死进程数、终止进程数。第三行为CPU状态,依次为用户占用、系统占用、优先进程占用、闲置进程占用。第四行为内存状态,依次为平均可用内存、已用内存、空闲内存、共享内存、缓存使用内存。第五行为交换状态,依次为平均可用交换容量、已用容量、闲置容量、高速缓存容量。然后下面就是和ps相仿的各进程情况列表了。

下面是该命令的语法格式:

top [-] [d delay] [q] [c] [s] [S]

d 指定每两次屏幕信息刷新之间的时间间隔。当然用户可以使用s交互命令来改变之。

top -5

q 该选项将使top没有任何延迟的进行刷新。如果调用程序有超级用户权限,那么top将以尽可能高的优先级运行。

S 指定累计模式。

s 使top命令在安全模式中运行。这将去除交互命令所带来的潜在危险。

i 使top不显示任何闲置或者僵死进程。

c 显示整个命令行而不只是显示命令名

top命令显示的项目很多,默认值是每5秒更新一次,当然这是可以设置的。

下面介绍在top命令执行过程中可以使用的一些交互命令。这些命令都是单字母的,如果在命令行选项中使用了s选项,则可能其中一些命令会被屏蔽掉。

<空格> 立即刷新显示。

Ctrl+L 擦除并且重写屏幕。

h或者? 显示帮助画面,给出一些简短的命令总结说明。

k 终止一个进程。系统将提示用户输入需要终止的进程PID,以及需要发送给该进程什么样的信号。一般的终止进程可以使用15信号;如果不能正常结束那就使用信号9强制结束该进程。默认值是信号15。在安全模式中此命令被屏蔽。

i 忽略闲置和僵死进程。这是一个开关式命令。

q 退出程序。

r 重新安排一个进程的优先级别。系统提示用户输入需要改变的进程PID以及需要设置的进程优先级值。输入一个正值将使优先级降低,反之则可以使该进程拥有更高的优先权。默认值是10。

S 切换到累计模式。

s 改变两次刷新之间的延迟时间。系统将提示用户输入新的时间,单位为s。如果有小数,就换算成m s。输入0值则系统将不断刷新,默认值是5 s。需要注意的是如果设置太小的时间,很可能会引起不断刷新,从而根本来不及看清显示的情况,而且系统负载也会大大增加。

f或者F 从当前显示中添加或者删除项目。

o或者O 改变显示项目的顺序。

l 切换显示平均负载和启动时间信息。

m 切换显示内存信息。

t 切换显示进程和CPU状态信息。

c 切换显示命令名称和完整命令行。

M 根据驻留内存大小进行排序。

P 根据CPU使用百分比大小进行排序。

T 根据时间/累计时间进行排序。

W 将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。

从上面的介绍中可以看到,top命令是一个功能十分强大的监控系统的工具,尤其对于系统管理员而言更是如此。一般的用户可能会觉得ps命令其实就够用了,但是top命令的强劲功能确实提供了不少方便。下面来看看实际使用的情况。

四、进程的常用函数1、 获取进程的基本信息1〉 获取进程

每个进程都有一个ID,调用该进程的进程的ID称为该进程的父ID。

#include<unistd.h>

pid_t getpid(void);//获取进程ID,返回值为long int

pid_t getppid(void);//获取父进程ID,返回值为long int

2〉 获取用户ID和用户组ID

用户为进程的所有者,每个用户都有一个用户ID。

由于进程要用到一些资源,而Linux对系统资源是进行保护的,为了获取一定的资源,进程还有一个有效用户ID。

和用户ID和有效用户ID对应的还有一个组ID和一个有效组ID。

#include<unistd.h>

#include<sys/type.h>

uid_t getuid(void);//获取用户ID,返回值类型为long int

uid_t geteuid(void);//获取有效用户ID,返回值类型为 long int

gid_t getgid(void);//获取用户组ID,返回值类型为 long int

gid_t getegid(void);//获取有效用户组ID,返回值类型为long int

3〉 获取用户的其他信息

首先介绍一个存储用户相关信息的结构体:

struct passwd

{

char*pw_name;//登录名称

char*pw_passwd;//登录口令

uid_tpw_uid;//用户ID

gid_tpw_gid;//用户组ID

char*pw_gecos;//用户的真名

char*pw_dir;//用户的目录

char*pw_shell;//用户的SHELL

}

获取用户信息的函数:

#include<pwd.h>

#include<sys/types.h>

struct passwd *getpwuid(uid_t uid);

4〉 例4.1.1

#include <unistd.h>

#include <pwd.h>

#include <sys/types.h>

#include <stdio.h>

int main(int argc,char *argv[])

{

pid_tmy_pid,parent_pid;

uid_tmy_uid,my_euid;

gid_tmy_gid,my_egid;

structpasswd *my_info;

my_pid= getpid();

parent_pid= getppid();

my_uid= getuid();

my_euid= geteuid();

my_gid= getgid();

my_egid= getegid();

my_info= getpwuid(my_uid);

printf("ProcessID: %ld\n",my_pid);

printf("ParentID: %ld\n",parent_pid);

printf("UserID: %ld\n",my_uid);

printf("EffectiveUser ID: %ld\n",my_euid);

printf("GroupID: %ld\n",my_gid);

printf("EffectiveGroup ID: %ld\n",my_egid);

if(my_info)

{

printf("MyLogin Name: %s\n",my_info->pw_name);

printf("MyPassword: %s\n",my_info->pw_passwd);

printf("MyUser ID: %ld\n",my_info->pw_uid);

printf("MyGroup ID: %ld\n",my_info->pw_gid);

printf("MyReal Name: %s\n",my_info->pw_gecos);

printf("MyHome Dir: %s\n",my_info->pw_dir);

printf("MyWork Shell: %s\n",my_info->pw_shell);

}

return0;

}

运行结果:

2、 进程的创建——fork

Linux是一个多用户操作系统,在同一时间会有许多用户争夺系统资源。因此创建子进程来争夺资源,可以早一点完成任务。

当一个进程调用fork函数,会克隆产生一个与自身完全相同的子进程。子进程和父进程唯一的区别就是进程ID。

父子进程一起从fork处开始执行,相互竞争系统资源。

1〉函数原型

#include<unistd.h>

pid_t fork();

2〉函数的返回值:

如果函数调用失败,则返回 -1;

对于父进程,返回子进程ID;

对于子进程,返回0。

3〉例2.2.1

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <errno.h>

#include <math.h>

#include <stdlib.h>

int main(int argc,char *argv[])

{

pid_tchild;

intstatus;

int i;

printf("*************begin************\n");

if((child= fork()) == -1)

{

printf("ForkError:%s\n",strerror());

exit(1);

}

elseif(child == 0)

{

printf("Iam the child : %d\n",getpid());

for(i=0;i<1000000;i++)sin(i);

}

elseif(child != 0)

{

printf("Iam the parent : %d\n",getpid());

for(i=0;i<1000000;i++)sin(i);

}

return0;

}

输出结果:

如果将printf("*************begin************\n");

改为printf("*************begin************");

则运行结果为:

原因在于printf函数:

用printf()输出时是先输出到缓冲区,然后只有在下述情况下,缓冲区中的内容才会送到屏幕上。

a. 使用fflush(stdout)强制刷新。

b.缓冲区已满。

c.scanf()要在缓冲区里取数据时会先将缓冲区刷新。

d.\n,\r进入缓冲区时。

e.线程结束的时候,如果该线程里也有printf(….);

f. 程序结束时。

3、 有时希望子进程继续执行,而父进程阻塞直到子进程完成任务。这时可以调用wait或者waitpid函数。1〉 函数原型

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int *stat_loc);

pid_t waitpid(pid_t pid,int *stat_loc,int options);

2〉 wait

函数功能:wait系统调用会使父进程阻塞直到一个子进程结束,或者父进程接收到一个信号

返回值:如果调用成功(即因一个子进程结束)将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc为子进程的退出状态。

如果父进程没有子进程,或者子进程已经结束了,wait会立即返回并且返回值为-1,同时errno被置为ECHILD。

3〉 waitpid

函数功能:等待指定的子进程直到子进程返回

函数参数:

pid为正值,等待为指定pid的进程;

pid为0,等待任何一个组ID与调用者组ID相同的进程。

pid小于-1时,等待任何一个组ID等于pid绝对值的进程。

stat_loc与wait中的意义一样。

Options可以决定父进程的状态,可以取以下两个值:

WNOTHING: 当没有子进程存在时,父进程立即返回。

WUNTACHED:当子进程结束时waitpid返回,但是子进程的退出状态不可得到

4〉 子进程的退出状态可以通过exit(),_exit(),return设置。

1#include<unistd.h>

void _exit(int status);

2#include<stdlib.h>

void_Exit(int status);

3#include<stdlib.h>

void exit(int status);

_exit()函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。

exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是"清理I/O缓冲"。

exit()在结束调用它的进程之前,要进行如下步骤:

1调用atexit()注册的函数(出口函数);按ATEXIT注册时相反的顺序调用所有由它注册的函数,这使得我们可以指定在程序终止时执行自己的清理动作.例如,保存程序状态信息于某个文件,解开对共享数据库上的锁等.

2cleanup();关闭所有打开的流,这将导致写所有被缓冲的输出,删除用TMPFILE函数建立的所有临时文件.

3最后调用_exit()函数终止进程。

_exit做3件事:

l Any open file descriptors belonging to theprocess are closed

l anychildren of the process are inherited byprocess 1, init

l theprocess’s parent is sent a SIGCHLD signal

exit执行完清理工作后就调用_exit来终止进程。

5〉 判断子进程退出状态的宏

WIFEXITED(status)如果子进程正常结束则为非0值。

WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。

WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真

WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。

WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。

WSTOPSIG(status)取得引发子进程暂停的信号代码,

6〉 例2.3.1

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <stdio.h>

#include <errno.h>

#include <math.h>

#include <stdlib.h>

void main(void)

{

pid_tchild;

intstatus;

intres;

structitimerval tick,old_tick;

if((child= fork()) == -1)

{

printf("ForkError:%s\n",strerror(errno));

exit(1);

}

elseif(child == 0)//child process

{

inti;

printf("Iam the child : %ld\n",getpid());

for(i= 0;i < 1000000;i++)

{

sin(i);

}

i= 5;

printf("Iexit with %d\n",i);

exit(i);//set stat_loc

}

while(((child= wait(&status)) == -1)&(errno == EINTR));

if(child== -1)

printf("WaitError:%s\n",strerror(errno));

elseif(!status)//status is 0

printf("Child%ld terminated normally return status is zero\n",child);

elseif(WIFEXITED(status))//status is nonzero and terminate normally

printf("Child%ld terminated normally return status is %d\n",child,WEXITSTATUS(status));

elseif(WIFSIGNALED(status))

//statusis nonzero and terminate normally by signal

printf("Child%ld terminated due to signal %d znot caught\n",child,WTERMSIG(status));

}

运行结果:

思想如钻子,必须集中在一点钻下去才有力量

Linux下的 进程

相关文章:

你感兴趣的文章:

标签云: