【操作系统】进程管理

实验2 进程控制

一、实验目的

    1、加深对进程的理解,掌握进程和程序的区别。

    2、进一步认识进程并发执行的实质。

    3、了解Linux系统中进程创建和执行的方法,掌握fork系统调用的用法。

4、了解Linux系统中进程通信的基本原理。Linux系统的进程通信机构(IPC) 允许在进程之间大批量地交换数据,通过实验掌握Linux支持的消息通讯机制和共享内存机制。

二、实验准备

(1)阅读进程PCB的数据结构,理解进程PCB的作用。Linux系统的进程控制块PCB用一个称为task-struct的结构体来描述。

http://www.cnblogs.com/hanxiaoyu/p/5549212.html

(2)Linux系统下进程相关命令练习,命令包括:ps,pstree,top, kill等。

三、实验内容

1、使用fork创建进程

编写程序,创建如下图的进程树,当此程序运行时,在系统中有一个父进程和多个子进程活动,父进程等子进程运行结束后退出。

在程序中,设置变量count=0,每一个进程在屏幕上显示不同的字符串,父进程的字符串内容要包括:父进程、PID、自己的学号、姓名、变量count的值;子进程字符串要包括:PID、子进程序号(子进程1或子进程2)、变量count的值,循环显示5次。变量count按一定的规律变化。记录屏幕上的显示结果,并分析变量count的变化规律。

getpid()获得进程的ID。

说明:学号最后一位为奇数,创建图1的进程树;若为偶数,创建图2的进程树。

    2、使用exec替换子进程程序

修改任务1编写的程序,将子进程改为独立的程序,父进程创建子进程,并进行程序替换,观察程序执行时屏幕出现的现象,并分析原因。

    3、分析进程的父进程

在系统中,所有的进程组成一个进程树,但在实际操作中,存在中孤儿进程情况,孤儿进程就是父进程已终止,但是子进程没终止,然后就成孤儿。分析孤儿进程的父进程如何变化。

编写程序,要求如下:

父进程创建子进程;

子进程每2秒输出父进程的ID,循环10次;

父进程3秒后,输出自己的ID,结束进程;

       分析程序的运行结果,重点说明子进程的父进程如何变化的。

          

4、共享存储区机制进程通信

编写生产者和消费者通过共享存储区通信的程序。生产者随机产生10个整型数据,写入共享存储区;消费者读出数据,并进行平方和平方根运算后输出。使用系统调用shmget()、shmat()、sgmdt()、shmctl()等, 实现程序。

5、消息队列实现进程通信

编写生产者和消费者通过消息队列通信的程序。父进程从键盘上接受10个数据,对其求和sum1,子进程求这10个数平方和sum2,使用消息队列方式将结果传给父进程,父进程计算sum1+sum2,打印结果。

四、测试数据与实验结果分析

1:使用fork创建进程

(1)创建exp2-1.c文件:vim exp2-1.c


(2)按下i键,进行文件编写,底部变为【插入】字样

(3)编写程序代码

(4)按下escape键,进入命令模式,底部插入字样消失

(5)保存文件编写内容::wq

(6)使用gcc生成可执行文件:gcc exp2-1.c

(7)运行文件:./a.out

【分析】

    父进程和子进程都有各自的地址空间,它们之间的count变化是独立的。按照if-else条件的判断顺序,创建的子进程1先执行,而子进程2后执行。最后由于父进程处有wait(NULL)操作,因此父进程会等所有子进程结束后执行。因此,终端依次输出子进程1在5个循环内的count自增变化、子进程2在5个循环内的count自减变化、父进程的count初始值内容。

2:使用exec替换子进程程序


(1)在文件界面,通过复制粘贴操作,创建exp2-2.c文件

(2)打开exp2-2.c文件:vim exp2-2.c

(3)编辑exp2-2.c文件(父进程文件)

(4)创建并编辑child-1.c文件(子进程1文件):vim child-1.c

(5)创建并编辑child-2.c文件(子进程2文件):vim child-2.c

(6)编译子进程文件和父进程文件,并生成【.o】文件:

子进程1文件:gcc -o child-1 child-1.c

子进程2文件:gcc -o child-2 child-2.c

父进程文件:gcc -o exp2-2 exp2-2.c

(7)运行父进程的可执行文件:./exp2-2

【分析】

    父进程创建子进程后,子进程使用 execlp 函数将自身的程序替换为child-1.o和child-2.o的可执行文件。execlp函数会加载并执行指定的【.o】文件,因此子进程的代码会被替换。exec函数族可以在运行时替换进程的程序,使得进程可以执行不同的程序,有利于实现进程间通信和控制。

3:分析进程的父进程


(1)创建exp2-3.c文件,并编写相应的程序

(2)编译exp2-3.c文件:gcc -o exp2-3 exp2-3.c

(3)运行可执行文件:./exp2-3


    等待文件运行,并输出最终的结果。

【分析】

    在上述的程序中,父进程创建了子进程,并在3秒后终止。子进程会循环输出其父进程的PID,共执行10次,每次间隔2秒。父进程首先输出自己的PID。子进程在父进程终止后仍然继续执行,并且在每次循环中输出父进程的PID。上述结果体现了Linux 进程管理的一个重要特性:当父进程终止后,子进程的父进程会变为init进程,以确保子进程不会成为孤儿进程。

4:共享存储区机制进程通信

(1)创建producer2-4.c文件和consumer2-4.c文件,并编写相应的程序


producer2-4.c:

consumer2-4.c:

(2)编译producer2-4.c文件和consumer2-4.c文件

生产者文件:gcc -o producer2-4 producer2-4.c

消费者文件:gcc -o consumer2-4 consumer 2-4.c -lm

(3)运行生产者的可执行文件:./ producer2-4

(4)运行消费者的可执行文件:./ consumer2-4

【分析】

在上述的程序中,使用shmget创建共享内存段,然后使用shmat将共享内存连接到生产者和消费者进程的地址空间。生产者将数据写入共享内存中,而消费者从共享内存中读取数据。最后使用shmdt分离共享内存,并使用shmctl删除共享内存。通过运行上述程序,可以观察到生产者和消费者之间的数据共享和通信,学习并验证共享内存机制的工作原理。

5:消息队列实现进程通信

(1)创建exp2-5.c文件,并编写相应的程序

(2)编译exp2-5.c文件:gcc -o exp2-5 exp2-5.c

(3)运行可执行文件:./ exp2-5

根据打印字符的提示,输入用户想要计算的数字。

最后等待运行结果total sum。


【分析】

    在上述的程序中,使用msgget创建消息队列,实现了父子进程之间的通信,父进程向子进程发送数据,子进程进行计算并返回结果。这种通信方式可以在不同的进程之间实现异步的数据交换,非常适合需要协调不同任务的情况。

五、实验心得与体会

1:基本的进程创建、终止和状态管理操作:学习了如何使用fork函数创建子进程,以及如何通过wait函数等待子进程的终止。

2:消息队列的灵活性:消息队列是一种异步通信方式,适用于需要松散耦合的进程通信。消息队列允许进程在不同的时间发送和接收消息,并实现进程间的解耦合。

3:exec 函数族的用途:exec 函数族(如 execl、execv)允许进程在运行时替换自身的程序。通过调用exec函数族,可以实现在父进程中创建子进程并替换为不同的程序。

4:孤儿进程与 init 进程:孤儿进程是指其父进程已经终止的子进程。在 Linux 系统中,孤儿进程会被 init 进程领养,init 进程成为其新的父进程,以确保孤儿进程不会成为僵尸进程。

5:进程通信的应用:进程通信是多进程操作系统中的重要概念,允许不同的进程之间共享数据、协同工作以及相互通信。不同的通信方式,如共享内存、消息队列、管道等,可以用于不同的应用场景。每种通信方式都有其特定的适用场景,根据需求选择合适的通信方式可以提高程序的性能和可维护性。

6:如果出现下图的报错,可以采用以下的方法进行解决——在编译时显式链接数学库,即在编译命令中添加【-lm】标志来链接数学库。在大多数情况下,数学库是libm。

六、实验源程序的代码

    由于未配置ssh,因此无法将服务器上的代码文件拖出,因此以下部分采用手动敲代码的方法,如有错误还请查看实验过程中的截图。

实验1

【exp2-1.c】

#include<unistd.h>

#include<sys/wait.h>

#include <sys/types.h>

int main(){

       int i,count=0;

       pid_t pid1,pid2;

       pid1=fork();

       if(pid1==0){

              for(i=0;i<5;i++){

                     printf("Child-1 pid=%d count=%d\n",getpid(),count);

                     count++;

              }

       }

       else if(pid1>0){

              pid2=fork();

              if(pid2==0){

                     for(i=0;i<5;i++){

                            printf("Child-2 pid=%d count=%d\n",getpid(),count);

                            count--;

                     }

              }

              else if(pid2>0){

                     wait(NULL);

                     printf("Parent pid=%d count=%d\n",getpid(),count);

                     count+=2;

                     printf("Student:Xia Wanke,ID:2020301010225");

              }

       }

       return 0;

}

实验2

【exp2-2.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <stdio.h>

int main(){

       int i,count=0;

       pid_t pid1,pid2;

       if(pid1==0){

              execlp("./child-1",0);

       }

       else if(pid1>0){

              pid2=fork();

              if(pid2==0){

                     execlp("./child-2",0);

              }

              else if(pid2>0){

                     wait(NULL);

                     printf("Parent pid=%d count=%d\n",getpid(),count);

                     count+=2;

                     printf("Student:Xia Wanke,ID:2020301010225");

              }

       }

       return 0;

}

【child-1.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <stdio.h>

int main(){

       int count=0,i;

       for(i=0;i<5;i++){

              printf("Child-1 pid=%d count=%d\n",getpid(),count);

              count++;

       }

       return 0;

}

【child-2.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <stdio.h>

int main(){

       int count=0,i;

       for(i=0;i<5;i++){

              printf("Child-2 pid=%d count=%d\n",getpid(),count);

              count--;

       }

       return 0;

}

实验3

【exp2-3.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <stdio.h>

int main(){

       int i;

       pid_t pid;

       pid=fork();

       printf("begin\n");

       if(pid==0){

              for(i=0;i<10;i++){

                     printf("This is child. PID of the parent is %d\n",getppid());

                     sleep(2);

              }

       }

       else if(pid>0){

              sleep(3);

              printf("This is parent. The parent process will stop running\n");

       }

       printf("end\n");

       printf("Student:Xia Wanke,ID:2020301010225");

       return 0;

}

实验4

【producer2-4.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/shm.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <stdio.h>

#define KEY 75

#define MEM 1024

int main(){

       int shmid,*p,i;

       shmid=shmget(KEY,MEM,0777|IPC_CREAT);

       char *shmaddr;

       shmaddr=shmat(shmid,0,0);

       p=(int*)shmaddr;

      

       for(i=0;i<10;i++){

              *p=rand()%100;

              sleep(1);

              printf("%d\n",*p);

              p++;

       }

       sleep(5);

       shmdt(shmaddr);

       printf("end\n");

       return 0;

}

【consumer2-4.c】

#include <stdlib.h>

#include <unistd.h>

#include <sys/shm.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <stdio.h>

#include <math.h>

#define KEY 75

#define MEM 1024

int main(){

       int mid,*q,*t,i;

       char *maddr;

       mid=shmget(KEY,MEM,0777);

       maddr=shmat(mid,0,0);

       q=(int*)maddr;

       sleep(12);

       for(i=0;i<10;i++){

              printf("%d's square: %d, sqrt: %f\n",(*q),(*q)*(*q),sqrt(*q));

              q++;

       }

       shmdt(maddr);

       shmctl(mid,IPC_RMID,0);

       return 0;

}

实验5

【exp2-5.c】

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <unistd.h>

// 消息队列结构体

struct msg_buffer {

    long mtype; // 消息类型

    int result; // 消息内容

};

int main() {

    int sum1 = 0;

    int sum2 = 0;

    int i;

    // 创建消息队列

    int msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);

    if (msgid == -1) {

        perror("msgget");

        exit(1);

    }

    // 创建子进程

    pid_t pid = fork();

    if (pid == -1) {

        perror("fork");

        exit(1);

    }

    if (pid == 0) { // 子进程

        // 接收父进程发送的10个数,计算平方和

        struct msg_buffer msg;

        for (i = 0; i < 10; i++) {

            msgrcv(msgid, &msg, sizeof(int), 1, 0);

            sum2 += msg.result* msg.result;

        }

        // 将平方和发送给父进程

        msg.mtype = 2;

        msg.result = sum2;

        msgsnd(msgid, &msg, sizeof(int), 0);

    } else { // 父进程

        // 从键盘输入10个数,计算和并发送给子进程

        struct msg_buffer msg;

        for (i = 0; i < 10; i++) {

            printf("Enter number %d: ", i + 1);

            scanf("%d", &msg.result);

            sum1 += msg.result;

            msg.mtype = 1;

            msgsnd(msgid, &msg, sizeof(int), 0);

        }

        // 等待子进程发送结果

        msgrcv(msgid, &msg, sizeof(int), 2, 0);

        // 计算总和并打印结果

        int total_sum = sum1 + msg.result;

        printf("Total sum: %d\n", total_sum);

        // 删除消息队列

        msgctl(msgid, IPC_RMID, NULL);

    }

    return 0;

}

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MorleyOlsen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值