execve(const char *filename, char *const argv[ ], char *const envp[ ])
视频教程以及实际代码可以看这一个教程
其他的需要的知识
GDT表
GDT表虚拟内存
页表
任务切换
fork实现
elf文件加载
这一个是一个Linux下面的标准接口
这一个的实际作用的是执行一个可执行文件
把当前程序替换成要执行的程序, 而同时保留原程序运行的方法是,fork+exec
第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
函数执行成功时没有返回值,执行失败时的返回值为-1.
#include<unistd.h>
main()
{ char *argv[ ]={"ls", "-al", "/etc/passwd", NULL}; char *envp[ ]={"PATH=/bin", NULL} execve("/bin/ls", argv, envp);
}
实现的思路
CPU寄存器的初始化
在使用的时候需要一个新的页表, 记录这一个新的进程使用的地址信息
由于使用系调用的时候会记录进入的时候的CPU寄存器信息, 返回的时候弹出信息, 这一个新的进程再返回的时候, 首先使用是栈里面的记录的信息, 而不是tss表里面的信息
返回的时候这里面的信息也需要进行更改, 如果不改变的话会返回之前的页表对应的位置
该变信息的时候可以使用tss里面的记录的esp0的值进行计算, 使用结构体syscall_frame_t(一个记录压栈时候寄存器顺序的结构体, 定位到对应的位置)
栈的初始化(参数传递)
在使用新的栈的时候, 设置esp需要减去一下系统调用的参数的位置(系统调用返回的时候使用retf会把栈里面的参数弹出来, 新的任务进入的时候栈里面没有这几个参数, 需要预留空间), 以及初始化一下main函数的参数在栈里面
这里可以使用把所有的信息放在栈里面
实际的实现
//执行一个可执行文件
int sys_execve(char *name, char ** argv, char ** env){task_t *task = task_current();//获取当前的任务uint32_t new_page_dir = memory_create_uvm();//获取一个新的页表给任务使用uint32_t old_page_dir = task->tss.cr3;//记录一下现在使用的页表//使用这一个新的文件的名字初始化任务名字kernel_strncpy(task->name, get_file_name(name), TASK_NAME_SIZE);if(! new_page_dir){goto exec_failed;}//获取这一个的入口, 以及加载这一个文件到新的页表里面//这里实际是加载一个elf文件, 以及从文件头获取他的入口地址//这里需要注意的是实际使用的虚拟地址是还未使用的页表里面的//实际加载的时候需要对使用的内存申请, 映射, 复制uint32_t entry = load_elf_file(task, name, new_page_dir);if(entry == 0){goto exec_failed;} //预留一段空间放参数(main函数的参数)uint32_t stack_top = 栈的顶部虚拟地址 - 预留的参数保存地址;//为这一个任务的新页表申请一下栈空间int err = memory_alloc_for_page_dir(new_page_dir, MEM_TASK_STACK_TOP - MEM_TASK_STACK_SIZE(实际的虚拟地址最小值), MEM_TASK_STACK_SIZE()大小, 权限(用户可使用, 可写));if(err < 0){goto exec_failed;}int argc = strings_count(argv);//获取参数的个数//把这一个参数按照之前图里面的格式复制到栈里面预留的空间//之后main函数可以直接使用err = copy_args((char *)stack_top, new_page_dir, argc, argv);if(err < 0){goto exec_failed;}//获取记录了栈里信息的地址syscall_frame_t * frame = (syscall_frame_t *)(系统调用的时候记录的特权级esp - sizeof(syscall_frame_t)(实际压入的信息的大小));//改变特权级0的栈里面的信息用于返回frame->eip = entry;frame->eax = frame->ebx = frame->ecx = frame->edx = 0;frame->esi = frame->edi = frame->ebp = 0;frame->eflags = EFLAGS_DEFAULT | EFLAGS_IF; //预留一下栈里面参数的位置frame->esp = stack_top - sizeof(uint32_t) * SYSCALL_PARAM_COUNT;//栈里面需要有初始的参数的值task->tss.cr3 = new_page_dir;mmu_set_page_dir(new_page_dir);//销毁之前的页表memory_destroy_uvm(old_page_dir);return 0;exec_failed:if(new_page_dir){//错误处理}return -1;
}