【操作系统】进程程序替换之模拟实现一个简易的Shell

1、替换原理

用fork创建子进程后执行的是和父进程相同的程序(也有可能执行不同的分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的ID并未改变。

这里写图片描述


2、替换函数:exec函数族
#include<unistd.h>

//argv[0]:可执行文件的文件名
//char *const envp[],构造环境变量

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[]);

命名理解:

  • l(list):表示参数采用列表
  • v(vector):参数用数组
  • p(path):有p自动搜索环境变量PATH
  • e(env):表示自己维护环境变量
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,需自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,需自己组装环境变量

函数解释:

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错,则返回-1。
  • exec函数只有出错的返回值,没有成功的返回值。

exec函数族关系图:

只有execve是真正的系统调用,其他的五个函数最终都通过调用execve来实现自己的功能。

这里写图片描述


3、实现一个简易的shell

首先编写一个shell,需要重复以下过程:

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程(wait)

实现代码:

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

char *argv[8];
int argc = 0;

void do_parse(char* buf){
    int i;
    int status = 0;

    for(argc=i=0;buf[i];i++){
        if(!isspace(buf[i]) && status == 0){
            argv[argc++] = buf + i;
            status = 1;
        }else if(isspace(buf[i])){
            status = 0;
            buf[i] = 0;
        }
    }
    argv[argc] = NULL;
}

void do_execute(){
    pid_t pid = fork();

    if(pid > 0){
        //father
        int st;
        while(wait(&st) != pid)
            ;
    }else if(pid == 0){
        //child
        execvp(argv[0],argv);
        perror("execvp");
        exit(EXIT_FAILURE);
    }else{
        perror("fork!");
        exit(EXIT_FAILURE);
    }
}

int main(){
    char buf[1024] = {};
    while(1){
        printf("myshell> ");
        fflush(stdout);//刷新缓冲区
        gets(buf);
        do_parse(buf);//解析命令行
        do_execute();//
    }

    return 0;
}

程序运行结果:

这里写图片描述

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【实验目的】 1. 理解进程的概念,熟悉进程的组成; 2. 用高级语言编写和调试一个进程调度程序,以加深对进程调度算法的理解。 【实验准备】 1. 几种进程调度算法  短进程优先调度算法  高优先权优先调度算法  先来先服务调度算法  基于时间片的轮转调度算法 2. 进程的组成  进程控制块(PCB)  程序段  数据段 3. 进程的基本状态  就绪W(Wait)  执行R(Run)  阻塞B(Block) 【实验内容】 1. 例题 设计一个有 N个进程共行的进程调度程序进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。 每个进程一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。进程的运行时间以时间片为单位进行计算。每个进程的状态可以是就绪 W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。就绪进程获得 CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。 4. 实验题目  编写并调试一个模拟进程调度程序,采用“最高优先数优先”调度算法对五个进程进行调度。“最高优先数优先”调度算法的基本思想是把CPU分配给就绪队列中优先数最高的进程。静态优先数是在创建进程时确定的,并在整个进程运行期间不再改变。动态优先数是指进程的优先数在创建进程时可以给定一个初始值,并且可以按一定原则修改优先数。例如在进程获得一次CPU后就将其优先数减少1。或者,进程等待的时间超过某一时限时增加其优先数的值,等等。  编写并调试一个模拟进程调度程序,采用“轮转法”调度算法对五个进程进行调度。轮转法可以是简单轮转法、可变时间片轮转法,或多队列轮转法。简单轮转法的基本思想是:所有就绪进程按 FCFS排成一个队列,总是把处理机分配给队首的进程,各进程占用CPU的时间片相同。如果运行进程用完它的时间片后还为完成,就把它送回到就绪队列的末尾,把处理机重新分配给队首的进程。直至所有的进程运行完毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值