一、實驗目的
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進程控制有了更加深入的理解,并且進一步培養了自己的編程能力和解決問題的能力。