Linux||Linux操作之Shell教程--使用fork创建进程、使用exec替换子进程程序、分析进程的父进程、共享存储区进程通信机制、消息队列实现进程通信(Ubuntu 16.04)
实验1、使用fork创建进程
编写程序,使用系统调用fork()创建如下的进程树,当此程序运行时,在系统中有一个父进程和多个子进程活动,父进程等子进程运行结束后退出。
设置变量X=0,每一个进程在屏幕上显示不同的字符串,父进程的字符串内容要包括自己的学号、姓名、变量X的值;子进程字符串要包括进程PID、子进程序号(子进程1或2或3)、变量X的值,循环显示4次。每次循环X的值加2。记录屏幕上的显示结果,并分析变量X的变化规律
说明:getpid()获得进程的ID。
答:分析规律:可以看出两个子进程执行互不干预,结果具有不可再现性。
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
int main()
{
int i=0,X=0;
pid_t p1,p2;
p1 = fork();//鍒涘缓涓€涓柊process
if(p1 < 0)
{
printf("fork() error");
exit(1);
}
else if(p1 == 0)
{
for(int i = 0;i<4;i++)
{
printf("The PID of child1 = %d, X = %d\n",getpid(),X);
X =X + 2;
}
}
else
{
p2 = fork();//鍒涘缓绗簩涓柊杩涚▼
if(p2 < 0)
{
printf("fork() error");
exit(1);
}
else if(p2 == 0)
{
for(int i = 0;i<4;i++)
{
printf("The PID of child2 = %d, X = %d\n",getpid(),X);
X = X + 2;
}
}
else
{
for(int i = 0;i<4;i++)
{
printf("The PID of Parent = 2021308310230 鏉庡▍濠? X=%d\n",X);
X = X +2;
}
}
}
getchar();//涓轰簡鎷︿綇杩涚▼锛屼笉璁╄繘绋嬬粨鏉?
return 0;
}
实验2、使用exec替换子进程程序
修改任务1编写的程序,将子进程改为独立的程序,父进程创建子进程并进行程序替换,观察程序执行时屏幕出现的现象,并分析原因。
答: printf("child process is running\n"); 未被打印,而是父进程回收到了子进程,正常退出。原因为:
1)进程间具有独立性,子进程的程序替换不会影响到父进程;
2)进程替换成功以后,就不会执行后续代码,即后续的printf语句不会执行;如果替换失败,就会继续执行后续代码。
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int i;
int p, ppid;
printf("begin\n");
p = fork();
if (p == -1)
{
printf("fork() error");
exit(-1);
}
if (p == 0) //child process
{
for (i=0; i<10; i++)
{
ppid=getppid();
printf("This is child. The PID of Parent is %d\n", ppid);
sleep(2);
}
}
else
{
sleep(3);
printf("This is parent. The parent process will stop running\n");
}
printf("The end\n");
return 0;
}
实验3、分析进程的父进程。
在系统中,所有的进程组成一个进程树,但在实际操作中,存在中孤儿进程情况,孤儿进程就是父进程已终止,但是子进程没终止,然后就成孤儿。分析孤儿进程的父进程如何变化。
编写程序,要求如下:
父进程创建子进程;
子进程每2秒输出父进程的ID,循环10次;
父进程3秒后,输出自己的ID,结束进程;
分析程序的运行结果,重点说明子进程的父进程如何变化的。
答:从执行结果来看,此时由pid == 3729父进程创建的子进程,其输出的父进程pid == 1435,说明当其为孤儿进程时已被回收,最终并不会占用资源。
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#define KEY 100
#define SIZE 10
struct msgform{
long mtype;
char mtext[100];
}msg,smsg;
int main()
{
int num[SIZE];
int shmid,i;
char *shmaddr;
int sum=0;
int *p;
int x;
int msgid;
//use shm to deliver data
shmid=shmget(KEY,sizeof(int)*SIZE,0777|IPC_CREAT);
if(shmid==-1){
printf("shmget error!\n");return 0;
}
shmaddr=shmat(shmid,NULL,0);
p=(int *)shmaddr;
//input data
for(i=0;i<SIZE;i++){
printf("杈撳叆绗?%d涓暟瀛?",i+1);
scanf("%d",p++);
}
p=p-5;
//compute sum of the five numbers
for(i=0;i<SIZE;i++){
sum+=*p++;
}
printf("SUM:%d\n",sum);
//use msg queue to deliver result
msgid=msgget(KEY+1,0777|IPC_CREAT);
if(msgid==-1){
printf("msgget error!");return 0;
}
if((x=fork())==-1){
printf("fork error!\n");return 0;
}
if(x==0){// child process
int sum_power2=0;
p=p-5;
for(i=0;i<SIZE;i++){
int temp=*p++;
sum_power2=sum_power2+temp*temp;
}
//send data
smsg.mtype=1;
sprintf(smsg.mtext,"%d",sum_power2);
if(msgsnd(msgid,&smsg,100,0)==-1){
printf("msgsnd error!\n");return 0;
}
return 0;
}else{//parent process
wait(NULL);
//receive data
msgrcv(msgid,&msg,100,1,0);
printf("square sum:%s\n",msg.mtext);
}
//release resources
msgctl(msgid,IPC_RMID,0);
shmdt(shmaddr);shmctl(shmid,IPC_RMID,0);return 0;
}
实验4、共享存储区机制进程通信
编程实现生产者和消费者共享存储区功能。生产者随机产生10个整型数据,写入共享存储区;消费者读出数据,并进行平方和平方根运算后输出。使用系统调用shmget()、shmat()、sgmdt()、shmctl()等, 实现程序。
生产者:
消费者:
实验5、消息队列实现进程通信
修改上面的程序。父进程从键盘上接受10个数据,对其求和sum1,子进程求这10个数平方和sum2,使用消息队列方式将结果传给父进程,父进程计算sum1+sum2,打印结果。
实验心得与体会
通过本次实验了解和掌握了系统中进程创建和执行的方法,以及使用fork系统调用的用法;进程间具有独立性,子进程的程序替换不会影响到父进程;并且了解到了Linux系统中进程通信的基本原理。Linux系统的进程通信机构(IPC) 允许在任意进程间大批量地交换数据,熟悉了Linux支持的消息通讯机制和共享内存机制。
在共享存储器系统中,相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。据此,又可把它们分成以下两种类型:
(1)基于共享数据结构的通信方式。在这种通信方式中,要求诸进程公用某些数据结构,借以实现诸进程间的信息交换,如在生产者-消费者问题中的有界缓冲区。操作系统仅提供共享存储器,由程序员负责对公用数据结构的设置及对进程间同步的处理。这种通信方式仅适于传递相对少量的数据,通信效率低下,属于低级通信。
(2)基于共享存储区的通信方式。为了传输大量数据,在内存中划出了一块共享存储区域,诸进程可通过对该共享区的读或写交换信息,实现通信,数据的形式和位置甚至访问控制都是由进程负责,而不是OS。这种通信方式属于高级通信。需要通信的进程在通信前,先向系统申请获得共享存储区中的一个分区,并将其附加到自己的地址空间中,便可对其中的数据进行正常读、写,读写完成或不再需要时,将其归还给共享存储区。
实验指导
实验3参考代码
#include <sys/types.h>
#include <stdio.h>
int main()
{ int i;
pid_t pid,ppid;
printf("begin\n");
pid = fork();
if(pid == -1)
{ printf("fork error");
exit(-1);
}
if (pid == 0) //child process
{ for (i=0; i<5; i++)
{ ppid=getppid();
printf("parent Pid:%d\n", ppid);
sleep(2);
}
}
else
{ sleep(3);
printf("The parent process will stop running\n");
}
printf("end");
}