[Linux Process Control (3)] Process Program Replacement–How to implement a bash interpreter yourself?

Blogger CSDN homepage:Hangdian Code Farmer-NEO
?
?Column classification:Linux from entry to proficiency?
?
Code Warehouse: NEO’s Learning Diary
?
Follow meTeach you more operating system knowledge
?

Process program replacement

  • 1 Introduction
  • 2. Understanding of exec series functions
  • 3. execl series functions
  • 4. execv series functions
  • 5. Usage scenarios of program replacement
  • 6. Implement a bash interpreter yourself
  • 7. Specialties of built-in commands
  • 8. Summary and expansion

1. Preface

This article is the last article on process control
Sometimes we encounter this scenario: a child process is created
After coming out, I don’t want to execute the code of the parent process, but
Want to execute the code of other programs to complete the task,
So in this scenario, program replacement is very important!

Key points of this chapter:

This article focuses on process program replacement
Usage of exec series functions (six in total),
And implement a bash interpreter independently.
Finally, expand how to use C to call programs in other languages

2. Understanding of exec series functions

After fork, if the child process wants to execute a
Brand new programs need to use this series of functions!

There are six functions in total, but they all have
Certain rules are not difficult to remember! They are all failures
Return -1 First, let’s introduce the simplest function:
execl

Its parameters respectively represent: the path of the program to be executed
And the name, and how to execute this program

Use first and then explain:

int main()
{<!-- -->
    printf("I want to replace the program...\
");
    int n = execl("/usr/bin/ls","ls","-a","-l",NULL);
    if(n==-1)
    {<!-- -->
        perror("execl");
    }
    printf("Program replacement completed!\
");
    return 0;
}

Phenomena:


It can be found that after printing: I want to perform program replacement
After that, I executed the ls program, and after the execution
“Program replacement completed” is not printed!

After surface execl, the code and data of the current process will be stored
Replace, including code that has not been executed!

3. execl series functions

First of all, execl belongs to the exec series, plus l,
l is list, which is equivalent to the path of the program to be executed.
To list them out, there are also some in the execl series
execlp and execle, now let’s explain these two

execlp function analysis:

int main()
{<!-- -->
    printf("I want to replace the program...\
");
    int n = execlp("ls","-l",NULL);
    if(n==-1)
    {<!-- -->
        perror("execl");
    }
    printf("Program replacement completed!\
");
    return 0;
}

It can be found that using the execlp function even without adding
Path can also find the program and allow it, but
Why is this? Bring p, p is PATH
environment variable, so the system will go to the environment variable PATH
Find the path strength in the middle, and if you find it, execute it directly!

execle function analysis:

int main()
{<!-- -->
    const char* _env[]={<!-- -->"MY_ENV=666",NULL};
    printf("I want to replace the program...\
");
    int n = execle("/usr/bin/ls","ls","-l",NULL,_env);//Define an environment variable MY_ENV=666 and pass it to the program to be executed
    if(n==-1)
    {<!-- -->
        perror("execl");
    }
    printf("Program replacement completed!\
");
    return 0;
}

execle can execute other programs before
Pass in your own defined environment variables to facilitate follow-up
Execution of the program! e is the abbreviation of env

4. execv series functions

The execv series of functions replace l with v.
v is vector, array, that is, using
Array to pass parameters

execv function analysis:

int main()
  {<!-- -->
char* const set[]={<!-- -->"ls","-a","-l",NULL};
      printf("I want to replace the program...\
");
      int n = execv("/usr/bin/ls",set);
      if(n==-1)
      {<!-- -->
          perror("execl");
      }
      printf("Program replacement completed!\
");
      return 0;
  }

Save the method we want to execute the program in an array
How to pass the array again! The following execvp and
The execvpe function is easy to understand. Adding p is nothing more than
Just go to the environment variable PATH to find the path and add e.
Just pass in the environment variables to the program to be executed, that’s all!

5. Usage scenarios of program replacement

In fact, under normal circumstances, program replacement is not
Replace it yourself, but create a subprocess to replace it.
Let the child process do the work, and the parent process acts as the “supervisor”

In this scenario, we can naturally think of it
The working principle of the bash interpreter may be to create
The child process performs the task, and the bash parent process itself
You just need to obtain instructions and convey them!

First of all, the bash interpreter must be a while
Infinite loop, because it will continue to print information to us:
Of course, you can customize this message

int main()
{<!-- -->
    while(1)
    {<!-- -->
        //Print prompt information
        printf("[kwy@localhost myshell]# ");
        fflush(stdout);
        ...
    }
    return 0;
}

Let’s first analyze the overall structure of bash,
Then implement it step by step:

  1. First we need to define two arrays A and B
    A is used to store all strings entered by the user
    B is used to store strings separated by spaces

  2. The second step, after obtaining the string entered by the user,
    Split the string by spaces

  3. The third step is to create a child process using the exec series
    Function to execute instructions entered by the user
    And bash itself acts as a supervisor and waits for the death of the child process

6. Implement a bash interpreter yourself

First create two arrays for backup
and then receive input from the user

#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//Save the complete command line string
char* my_argv[SIZE];//Save the broken string
if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)//Use fgets to input the standard input into the array
    continue;
cmd_line[strlen(cmd_line)-1] = '\0';//Clear the input newline character

The next step is to break up the string by dividing it with spaces.
When learning C language, the strtok function can help
We solve this problem with the following functionality:

By default everyone knows how to use this function.
So I directly wrote the code for splitting the string:

//Command line string parsing: separated by spaces
my_argv[0]=strtok(cmd_line," ");//Propose the first part
int index=1;
while(my_argv[index + + ] = strtok(NULL," "));//If you still want to parse the string of the first call when strtok is called for the second time, pass NULL

After writing this code, the string has been
Split into several small strings, such as user input
“ls -a -l” is converted into “ls”, “-a”, “-l”, next
Just create a child process to complete the task!

//Shell operating principle: Execute commands through the child process, and the parent process waits & amp; & amp;parses the command
//The command line interpreter is a resident program
#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//Save the complete command line string
char* my_argv[SIZE];//Save the broken string

int main()
{<!-- -->
    while(1)
    {<!-- -->
        //Print prompt information
        printf("[kwy@localhost myshell]# ");
        fflush(stdout);
        memset(cmd_line,'\0',sizeof cmd_line);
        //Get the user's keyboard input
        if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
            continue;
        cmd_line[strlen(cmd_line)-1] = '\0';//Clear the input newline character
        //Command line string parsing: separated by spaces
        my_argv[0]=strtok(cmd_line," ");//Propose the first part
        int index=1;
        while(my_argv[index + + ] = strtok(NULL," "));//If you still want to parse the string of the first call when strtok is called for the second time, pass NULL
        //After fork, the child process completes the task
        pid_t id=fork();
        if(id == 0)//Subprocess
        {<!-- -->
            printf("The following functions are executed by the child process\
");
            //When executing commands such as cd, the path of the child process changes, but the path of the parent process remains unchanged.
            execvp(my_argv[0],my_argv);
            exit(1);//Return 1 if execution fails
        }
        //The code of the parent process, when the supervisor
        int status = 0;
        pid_t ret = waitpid(-1, & status,0);
        if(ret>0) printf("exit code: %d\
",WEXITSTATUS(status));
    }
    return 0;
}
</code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png" alt ="" title="">

Explanations about the code are in the comments
If there is anything you don’t understand, please send a private message

7. Specialties of built-in commands

When implementing bash, you may encounter a problem:
It seems that the cd command to enter a certain folder is useless.

This is actually easy to understand, because the command cd is
Enter a folder, and of course enter this folder
The current process has entered, if a child process is created
Go to the folder. Due to copy-on-write reasons, the parent
The process will not enter, so for things like cd
Commands are called built-in commands, that is, they cannot be
The commands that are completed by the child process can only be done by the parent process!

if(strcmp(my_argv[0],"cd")==0)
{<!-- -->
   if(my_argv[1]!=NULL)
       chdir(my_argv[1]);
   continue;//Jump directly to while(1)
}

chdir is to switch the current working directory

Built-in commands include more than just cd, such as export and kill
and history, etc. are also built-in commands!

8. Summary and expansion

Process program replacement can help us accomplish a lot
The task, to make a simple bash interpreter is just
One of many applications, as we learn more
You’ll also discover new lands!

Extensions to program replacement:

Under Linux, C language programs can not only be replaced by
To execute a C language program, you can also replace it with python
Or Java or even bash and other programs to execute them
Language code:

For example, python script:

#!/usr/bin/python3.6
print("hello Python/n")

Run command: python test.py

Replace the process with a Python program: execlp("python", "python", "test.py", NULL);

This way you can execute python code directly on the C program!

Next issue preview: Basic IO