OS之子进程简单模拟shell解释环境

(1)每次主进程在接收指令进行解释之前,调用waitpid(-1,NULL, WNOHANG);释放可能存在的执行完毕的子进程资源(不会等待);

(2)当输入一条指令后,将指令拆分成独立的符号;

(3)如果指令的最后一个符号是’&’,则(5)子进程执行指令的时候,父进程将不会wait()子进程执行完毕,继续接收下一条指令进行解释;

(4)History指令由主程序使用循环队列进行维护,最多只记录10条历史指令;

(5)如果是非history指令,fork()一个子进程,调用execvp()进行解释执行;


一、主程序实现代码:

/*
** Desc:
**   simulate the shell interepter.
**   a command line can only be up to 80 characters, the 
** command line will be divided into different tokens
** stored into the 'args' array. Then call the function
** execvp() to execute the command in the child process.
**   if the last token is '&', the parent process will 
** not wait the child process to complete, just continue.
** Otherwise, the parent should wait for it.
**
** Bugs: many
**   1. when execute history command, maybe suffer infinite loop 
**   2. some commands like 'cd', no effect
**   3. some shell syntax, can'te recognize, like '(ls -trl)'
**   4. more and more... 
**
** Date: 2015-01-23
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>
#include<unistd.h>
#include<sys/wait.h>
/*
** define a queue to manage the history commands,
** but only last 10 commands
*/
#include"queue.h"

#define MAX_TOKEN (MAX_LINE / 2 + 1)
#define _FL_ __FILE__, __LINE__
#define TRUE  1
#define FALSE 0

#define CLR_BUF while(getchar() != '\n') continue

/*
** split the command line into seperate tokens
*/
int token(char *str, char **args, int num);

int
main(void)
{
  char *args[MAX_TOKEN];               /* command line tokens */
  char *his_cmd;                       /* hold some history command fetched from the queue */
  char cmd[MAX_LINE + 1];              /* record the command line */
  int should_run = TRUE;               /* flag to detemine when to exit program */
  int is_wait = TRUE;                  /* whether wait for the child process */
  int is_go   = FALSE;
  int token_num = 0;                   /* how many tokens */
  int i = 0;                           /* just for loop */
  int his_num;                         /* which history command */
  pid_t pid = -1;                      /* record the child process identifier */
  Qptr qptr;                           /* pointer to the queue, declared in queue.h */

  /*
  ** allocate a queue to manage history commands
  ** and initialize it
  */
  qptr = (Qptr)malloc(sizeof(Queue));
  if(NULL == qptr)
  {
    fprintf(stderr, "[%s:%d]malloc error[%s]\n", _FL_, strerror(errno));
    return 1;
  }

  init_queue(qptr); 

  /*
  ** the shell interpreter begin
  */
  while(should_run)
  {
    memset(cmd, 0x00, sizeof(cmd));
    memset(args, 0x00, sizeof(args));
    /*
    ** waitpid
    **   recycle the posible zombie child process
    **   but it will not block to wait
    **   if this function doesn't exist, execut the command like:
    **   ls -trl &
    **   the child process will become zombie process and influence 
    **   the coming process
    */
    waitpid(-1, NULL, WNOHANG); 
    
    printf("osh>");
    fflush(stdout);

    /*
    ** After reading user input, the steps are:
    ** (0) preprocess, get the command line and split it 
    **     into tokens
    ** (1) whether input the history command? if it is, 
    **     fetch the old command, continue step 2; or just display 
    **     the last commands and then go to (0).
    **     Otherwise, record the command into the queue, and
    **     go to step (2) 
    ** (2) if input 'exit', go to (4) 
    **     Otherwise, fork a child process using fork()
    ** (3) the child process will invoke execvp() to execute
    **     if command included &, parent will not wait(). Go to
    **     (0)
    ** (4) exit the program
    */

    /*
    ** (0) get the command line up to 80 characters 
    */
    fgets(cmd, MAX_LINE, stdin);
    if(cmd[strlen(cmd) - 1] != '\n')
    {
      fprintf(stderr, "[%s:%d]The command is too long\n", _FL_);
      CLR_BUF;
      continue;
    }

    /*
    ** (0) split the command line into tokens
    */
    token_num = token(cmd, args, MAX_TOKEN);
    if(-1 == token_num)
    {
      fprintf(stderr, "[%s:%d]token error\n", _FL_);
      return 1;
    }
    for(i = token_num; i < MAX_TOKEN; i++)
      args[i] = NULL;

    /*
    ** (0) whether the parent process will wait for the child process
    */
    if(token_num > 0 && 0 == strcmp(args[token_num - 1], "&"))
    {
      is_wait = FALSE;
      args[token_num - 1] = NULL;
    }

    /*
    ** (1) whether input history command
    */
    if(token_num > 0 && 0 == strcmp(args[0], "history"))
    {
      while(TRUE)
      {
        if(1 == token_num)
        {
          /* 
          ** just show the old commands 
          */
          insert_queue(qptr, cmd);
          display_queue(qptr); 
          is_go = FALSE;
          break;
        }
        else if(2 == token_num)
        {
          /*
          ** fetch the history command, then execute it
          */
          his_num = atoi(args[1]);
          if(NULL != (his_cmd = fetch_queue(qptr, his_num)))
          {
            token_num = token(his_cmd, args, MAX_TOKEN);
            if(-1 == token_num)
            {
              fprintf(stderr, "[%s:%d]token error\n", _FL_);
              return 1;
            }
            for(i = token_num; i < MAX_TOKEN; i++)
              args[i] = NULL;
        
            if(token_num > 0 && 0 == strcmp(args[token_num - 1], "&"))
            {
              is_wait = FALSE;
              args[token_num - 1] = NULL;
            }

            if(token_num > 0 && 0 != strcmp(args[0], "history"))
            {
              insert_queue(qptr, cmd);
              is_go = TRUE;
              break;
            }
          }
          else
          {
            fprintf(stderr, "Not found\n");
            is_go = FALSE;
            break;
          }
        }
        else
        {
          fprintf(stderr, "usage: history [record num]\n");
          is_go = FALSE;
          break;
        }
      }

      /*
      ** get the final history command, continue executing
      */
      if(!is_go)
        continue;
    }
    else if(token_num > 0)
    {
      /*
      ** not history command, just record it 
      */
      insert_queue(qptr, cmd);
    }

    /*
    ** (2) whether input "exit"
    */
    if(token_num > 0 && 0 != strcmp(args[0], "exit"))
    {
      /*
      ** not 'exit', fork a child process to execute 
      */
      pid = fork();

      if(pid < 0)
      {
        fprintf(stderr, "[%s:%d]fork() err[%s]\n", _FL_, strerror(errno));
        return 1;
      }
      else if(0 == pid)     /* child process */
      {
         execvp(args[0], args);
         break;
      }
      else                  /* parent process */
      {
        if(is_wait)
        {
          wait(NULL);
        }
        is_wait = TRUE;
      }
    }
    else if(0 == token_num)
    {
      continue;
    }
    else     
    {
      /*
      ** input 'exit', exit the program
      */
      should_run = FALSE;
    }
    /*
    ** free the heap resource
    */
    for(i = 0; i < token_num; i++)
      free(args[i]);
  }
  return 0;
}

/*
** int token(char *str)
** operation:
**   divide the command string into seperate tokens
** input:
**   @str          -- command string
**   @args         -- hold the tokens
**   @num          -- maximum tokens
** output:
**   return the number of tokens
*/
int token(char *str, char **args, int num)
{
  int is_space = TRUE;
  int i = 0;
  int j = 0;
  int k = 0;
  char tmp[MAX_LINE];

  if(num <= 0)
  {
    fprintf(stderr, "[%s:%d]Illegal arguments num(> 0): %d\n", _FL_, num);
    return 1;
  }

  while(str[i] != '\0')
  {
    if(isspace(str[i]))
    {
      if(!is_space)     /* have fetched a token */
      {
        if(k >= num)
        {
          fprintf(stderr, "[%s:%d]Reach the mamximum tokens: %d\n", _FL_, num);
          return -1;
        }

        /*
        ** generate a new token
        */
        tmp[j] = '\0';
        args[k] = (char *)malloc(MAX_LINE * sizeof(char));
        if(NULL == args[k])
        {
          fprintf(stderr, "[%s:%d]malloc err[%s]\n", _FL_, strerror(errno));
          return -1;
        }
        strcpy(args[k], tmp);

        /*
        ** after handling the token, initialize the variables
        */
        k++;      /* next token */
        j = 0;    /* begin to fetch token */
      }
      is_space = TRUE;
    }
    else if(isprint(str[i]))
    {
      is_space = FALSE;
      tmp[j++] = str[i];
    }
    else
    {
      fprintf(stderr, "[%s:%d]Illegal input: %03o\n", _FL_, str[i]);
      return -1;
    }
    i++;     /* next character */
  }
  return k;
}

二、history指令维护的队列接口声明:

/*
** queue.h
**   declare a data structure to manage the history command
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define HIS_NUM 10
#define REL_NUM (HIS_NUM + 1)
#define MAX_LINE 80

typedef struct queue
{
  char content[REL_NUM][MAX_LINE];
  int bgn_index;
  int end_index;
  int count;
} Queue;

typedef Queue* Qptr;

void init_queue(Qptr qptr);

void insert_queue(Qptr qptr, char *cmd);

void display_queue(Qptr qptr);

char *fetch_queue(Qptr qptr, int his_num);

三、history指令维护的队列的接口实现:

/*
** queue.h
**   declare a data structure to manage the history command
*/
#include"queue.h"

void init_queue(Qptr qptr)
{
  qptr->bgn_index = 0;
  qptr->end_index = 0;
  qptr->count = 0;
}

void insert_queue(Qptr qptr, char *cmd)
{
  strcpy(qptr->content[qptr->end_index], cmd);
  qptr->end_index = (qptr->end_index + 1) % REL_NUM;

  if(qptr->count < HIS_NUM)
    qptr->count++;

  if(qptr->end_index == qptr->bgn_index)
    qptr->bgn_index = (qptr->bgn_index + 1) % REL_NUM;
}

void display_queue(Qptr qptr)
{
  int i = 0;
  int count = qptr->count;
  if(qptr->end_index != qptr->bgn_index)
  {
    i = qptr->bgn_index;
    while(i != qptr->end_index)
    {
      printf("%02d %s\n", count, qptr->content[i]);
      HIS_NUM == i ? i = 0 : i++;
      count--;
    }
  }
  else
    printf("No history command\n");
}

char *fetch_queue(Qptr qptr, int his_num)
{
  int i = 0;
  char *tmp = NULL;
  if(his_num <= 0 || his_num > qptr->count)
  {
    fprintf(stderr, "Bad history record\n");
  }
  else
  {
    i = qptr->end_index;
    if(i - his_num < 0)
       i += HIS_NUM;
    i -= his_num;
    tmp = qptr->content[i]; 
  }
  return tmp;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值