实验二-进程管理Word格式.doc
- 文档编号:13082076
- 上传时间:2022-10-04
- 格式:DOC
- 页数:39
- 大小:111KB
实验二-进程管理Word格式.doc
《实验二-进程管理Word格式.doc》由会员分享,可在线阅读,更多相关《实验二-进程管理Word格式.doc(39页珍藏版)》请在冰豆网上搜索。
在2.4.4版内核中,getpid是第20号系统调用,其在Linux函数库中的原型是:
#include<
sys/types.h>
/*提供类型pid_t的定义*/
unistd.h>
/*提供函数的定义*/
pid_tgetpid(void);
getpid的作用很简单,就是返回当前进程的进程ID,请大家看以下的例子:
/*getpid_test.c*/
#include<
main()
{
printf("
ThecurrentprocessIDis%d\n"
getpid());
}
这个程序的定义里并没有包含头文件sys/types.h,这是因为我们在程序中没有用到pid_t类型,pid_t类型即为进程ID的类型。
事实上,在i386架构上(就是我们一般PC计算机的架构),pid_t类型是和int类型完全兼容的,我们可以用处理整形数的方法去处理pid_t类型的数据,比如,用"
%d"
把它打印出来。
编译并运行程序getpid_test.c:
$gccgetpid_test.c-ogetpid_test
$./getpid_test
ThecurrentprocessIDis1980
(你自己的运行结果很可能与这个数字不一样,这是很正常的。
)
再运行一遍:
ThecurrentprocessIDis1981
正如我们所见,尽管是同一个应用程序,每一次运行的时候,所分配的进程标识符都不相同。
2、fork
在2.4.4版内核中,fork是第2号系统调用,其在Linux函数库中的原型是:
pid_tfork(void);
创建一个新进程。
系统调用格式:
pid=fork()
fork()返回值意义如下:
0:
在子进程中,pid变量保存的fork()返回值为0,表示当前进程是子进程。
>
在父进程中,pid变量保存的fork()返回值为子进程的id值(进程唯一标识符)。
-1:
创建失败。
如果fork()调用成功,它向父进程返回子进程的PID,并向子进程返回0,即fork()被调用了一次,但返回了两次。
此时OS在内存中建立一个新进程,所建的新进程是调用fork()父进程(parentprocess)的副本,称为子进程(childprocess)。
子进程继承了父进程的许多特性,并具有与父进程完全相同的用户级上下文。
父进程与子进程并发执行。
核心为fork()完成以下操作:
(1)为新进程分配一进程表项和进程标识符
进入fork()后,核心检查系统是否有足够的资源来建立一个新进程。
若资源不足,则fork()系统调用失败;
否则,核心为新进程分配一进程表项和唯一的进程标识符。
(2)检查同时运行的进程数目
超过预先规定的最大数目时,fork()系统调用失败。
(3)拷贝进程表项中的数据
将父进程的当前目录和所有已打开的数据拷贝到子进程表项中,并置进程的状态为“创建”状态。
(4)子进程继承父进程的所有文件
对父进程当前目录和所有已打开的文件表项中的引用计数加1。
(5)为子进程创建进程上、下文
进程创建结束,设子进程状态为“内存中就绪”并返回子进程的标识符。
(6)子进程执行
虽然父进程与子进程程序完全相同,但每个进程都有自己的程序计数器PC(注意子进程的PC开始位置),然后根据pid变量保存的fork()返回值的不同,执行了不同的分支语句。
例:
…..
pid=fork();
if(pid==0)
printf("
I'
mthechildprocess!
\n"
);
elseif(pid>
0)
mtheparentprocess!
\n"
else
printf("
Forkfail!
……
PC
fork()调用前
fork()调用后
if(pid==0)
\n"
3、exit
在2.4.4版内核中,exit是第1号调用,其在Linux函数库中的原型是:
stdlib.h>
voidexit(intstatus);
不像fork那么难理解,从exit的名字就能看出,这个系统调用是用来终止一个进程的。
无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩下的所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。
请看下面的程序:
/*exit_test1.c*/
thisprocesswillexit!
exit(0);
neverbedisplayed!
编译后运行:
$gccexit_test1.c-oexit_test1
$./exit_test1
我们可以看到,程序并没有打印后面的"
,因为在此之前,在执行到exit(0)时,进程就已经终止了。
exit系统调用带有一个整数类型的参数status,我们可以利用这个参数传递进程结束时的状态,比如说,该进程是正常结束的,还是出现某种意外而结束的,一般来说,0表示没有意外的正常结束;
其他的数值表示出现了错误,进程非正常结束。
我们在实际编程时,可以用wait系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理。
exit和_exit
_exit在Linux函数库中的原型是:
void_exit(intstatus);
_exit()函数的作用最为简单:
直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;
exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,也是因为这个原因,有些人认为exit已经不能算是纯粹的系统调用。
exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。
在Linux的标准函数库中,有一套称作“高级I/O”的函数,我们熟知的printf()、fopen()、fread()、fwrite()都在此列,它们也被称作“缓冲I/O(bufferedI/O)”,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符\n和文件结束符EOF),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。
如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。
请看以下例程:
/*exit2.c*/
outputbegin\n"
contentinbuffer"
编译并运行:
$gccexit2.c-oexit2
$./exit2
outputbegin
contentinbuffer
/*_exit1.c*/
_exit(0);
$gcc_exit1.c-o_exit1
$./_exit1
在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构。
在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集。
/*zombie.c*/
#include<
pid_tpid;
pid=fork();
if(pid<
0) /*如果出错*/
printf("
erroroccurred!
elseif(pid==0)/*如果是子进程*/
exit(0);
else /*如果是父进程*/
sleep(60);
/*休眠60秒,这段时间里,父进程什么也干不了*/
wait(NULL);
/*收集僵尸进程*/
sleep的作用是让进程休眠指定的秒数,在这60秒内,子进程已经退出,而父进程正忙着睡
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 进程 管理