if (fork()==0) { …;
execl(…); …; }
wait(&status);
4.wait( )
等待子进程运行结束。如果子进程没有完成,父进程一直等待。wait()将调用进程挂起,直至其子进程因暂停或终止而法来软中断信号为止。如果在wait()前已有子进程暂停或终止,则调用进程作适当处理后便返回。 系统调用格式:
int wait(status)int *status; 5.exit( )
终止进程的执行。 系统调用格式: void exit(status) int status;
其中,status是返回给父进程的一个整数,0表示正常结束,其他的数值表示出现了错误。
为了及时回收进程所占用的资源并减少父进程的干预,UNIX/Linux利用exit( )来实现进程的自我终止,通常父进程在创建子进程时,应在进程的末尾安排一条exit(),使子进程自我终止。exit(0)表示进程正常终止,exit(1)表示进程运行有错,异常终止。 6.lockf(files,function,size)
用作锁定文件的某些段或整个文件。 本函数的头文件为:#include “unistd.h” 系统调用格式:
int lockf(files,function,size) int files,function; long size;
19
其中,file是文件描述符;function是锁定和解锁:1 表示锁定,0 表示解锁。size是锁定或解锁的字节数,为0,表示从文档的当前位置到文件尾。 7.getpid()
本函数的头文件为: #include
功能:用来得到进程唯一的pid号。 格式: pid-t getpid(void)
3.2 程序实例
例:编写一段程序,利用系统调用fork( )创建两个进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。
#include
int p1,p2;
while ((p1=fork())= =-1); if (p1= =0) putchar(?b?); else {
while ((p2=fork()= =-1); if (p2= =0)
putchar(?c?); else
putchar(?a?);
} }
分析:从进程并发执行来看,输出bac,acb或bca都有可能。
原因:fork()创建进程所需要的时间要多于输出一个字符的时间,因此在主进程创建进程2的同时,进程1就输出了”b”,而进程2和主程序的输出次序是有随机性的,所以会出现多种可能结果。
例:修改已经编号的程序,将每个进程输出一个字符改为每个进程输出一句话,
20
在观察程序执行时屏幕上出现的现象,并分析原因。
如果在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。 参考程序1:
#include
int p1,p2;
while ((p1=fork()= =-1); if (p1= =0)
for(i=0;i<50;i++)
printf(“child%d\\n”,i); else {
while ((p2=fork()= =-1); if (p2= =0)
for(i=0;i<50;i++)
printf(“son%d\\n”,i); else
for(i=0;i<50;i++)
printf(“daughter%d\\n”,i);
} }
运行结果: child… son…
daughter…
或 child
…son …child …son
…daughter 等
分析:由于函数printf()输出的字符串之间不会被中断,因此,字符串内部的字符顺序输出时不变。但是,由于进程并发执行时的调度顺序和父子进程的抢占处理机问题,处处字符串的顺序和先后随着执行的不同而发生变化。 参考程序2:
#include
#include
21
{
int p1,p2,I;
while ((p1=fork()= =-1); if (p1= =0) {
lockf(1,1,0); for(i=0;i<50;i++) printf(“child%d\\n”,i); lockf(1,0,0);
} else {
while ((p2=fork()= =-1); if (p2= =0) {
lockf(1,1,0); for(i=0;i<50;i++) printf(?son%d\\n”,i); lockf(1,0,0);
} else {
lockf(1,1,0); for(i=0;i<50;i++)
printf(“daughter%d\\n”,i); lockf(1,0,0); } } }
实验四 进程通信
4.1父子进程同步
在linux系统中,系统调用wait()和exit()是进程同步的主要手段。执行
exit可以终止进程的执行,使调用他的进程进入等待善后处理状态,并等待父进程对终止的子进程进行善后处理。因此在用户态程序中,可以调用fork产生子进程,当子进程希望终止时,可以调用exit。在父进程中可以使用wait等待其子进程终止。
父进程等待子进程终止:
22
系统调用wait对处于等待善后处理状态的子进程进行善后处理。父进程调用wait(status)等待它的一个子进程终止。
如果调用wait()之前,已有一个子进程结束了,则父进程对其善后处理后返回。
如果调用wait()的进程没有子进程,返回-1。
如果调用wait()时它的子进程还没有终止,则进入阻塞状态。 #include
pid_t pc,pr; pc=fork(); if(pc<0)
printf(“error occurred!\\n”); else if(pc= =0) {
printf(“This is child process with pid of %d\\n”,getpid()); sleep(10);
} else {
pr=wait(NULL);
printf(“I catched a child process with pid of %d\\n”,pr); } exit(0); }
23