操作系统:实验一进程控制实验

一、实验目的

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;

}

执行结果及结果分析:

首先父进程会fork()创建一个新的子进程也就是b进程,当b进程运行到if(p1==0)语句时就会打印出“b ”,然后父进程会进入else语句,再创建一个子进程c,由于在父进程中p2>0所以进入else语句,打印“a ”,在创建出来的c进程中,p2=0打印出“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)    //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;

}

父进程a首先创建出一个子进程p1,在p1进程中p1=0,打印出p1的pid和ppid,可以看出b的父进程为a,然后主进程运行到else语句中,再次创建一个子进程p2,在p2进程中p2=0,打印出c的pid和ppid,可以看出,c的父进程为a,主进程运行到else语句,打印出自己的pid。

改进上题,使父进程等待两个子进程结束之后再结束。(1分)

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.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;

}

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

图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();

}

编译及执行程序:

结果截屏:

 

3.模仿第2题,按图2进程树编写程序,给出编译及执行过程和结果截屏。(1分)

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>



int main() {

    int p1, p2, p3, p4, p5;

    while ((p2 = fork()) == -1 );

    if(p2 > 0){

        waitpid(p2,NULL, 0);

        printf("a: pid=%d\n", getpid());

        while ((p4 = fork()) == -1);

        if(p4 == 0){

           waitpid(p4, NULL, 0);

           printf("d: pid=%d, ppid=%d\n", getpid(), getppid());

           while ((p5 = fork()) == -1);

           if(p5 == 0){

               waitpid(p5, NULL, 0);

               printf("d: pid=%d, ppid=%d\n", getpid(), getppid());

           }

           exit(0);

        }

        waitpid(p4, NULL, 0);

    }

    else if(p2 == 0){

        waitpid(p2, NULL, 0);

        printf("b: pid=%d, ppid=%d\n", getpid(), getppid());

        while ((p3 = fork()) == -1);

        if (p3 == 0) {

           waitpid(p3, NULL, 0);

            printf("c: pid=%d, ppid=%d\n", getpid(), getppid());

            exit(0);

        }

        waitpid(p3, NULL, 0);

    }

   

    return 0;

}

 

4.分析程序,给出编译及执行过程和结果截屏。(2分)

(1)

#include<unistd.h>

#include<stdlib.h>

#include<stdio.h>

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);

}

编译及执行过程和运行结果截屏:

分析程序功能:

这段程序的功能是创建一个子进程,然后父进程非阻塞地等待子进程结束。父进程调用fork()创建子进程。如果fork()成功,子进程会打印一条消息并休眠10秒,然后退出。如果fork()失败,父进程会继续尝试创建子进程,直到成功。父进程在一个循环中使用waitpid()函数以非阻塞方式等待子进程结束。这意味着父进程会定期检查子进程的状态而不会被阻塞。如果waitpid()返回0,表示子进程尚未退出,父进程会打印一条消息,并休眠1秒。循环直到waitpid()返回的进程ID等于子进程的ID,表示子进程已经退出。最后,父进程根据waitpid()返回的值来判断是否成功等待子进程退出,并打印相应的消息。综上所述,该程序实现了父进程非阻塞地等待子进程退出,并在子进程退出后打印相应的消息。

(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");}

 

编译及执行过程和运行结果截屏:

分析程序功能:

这段程序的功能是创建一个子进程,然后父进程阻塞地等待子进程结束,接着根据waitpid()返回的值判断子进程是否成功退出,并打印相应的消息。父进程调用fork()创建子进程。如果fork()成功,子进程会调用execl()函数执行另一个程序/sy/sy1/welcome.out,然后子进程会退出。如果fork()失败,父进程会继续尝试创建子进程,直到成功。父进程使用waitpid()函数以阻塞方式等待子进程结束,其中参数NULL表示不获取子进程的退出状态,参数0表示等待任何子进程退出。当子进程退出后,waitpid()会返回子进程的进程ID。父进程根据waitpid()返回的值来判断是否成功等待子进程退出,并打印相应的消息。综上所述,该程序实现了父进程阻塞地等待子进程退出,并在子进程退出后打印相应的消息。

 

 

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>

#include <sys/types.h>



int main() {

    int child1, child2, p;

   

    while ((child1 = fork()) == -1);

   

    if (child1 == 0) {

        execl("/sy/sy1/welcome.out", "", NULL);

        exit(0);

    } else {

        while ((child2 = fork()) == -1);

       

        if (child2 == 0) {

            // 子进程2暂停10秒后退出

            sleep(10);

            exit(0);

        } else {

            // 父进程等待子进程1结束

            p = waitpid(child1, NULL, 0);

            if (p == child1)

                printf("Get child1 exitcode then exit!\n");

            else

                printf("Error occurred!\n");

           

            // 父进程非阻塞方式等待子进程2结束

            do {

                p = waitpid(child2, NULL, WNOHANG);

                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 occurred!\n");

        }

    }

    exit(0);

}

编译及执行过程:

结果截屏:

 

6.编写一个简易的shell解释程序。其运行原理是:当命令行上有命令需要执行时,shell进程获得该命令,然后创建子进程,让子进程执行该命令,shell进程等待子进程退出,之后继续等待命令行上的命令周而复始。(附加题)

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

#include <string.h>

#define MAX_COMMAND_LENGTH 100



int main() {

    char command[MAX_COMMAND_LENGTH];

    pid_t pid;

    int status;

   

    while (1) {

        // 打印提示符

        printf("shell> ");

        // 读取命令行输入

        if (fgets(command, sizeof(command), stdin) == NULL) {

            printf("Error reading command\n");

            continue;

        }

       

        // 移除命令行输入中的换行符

        command[strcspn(command, "\n")] = '\0';

       

        // 创建子进程

        pid = fork();

        if (pid < 0) {

            perror("fork");

            exit(EXIT_FAILURE);

        } else if (pid == 0) {

            // 在子进程中执行命令

            execlp(command, command, NULL);

            // 如果execlp返回,说明命令执行失败

            perror("execlp");

            exit(EXIT_FAILURE);

        } else {

            // 父进程等待子进程退出

            waitpid(pid, &status, 0);

        }

    }

   

    return 0;

}

三、实验总结和体会(1分)

通过本次实验,我学到了以下几点:

学习了如何使用fork()函数创建子进程,以及父子进程之间的关系。

理解了使用wait()和waitpid()函数等待子进程结束,并获取子进程的退出状态。

掌握了使用execl()函数在子进程中执行外部命令。

加深了对进程间通信和进程控制的理解,例如父进程等待子进程退出的过程。

熟悉了在C语言中使用系统调用来实现基本的shell解释器功能。

总的来说,通过实验,我加深了对进程管理、进程通信和操作系统底层的理解,并且了解了基本的Linux系统

 

实验进程管理   Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。对于C++ 运行库来说,这调用发生在应用程序的main() 函数返回之后。 1. 创建进程 CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。表 2-1详细地列出了每个参数的类型和名称。   表2-1 CreateProcess() 函数的参数 参数名称 使用目的 LPCTSTR lpApplivationName 全部或部分地指明包括可执行代码的EXE文件的文件名 LPCTSTR lpCommandLine 向可执行文件发送的参数 LPSECURIITY_ATTRIBUTES lpProcessAttributes 返回进程句柄的安全属性。主要指明这句柄是否应该由其他子进程所继承 LPSECURIITY_ATTRIBUTES lpThreadAttributes 返回进程的主线程的句柄的安全属性 BOOL bInheritHandle 种标志,告诉系统允许新进程继承创建者进程的句柄 DWORD dwCreationFlage 特殊的创建标志 (如CREATE_SUSPENDED) 的位标记 LPVOID lpEnvironment 向新进程发送的套环境变量;如为null值则发送调用者环境 LPCTSTR lpCurrentDirectory 新进程的启动目录 STARTUPINFO lpStartupInfo STARTUPINFO结构,包括新进程的输入和输出配置的详情 LPPROCESS_INFORMATION lpProcessInformation 调用的结果块;发送新应用程序的进程和主线程的句柄和ID   可以指定第个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者利用搜索方法找到的路径;lpCommandLine参数允许调用者向新应用程序发送数据;接下来的三个参数与进程和它的主线程以及返回的指向该对象的句柄的安全性有关。 然后是标志参数,用以在dwCreationFlags参数中指明系统应该给予新进程什么行为。经常使用的标志是CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用ResumeThread() API来启动进程。另个常用的标志是CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口,而不是利用父窗口。这参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他的活动进程来说,给此进程多少CPU时间。 接着是CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第个参数是lpEnvironment参数,指明为新进程提供的环境;第二个参数是lpCurrentDirectory,可用于向主创进程发送与缺省目录不同的新进程使用的特殊的当前目录;第三个参数是STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。 CreateProcess() 的最后个参数是用于新进程对象及其主线程的句柄和ID的返回值缓冲区。以PROCESS_INFORMATION结构中返回的句柄调用CloseHandle() API函数是重要的,因为如果不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。 2. 正在运行的进程 如果进程拥有至少个执行线程,则为正在系统中运行的进程。通常,这种进程使用主线程来指示它的存在。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少数特性也允许加以修改。 首先可查看的进程特性是系统进程标识符 (PID) ,可利用GetCurrentProcessId() API函数来查看,与GetCurrentProcess() 相似,对该函数的调用不能失败,但返回的PID在整个系统中都可使用。其他的可显示当前进程信息的API函数还有GetStartupInfo()和GetProcessShutdownParameters() ,可给出进程存活期内的配置详情。 通常,进程需要它的运行期环境的信息。例如API函数GetModuleFileName() 和GetCommandLine() ,可以给出用在CreateProcess() 中的参数以启动应用程序。在创建应用程序时可使用的另
1.基本系统进程   Csrss.exe:这是子系统服务器进程,负责控制Windows创建或删除线程以及16位的虚拟DOS环境。   System Idle Process:这个进程是作为单线程运行在每个处理器上,并在系统不处理其它线程的时候分派处理器的时间。   Smss.exe:这是个会话管理子系统,负责启动用户会话。   Services.exe:系统服务的管理工具。   Lsass.exe:本地的安全授权服务。   Explorer.exe:资源管理器。   Spoolsv.exe:管理缓冲区中的打印和传真作业。   Svchost.exe:这个进程要着重说明下,有不少朋友都有这种错觉:若是在“任务管理器”中看到多个Svchost.exe在运行,就觉得是有病毒了。其实并不定,系统启动的时候,Svchost.exe将检查注册表中的位置来创建需要加载的服务列表,如果多个Svchost.exe同时运行,则表明当前有多组服务处于活动状态;多个DLL文件正在调用它。   至于其它些附加进程,大多为系统服务,是可以酌情结束运行的。由于其数量众多,我们在此也不便于列举。   在系统资源紧张的情况下,我们可以选择结束些附加进程,以增加资源,起到优化系统的作用。在排除基本系统及附加进程后,新增的陌生进程就值得被大家怀疑了。 更多内容请看Windows操作系统安装、系统优化大全、系统安全设置专题,或进入讨论组讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值