首页 > 解决方案 > 奇怪的 Malloc 错误(以及涉及此 shell 的其他问题)

问题描述

我正在为我正在从事的 C 项目开发 Shell,每当我直接与存储所有作业和进程的列表交互时,我都会遇到这个奇怪的 Malloc 错误,我总是得到一个奇怪的 Malloc 错误,看起来像这个:

这里发生了什么?

你能帮我弄清楚这件事是怎么回事吗?如果可能的话,也许你也可以给我其他关于如何改进它的建议?因为我真的很感激。

下面是代码:

 *
 * $ gcc shell.c csapp.c -lpthread -o shell
 *
 *
 * $ ./shell
 */

#include "csapp.h"

#define TRUE 1
#define FALSE 0

#define MAXARGS 128

#define MAXCHARS 64

pid_t fg_pid = 0;
int next_jid = 1;


typedef struct list_t
{
    pid_t pid;
    int jid;
    char *runstat;
    char *cmdline;
    struct list_t *next;
} list_t;

list_t *jobs_list = NULL;

void add_element(list_t **list, pid_t pid, int jid, char *runstat, char *cmdline)
{
    list_t *e;

    if (*list == NULL)  // New empty list.
    {
        *list = (list_t *) malloc(sizeof(list_t));
        (*list)->pid = pid;
        (*list)->jid = jid;
        (*list)->runstat = strndup(runstat, MAXCHARS);
        (*list)->cmdline = strndup(cmdline, MAXCHARS);
        (*list)->next = NULL;
    }
    else  // List with at least one element.
    {
        // Loop through elements, so that e is left
        // pointing to the last one in the list.
        for (e = *list; e->next != NULL; e = e->next)
            ; // (Do nothing.)

        e->next = (list_t *) malloc(sizeof(list_t));
        e = e->next;
        e->pid = pid;
        e->jid = jid;
        e->runstat = strndup(runstat, MAXCHARS);
        e->cmdline = strndup(cmdline, MAXCHARS);
        e->next = NULL;
    }
}

void fg_list_handler(list_t ** list, pid_t pid, int jid) {

}

void change_running_status(list_t **list, pid_t pid, char *runstat) {
  //THe code I wrote with changing statuses in the list for programs.

  list_t *e;

  e = *list;

  if (e->next == NULL) {
    strncpy(e->runstat, runstat, MAXCHARS);
  } else {
    for (e; e != NULL; e->next) {
      if (pid == e->pid) {
        strncpy(e->runstat, runstat, MAXCHARS);
        break;
        }
  
    }
  }

 

}

void sigint_handler(int signal) {

    // Restore default behavior for next SIGINT (which will likely
    // come from call to raise at the end of this function).
    Signal(SIGINT, SIG_DFL);

    if (fg_pid != 0) {
      Kill(-fg_pid, SIGINT); //Exits out of the child process (- = Send to group).
      printf("Job %d has been terminated by: User Interrupt (SIGINT) \n", fg_pid);
      Signal(SIGINT, sigint_handler);
    } else {
      // Send SIGINT to self.  (This time won't be caught be handler,
      // will instead cause process to terminate.)
      raise(SIGINT); 
    }
  
    
}


void sigtstp_handler(int signal) {
  //Restores SIGSTOP to normal behavior.
  Signal(SIGTSTP, SIG_DFL);
  
  //Stops the process.
  if (fg_pid != 0) {
    kill(-fg_pid, SIGTSTP);
    printf("Job %d has been stopped by: User Stop (SIGTSTP)\n", fg_pid);
    Signal(SIGTSTP, sigtstp_handler);
  } else { 
    raise(SIGTSTP);
  }

}

/*
 * Populate argv with pointers to places in line where arguments
 * begin (and put \0 in buf where arguments end), so that argv[0] is
 * pointer to first argument, argv[1] pointer to second, etc.
 *
 * (You should't need to make any changes to this function.)
 */
int parseline(char *line, char **argv) {
    char *cp;
    int in_arg = FALSE;
    int argc = 0;
    int bg = FALSE;

    // Go through line, one character at a time, until reaching the
    // newline character at the end.
    for (cp = line; *cp != '\n'; cp++) {
    
        if (in_arg) {
        
            // If at the end of an argument...
            if (*cp == ' ') {
                *cp = '\0'; // Mark end of argument.
                in_arg = FALSE;
            }
        } else if (*cp != ' ') { // If at beginning of new argument...
            argv[argc++] = cp;   // Set argv array element to point to
                                 // new argument.
            in_arg = TRUE;
        }
    }

    *cp = '\0';  // Mark end of last argument (which was probably
                 // followed by \n, not space).

    // If at least one argument, and last argument is &, process is
    // to be run in background.
    if (argc > 0 && *argv[argc - 1] == '&') {
        bg = TRUE;
        argv[argc - 1] = NULL; // Overwrite address of "&" to mark
                               // end of argv.
    
    } else {                   // (Process should run in foreground.)
        argv[argc] = NULL;     // Mark end of argv.
    }

    return bg;
}

/*
 * If argv[0] is a builtin command, run it and return TRUE.  If it's
 * not, return FALSE.
 */
int builtin_command(char **argv) {

    if (strcmp(argv[0], "quit") == 0) {
        // (Don't bother to return, just end the program.)
        exit(0);
    
    } else if (strcmp(argv[0], "&") == 0) {
        // (Ignore & if it isn't preceded by a command.)
        return TRUE;
    } else if (strcmp(argv[0], "jobs") == 0) {
      // Prints list of background and stopped jobs.

      list_t *e;

      char *runstat[MAXLINE];
      for (e = jobs_list; e != NULL; e = e->next) {
        strncpy(runstat, e->runstat, MAXCHARS);
        //Eventually going to add an additional argument to allow it to print different lists depending on the argument.
        
        //Prints the process only if it's currently running in the system.
        if (strncmp(e->runstat, "running", MAXCHARS) == 0) {
          printf("[%d], %d, %s, %s", e->jid, e->pid, e->runstat, e->cmdline);
        }
        
      }
        

      return TRUE;
    } else if (strcmp(argv[0], "bg")) {

    }

    return FALSE;
}


/*
 * Evaluate command (a line of arguments).
 */
void eval(char *cmdline, char **envp) {
    char *argv[MAXARGS];
    char buf[MAXLINE];
    int bg;
    pid_t pid;
    int jid;
    char *runstat[MAXLINE];
    
    //Used for my current implementation of status checking.
    int status;

    // Copy cmdline to buf, use parseline to populate argv based
    // on what's in buf (and set bg based on value returned from
    // parseline).
    strcpy(buf, cmdline);
    bg = parseline(buf, argv);

    // If at least one argument, and it's not a builtin command...
    // (If it is a builtin command the builtin_command function will
    // run it too, not just check whether it's builtin.)
    if (argv[0] != NULL && !builtin_command(argv)) {
        pid = Fork();

        if (pid == 0) { // In child.
          //Added to work with child processes and groups of processes.
          pid = getpid();
          setpgid(pid, pid);

          if (execve(argv[0], argv, envp) < 0) {
            printf("%s is an invalid command.\n", argv[0]);
            exit(0);
          }   

        } else if (!bg) { // In parent, child running in foreground.
            fg_pid = pid;
            strncpy(runstat, "running", MAXCHARS);
            
            jid = next_jid++;
            //Testing Print.
            printf("[%d] %d %s %s", jid, pid, runstat, cmdline);
            add_element(&jobs_list, pid, jid, runstat, cmdline);

            
            if (waitpid(pid, &status, WUNTRACED) != 0)
            {
            if (fg_pid != 0) {
              //added check due to the first if executing down here for no reason.
            
              if (WIFEXITED(status) >= 1) {

                strncpy(runstat, "exited", MAXCHARS);
                //change_running_status(&jobs_list, pid, runstat);
                printf("[%d] %d %s %s", jid, pid, runstat, cmdline);

              } else if (WIFSIGNALED(status) >= 1) {

                strncpy(runstat, "interrupted", MAXCHARS);
                //change_running_status(&jobs_list, pid, runstat);
                printf("[%d] %d %s %s", jid, pid, runstat, cmdline);
              
              } else if (WIFSTOPPED(status) >= 1) {
                strncpy(runstat, "stopped", MAXCHARS);
                change_running_status(&jobs_list, pid, runstat);
                printf("[%d] %d %s %s", jid, pid, runstat, cmdline);

              }
            }
 
            }
            
            
            
          fg_pid = 0;
            
            
            
        } else {          // In parent, child running in background.
        //Implemented the whole running thing in my usual crude methods of doing so.
            strncpy(runstat, "running", MAXCHARS);
            jid = next_jid++;
            //runstat = 'Running';
            //printf("[%d] %d %s %s", jid, pid, runstat, cmdline);

            add_element(&jobs_list, pid, jid, runstat, cmdline);
        }
    }
}

int main(int argc, char **argv, char **envp) {
    char cmdline[MAXLINE];

    Signal(SIGINT, sigint_handler);
    Signal(SIGTSTP, sigtstp_handler);

    while (TRUE) {      // exit(0) will be called from builtin_command
                        // if user enters "quit" command.
        printf("> ");
        Fgets(cmdline, MAXLINE, stdin);
        eval(cmdline, envp);
    }
}

标签: clistshellgccmalloc

解决方案


  • 不要使用strndup()strncpy()在您阅读他们的手册页之前
  • [使用手册页后,您无论如何都不会使用它们]
  • 不要printf()在信号处理程序中使用和朋友;它们不是信号安全的。

推荐阅读