首页 > 解决方案 > 带有后台任务的 C shell

问题描述

我即将在 C 中实现一个 shell,我有一些问题。

它应该能够在前台或后台运行程序。如果在命令前输入“job”,它应该在后台运行,我想将这些后台任务组织在一个列表中。使用命令“list”,所有后台任务都应显示在命令行上。

如果我使用作业命令添加一些后台任务,我会遇到 pid 始终为 0 的问题,例如当我添加两个任务然后执行 list-command 时,两者的 pid 都是 0。我的错误是什么?

另一个问题是,我想为新的列表元素设置属性“命令”,但列表中每个元素的命令总是会设置为这个值。我在这里错在哪里?我该如何解决这个问题?

这是我的代码:

shell.c(主类):

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

#include "dll.h"

#define MAXIMUM_INPUT 256

struct proc_info {
    struct list_head head;
    pid_t pid;
    char* command;
    char* running;
    int number;
    int status;
};

void print_procs(struct proc_info *proc_list);

void executecmd(char* command);

int makeargv(const char *s, const char *delimiters, char ***argvp);

void freemakeargv(char **argv);

int main() {
    //Initialize list
    struct list_head anchor;
    list_init(&anchor);
    int count = 0;
    char input[256];
    pid_t newProcess;

    while(1) {

        //Prompt
        printf("tsh> ");

        //Read input
        if (fgets(input, 256, stdin) == NULL)
            continue;
        int len = strlen(input);
        if(len == 1) //Only the "\n", fgets puts "\n" at the end of the string
            continue;
        else if (input[len - 1] == '\n')
            input[len - 1] = 0;

        //Make a list of strings with each argument out of the string of all arguments
        char** args;
        makeargv(input , " ", &args);

        char* firstArgument = *args;

        if(strcmp(firstArgument, "job") == 0) {
            char *command;
            strncpy(command, input+4, len-4);

            struct proc_info *new_proc;
            new_proc = malloc(sizeof(struct proc_info));
            new_proc->command = command;
            new_proc->number = count;
            new_proc->running = "running";
            count++;
            list_add_tail(&(new_proc->head), &anchor);

            if ((newProcess = fork()) == -1)
                perror("Error when trying to fork");
            else if (newProcess == 0) {
                new_proc->pid = getpid();
                execvp(args[1], args+1);
                perror("Cannot execute command");
                printf("tsh>");
                exit(1);                
            }
        } else if(strcmp(firstArgument, "list") == 0) {
            print_procs((struct proc_info *) &anchor);
        } else if(strcmp(firstArgument, "info") == 0) {
            printf("ToDo");
        } else if(strcmp(firstArgument, "wait") == 0) {
            printf("ToDo");
        } else if(strcmp(firstArgument, "kill") == 0) {
            printf("ToDo");
        } else if(strcmp(firstArgument, "quit") == 0) {
            return 0;
        } else {
            int status = system(input);
            if(status != 0) {
                printf("--->[invalid command]<---\n");
            }
            printf("--->[status=%d]<---\n", status);     
        }

        //Free allocated memory for the arguments
        freemakeargv(args);
    }
}

void print_procs(struct proc_info *proc_list) {
    struct proc_info *elem = proc_list;
    elem = (struct proc_info *) elem->head.next;
    printf(" No | PID    | Running  | Status | Command\n");
    while(proc_list != elem) {
        printf("%3d | %6d | %8s | %6d | %s\n", elem->number, elem->pid, elem->running, elem->status, elem->command);
        elem = (struct proc_info *) elem->head.next;
    }
}

int makeargv(const char *s, const char *delimiters, char ***argvp) {
   int error;
   int i;
   int numtokens;
   const char *snew;
   char *t;

   if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
      errno = EINVAL;
      return -1;
   }
   *argvp = NULL;                           
   snew = s + strspn(s, delimiters);         /* snew is real start of string */
   if ((t = malloc(strlen(snew) + 1)) == NULL) 
      return -1; 
   strcpy(t, snew);               
   numtokens = 0;
   if (strtok(t, delimiters) != NULL)     /* count the number of tokens in s */
      for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; 

                             /* create argument array for ptrs to the tokens */
   if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
      error = errno;
      free(t);
      errno = error;
      return -1; 
   } 
                        /* insert pointers to tokens into the argument array */
   if (numtokens == 0) 
      free(t);
   else {
      strcpy(t, snew);
      **argvp = strtok(t, delimiters);
      for (i = 1; i < numtokens; i++)
          *((*argvp) + i) = strtok(NULL, delimiters);
    } 
    *((*argvp) + numtokens) = NULL;             /* put in final NULL pointer */
    return numtokens;
}     

void freemakeargv(char **argv) {
   if (argv == NULL)
      return;
   if (*argv != NULL)
      free(*argv);
   free(argv);
}

dll.c:

#include <stdlib.h>
#include <stdio.h>
#include "dll.h"

/* initialize "shortcut links" for empty list */
void
list_init(struct list_head *head)
{
    head->prev = head;
    head->next = head;
}

/* insert new entry after the specified head */
void 
list_add(struct list_head *new, struct list_head *head)
{
    new->prev = head;
    new->next = head->next;
    head->next = new;
    new->next->prev = new;
}

/* insert new entry before the specified head */
void 
list_add_tail(struct list_head *new, struct list_head *head)
{
    //Insterting before head is equal to inserting after the previous of the head
    list_add(new, head->prev);
}

/* deletes entry from list and reinitialize it, returns pointer to entry */
struct list_head* 
list_del(struct list_head *entry)
{
    struct list_head *previous = entry->prev;
    struct list_head *following = entry->next;
    previous->next = following;
    following->prev = previous;
    entry->prev = NULL;
    entry->next = NULL;
    return entry;
}

/* delete entry from one list and insert after the specified head */
void 
list_move(struct list_head *entry, struct list_head *head)
{
    list_add(list_del(entry), head);
}

/* delete entry from one list and insert before the specified head */
void 
list_move_tail(struct list_head *entry, struct list_head *head)
{
    list_add_tail(list_del(entry), head);
}

/* tests whether a list is empty */
int 
list_empty(struct list_head *head)
{
    return (head == head->prev && head == head->next);
}

dll.h:

#ifndef DLL_H
#define DLL_H

struct list_head {
    struct list_head *next, *prev;
};

/* initialize "shortcut links" for empty list */
extern void
list_init(struct list_head *head);

/* insert new entry after the specified head */
extern void 
list_add(struct list_head *new, struct list_head *head);

/* insert new entry before the specified head */
extern void 
list_add_tail(struct list_head *new, struct list_head *head);

/* deletes entry from list and reinitialize it, returns pointer to entry */
extern struct list_head* 
list_del(struct list_head *entry);

/* delete entry from one list and insert after the specified head */
extern void 
list_move(struct list_head *entry, struct list_head *head);

/* delete entry from one list and insert before the specified head */
extern void 
list_move_tail(struct list_head *entry, struct list_head *head);

/* tests whether a list is empty */
extern int 
list_empty(struct list_head *head);

#endif

标签: cshellbackgroundtask

解决方案


错误是,您在孩子中设置了进程ID

        if ((newProcess = fork()) == -1)
            perror("Error when trying to fork");
        else if (newProcess == 0) {
            // This is executed in the CHILD PROCESS!!
            new_proc->pid = getpid();
            execvp(args[1], args+1);
            perror("Cannot execute command");
            printf("tsh>");
            exit(1);                
        }

子进程不与父进程共享内存,因此信息不会自动传输到父进程。你必须在你的 if-block 之后添加这个:

        new_proc->pid = newProcess;

这样子 pid 就保存在进程中。

对于command-issue:指针未初始化:

char *command;
strncpy(command, input+4, len-4);

您必须在复制到它之前分配内存并确保结果以空字节终止:

char *command = malloc(len-4+1);
strncpy(command, input+4, len-4);
command[len-4] = '\0';

推荐阅读