欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux进程(三)程序替换(编写myshell)

程序员文章站 2022-07-12 12:30:46
...

进程程序替换

看到进程我们首先就要想到PCB。每一个程序从磁盘加载到内存里就变成了一个进程,而每一个进程都有自己的PCB,在这个结构体里有着自己的虚拟地址空间,存在于虚拟地址空间的数据和代码的地址都会通过一个叫页表的东西映射物理内存上。
替换原理
用fork创建子程序后执行的是和父进程相同的程序(但是有可能执行不同的代码分支)子进程往往要调用一种exec函数来执行另一个程序。当进程调用一种exec函数的时候,该进程的用户空间代码和数据会被新进程所替换,从新进程的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并没有改变。
我们画个图清楚地了解下
Linux进程(三)程序替换(编写myshell)
图片最右边的就是磁盘,里面那个就是要执行的程序

替换函数

#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(cinst char *file,char *const argv[]);
int execve(const chat *path,char *const argv[],char *const envp[]);

函数理解
这些函数如果调用成功则加载新的程序从启动代码开始执行,不在返回;
如果调用出错就返回-1;
所以exec函数只有出错的返回值,当它成功的时候没有返回值

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

#include<unistd.h>
int main()
{
    char *const argv[]={"ps","-ef",NULL};
    char *const envp[]={"PATH=/bin:/usr/bin","TERM=console",NULL};
    execl("/bin/ps","ps","-ef",NULL);
    execlp("ps","ps","-ef",NULL);
    //带p,可以使用环境变量PATH,无需写全路径
    execle("ps","ps","-ef",NULL,envp);
    //带e的,需要自己组装环境变量,无需写全路径
    execv("/bin/ps",argv);
    execvp("ps",argv);
    execve("/bin/ps",argv,envp);
    exit(0);
}

事实上,只有execve是真正的系统调用,其他五个函数最终都调用execve,所以execve在man手册的第二节,其他的都在第三节

根据上一篇Linux进程(二)里面的程序等待,和上面说的程序替换,我们就可以来写一个自己的shell
首先我们要明确要写一个shell所需要的过程:
1:获取命令行
2:解析命令行
3:建立一个子程序
4:替换子进程
5:父进程等待子进程退出
好,直接上代码

我们先写上所需要的头文件

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

下面我们需要来分析一下,我们在屏幕上打出一段命令
它是如何执行的呢?
它是通过创建子程序,然后进行程序替换,用子程序去完成整个字符串命令,但是子程序每次只完成一个命令,所以就需要我们将字符串命令打散成一个一个的命令,然后放到一个指针数组里面。这个数组里面放的就是每一个命令的指针

void dasan(char buf[],char *argv[])
{
    int argc=0;//打散后指针的个数
    char *ptr=strtok(buf," ");//用strtok函数直接打散字符串
    //第一个参数是数组buf,第二个参数是以空格为分隔符
    argv[argc++]=ptr;
    while(ptr!=NULL)
    {
        ptr=strtok(NULL," ");//这里的第一个参数是NULL是规定,手册里面写的。
        argv[argc++]=ptr;   
    }
    argv[argc]=NULL;//把最后一个指针赋为NULL;
    return;
}
void zhixing(char *argv[])
{
    pid_t pid=fork();
    if(pid>0)
    {
        wait(NULL);
    }
    else if(pid==0)
    {
        execvp(argv[0],argv);
    }
    else
        peroor("fork");
    return;
}

void run()
{
    while(1)
    {
        char buf[1024]={0};
        printf("myshell->");//打印myshell形状
        gets[buf];//使用get函数将buf引入
        char *argv[8];//给定指针数组的大小;
        dasan(buf,argv);
        zhixing(argv);
    }
}
int main()
{
    run();
    return 0;
}

ok到这里我们就完成了我们最初级的myshell


如果有错误请指出,谢谢