Linux——进程控制2|进程程序替换|替换原理|替换函数|替换函数创建子进程|其余替换函数介绍|使用替换致函执行其它文件程序|使用替换致函执行其它语言文件|execlp |

目录

进程程序替换

 替换原理

替换函数 

替换函数创建子进程 

其余替换函数介绍 

 使用替换致函执行其它文件程序

使用替换致函执行其它语言文件 

execlp 

Shell简单实现  


进程程序替换

 替换原理

 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。

调用exec并不创建新进程,所以调用exec前后该进程的id并未改变

 程序替换就是通过特定的接口,加载磁盘上的一个全新的程序(数据和代码)。加载到调用进程的上下文中(地址空间中),重新改一下页表和物理内存的关系就行,进程替换并没有创建新的子进程

替换函数 

 其实有六种以exec开头的函数,统称exec函数,本质就是加载程序的函数(从磁盘加载到内存):

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

int execl(const char *path,const char *arg,...);

 path:路径+目标文件名

…:可变参数列表

函数里可传入多个参数,最后一个参数,必须是NULL,表示参数传递完毕

演示:

注意这里ls的路径

 这段程序正常运行

 

加上execl,注意第一个参数和最后一个参数之间的参数,是我们在命令行上如何使用,就在这里如何书写

 

 

 在调用excel之后打印出来的内容跟直接调用ls一模一样

 

 execl运行成功以后,原程序的内容便不再执行,我们原始代码里还有一个printf没打印出来

execl是程序替换,调用该函数成功之后,会将当前进程的所有代码和数据进行替换,包括已经执行的和没执行的

所以,一旦调用成功,后续所有代码都不会再执行

故意写一个错代码

 execl调用失败

 execl函数出错的时候才有返回值,返回值是-1,成功时没有返回值

 我们一般这样写,如果执行了exit,则一定调用失败了

替换函数创建子进程 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
        pid_t id = fork();
        if(id == 0)
        {
           //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            execl("/usr/bin/ls","ls","-a","-l",NULL);
            sleep(3);
        }
        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;
}

阻塞等待, 一定是子进程先运行完毕,然后父进程获取之后,才退出!

创建子进程原因:如果不创建,替换的就是父进程,如果创建了,替换子进程,就不会影响父进程。

当子进程加载新程序,子进程和父进程代码会分离,也就是子进程会写时拷贝

其余替换函数介绍 

 exec系列函数的功能其实iu是加载器的底层接口

int execv(const char *path,char *const argv[])

path:文件路径+文件名

argv:指针数组,最后要以NULL结尾

execv中的v可看作vector方便记忆。

 

 

int execlp(const char *file,const char *arg,...);

file:文件名(要执行谁),execlp,p代表会在path环境中查找

arg:命令行模式下使用格式,命令行参数必须以NULL结尾

 

int execvp(const char *file,char *const argv[]);

file:文件名(你要执行谁)

argv:指针数组

还有execv系列

函数名带

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
 

 系统直接调用的是execve,上面的这些函数最终调用的都是excve这个接口,

 使用替换致函执行其它文件程序

 创建一个mycmd.c文件

修改makefile,方便一次生成俩个文件

 这里的all不需要依赖关系,只要依赖方法

 我们现在想用exce来执行mycmd

记住这个路径

 

 

成功调用了mycmd文件 

测试-b命令

 程序运行成功

 也可把程序里面的路径设置为相对路径(用.或..)

使用替换致函执行其它语言文件 

 创建俩个文件

 

 测试一下代码

 sh文件

 

 

 

 也可加上执行权限直接运行

execlp 

  最终boss:execle

int execle(const char *path,const char *arg,...,char *const envp[]);

path:路径+文件名

arg:命令行模式下使用格式,命令行参数必须以NULL结尾

envp:环境变量

mycmd.c文件

 我们使用getenv获取环境变量,getenv根据环境变量名获取环境变量

 

 Come on这个环境变量并不存在,这是我们自己写的

 exec.c文件

 执行这个文件,注意我们没有给函数传环境变量

 我们传一个环境变量

 

 我们发现come on打印成功,这是因为再exec.c文件中父进程把环境变量通过execle传给了子进程,子execle函数调用了mycmd.c文件,mycmd.c通过getenv获取到了环境变量Come on

Shell简单实现  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define NUM 1024
#define SIZE 32
#define EP " "
char cmd_line[NUM];//保存完整的字符串
char* g_garp[SIZE];//保存打散后的字符串
int main()
{
    while (1)
    {
        printf("[root@localhost myshell]# ");
        fflush(stdout);
        memset(cmd_line, '\0', sizeof cmd_line);
        if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
        {
            continue;
        }
        cmd_line[strlen(cmd_line) - 1] = '\0';
        g_garp[0] = strtok(cmd_line, EP);
        int index = 1;//作为打散后要保存字符串的下标
        if (strcmp(g_garp[0], "ls") == 0)
        {
            g_garp[index++] = "--color=auto";
        }
        if (strcmp(g_garp[0], "ll") == 0)
        {
            g_garp[0] = "ls";
            g_garp[index++] = "-l";
            g_garp[index++] = "--color=auto";
        }
        while (g_garp[index++] = strtok(NULL, EP));//分隔字符串,并复制
        if (strcmp(g_garp[0], "cd") == 0)
        {
            if (g_garp[1] != NULL);
            chdir(g_garp[1]);
            continue;
        }
        pid_t id = fork();
        if (id == 0)
        {
            printf("下面子进程要进行的是\n");
            execvp(g_garp[0], g_garp);
            exit(0);
        }
        //父进程获取子进程信息
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0)
            printf("exit code:%d\n", WEXITSTATUS(status));
    }
    return 0;
}

 如果这样打印,没有换行,这是因为我们没有刷新缓冲区

 这种情况会自动换行,是因为我们按了回车,多了\n,我们把\n改为\0就行

输入cd命令没反应

 这里输入cd其实是想让父进程执行自己的命令,这种命令叫内置命令(内建命令)

内建命令本质其实就是shell中的一个函数调用

系统中有一个调用接口chdir,表示更改当前目录

 这里chdir(g_garv[1])//表示切换到数组下标为1的这个路径

chdir函数用于改变当前工作目录。调用参数是指向目录的指针

猜你喜欢

转载自blog.csdn.net/weixin_49449676/article/details/127094383