#include
main()
{
inti,pid;
for(i=1;i<4;i++)
if(pid=fork())break;
}
实验4:
编写程序。
使用fork()和exec()等系统调用创建三个子进程。
子进程分别启动不同程序,并结束。
反复执行该程序,观察运行结果,结束的先后,看是否有不同次序。
结果:
思考:
子进程运行其它程序后,进程运行环境怎样变化的?
反复运行此程序看会有什么情况?
解释一下。
答:
子进程运行其他程序后,这个进程就完全被新程序代替。
由于并没有产生新进程所以进程标识号不改变,除此之外的旧进程的其他信息,代码段,数据段,栈段等均被新程序的信息所代替。
新程序从自己的main()函数开始进行。
反复运行此程序发现结束的先后次序是不可预知的,每次运行结果不一样。
原因是当每个子进程运行其他程序时,他们的结束随着其他程序的结束而结束,所以结束的先后次序在改变。
实验5:
编写程序。
验证子进程继承父进程的程序、数据等资源。
如用父、子进程修改公共变量和私有变量的处理结果;父、子进程的程序区和数据区的位置。
结果:
思考:
子进程被创建后,对父进程的运行环境有影响吗?
解释一下。
答:
子进程被创建后,对父进程的运行环境无影响,因为当子进程在运行时,他有自己的代码段和数据段,这些都可以作修改,但是父进程的代码段和数据段是不会随着子进程数据段和代码段的改变而改变。
实验6:
复习管道通信概念,编写一个程序。
父进程创建两个子进程,父子进程之间利用管道进行通信。
要求能显示父进程、子进程各自的信息,体现通信效果。
结果:
思考:
①什么是管道?
进程如何利用它进行通信的?
解释一下实现方法。
②修改睡眠时机、睡眠长度,看看会有什么变化。
请解释。
③加锁、解锁起什么作用?
不用它行吗?
答1:
管道:
是指能够连接一个写进程和一个读进程、并允许它们以生产者-消费者方式进行通信的一个共享文件,又称为pipe文件。
由于写进程从管道的入端将数据写入管道,而读进程则从管道的出端读出数据。
进程利用管道进行通信:
将索引结点中的直接地址项作为一个循环队列来管理,为它设置一个读指针,一个写指针,按先进先出的顺序读写。
在该例中,利用fd[2]这个数组来确定读写替换。
执行进程1时,lockf(fd[1],1,0)使得管道写入端加锁,这样就可以进行写入了,write(fd[1],buf,50);/*信息写入管道*/lockf(fd[1],0,0);/*管道写入端解锁*/这样其他进程就能继续进行读写操作了。
然后执行进程2的写入,过程同上。
最后父进程的进行读,read(fd[0],s,50)。
读写之间、写和写之间也是互斥的,多以会加锁。
答2:
修改1号子进程的睡眠时间长度修改为20s后,等待了较长时间,然后出现了下面的结果:
2号子进程先执行了,然后父进程执行。
原因应该是:
父进程进行wait()等待子进程,由于2号子进程的睡眠时间短,于是就先执行完了2号子进程的内容,然后才第二次wait()时才执行1号子进程,最后父进程结尾。
答3:
加锁解锁的作用:
为了使得读、写进程能够互斥的访问pipe文件,每逢继承访问pipe文件前,都需检查该索引节点是否已经上锁,若已经锁住,进程便睡眠等待;否则,将索引节点上锁,然后再进行读、写操作,操作结束后又将该索引节点解锁,并唤醒因该节点上锁而睡眠的进程。
这样就能实现了读、写之间的互斥操作,每次只能有一个进程进行访问pipe文件。
实验7:
编程验证:
实现父子进程通过管道进行通信。
进一步编程,验证子进程结束,由父进程执行撤消进程的操作。
测试父进程先于子进程结束时,系统如何处理“孤儿进程”的。
只要在父进程后加上wait()函数,然后打印“子进程已经结束”,一旦子进程结束,父进
程撤销进程。
当父进程先于子进程终止时.所有子进程的父进程改变为init进程称为进程由init进程领养。
实验8:
编写两个程序,一个是服务者程序,一个是客户程序。
执行两个进程之间通过消息机制通信。
消息标识MSGKEY可用常量定义,以便双方都可以利用。
客户将自己的进程标识(pid)通过消息机制发送给服务者进程。
服务者进程收到消息后,将自己的进程号和父进程号发送给客户,然后返回。
客户收到后显示服务者的pid和ppid,结束。
结果:
思考:
想一下服务者程序和客户程序的通信还有什么方法可以实现?
解释一下你的设想,有兴趣试一试吗。
答:
还可以通过管道操作。
或者用信号量机制来实现。
信号量是一个整形计数器,用来控制多个进程对共享资源的访问。
或者通过消息队列信号机制,通过向消息队列发送信息、接收信息来实现进程间的通信。
实验9:
编程实现软中断信号通信。
父进程设定软中断信号处理程序,向子进程发软中断信号。
子进程收到信号后执行相应处理程序。
结果:
思考:
这就是软中断信号处理,有点儿明白了吧?
讨论一下它与硬中断有什么区别?
看来还挺管用,好好利用它。
答:
软中断发生的时间是由程序控制的,而硬中断发生的时间是随机的。
软中断是由程序调用发生的,而硬中断是由外设引发的。
实验10:
用信号量机制编写一个解决生产者—消费者问题的程序。
研究并讨论:
1.讨论Linux系统进程运行的机制和特点,系统通过什么来管理进程?
系统通过进程控制块(PCB)来管理进程。
PCB主要分为四部分:
(1)进程表项,
(2)U区,(3)系统区表,(4)进程区表。
2.C语言中是如何使用Linux提供的功能的?
用程序及运行结果举例说明。
Linux系统的一种用户接口是程序员用的编程接口,即系统调用。
系统调用的目的
是使用户可以使用操作系统提供的有关进程控制、文件系统、输入输出系统、设备管理、
通信及存储管理等方面的功能,而不必涉及系统内部结构和硬件细节,大大减少用户程序
设计和编程难度。
Linux的系统调用以标准实用子程序形式提供给用户在编程中使用。
一般使用系统调
用应注意以下三点:
(1)函数所在的头文件
(2)函数调用格式,包括参数格式、类型。
(3)返回值格式、类型。
例如:
程序:
在该程序中调用了系统程序"date".
#include
main()
{
execlp("date","date",(char*)0);
return;
}
结果如下:
3.什么是进程?
如何产生的?
举例说明。
答:
进程实体是程序段、相关的数据段和PCB三部分构成的实体。
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
进程的产生:
进程是由创建而产生的,即使进程实体运行时而产生的。
例如:
父进程通过fork()程序创建一个子进程。
4.进程控制如何实现的?
举例说明。
答:
进程控制一般由OS的内核中的原语来实现的。
例如:
用完了I/O设备的进程调用原语weakup(),将等待该事件的进程唤醒。
5.进程通信方式各有什么特点?
用程序及运行结果举例说明。
答:
通信方式有:
(1)共享存储器系统:
相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。
(2)消息传递系统:
源程序可以直接或间接地将消息传送给目标进程。
(3)管道通信系统:
能够连接一个写进程和一个读进程、并允许它们以生产者-消费者方式进行通信的一个共享文件,又称为pipe文件。
由于写进程从管道的入端将数据写入管道,而读进程则从管道的出端读出数据。
6.管道通信如何实现?
该通信方式可以用在何处?
答:
实现:
(1)互斥,即当一个进程正在对pipe执行读/写操作时其它进程必须等待;
(2)同步,指当写(输入)进程把一定数量的数据写入pipe时,便去睡眠等待,知道读(输出)进程取走数据后,再把它唤醒。
当读进程读一空pipe时,也应睡眠等待,直至写进程将数据写入管道后,才将之唤醒;(3)确定对方是否存在,只有确定了对方已存在时,才能进行通信。
用法:
用于链接一个读进程和一个写进程以实现它们之间的通信的一个共享文件。
7.什么是软中断?
软中断信号通信如何实现?
答:
每个信号读对应一个正整数常量,代表同一用户的诸进程之间传送事先约定的信息的类型,用于通知某进程发生了某一场时间。
每个进程在运行时,都要通过信号量机制来检查是否有信号到达。
若有,便中断正在执行的程序,转向与该信号对应的处理程序,以完成对该事件的处理,处理结束后再返回到原来的断电继续执行。
实质上,信号机制是对中断机制的一种模拟。
体会:
在本次实验中,我利用Linux提供的系统调用设计程序,加深了对进程概念的理解,也体会到了系统进程调度的方法和效果,还了解了进程之间的通信方式以及各种通信方式的使用。
虽然在实验的过程中遇到很多困难,但是还好有张涛老师的悉心指点和网上资料的帮助,使我能渡过难关,圆满完成本次实验,在此也向他们说一声感谢。
附录:
(源程序)
1、
#include
#include
#include
#include
intmain(void)
{
printf("processid:
%d\n",getpid());
printf("processgroupid:
%d\n",getpgrp());
printf("callingprocess'srealuserid=%d\n",getuid());
sleep(5);
intchild_pid;
child_pid=fork();
if(child_pid==0)
{
exit
(1);
}
wait(0);
printf("Doyouwanttostoptheprocess?
\n");
charx;
scanf("%c",&x);
if(x=='y')
exit(0);
return0;
}
2、
#include
#include
#include
#include
intmain()
{
inti;
if(fork())
{
i=wait(0);
printf("Itisparentprocess.\n");
printf("Thechildprocess,IDnumber%d,isfinished.\n",i);
}
else
{
printf("Itischildprocess.\n");
sleep(10);
exit(0);
}
}
3、
#include
#include
#include
#include
intmain(void)
{
inti,j;
printf("Mypidis%d,myfather’spidis%d\n",getpid(),getppid());
for(i=0;i<3;i++)
if(fork()==0)
printf("%dpid=%dppid=%d\n",i,getpid(),getppid());
else
{
j=wait(0);
printf("%d:
Thechile%disfinished.\n",getpid(),j);
}
}
4、
#include
#include
#include
#include
intmain(void)
{
intchild_pid1,child_pid2,child_pid3;
intpid,status;setbuf(stdout,NULL);
child_pid1=fork();
if(child_pid1==0)
{
execlp("echo","echo","childprocess1",(char*)0);
perror("exec1error.\n");
exit
(1);
}
child_pid2=fork();
if(child_pid2==0)
{
execlp("date","date",(char*)0);
perror("exec2error.\n");exit
(2);
}
child_pid3=fork();
if(child_pid3==0)
{
execlp("ls","ls",(char*)0);
perror("exec3error.\n");
exit(3);
}
puts("Parentprocessiswaitingforchileprocessreturn!
");
while((pid=wait(&status))!
=-1)
{
if(child_pid1==pid)
printf("childprocess1terminatedwithstatus%d\n",(status>>8));
else
{
if(child_pid2==pid)
printf("childprocess2terminatedwithstatus%d\n",(status>>8));
else
{
if(child_pid3==pid)
printf("childprocess3terminatedwithstatus%d\n",(status>>8));
}
}
}
puts("Allchildprocessesterminated.");
puts("Parentprocessterminated.");
exit(0);
}
5、
#include
#include
#include
#include
intgloba=4;
intmain(void)
{
pid_tpid;
intvari=5;
printf("beforefork.\n");
if((pid=fork())<0)
{
printf("forkerror.\n");
exit(0);
}
else
if(pid==0)
{
globa++;
vari--;
printf("Child%dchangedthevariandgloba.\n",getpid());
}
else
printf("Parent%ddidnotchangedthevariandgloba.\n",getpid());
printf("pid=%d,globa=%d,vari=%d\n",getpid(),globa,vari);
exit(0);
}
6、
#include
#include
#include
#include
#include
intmain(void)
{
inti,r,j,k,l,p1,p2,fd[2];
charbuf[50],s[50];
pipe(fd);
while((p1=fork())==-1);
if(p1==0)
{
lockf(fd[1],1,0);
sprintf(buf,"ChildprocessP1issendingmessages!
\n");
printf("ChildprocessP1!
\n");
write(fd[1],buf,50);lockf(fd[1],0,0);
sleep(5);
j=getpid();
k=getppid();
printf("P1%disweakup.MyparentprocessIDis%d.\n",j,k);
exit(0);
}
else
{
while((p2=fork())==-1);
if(p2==0)
{
lockf(fd[1],1,0);
sprintf(buf,"ChildprocessP2issendingmessages!
\n");
printf("ChildprocessP2!
\n");write(fd[1],buf,50);lockf(fd[1],0,0);
sleep(5);
j=getpid();
k=getppid();
printf("P2%disweakup.MyparentprocessIDis%d.\n",j,k);
exit(0);
}
else
{
l=getpid();
wait(0);
if(r=read(fd[0],s,50)==-1)
printf("Can'treadpipe.\n");
else
printf("Parent%d:
%s\n",l,s);
wait(0);
if(r=read(fd[0],s,50)==-1)
printf("Can'treadpipe.\n");
else
printf("Parent%d:
%s\n",l,s);
exit(0);
}
}
}
7、
8、
服务器程序:
#include
#include
#include
#include
#include
#include
#include
#defineMSGKEY75
structmsgform
{
longmtype;
charmtext[256];
}msg;
intmsgqid;
intmain()
{
inti,pid,*pint;
externcleanup();
for(i=0;i<20;i++)
signal(i,cleanup);
msgqid=msgget(MSGKEY,0777|IPC_CREAT);
for(;;)
{
msgrcv(msgqid,&msg,256,1,0);
pint=(int*)msg.mtext;
pid=*pint;
printf("Server:
receivefrompid%d\n",pid);
msg.mtype=pid;
*pint=getpid();
msgsnd(msgqid,&msg,sizeof(int),0);
}
}
cleanup()
{
msgctl(msgqid,IPC_RMID,0);
exit(0);
}
客户端程序:
#include
#include
#include
#include
#include
#include
#include
#defineMSGKEY75
structmsgform
{
longmtype;
charmtext[256];
};
intmain(void)
{
structmsgformmsg;
intmsgqid,pid,*pint;
msgqid=msgget(MSGKEY,0777);
pid=getpid();
pint=(int*)msg.mtext;
*pint=pid;
msg.mtype=1;
msgsnd(msgqid,&msg,sizeof(int),0);
msgrcv(msgqid,&msg,256,pid,0);
printf("Clint:
receivefrompid%d\n",*pint);
}
9、
#include
#include
#include
#include
#include
#include
#include
#defineMSGKEY75
intmain(void)
{
inti,j,k;
intfunc();
signal(18,func());
if(i=fork())
{
j=kill(i,18);
printf("Parent:
signal18hasbeensenttochild%d,returned%d.\n",i,j);
k=wait(0);
printf("Afterwait%d,Parent%d: