Linux的学习之路:13、进程替代

简介: Linux的学习之路:13、进程替代

摘要

本章主要是说一下进程替代用到的函数exec以及自己实现的简易shell


一、进程程序替换

1、替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。


也就是说可以利用一个函数进行控制另外一个函数,就像可以利用c语言写一个程序从而控制c++的程序,c++的写法也不需要我会,只需要给一个参数的传递,c++那边用自己的语法进行去编写就可以利用c语言进行控制了。

2  、替换函数


其实有六种以exec开头的函数,统称exec函数,这里是直接利用man指令进行查看,这里我是使用man 3 exec进行查看,但是刚开始我没有找到,然后发现是因为没有安装库函数包,下面这行代码是进行安装的。

sudo yum install man-pages


3、函数解释与命令理解

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。


命名理解如下:


l(list) : 表示参数采用列表


v(vector) : 参数用数组


p(path) : 有p自动搜索环境变量PATH


e(env) : 表示自己维护环境变量

4、代码演示

1、execl

如上方函数的execl的使用如下方代码所示就是,个人上方手册查到的函数用法,execl就是需要绝对路径去进行访问使用,也就是环境变量,然后后面是指令使用,这里是利用ls指令进行演示的,

  1 #include <stdio.h>                                                                                                                                                                                            
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/wait.h>
  5 
  6 int main()
  7 {
  8     pid_t id=fork();
  9     if(id==0)
 10     {   //子进程
 11         printf("子进程开始运行,pid:%d\n",getpid());
 12         sleep(1);
 13         execl("/usr/bin/ls","ls","-a","-l",NULL);
 14         exit(1);
 15     }
 16     else
 17     {   //父进程
 18         printf("父进程开始运行,pid:%d\n",getpid());
 19         int status=0;
 20         pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码
 21         if(id>0)
 22         {
 23             printf("wait success,exit code:%d\n",WEXITSTATUS(status));
 24         }
 25     }
 26     return 0;
 27 }
~


2、execv

这个函数后面是可以利用数组进行访问这个这个命令的指令,也就是可以用数组进行匹配使用。

 8 int main()
  9 {
 10     pid_t id=fork();
 11     if(id==0)
 12     {   //子进程
 13         printf("子进程开始运行,pid:%d\n",getpid());
 14         sleep(1);
 15         char *const _argv[NUM]=                                                                                                                                                                               
 16         {
 17             (char*)"-ls",
 18             (char*)"-a",
 19             (char*)"-l",
 20             NULL
 21         };
 22        // execl("/usr/bin/ls","ls","-a","-l",NULL);
 23         execv("/usr/bin/ls",_argv);
 24         exit(1);
 25     }
 26     else
 27     {   //父进程
 28         printf("父进程开始运行,pid:%d\n",getpid());
 29         int status=0;                                                                  
 30         pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码
 31         if(id>0)                        
 32         {          
 33             printf("wait success,exit code:%d\n",WEXITSTATUS(status));
 34         }                            
 35     }                  
 36     return 0;                           
 37 }                      
~

3、execlp

下方这块代码和execl使用方式差不多,但是有一点不同,就是环境变量的寻找这个不需要绝对路径。

 int main()
    9 {
   10     pid_t id=fork();
   11     if(id==0)
   12     {   //子进程
   13         printf("子进程开始运行,pid:%d\n",getpid());
   14         sleep(1);
W> 15         char *const _argv[NUM]=
   16         {
   17             (char*)"-ls",
   18             (char*)"-a",
   19             (char*)"-l",
   20             NULL
   21         };
   22        //execl("/usr/bin/ls","ls","-a","-l",NULL);
   23         execlp("ls","ls","-a","-l",NULL);
   24         //execv("/usr/bin/ls",_argv);                                                                                                                                                                       
   25         exit(1);
   26     }
   27     else
   28     {   //父进程
   29         printf("父进程开始运行,pid:%d\n",getpid());                                  
   30         int status=0;                             
   31         pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码
   32         if(id>0) 
   33         {                                   
   34             printf("wait success,exit code:%d\n",WEXITSTATUS(status));
   35         }            
   36     }                                 
   37     return 0;        
   38 }

4、execvp

这里和上面execl差不多,但是也只需要相对路径就可以了,这里就演示了四个,后面两个用法就是按照手册进行更改就可以了。

8 int main()
  9 {
 10     pid_t id=fork();
 11     if(id==0)
 12     {   //子进程
 13         printf("子进程开始运行,pid:%d\n",getpid());
 14         sleep(1);
 15         char *const _argv[NUM]=
 16         {
 17             (char*)"-ls",
 18             (char*)"-a",
 19             (char*)"-l",
 20             NULL
 21         };
 22         //execl("/usr/bin/ls","ls","-a","-l",NULL);
 23         //execlp("ls","ls","-a","-l",NULL);
 24         //execv("/usr/bin/ls",_argv);
 25         execvp("ls",_argv);                                                                                                                                                                                   
 26         exit(1);
 27     }
 28     else   
 29     {   //父进程                                                                       
 30         printf("父进程开始运行,pid:%d\n",getpid()); 
 31         int status=0;                   
 32         pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码
 33         if(id>0)                              
 34         {                            
 35             printf("wait success,exit code:%d\n",WEXITSTATUS(status));
 36         }                               
 37     }                  
 38     return 0;
 39 }                                                                  

 

二、简易的myshell

这里将实现一个简易的shell,shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。所以要写一个shell,需要循环以下过程:


1. 获取命令行


2. 解析命令行


3. 建立一个子进程(fork)


4. 替换子进程(execvp)


5. 父进程等待子进程退出(wait)


从而就可以拆分出下面几步


1. 打印出提示信息


2. 获取用户的键盘输入[输入的是各种指令和选项: "ls -a -l -i"]


3. 命令行字符串解析:"ls -a -l -i" -> "ls" "-a" "-i"(这里完全可以利用strtok这个函数进行拆分字符串)


4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令


5. fork()


也就是对应上面的五个过程,代码如下测试如下。

#include <stdio.h>
    2 #include <stdlib.h>
    3 #include <unistd.h>
    4 #include <sys/wait.h>
    5 #include <string.h>
    6 #include <sys/types.h>
    7 
    8 #define NUM 1024
    9 #define SIZE 32
   10 #define SEP " "
   11 
   12 char cmd_line[NUM];
   13 char* g_argv[SIZE];
   14 
   15 int main()
   16 {
   17     while(1)
   18     {   //1、打印出信息提示
   19         printf("[root@localhost myshell]#");
   20         fflush(stdout);
   21         memset(cmd_line,'\0',sizeof cmd_line);
   22         //2、获取从键盘输入的各种数据
   23         if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
   24         {
   25             continue;
   26         }
   27         cmd_line[strlen(cmd_line)-1]='\0';
   28         //3、命令字符串进行解析
   29         g_argv[0]=strtok(cmd_line,SEP);//第一调用,需要首先获取原始字符串
   30         int index=1;
   31         if(strcmp(g_argv[0], "ls") == 0)
   32         {
W> 33             g_argv[index++] = "--color=auto";
   34         }
   35         if(strcmp(g_argv[0], "ll") == 0)
   36         {
W> 37             g_argv[0] = "ls";
W> 38             g_argv[index++] = "-l";
W> 39             g_argv[index++] = "--color=auto";
   40         }
W> 41         while(g_argv[index++]=strtok(NULL,SEP));
   42         if(strcmp(g_argv[0], "cd") == 0) //not child execute, father execute                                                                                                                                
   43         {
   44             if(g_argv[1] != NULL) chdir(g_argv[1]); //cd path, cd ..
   45 
   46             continue;
   47         }
   48           pid_t id=fork();
   49         if(id==0)
   50         {
   51             printf("下面功能让子进程进程的\n");
   52             execvp(g_argv[0],g_argv);
   53             exit(1);
   54         }
   55         int status=0;
   56         pid_t ret=waitpid(id,&status,0);
   57         if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));
   58         
   59     }
   60     return 0;
   61 
   62 }
 

三、代码

myshell

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>
 
#define NUM 1024
#define SIZE 32
#define SEP " "
 
char cmd_line[NUM];
char* g_argv[SIZE];
int main()
{
    while(1)
    {   //1、打印出信息提示
        printf("[root@localhost myshell]#");
        fflush(stdout);
        memset(cmd_line,'\0',sizeof cmd_line);
        //2、获取从键盘输入的各种数据
        if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
        {
            continue;
        }
        cmd_line[strlen(cmd_line)-1]='\0';
        //3、命令字符串进行解析
        g_argv[0]=strtok(cmd_line,SEP);//第一调用,需要首先获取原始字符串
        int index=1;
        if(strcmp(g_argv[0], "ls") == 0)
        {
            g_argv[index++] = "--color=auto";
        }
        if(strcmp(g_argv[0], "ll") == 0)
        {
            g_argv[0] = "ls";
            g_argv[index++] = "-l";
            g_argv[index++] = "--color=auto";
        }
        while(g_argv[index++]=strtok(NULL,SEP));
        if(strcmp(g_argv[2], "cd") == 0) //not child execute, father execute
        {
            if(g_argv[1] != NULL) chdir(g_argv[1]); //cd path, cd ..
 
            continue;
        }
        pid_t id=fork();
        if(id==0)
        {
            printf("下面功能让子进程进程的\n");
            execvp(g_argv[0],g_argv);
            exit(1);
        }
        int status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));
        
    }
    return 0;
 
}


exec

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
#define NUM 10
 
int main()
{
    pid_t id=fork();
    if(id==0)
    {   //子进程
        printf("子进程开始运行,pid:%d\n",getpid());
        sleep(1);
        char *const _argv[NUM]=
        {
            (char*)"-ls",
            (char*)"-a",
            (char*)"-l",
            NULL
        };
        //execl("/usr/bin/ls","ls","-a","-l",NULL);
        //execlp("ls","ls","-a","-l",NULL);
        //execv("/usr/bin/ls",_argv);
        execvp("ls",_argv);
        exit(1);
    }
    else 
    {   //父进程
        printf("父进程开始运行,pid:%d\n",getpid());
        int status=0;
        pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码
        if(id>0)
        {
            printf("wait success,exit code:%d\n",WEXITSTATUS(status));
        }
    }
    return 0;
}


目录
相关文章
|
9月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
7月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
282 67
|
6月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
200 16
|
6月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
139 20
|
5月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
123 0
|
5月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
181 0
|
5月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
126 0
|
5月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
154 0
|
8月前
|
存储 IDE Linux
零基础保姆级教程!手把手教你免费玩转Linux CentOS安装+学习环境搭建(附避坑指南)
本文详细介绍了在VMware虚拟机中安装CentOS 6.8的全过程。首先,需确保已安装VMware并开启V-CPU虚拟化功能,可通过BIOS设置或使用LeoMoon CPU-V工具检测。接着,下载CentOS镜像文件,并在VMware中新建虚拟机,配置CPU、内存、硬盘等参数。最后,加载ISO镜像启动虚拟机,按照提示完成CentOS的安装,包括语言、键盘、存储方式、地区、密码设置及硬盘分区等步骤。安装完成后,以root用户登录即可进入系统桌面,开始学习Linux命令和操作。
862 12
零基础保姆级教程!手把手教你免费玩转Linux CentOS安装+学习环境搭建(附避坑指南)
|
9月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
413 34
下一篇
oss云网关配置