目录
前言
替换的原理
替换函数
记忆技巧
函数使用
execl
execlp
execv
execvp
execle
execvpe
调用其它语言的程序
模拟实现一个shell
前言
关于本文可以先去看看上一篇【Linux】进程控制详解-CSDN博客可以更好的理解这里的内容
学完本篇文章,你就可以自己设计一个mini版的shell解释器,还可以用你写自己的代码区执行其它语言的程序。
替换的原理
用fork创建子进程后执行的是和父进程相同的代码,但有可能需要执行不同的代码分支,那么子进程往往要调用一种exec系列函数以执行另一个全新程序。当进程调用一种exec系列函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec系列函数并不创建新进程,所以调用exec系列函数前后该进程的ID并未改变。
替换函数
返回值:如果调用成功则加载新的程序从启动代码开始执行,不在返回;调用失败则返回-1。
记忆技巧
- l(list):表示参数采用列表
- v(vector):表示参数采用数组
- p(path):第一个参数path不用输入路径,给出命令名即可,它会在环境变量PATH当中搜索对应的命令
- e(env):将自己维护的环境变量传递给需要替换的进程
函数名 | 参数格式 | 是否带路径 | 是否使用当前环境变量 |
execl | 列表 | 否 | 是 |
execlp | 列表 | 是 | 是 |
execle | 列表 | 否 | 否,需自己维护环境变量 |
execv | 数组 | 否 | 是 |
execvp | 数组 | 是 | 是 |
execvpe | 数组 | 否 | 否,需自己维护环境变量 |
函数使用
一旦发生了替换,那么替换函数后面的代码就不会再执行了。
int main()
{
printf("当前进程的开始代码\n");
execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
printf("当前进程的结束代码\n");
return 0:
}
注:在调用替换函数时末尾最好加上NULL代表结束。
虽然我们可以不用创建子进程来使用替换函数,但是我们创建了子进程,替换的进程就是子进程而父进程不受影响,那么父进程就可以聚焦在读取数据,解析数据,指派进程执行代码等功能了。
execl
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
execlp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
execlp("ls", "ls", "-a", "-l", NULL);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
execv
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM 16
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
char* const _argv[NUM] = {
(char*)"ls",
(char*)"-a",
(char*)"-l",
NULL
};
execv("/usr/bin/ls", _argv);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
execvp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM 16
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
//execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
//execlp("ls", "ls", "-a", "-l", NULL);
char* const _argv[NUM] = {
(char*)"ls",
(char*)"-a",
(char*)"-l",
NULL
};
execvp("ls", _argv);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
execle
mycmd.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("can not execute\n");
exit(1);
}
printf("获取环境变量:MY_VALUE:%s\n", getenv("MA_VALUE"));
if(strcmp(argv[1], "-a") == 0)
{
printf("hello 我是a\n");
}
else if(strcmp(argv[1], "-b") == 0)
{
printf("hello 我是b\n");
}
else
{
printf("defalut\n");
}
return 0;
}
myproc.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM 16
const char* myfile = "/home/hjx/for_linuxtest/test43/mycmd";
int main(int argc, char* argv[], char* env[])
{
char* const _env[NUM] = {
(char*)"MY_VALUE=332335454",
NULL
};
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
execle(myfile, "mycmd", "-a", NULL, _env);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
所以mycmd.c就拿到了这里的环境变量
execvpe
和上面的类似就不再演示了
其实系统调用的接口只有一个——execve
而以上介绍的函数是操作系统是为了满足不同的调用场景提供的基本封装。
调用其它语言的程序
print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);//子进程创建失败
}
else if(id == 0)
{
//子进程
printf("子进程开始执行,pid:%d\n", getpid());
execlp("python", "python", "test.py", NULL);
exit(1);//替换失败则终止进程
}
else
{
//父进程
printf("父进程开始执行,pid:%d\n", getpid());
int status;
pid_t wid = waitpid(-1, &status, 0);//阻塞等待
if(wid > 0)
{
printf("wait success, exit code:%d\n", WEXITSTATUS(status));
}
}
return 0;
}
模拟实现一个shell
有了上面的这些知识,那么我们可以自己设计一个简易版的shell。
shell代码链接:minishell
效果展示
今天的分享就到这里了,如果内容有错的话,还望指出,谢谢!!!