操作系统实验:进程控制

【实验目的】

实验目的:

1、掌握进程的概念,明确进程和程序的区别。

2、认识和了解并发执行的实质。

3、分析进程争用资源的现象,学习解决进程互斥的方法。

实验要求:

用C语言编写程序,模拟实现创建新的进程,掌握进程并发执行的实质,实现创建进程树。

【实验内容】

1、进程的创建

编写一段程序,使用系统调用 fork( )创建两个子进程,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b” 和“c”。试观察记录屏幕上的显示结果,并分析原因。

2、 修改已编写的程序,将每个进程的输出由单个字符改为一句话,再观察程序执行时屏幕上出现的现象,并分析其原因。

3、编写程序创建进程树如图 1 和图 2 所示,在每个进程中显示当前进程识别码和父进程识别码。

【实验环境】(含主要设计设备、器材、软件等)

Pc电脑一台,Linux环境

【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等) 
1.fork()函数创建一个新的进程,这个新的进程是当前进程的一个复制品。创建一个子进程p1。如果创建成功,p1在父进程中的值为子进程的ID,在子进程中的值为0。然后检查p1的值。如果p1的值为0,说明当前进程是子进程p1,并打印字符’b’。如果p1的值不为0,说明当前进程是p1的父进程。p1的父进程创建另一个子进程p2。同理检查p2的值。如果p2的值为0,说明当前进程是子进程p2,并打印字符’c’。如果p2的值不为0,说明当前进程是p2的父进程,并打印字符’a’。

#include <stdio.h>
#include <unistd.h>

int main() {
    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');
        }
    }

    return 0;
}

2.与第1问类似,只是将每个进程的输出由单个字符改成了一句话。

#include <stdio.h>
#include <unistd.h>

int main() {
    int p1, p2;
    while ((p1 = fork()) == -1);
	if (p1 == 0) {
	    printf("child process p1 here b\n");
	} else {
	    while ((p2 = fork()) == -1);
	    if (p2 == 0) {
	        printf("child process p2 here c\n");
	    } else {
	        printf("father process a\n");
	    }
	}
    return 0;
}

3.1 图1进程树是由a进程创建b进程,b进程创建c进程,c进程再创建d进程。创建子进程的原理同1类似。首先创建了一个父进程a,a创建子进程b,父进程a等待子进程b执行完毕后输出进程a的信息。接着,子进程b创建子进程c,父进程b等待子进程c执行完毕后输出进程b的信息。最后,子进程c创建子进程d,父进程c等待子进程d执行完毕后输出进程c的信息,而子进程d则直接输出自己的信息。每个进程在完成相应的子进程等待后输出自身的进程ID和父进程ID。

流程图:

#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    int p_b, p_c, p_d;
    while((p_b=fork())==-1) ;
    if(p_b>0)
    {
    	wait(0);
        printf("process a pid is %d, a ppid is %d\n", getpid(), getppid());
    } 
    else 
    {
        while((p_c=fork()) == -1) ;
        if(p_c>0)
        {
        	wait(0);
            printf("process b pid is %d, b ppid is %d\n", getpid(), getppid());
        }
        else
        {
            while((p_d=fork()) == -1) ;
            if(p_d>0)
            {
            	wait(0);
                printf("process c pid is %d, c ppid is %d\n", getpid(), getppid());
            }
            else
            {
                printf("process d pid is %d, d ppid is %d\n", getpid(), getppid());
            }
        }
    }
    return 0;
}

3.2 同理可得图2进程树是由父进程a同时建立b,d子进程,再由b进程创建c子进程,d子进程创建e子进程。首先创建了一个父进程a,a同时创建两个子进程b和d。父进程a等待全部子进程执行完毕后输出进程a的信息。子进程b在创建后,再创建一个子进程c,父进程b等待子进程c执行完毕后输出进程b的信息。子进程c直接输出自身的信息。另一方面,子进程d在创建后,再创建一个子进程e,父进程d等待子进程e执行完毕后输出进程d的信息。子进程e直接输出自身的信息。每个进程在完成相应的子进程等待后输出自身的进程ID和父进程ID。

流程图:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    int p_b, p_d, p_c, p_e, status;

    // 创建 b 和 d 两个子进程
    while ((p_b = fork()) == -1 || (p_d = fork()) == -1);

    if (p_b > 0 && p_d > 0) {
        // 父进程 a
        waitpid(p_b, &status, 0);
        waitpid(p_d, &status, 0);
        printf("process a pid is %d, a ppid is %d\n", getpid(), getppid());
    } else if (p_b >0) {
        // 子进程 b
        while ((p_c = fork()) == -1);
        if (p_c > 0) {
            waitpid(p_c, &status, 0);
            printf("process b pid is %d, b ppid is %d\n", getpid(), getppid());
        } else {
            //  c
            printf("process c pid is %d, c ppid is %d\n", getpid(), getppid());
        }
    } else if (p_d >0) {
        // d
        while ((p_e = fork()) == -1);
        if (p_e > 0) {
            waitpid(p_e, &status, 0);
            printf("process d pid is %d, d ppid is %d\n", getpid(), getppid());
        } else {
            // 子进程 e
            printf("process e pid is %d, e ppid is %d\n", getpid(), getppid());
        }
    }

    return 0;
}

【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)

1.程序输出为’bac’,其中’b’为p1的输出,’a’为父进程的输出,’c’为p2的输出。

首先在执行语句p1 = fork()时,创建了子进程p1,此时打印’b’;

然后在执行语句p2 = fork()时,创建了子进程p2,此时打印’c’;

最后父进程运行,打印’a’。

程序运行多次,发现结果不相同,输出有"cab", “abc”, "acb"等。这是因为在执行到fork()之前,只有一个进程在运行,执行到fork()后,父进程产生子进程,父子进程并行执行并没有先后顺序之分,所以使得运行结果不同。

2.输出由第1问的单个字符变成了一句话。程序运行多次,发现结果不相同,进程的执行顺序不同,原理和第1问类似。

3.1 进程d的父进程ppid是388,即进程c;进程c的父进程ppid是387,即进程b;进程b的父进程ppid是386即进程a。由此可以看出进程a是进程b的父进程,进程b是进程c的父进程,进程c是进程d的父进程。

3.2 进程c的父进程ppid是454,即进程b;进程e的父进程ppid是453,即进程d;进程d和进程b的父进程ppid都是452,即进程a;因此进程a是进程b和d的父进程,进程b是进程c的父进程,进程d是进程e的父进程。程序运行多次,结果不相同,进程的执行顺序不同。

【思考题】
1、系统是怎样创建进程的?

一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

2、当首次调用新创建进程时,其入口在哪里?

fork调用成功后,子进程与父进程并发执行相同的代码。但由于子进程也继承了父进程的程序指针,所以子进程是从fork()后的语句开始执行(也就是新进程调用的入口)。

3、当前运行的程序(主进程)的父进程是什么?
调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程
感想:通过这次实验对理论课上所讲的fork()函数以及软中断等方法有了更深刻的认识,更深刻的懂得了fork()的继承性,对软中断的各个函数有了了解,进一步了解信号与中断的联系与区别。

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实验目的: 1. 理解进程的概念与特点; 2. 掌握进程创建、撤销和阻塞唤醒的实现方法; 3. 掌握进程调度算法的实现; 4. 掌握进程同步与互斥的实现方法。 实验内容: 1. 进程控制块(PCB)的定义 进程控制块是操作系统的一种数据结构,用于描述进程的基本信息和运行状态。它包含了进程的标识符、程序计数器、寄存器、内存分配情况、打开文件列表、进程优先级、进程状态等信息。PCB 是操作系统内核的重要组成部分,是操作系统实现进程管理和调度的关键数据结构。 2. 进程创建、撤销和阻塞唤醒 进程创建是指在操作系统新建一个进程并为其分配资源的过程。进程的撤销是指终止一个正在运行的进程并回收其占用的资源。进程的阻塞是指在执行过程暂停进程的执行,等待某种条件满足后再继续执行。进程的唤醒是指在进程阻塞期间,当等待的条件满足后,操作系统会将进程重新唤醒并恢复其执行。 3. 进程调度算法 进程调度算法是指操作系统用于决定哪个进程能够占用 CPU 资源的算法。常见的进程调度算法有先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)、优先级调度等。 4. 进程同步与互斥 进程同步与互斥是指在多进程环境下,为了保证进程之间的合作和协调,需要采用一些机制来保证进程之间的正确性和互不干扰。常用的进程同步与互斥方法有信号量、互斥锁、条件变量等。 实验要求: 1. 完成进程创建、撤销和阻塞唤醒的实现; 2. 实现先来先服务(FCFS)和时间片轮转(RR)两种进程调度算法; 3. 实现基于信号量的进程同步与互斥。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值