• 2018-2019-1 20189205《Linux内核原理与分析》第七周作业


    进程的描述与进程的创建

    编程实现一个一个具有执行命令功能的shell

    主要思路是通过利用exec函数族来实现用户输入的命令,但是调用exec函数族将会覆盖源程序,因此需要先使用fork()函数生成子进程,在子进程中调用exec函数族,而父进程则使用wait()函数等待子进程完成。
    本程序使用的exec函数execve(),函数原型为int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

    • 第一个参数filename存储的是执行命令的可执行文件,由于shell中命令的执行文件是存储在/bin/目录下,因此只需在字符串"/bin/"后拼接上输入的命令;
    • 第二个参数argv[ ]类型为char * const,存储的是命令的各个参数,生成时把用户输入的命令中的各个参数存储在一个字符串数组中,将argv参数指向各个字符串,最后以NULL结尾;
    • 第三个参数envp[ ]存储的则是传递给执行文件的新环境变量数组,直接定义为环境变量PATH
      程序代码如下:
    //myshell.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <wait.h>
    #include <sys/types.h>
    
    int main ()
    {
        pid_t pid;
        int status = -1 ,flag =0,i=0,len=0,n=0,m=0;
        char code[80],s_code[20][20];
        char file[30]="/bin/";
        char* argv[80]; 		//存储命令的参数 
        char* envp[ ]={"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",0};//存储PATH 
        
        while (flag == 0)
        {
            printf("9205-myshell:/$ ");
            fgets(code,80,stdin);	//读取命令 
            if (code[0]=='e' && code[1]=='x' && code[2]== 'i' && code[3]== 't' ) flag = 1;	//如果命令是exit则退出程序 
            else
            {    
                //shell
                pid = fork();
                if (pid < 0) printf("error1!!");	//分化进程错误 
                else if (pid > 0)	//父进程 
                {
                    wait(&status);	//父进程等待子进程完成 
                }
                else if(pid==0)		//子进程 
                {
                    //将命令中的各个参数存储进 argv
                    while (code[i] !='
    ' )
                    {
                        if (code[i]!=' ') 
    			        {
    				        s_code[m][n]=code[i];
    				        n++;
    			        }
                        else 
                        {
                	        s_code[m][n]='';
                	        argv[m]=s_code[m];
                            m++;
                	        n=0;
    			        }
                        i++;
                    }
                        s_code[m][n]='';
                        argv[m]=s_code[m];
                        m++;
                	    n=0;
                        argv[m]=(char *)0;
    
                    //构成filename
                    strcat(file,s_code[0]);
    				
    				//执行命令 
                    execve(file, argv, envp);
                    
                    //若执行到以下步骤则调用命令时发生错误 
                    printf ("error2!!");
                    exit(0);
                }
            }
            
        }
        return 0;
    }
    

    程序执行效果如下图所示:

    学习测试wait()waitpid()函数

    wait( )函数
    其函数功能为等待子进程中断或结束,函数原型为pid_t wait (int * status);
    其参数 status 是一个整形指针。如果status不是一个空指针,则终止进程的终止状态将存储在该指针所指向的内存单元中。如果不关心终止状态,可以将 status参数设置为NULL。以下有几个宏可判别status中存储的结束情况:

    • WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。
    • WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
    • WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。
    • WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。
    • WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。
    • WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

    调用 wait 函数时,调用进程将会出现下面的情况:

    • 如果其所有子进程都还在运行,则阻塞。
    • 如果一个子进程已经终止,正等待父进程获取其终止状态,则获取该子进程的终止状态然后立即返回。
    • 如果没有任何子进程,则立即出错返回。
      wait( )函数如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。

    waitpid()函数
    其函数功能为:暂时停止目前进程的执行,直到有信号来到或子进程结束。与wait( )函数相似但功能比其更加强大。
    其函数原型为pid_t waitpid(pid_t pid,int * status,int options);
    第一个参数pid为欲等待的子进程识别码,其数值意义如下:

    • pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
    • pid=-1 等待任何子进程,相当于 wait()。
    • pid=0 等待进程组识别码与目前进程相同的任何子进程。
    • pid>0 等待任何子进程识别码为 pid 的子进程。
      第二个参数status将存储子进程的终止状态。
      第三个参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,其选项包括如下:
      WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
      WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。
      WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。
      waitpid( )函数如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。

    测试程序
    包括了使用了wait( )函数的wait测试程序和没事用函数的sleep对比程序,以对照查看wait( )函数的运行结果。

    //wait测试程序
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main()
    {
        pid_t pid;
        int num1,num2,status=0;
    	
    	//在无子进程的状态下执行wait() 
        num1=wait(&status);
        printf("the wait1:%d
    ",num1);
    
        pid = fork();
        if (pid < 0) printf("error 1!!");
        else if (pid == 0)	//子进程 
        {
            int i;
            printf("i an the son | the pid:%d | the parents pid:%d
    ",getpid(),getppid());
            for (i=0; i<5 ;i++)	//子进程sleep 
            {
                sleep(1);
                printf ("sleep-----%d
    ",i);
            }
        }
        else if (pid > 0)  //父进程
        {
            num2=wait(&status);	//父进程wait() 
            printf ("i am the parents | the pid:%d | the wait2:%d
    ",getpid(),num2);
        }
    
        return 0;
    }
    
    //对比sleep程序
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main()
    {
        pid_t pid;
        int num1,num2,status=0;
    
        pid = fork();
        if (pid < 0) printf("error 1!!");
        else if (pid == 0)  //子进程
        {
            int i;
            printf("i an the son | the pid:%d | the parents pid:%d
    ",getpid(),getppid());
            for (i=0; i<5 ;i++)
            {
                sleep(1);
                printf ("sleep-----%d
    ",i);
            }
        }
        else if (pid > 0)  //父进程
        {
            printf ("i am the parents | the pid:%d 
    ",getpid());
        }
    
        return 0;
    }
    

    运行结果如图所示:

  • 相关阅读:
    序列化与反序列化
    JAVA常用设计模式(一、抽象工厂模式)
    JAVA基础部分复习(七、JAVA枚举类型使用)
    JAVA常用设计模式(一、单例模式、工厂模式)
    JAVA高级篇(二、JVM内存模型、内存管理之第一篇)
    JAVA高级篇(一、JVM基本概念)
    linux常用命令
    JAVA基础部分复习(六、常用关键字说明)
    JAVA基础部分复习(五、JAVA反射)
    JAVA基础部分复习(三、泛型)
  • 原文地址:https://www.cnblogs.com/hzj20189205/p/10017462.html
Copyright © 2020-2023  润新知