实验一 进程控制实验
一、实验目的
1、掌握进程的概念,理解进程和程序的区别。
2、认识和了解并发执行的实质。
3、学习使用系统调用fork()创建新的子进程方法,理解进程树的概念。
4、学习使用系统调用wait()或waitpid()实现父子进程同步。
5、学习使用getpid()和getppid()获得当前进程和父进程的PID号。
6、掌握使用exec簇函数实现进程映像更换的方法。
7、了解系统编程,学习父进程如何通过创建一个子进程来完成某项特定任务的方法。
二、实验内容
1、进程的创建
编写一段程序,使用系统调用fork( )创建两个子进程,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b” 和“c”。试观察记录屏幕上的显示结果,并分析结果。(1分)
<参考程序>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{ int p1, p2;
while((p1=fork())==-1);
if(p1==0)
printf("b ");
else
{ while((p2=fork())==-1);
if(p2==0)
printf("c ");
else
printf("a ");
}
return 0;
}
执行结果及结果分析:
shell窗口也是一个进程,所以看到4个进程信息,一个父进程,二个子进程和一个shell进程。先是父进程输出a, 子进程1输出b,之后是子进程2输出c,之后输出shell窗口提示符。
- 修改第一题,在父进程中显示当前进程识别码,在每个子进程中显示当前进程识别码和父进程识别码,运行程序查看结果,分析运行结果。
试做:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{ int p1, p2;
while((p1=fork())==-1);
if(p1==0) //p1子进程
printf("b: pid=%d ppid=%d\n",getpid(),getppid());
else //父进程
{ while((p2=fork())==-1);
if(p2==0)//p2子进程
printf("c: pid=%d ppid=%d\n",getpid(),getppid());
else //父进程
printf("a: pid=%d\n",getpid());
}
return 0;
}
运行结果:
结果分析:
先打印父进程 然后创建第一个子进程p1,然后父进程创建第二个子进程p2,如果不想让父进程在子进程结束之前结束,可以用wait(0)让父进程等待子进程结束,之后父进程再结束。2个wait(0)是因为有2个子进程要等待。
- 改进第二题,使父进程等待两个子进程结束之后再结束。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{ int p1, p2;
while((p1=fork())==-1);
if(p1==0)
printf("b:pid=%d ppid=%d\n",getpid(),getppid());
else
{ while((p2=fork())==-1);
if(p2==0)
printf("c:pid=%d ppid=%d\n",getpid(),getppid());
else
{
wait(0);
wait(0);
printf("a:pid=%d\n",getpid());
}
}
return 0;
}
运行结果:
从上述子进程和父进程识别码可以看出父子进程之间的关系。
图1进程树的参考程序:
#include<stdio.h>
#include<unistd.h>
int main()
{
int p1,p2,p3;
while((p1=fork())== -1);
if(p1==0)
{
while((p2=fork())==-1);
if(p2==0)
{
while((p3=fork())==-1);
if(p3==0) //p3子进程
printf(" d,Mypid=%d, myppid=%d\n", getpid(), getppid());
else //p2子进程
printf(" c,Mypid=%d, myppid=%d\n", getpid(), getppid());
}
else //p1子进程
printf(" b,Mypid=%d, myppid=%d\n", getpid(), getppid());
}
else //主进程
printf(" a,Mypid is %d\n", getpid());
getchar();
}
编译及执行程序:
结果截屏:
- 模仿第2题,按图2进程树编写程序,给出编译及执行过程和结果截屏。(1分)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main() {
pid_t a, b, c, d, e;
a = fork();
if (a < 0) {
perror("fork");
exit(1);
}
if (a == 0) {
printf("I am process A\n");
b = fork();
if (b < 0) {
perror("fork");
exit(1);
}
if (b == 0) {
printf("I am process Bn");
c = fork();
if (c < 0) {
perror("fork");
exit(1);
}
if (c == 0) {
printf("I am process C\n");
} else {
printf("I am process B, and my child is process C\n");
wait(NULL);
}
} else {
printf("I am process A, and my child is process B\n");
wait(NULL);
}
} else {
d = fork();
if (d < 0) {
perror("fork");
exit(1);
}
if (d == 0) {
printf("I am process Dn");
e = fork();
if (e < 0) {
perror("fork");
exit(1);
}
if (e == 0) {
printf("I am process E\n");
} else {
printf("I am process D, and my child is process E\n");
wait(NULL);
}
} else {
printf("I am process A, and my child is process D\n");
wait(NULL);
}
}
return 0;
}
结果截屏:
- 分析程序,给出编译及执行过程和结果截屏。(2分)
- #include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include <sys/wait.h>
int main()
{ int child,p;
while((child=fork())==-1);
if(child==0) //子进程下
{ printf("In child: sleep for 10 seconds and then exit. \n");
sleep(10);
exit(0);
}
else //父进程下
{ do
{ p=waitpid(child,NULL,WNOHANG); //非阻塞式等待子进程结束
if(p==0)
{ printf("In father: The child process has not exited.\n");
sleep(1);
}
}while(p==0);
if(p==child)
{ printf("Get child exitcode then exit!\n");}
else
{ printf("Error occured!\n");}
}
exit(0);
}
编译及执行过程和运行结果截屏:
分析程序功能:
该程序的功能是创建一个子进程,并在子进程中睡眠10秒后退出,父进程不阻塞地等待子进程结束并获取退出状态码,然后退出。
2) #include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
main()
{ int child,p;
while((child=fork())==-1);
if(child==0) //子进程下
{ execl("/home/student/welcome.out","",NULL);
exit(0);
}
else //父进程下
{ p=waitpid(child,NULL,0); //阻塞式等待子进程结束
if(p==child)
printf("Get child exitcode then exit!\n");
else
printf("Error occured!\n");
}
exit(0);
}
子进程要加载程序的源程序welcome.c:
#include<stdio.h>
main()
{ printf("Hello! This is another process.\n");}
编译及执行过程和运行结果截屏:
5. 编程创建2个子进程,子进程1运行指定路径下的可执行文件(如:/home/student/welcome.out),子进程2暂停10s之后退出,父进程先用阻塞方式等待子进程1的结束,然后用非阻塞方式等待子进程2的结束,待收集到二个子进程结束的信息,父进程就返回。(2分)
参考程序框架:
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/wait.h>
int main()
{ int child1,child2,p;
while((child1=fork())==-1);
if(child1==0) //child1置换进程
{
execl("/home/gzh0624/welcome.o","welcome.o",NULL);
perror("execl");
exit(0);
}
else //father
{ while((child2=fork())==-1);
if(child2==0) //child2暂停10秒退出
{
sleep(10);
exit(0);
}
else //father等待子进程结束
{ p=waitpid(child1,NULL,0);//阻塞式等待子进程1结束
if(p==child1)
printf("Get child1 exitcode then exit!\n");
else
printf("Error occured!\n");
do
{p=waitpid(child2,NULL,WNOHANG);//非阻塞式等待子进程2结束
if(p==0)
{
printf("In father: The child2 process has not exited.\n");
sleep(1);
}
}while(p==0);
if(p==child2)
printf("Get child2 exitcode then exit!\n");
else
printf("Error occured!\n");
}
}
exit(0);
}编译及执行过程:
结果截屏:
分析程序功能:
程序的功能是创建两个子进程,其中一个子进程调用另一个可执行文件welcome.o,另一个子进程暂停10秒后退出。父进程等待子进程结束,并打印相应的信息。
- 编写一个简易的shell解释程序。其运行原理是:当命令行上有命令需要执行时,shell进程获得该命令,然后创建子进程,让子进程执行该命令,shell进程等待子进程退出,之后继续等待命令行上的命令周而复始。(附加题)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define MAX_CMD_LENGTH 100
void execute_command(char *command) {
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed\n");
exit(1);
} else if (pid == 0) { // Child process
char *args[MAX_CMD_LENGTH];
// Split the command into arguments
char *token = strtok(command, " ");
int i = 0;
while (token != NULL) {
args[i++] = token;
token = strtok(NULL, " ");
}
args[i] = NULL;
// Execute the command
execvp(args[0], args);
// If execvp returns, there was an error
fprintf(stderr, "Command not found: %s\n", args[0]);
exit(1);
} else { // Parent process
int status;
waitpid(pid, &status, 0);
}
}
int main() {
char command[MAX_CMD_LENGTH];
while (1) {
printf("shell> ");
fgets(command, MAX_CMD_LENGTH, stdin);
// Remove new line character
command[strcspn(command, "\n")] = 0;
// Check if user wants to exit
if (strcmp(command, "exit") == 0) {
break;
}
execute_command(command);
}
return 0;
}
编译及执行过程:
结果截屏:
三、实验总结和体会
在本次实验中,我们深入学习并实践了Linux进程控制的相关知识和技术。通过对进程创建、销毁、管理等操作的实践,我对Linux进程控制有了更加深入的理解和掌握。
首先,通过实验,我学会了如何创建新的进程。我们使用了fork()系统调用,它可以创建一个与原进程几乎完全相同的新进程,包括代码、数据、运行时堆栈等。我们还学习了如何在新的进程中执行不同的代码,以实现不同的功能。
其次,我学会了如何控制进程的执行。通过实验中的信号处理,我们可以向进程发送信号,从而实现对进程的控制。我们学习了不同的信号及其对应的处理方式,如SIGINT、SIGKILL、SIGSTOP等。通过实验,我更加了解了信号的工作原理和使用方法。
此外,我还学会了如何管理进程的资源。通过实验中的共享内存和进程间通信,我们可以实现不同进程之间的数据传递和共享。我们学习了共享内存的创建、映射和销毁等操作,以及如何使用信号量进行进程间的同步和互斥。
通过这次实验,我深刻体会到了进程控制在操作系统中的重要性。进程是操作系统中最基本的运行单位,掌握好进程控制的知识和技术对于编程和系统管理都非常重要。通过实践,我对Linux进程控制有了更加深入的理解,并且进一步培养了自己的编程能力和解决问题的能力。