【问题标题】:Weird Malloc Error (and other problems involving this shell)奇怪的 Malloc 错误(以及涉及此 shell 的其他问题)
【发布时间】:2021-06-16 01:05:27
【问题描述】:

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

What's going on here?

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

下面是代码:

 *
 * $ 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);
    }
}

【问题讨论】:

  • 我强烈怀疑您可以简化相关代码的数量。 stackoverflow.com/help/minimal-reproducible-example
  • Do I cast the result of malloc?。看起来像您的情况,因为您不包括 stdlib.h
  • 我正在使用名为 CSAPP.h 和 CSAPP.c 的第三方软件包,其中包含所有内容。我现在就上传其余的。
  • 这称为“堆损坏”。这意味着您覆盖了一些不属于您的内存并且它由 malloc 拥有并且您使 malloc 崩溃。试着找出你在哪里覆盖了不属于你的内存。
  • 用gcc -Wall -Wextra -g 编译你的(和其他)C 代码,然后使用valgrind 和GDB 或者strace(1)

标签: c list shell gcc malloc


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

【讨论】:

    【解决方案2】:

    Sleeper.c:

    /*
     * sleeper.c
     */
    
    /*
     * Get csapp.h and csapp.c from http://csapp.cs.cmu.edu/3e/code.html
     *
     * $ gcc sleeper.c csapp.c -o sleeper
     *
     * (Ignore any compliation warnings related to code in csapp.c.)
     *
     * $ ./sleeper
     *
     * ... CTRL-C to terminate and see message indicating sleeper
     * caught SIGINT.  CTRL-Z to stop and see message indicating
     * sleeper caught SIGTSTP.  "jobs" to see list of stopped jobs
     * with numerical IDs (including ./sleeper).  "fg %1" to restart
     * job with ID 1 (i.e., sleeper) and see message indicating
     * sleeper caught SIGCONT.
     */
    
    #include "csapp.h"
    
    int pid;
    
    pid_t fg_pid;
    
    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);
    
        printf("\nsleeper (%d) caught SIGINT\n", pid);
    
        // 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) {
       
        // Restore default behavior for next SIGTSTP ...
        Signal(SIGTSTP, SIG_DFL);
        
        printf("\nsleeper (%d) caught SIGTSTP\n", pid);
       
        // Send SIGTSTP to self ...
        raise(SIGTSTP);
    }
    
    void sigcont_handler(int signal) {
    
        // Don't need to restore default behavior of SIGCONT, because
        // default (for already running process) is to ignore it.
    
        // Prepare for next SIGTSTP signal.
        Signal(SIGTSTP, sigtstp_handler);
    
        printf("\nsleeper (%d) caught SIGCONT\n", pid);
    
        // Don't need to raise SIGCONT, because process is already
        // running (and would ignore it).
    }
    
    // First command line argument is number of times to sleep.  (0
    // or no argument means to continue indefinitely.)  Second argument
    // -q for quiet sleeper.
    int main(int argc, char **argv) {
        int i, n, q;
    
        n = argc > 1 ? atoi(argv[1]) : 0;
        q = argc > 2 && strcmp("-q", argv[2]) == 0;
        pid = getpid();
    
        // Override default behavior for SIGINT, SIGTSTP and SIGCONT.
        Signal(SIGINT, sigint_handler);
        Signal(SIGTSTP, sigtstp_handler);
        Signal(SIGCONT, sigcont_handler);
    
        // Sleep 1 second, wake up, go back to sleep...
        for (i = 0; n == 0 || i < n; i++) {
            Sleep(1);
            
            if (!q) {
                printf("sleeper (%d) slept %d second(s)\n", pid, i + 1);
            }
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      csapp.c:

       * csapp.c
       */
      /* $begin csapp.c */
      #include "csapp.h"
      
      /************************** 
       * Error-handling functions
       **************************/
      /* $begin errorfuns */
      /* $begin unixerror */
      void unix_error(char *msg) /* Unix-style error */
      {
          fprintf(stderr, "%s: %s\n", msg, strerror(errno));
          exit(0);
      }
      /* $end unixerror */
      
      void posix_error(int code, char *msg) /* Posix-style error */
      {
          fprintf(stderr, "%s: %s\n", msg, strerror(code));
          exit(0);
      }
      
      void gai_error(int code, char *msg) /* Getaddrinfo-style error */
      {
          fprintf(stderr, "%s: %s\n", msg, gai_strerror(code));
          exit(0);
      }
      
      void app_error(char *msg) /* Application error */
      {
          fprintf(stderr, "%s\n", msg);
          exit(0);
      }
      /* $end errorfuns */
      
      void dns_error(char *msg) /* Obsolete gethostbyname error */
      {
          fprintf(stderr, "%s\n", msg);
          exit(0);
      }
      
      
      /*********************************************
       * Wrappers for Unix process control functions
       ********************************************/
      
      /* $begin forkwrapper */
      pid_t Fork(void) 
      {
          pid_t pid;
      
          if ((pid = fork()) < 0)
          unix_error("Fork error");
          return pid;
      }
      /* $end forkwrapper */
      
      void Execve(const char *filename, char *const argv[], char *const envp[]) 
      {
          if (execve(filename, argv, envp) < 0)
          unix_error("Execve error");
      }
      
      /* $begin wait */
      pid_t Wait(int *status) 
      {
          pid_t pid;
      
          if ((pid  = wait(status)) < 0)
          unix_error("Wait error");
          return pid;
      }
      /* $end wait */
      
      pid_t Waitpid(pid_t pid, int *iptr, int options) 
      {
          pid_t retpid;
      
          if ((retpid  = waitpid(pid, iptr, options)) < 0) 
          unix_error("Waitpid error");
          return(retpid);
      }
      
      /* $begin kill */
      void Kill(pid_t pid, int signum) 
      {
          int rc;
      
          if ((rc = kill(pid, signum)) < 0)
          unix_error("Kill error");
      }
      /* $end kill */
      
      void Pause() 
      {
          (void)pause();
          return;
      }
      
      unsigned int Sleep(unsigned int secs) 
      {
          unsigned int rc;
      
          if ((rc = sleep(secs)) < 0)
          unix_error("Sleep error");
          return rc;
      }
      
      unsigned int Alarm(unsigned int seconds) {
          return alarm(seconds);
      }
       
      void Setpgid(pid_t pid, pid_t pgid) {
          int rc;
      
          if ((rc = setpgid(pid, pgid)) < 0)
          unix_error("Setpgid error");
          return;
      }
      
      pid_t Getpgrp(void) {
          return getpgrp();
      }
      
      /************************************
       * Wrappers for Unix signal functions 
       ***********************************/
      
      /* $begin sigaction */
      handler_t *Signal(int signum, handler_t *handler) 
      {
          struct sigaction action, old_action;
      
          action.sa_handler = handler;  
          sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
          action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
      
          if (sigaction(signum, &action, &old_action) < 0)
          unix_error("Signal error");
          return (old_action.sa_handler);
      }
      /* $end sigaction */
      
      void Sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
      {
          if (sigprocmask(how, set, oldset) < 0)
          unix_error("Sigprocmask error");
          return;
      }
      
      void Sigemptyset(sigset_t *set)
      {
          if (sigemptyset(set) < 0)
          unix_error("Sigemptyset error");
          return;
      }
      
      void Sigfillset(sigset_t *set)
      { 
          if (sigfillset(set) < 0)
          unix_error("Sigfillset error");
          return;
      }
      
      void Sigaddset(sigset_t *set, int signum)
      {
          if (sigaddset(set, signum) < 0)
          unix_error("Sigaddset error");
          return;
      }
      
      void Sigdelset(sigset_t *set, int signum)
      {
          if (sigdelset(set, signum) < 0)
          unix_error("Sigdelset error");
          return;
      }
      
      int Sigismember(const sigset_t *set, int signum)
      {
          int rc;
          if ((rc = sigismember(set, signum)) < 0)
          unix_error("Sigismember error");
          return rc;
      }
      
      int Sigsuspend(const sigset_t *set)
      {
          int rc = sigsuspend(set); /* always returns -1 */
          if (errno != EINTR)
              unix_error("Sigsuspend error");
          return rc;
      }
      
      /*************************************************************
       * The Sio (Signal-safe I/O) package - simple reentrant output
       * functions that are safe for signal handlers.
       *************************************************************/
      
      /* Private sio functions */
      
      /* $begin sioprivate */
      /* sio_reverse - Reverse a string (from K&R) */
      static void sio_reverse(char s[])
      {
          int c, i, j;
      
          for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
              c = s[i];
              s[i] = s[j];
              s[j] = c;
          }
      }
      
      /* sio_ltoa - Convert long to base b string (from K&R) */
      static void sio_ltoa(long v, char s[], int b) 
      {
          int c, i = 0;
          int neg = v < 0;
      
          if (neg)
          v = -v;
      
          do {  
              s[i++] = ((c = (v % b)) < 10)  ?  c + '0' : c - 10 + 'a';
          } while ((v /= b) > 0);
      
          if (neg)
          s[i++] = '-';
      
          s[i] = '\0';
          sio_reverse(s);
      }
      
      /* sio_strlen - Return length of string (from K&R) */
      static size_t sio_strlen(char s[])
      {
          int i = 0;
      
          while (s[i] != '\0')
              ++i;
          return i;
      }
      /* $end sioprivate */
      
      /* Public Sio functions */
      /* $begin siopublic */
      
      ssize_t sio_puts(char s[]) /* Put string */
      {
          return write(STDOUT_FILENO, s, sio_strlen(s)); //line:csapp:siostrlen
      }
      
      ssize_t sio_putl(long v) /* Put long */
      {
          char s[128];
          
          sio_ltoa(v, s, 10); /* Based on K&R itoa() */  //line:csapp:sioltoa
          return sio_puts(s);
      }
      
      void sio_error(char s[]) /* Put error message and exit */
      {
          sio_puts(s);
          _exit(1);                                      //line:csapp:sioexit
      }
      /* $end siopublic */
      
      /*******************************
       * Wrappers for the SIO routines
       ******************************/
      ssize_t Sio_putl(long v)
      {
          ssize_t n;
        
          if ((n = sio_putl(v)) < 0)
          sio_error("Sio_putl error");
          return n;
      }
      
      ssize_t Sio_puts(char s[])
      {
          ssize_t n;
        
          if ((n = sio_puts(s)) < 0)
          sio_error("Sio_puts error");
          return n;
      }
      
      void Sio_error(char s[])
      {
          sio_error(s);
      }
      
      /********************************
       * Wrappers for Unix I/O routines
       ********************************/
      
      int Open(const char *pathname, int flags, mode_t mode) 
      {
          int rc;
      
          if ((rc = open(pathname, flags, mode))  < 0)
          unix_error("Open error");
          return rc;
      }
      
      ssize_t Read(int fd, void *buf, size_t count) 
      {
          ssize_t rc;
      
          if ((rc = read(fd, buf, count)) < 0) 
          unix_error("Read error");
          return rc;
      }
      
      ssize_t Write(int fd, const void *buf, size_t count) 
      {
          ssize_t rc;
      
          if ((rc = write(fd, buf, count)) < 0)
          unix_error("Write error");
          return rc;
      }
      
      off_t Lseek(int fildes, off_t offset, int whence) 
      {
          off_t rc;
      
          if ((rc = lseek(fildes, offset, whence)) < 0)
          unix_error("Lseek error");
          return rc;
      }
      
      void Close(int fd) 
      {
          int rc;
      
          if ((rc = close(fd)) < 0)
          unix_error("Close error");
      }
      
      int Select(int  n, fd_set *readfds, fd_set *writefds,
             fd_set *exceptfds, struct timeval *timeout) 
      {
          int rc;
      
          if ((rc = select(n, readfds, writefds, exceptfds, timeout)) < 0)
          unix_error("Select error");
          return rc;
      }
      
      int Dup2(int fd1, int fd2) 
      {
          int rc;
      
          if ((rc = dup2(fd1, fd2)) < 0)
          unix_error("Dup2 error");
          return rc;
      }
      
      void Stat(const char *filename, struct stat *buf) 
      {
          if (stat(filename, buf) < 0)
          unix_error("Stat error");
      }
      
      void Fstat(int fd, struct stat *buf) 
      {
          if (fstat(fd, buf) < 0)
          unix_error("Fstat error");
      }
      
      /*********************************
       * Wrappers for directory function
       *********************************/
      
      DIR *Opendir(const char *name) 
      {
          DIR *dirp = opendir(name); 
      
          if (!dirp)
              unix_error("opendir error");
          return dirp;
      }
      
      struct dirent *Readdir(DIR *dirp)
      {
          struct dirent *dep;
          
          errno = 0;
          dep = readdir(dirp);
          if ((dep == NULL) && (errno != 0))
              unix_error("readdir error");
          return dep;
      }
      
      int Closedir(DIR *dirp) 
      {
          int rc;
      
          if ((rc = closedir(dirp)) < 0)
              unix_error("closedir error");
          return rc;
      }
      
      /***************************************
       * Wrappers for memory mapping functions
       ***************************************/
      void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) 
      {
          void *ptr;
      
          if ((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *) -1))
          unix_error("mmap error");
          return(ptr);
      }
      
      void Munmap(void *start, size_t length) 
      {
          if (munmap(start, length) < 0)
          unix_error("munmap error");
      }
      
      /***************************************************
       * Wrappers for dynamic storage allocation functions
       ***************************************************/
      
      void *Malloc(size_t size) 
      {
          void *p;
      
          if ((p  = malloc(size)) == NULL)
          unix_error("Malloc error");
          return p;
      }
      
      void *Realloc(void *ptr, size_t size) 
      {
          void *p;
      
          if ((p  = realloc(ptr, size)) == NULL)
          unix_error("Realloc error");
          return p;
      }
      
      void *Calloc(size_t nmemb, size_t size) 
      {
          void *p;
      
          if ((p = calloc(nmemb, size)) == NULL)
          unix_error("Calloc error");
          return p;
      }
      
      void Free(void *ptr) 
      {
          free(ptr);
      }
      
      /******************************************
       * Wrappers for the Standard I/O functions.
       ******************************************/
      void Fclose(FILE *fp) 
      {
          if (fclose(fp) != 0)
          unix_error("Fclose error");
      }
      
      FILE *Fdopen(int fd, const char *type) 
      {
          FILE *fp;
      
          if ((fp = fdopen(fd, type)) == NULL)
          unix_error("Fdopen error");
      
          return fp;
      }
      
      char *Fgets(char *ptr, int n, FILE *stream) 
      {
          char *rptr;
      
          if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream))
          app_error("Fgets error");
      
          return rptr;
      }
      
      FILE *Fopen(const char *filename, const char *mode) 
      {
          FILE *fp;
      
          if ((fp = fopen(filename, mode)) == NULL)
          unix_error("Fopen error");
      
          return fp;
      }
      
      void Fputs(const char *ptr, FILE *stream) 
      {
          if (fputs(ptr, stream) == EOF)
          unix_error("Fputs error");
      }
      
      size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 
      {
          size_t n;
      
          if (((n = fread(ptr, size, nmemb, stream)) < nmemb) && ferror(stream)) 
          unix_error("Fread error");
          return n;
      }
      
      void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 
      {
          if (fwrite(ptr, size, nmemb, stream) < nmemb)
          unix_error("Fwrite error");
      }
      
      
      /**************************** 
       * Sockets interface wrappers
       ****************************/
      
      int Socket(int domain, int type, int protocol) 
      {
          int rc;
      
          if ((rc = socket(domain, type, protocol)) < 0)
          unix_error("Socket error");
          return rc;
      }
      
      void Setsockopt(int s, int level, int optname, const void *optval, int optlen) 
      {
          int rc;
      
          if ((rc = setsockopt(s, level, optname, optval, optlen)) < 0)
          unix_error("Setsockopt error");
      }
      
      void Bind(int sockfd, struct sockaddr *my_addr, int addrlen) 
      {
          int rc;
      
          if ((rc = bind(sockfd, my_addr, addrlen)) < 0)
          unix_error("Bind error");
      }
      
      void Listen(int s, int backlog) 
      {
          int rc;
      
          if ((rc = listen(s,  backlog)) < 0)
          unix_error("Listen error");
      }
      
      int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) 
      {
          int rc;
      
          if ((rc = accept(s, addr, addrlen)) < 0)
          unix_error("Accept error");
          return rc;
      }
      
      void Connect(int sockfd, struct sockaddr *serv_addr, int addrlen) 
      {
          int rc;
      
          if ((rc = connect(sockfd, serv_addr, addrlen)) < 0)
          unix_error("Connect error");
      }
      
      /*******************************
       * Protocol-independent wrappers
       *******************************/
      /* $begin getaddrinfo */
      void Getaddrinfo(const char *node, const char *service, 
                       const struct addrinfo *hints, struct addrinfo **res)
      {
          int rc;
      
          if ((rc = getaddrinfo(node, service, hints, res)) != 0) 
              gai_error(rc, "Getaddrinfo error");
      }
      /* $end getaddrinfo */
      
      void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, 
                       size_t hostlen, char *serv, size_t servlen, int flags)
      {
          int rc;
      
          if ((rc = getnameinfo(sa, salen, host, hostlen, serv, 
                                servlen, flags)) != 0) 
              gai_error(rc, "Getnameinfo error");
      }
      
      void Freeaddrinfo(struct addrinfo *res)
      {
          freeaddrinfo(res);
      }
      
      void Inet_ntop(int af, const void *src, char *dst, socklen_t size)
      {
          if (!inet_ntop(af, src, dst, size))
              unix_error("Inet_ntop error");
      }
      
      void Inet_pton(int af, const char *src, void *dst) 
      {
          int rc;
      
          rc = inet_pton(af, src, dst);
          if (rc == 0)
          app_error("inet_pton error: invalid dotted-decimal address");
          else if (rc < 0)
              unix_error("Inet_pton error");
      }
      
      /*******************************************
       * DNS interface wrappers. 
       *
       * NOTE: These are obsolete because they are not thread safe. Use
       * getaddrinfo and getnameinfo instead
       ***********************************/
      
      /* $begin gethostbyname */
      struct hostent *Gethostbyname(const char *name) 
      {
          struct hostent *p;
      
          if ((p = gethostbyname(name)) == NULL)
          dns_error("Gethostbyname error");
          return p;
      }
      /* $end gethostbyname */
      
      struct hostent *Gethostbyaddr(const char *addr, int len, int type) 
      {
          struct hostent *p;
      
          if ((p = gethostbyaddr(addr, len, type)) == NULL)
          dns_error("Gethostbyaddr error");
          return p;
      }
      
      /************************************************
       * Wrappers for Pthreads thread control functions
       ************************************************/
      
      void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp, 
                  void * (*routine)(void *), void *argp) 
      {
          int rc;
      
          if ((rc = pthread_create(tidp, attrp, routine, argp)) != 0)
          posix_error(rc, "Pthread_create error");
      }
      
      void Pthread_cancel(pthread_t tid) {
          int rc;
      
          if ((rc = pthread_cancel(tid)) != 0)
          posix_error(rc, "Pthread_cancel error");
      }
      
      void Pthread_join(pthread_t tid, void **thread_return) {
          int rc;
      
          if ((rc = pthread_join(tid, thread_return)) != 0)
          posix_error(rc, "Pthread_join error");
      }
      
      /* $begin detach */
      void Pthread_detach(pthread_t tid) {
          int rc;
      
          if ((rc = pthread_detach(tid)) != 0)
          posix_error(rc, "Pthread_detach error");
      }
      /* $end detach */
      
      void Pthread_exit(void *retval) {
          pthread_exit(retval);
      }
      
      pthread_t Pthread_self(void) {
          return pthread_self();
      }
       
      void Pthread_once(pthread_once_t *once_control, void (*init_function)()) {
          pthread_once(once_control, init_function);
      }
      
      /*******************************
       * Wrappers for Posix semaphores
       *******************************/
      
      void Sem_init(sem_t *sem, int pshared, unsigned int value) 
      {
          if (sem_init(sem, pshared, value) < 0)
          unix_error("Sem_init error");
      }
      
      void P(sem_t *sem) 
      {
          if (sem_wait(sem) < 0)
          unix_error("P error");
      }
      
      void V(sem_t *sem) 
      {
          if (sem_post(sem) < 0)
          unix_error("V error");
      }
      
      /****************************************
       * The Rio package - Robust I/O functions
       ****************************************/
      
      /*
       * rio_readn - Robustly read n bytes (unbuffered)
       */
      /* $begin rio_readn */
      ssize_t rio_readn(int fd, void *usrbuf, size_t n) 
      {
          size_t nleft = n;
          ssize_t nread;
          char *bufp = usrbuf;
      
          while (nleft > 0) {
          if ((nread = read(fd, bufp, nleft)) < 0) {
              if (errno == EINTR) /* Interrupted by sig handler return */
              nread = 0;      /* and call read() again */
              else
              return -1;      /* errno set by read() */ 
          } 
          else if (nread == 0)
              break;              /* EOF */
          nleft -= nread;
          bufp += nread;
          }
          return (n - nleft);         /* Return >= 0 */
      }
      /* $end rio_readn */
      
      /*
       * rio_writen - Robustly write n bytes (unbuffered)
       */
      /* $begin rio_writen */
      ssize_t rio_writen(int fd, void *usrbuf, size_t n) 
      {
          size_t nleft = n;
          ssize_t nwritten;
          char *bufp = usrbuf;
      
          while (nleft > 0) {
          if ((nwritten = write(fd, bufp, nleft)) <= 0) {
              if (errno == EINTR)  /* Interrupted by sig handler return */
              nwritten = 0;    /* and call write() again */
              else
              return -1;       /* errno set by write() */
          }
          nleft -= nwritten;
          bufp += nwritten;
          }
          return n;
      }
      /* $end rio_writen */
      
      
      /* 
       * rio_read - This is a wrapper for the Unix read() function that
       *    transfers min(n, rio_cnt) bytes from an internal buffer to a user
       *    buffer, where n is the number of bytes requested by the user and
       *    rio_cnt is the number of unread bytes in the internal buffer. On
       *    entry, rio_read() refills the internal buffer via a call to
       *    read() if the internal buffer is empty.
       */
      /* $begin rio_read */
      static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
      {
          int cnt;
      
          while (rp->rio_cnt <= 0) {  /* Refill if buf is empty */
          rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, 
                     sizeof(rp->rio_buf));
          if (rp->rio_cnt < 0) {
              if (errno != EINTR) /* Interrupted by sig handler return */
              return -1;
          }
          else if (rp->rio_cnt == 0)  /* EOF */
              return 0;
          else 
              rp->rio_bufptr = rp->rio_buf; /* Reset buffer ptr */
          }
      
          /* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
          cnt = n;          
          if (rp->rio_cnt < n)   
          cnt = rp->rio_cnt;
          memcpy(usrbuf, rp->rio_bufptr, cnt);
          rp->rio_bufptr += cnt;
          rp->rio_cnt -= cnt;
          return cnt;
      }
      /* $end rio_read */
      
      /*
       * rio_readinitb - Associate a descriptor with a read buffer and reset buffer
       */
      /* $begin rio_readinitb */
      void rio_readinitb(rio_t *rp, int fd) 
      {
          rp->rio_fd = fd;  
          rp->rio_cnt = 0;  
          rp->rio_bufptr = rp->rio_buf;
      }
      /* $end rio_readinitb */
      
      /*
       * rio_readnb - Robustly read n bytes (buffered)
       */
      /* $begin rio_readnb */
      ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 
      {
          size_t nleft = n;
          ssize_t nread;
          char *bufp = usrbuf;
          
          while (nleft > 0) {
          if ((nread = rio_read(rp, bufp, nleft)) < 0) 
                  return -1;          /* errno set by read() */ 
          else if (nread == 0)
              break;              /* EOF */
          nleft -= nread;
          bufp += nread;
          }
          return (n - nleft);         /* return >= 0 */
      }
      /* $end rio_readnb */
      
      /* 
       * rio_readlineb - Robustly read a text line (buffered)
       */
      /* $begin rio_readlineb */
      ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 
      {
          int n, rc;
          char c, *bufp = usrbuf;
      
          for (n = 1; n < maxlen; n++) { 
              if ((rc = rio_read(rp, &c, 1)) == 1) {
              *bufp++ = c;
              if (c == '\n') {
                      n++;
                  break;
                  }
          } else if (rc == 0) {
              if (n == 1)
              return 0; /* EOF, no data read */
              else
              break;    /* EOF, some data was read */
          } else
              return -1;    /* Error */
          }
          *bufp = 0;
          return n-1;
      }
      /* $end rio_readlineb */
      
      /**********************************
       * Wrappers for robust I/O routines
       **********************************/
      ssize_t Rio_readn(int fd, void *ptr, size_t nbytes) 
      {
          ssize_t n;
        
          if ((n = rio_readn(fd, ptr, nbytes)) < 0)
          unix_error("Rio_readn error");
          return n;
      }
      
      void Rio_writen(int fd, void *usrbuf, size_t n) 
      {
          if (rio_writen(fd, usrbuf, n) != n)
          unix_error("Rio_writen error");
      }
      
      void Rio_readinitb(rio_t *rp, int fd)
      {
          rio_readinitb(rp, fd);
      } 
      
      ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n) 
      {
          ssize_t rc;
      
          if ((rc = rio_readnb(rp, usrbuf, n)) < 0)
          unix_error("Rio_readnb error");
          return rc;
      }
      
      ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 
      {
          ssize_t rc;
      
          if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0)
          unix_error("Rio_readlineb error");
          return rc;
      } 
      
      /******************************** 
       * Client/server helper functions
       ********************************/
      /*
       * open_clientfd - Open connection to server at <hostname, port> and
       *     return a socket descriptor ready for reading and writing. This
       *     function is reentrant and protocol-independent.
       *
       *     On error, returns: 
       *       -2 for getaddrinfo error
       *       -1 with errno set for other errors.
       */
      /* $begin open_clientfd */
      int open_clientfd(char *hostname, char *port) {
          int clientfd, rc;
          struct addrinfo hints, *listp, *p;
      
          /* Get a list of potential server addresses */
          memset(&hints, 0, sizeof(struct addrinfo));
          hints.ai_socktype = SOCK_STREAM;  /* Open a connection */
          hints.ai_flags = AI_NUMERICSERV;  /* ... using a numeric port arg. */
          hints.ai_flags |= AI_ADDRCONFIG;  /* Recommended for connections */
          if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
              fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
              return -2;
          }
        
          /* Walk the list for one that we can successfully connect to */
          for (p = listp; p; p = p->ai_next) {
              /* Create a socket descriptor */
              if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
                  continue; /* Socket failed, try the next */
      
              /* Connect to the server */
              if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 
                  break; /* Success */
              if (close(clientfd) < 0) { /* Connect failed, try another */  //line:netp:openclientfd:closefd
                  fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
                  return -1;
              } 
          } 
      
          /* Clean up */
          freeaddrinfo(listp);
          if (!p) /* All connects failed */
              return -1;
          else    /* The last connect succeeded */
              return clientfd;
      }
      /* $end open_clientfd */
      
      /*  
       * open_listenfd - Open and return a listening socket on port. This
       *     function is reentrant and protocol-independent.
       *
       *     On error, returns: 
       *       -2 for getaddrinfo error
       *       -1 with errno set for other errors.
       */
      /* $begin open_listenfd */
      int open_listenfd(char *port) 
      {
          struct addrinfo hints, *listp, *p;
          int listenfd, rc, optval=1;
      
          /* Get a list of potential server addresses */
          memset(&hints, 0, sizeof(struct addrinfo));
          hints.ai_socktype = SOCK_STREAM;             /* Accept connections */
          hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
          hints.ai_flags |= AI_NUMERICSERV;            /* ... using port number */
          if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
              fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
              return -2;
          }
      
          /* Walk the list for one that we can bind to */
          for (p = listp; p; p = p->ai_next) {
              /* Create a socket descriptor */
              if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
                  continue;  /* Socket failed, try the next */
      
              /* Eliminates "Address already in use" error from bind */
              setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,    //line:netp:csapp:setsockopt
                         (const void *)&optval , sizeof(int));
      
              /* Bind the descriptor to the address */
              if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
                  break; /* Success */
              if (close(listenfd) < 0) { /* Bind failed, try the next */
                  fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
                  return -1;
              }
          }
      
      
          /* Clean up */
          freeaddrinfo(listp);
          if (!p) /* No address worked */
              return -1;
      
          /* Make it a listening socket ready to accept connection requests */
          if (listen(listenfd, LISTENQ) < 0) {
              close(listenfd);
          return -1;
          }
          return listenfd;
      }
      /* $end open_listenfd */
      
      /****************************************************
       * Wrappers for reentrant protocol-independent helpers
       ****************************************************/
      int Open_clientfd(char *hostname, char *port) 
      {
          int rc;
      
          if ((rc = open_clientfd(hostname, port)) < 0) 
          unix_error("Open_clientfd error");
          return rc;
      }
      
      int Open_listenfd(char *port) 
      {
          int rc;
      
          if ((rc = open_listenfd(port)) < 0)
          unix_error("Open_listenfd error");
          return rc;
      }
      
      /* $end csapp.c */
      

      【讨论】:

        【解决方案4】:

        哦,这还不是全部代码。还有其他几个文件(忽略 CSAPP.c 的错误。当您在编译中使用 lpthread 时它们会自行解决)。

         * csapp.h
         */
        /* $begin csapp.h */
        #ifndef __CSAPP_H__
        #define __CSAPP_H__
        
        #include <stdio.h>
        #include <stdlib.h>
        #include <stdarg.h>
        #include <unistd.h>
        #include <string.h>
        #include <ctype.h>
        #include <setjmp.h>
        #include <signal.h>
        #include <dirent.h>
        #include <sys/time.h>
        #include <sys/types.h>
        #include <sys/wait.h>
        #include <sys/stat.h>
        #include <fcntl.h>
        #include <sys/mman.h>
        #include <errno.h>
        #include <math.h>
        #include <pthread.h>
        #include <semaphore.h>
        #include <sys/socket.h>
        #include <netdb.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        
        /* Default file permissions are DEF_MODE & ~DEF_UMASK */
        /* $begin createmasks */
        #define DEF_MODE   S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
        #define DEF_UMASK  S_IWGRP|S_IWOTH
        /* $end createmasks */
        
        /* Simplifies calls to bind(), connect(), and accept() */
        /* $begin sockaddrdef */
        typedef struct sockaddr SA;
        /* $end sockaddrdef */
        
        /* Persistent state for the robust I/O (Rio) package */
        /* $begin rio_t */
        #define RIO_BUFSIZE 8192
        typedef struct {
            int rio_fd;                /* Descriptor for this internal buf */
            int rio_cnt;               /* Unread bytes in internal buf */
            char *rio_bufptr;          /* Next unread byte in internal buf */
            char rio_buf[RIO_BUFSIZE]; /* Internal buffer */
        } rio_t;
        /* $end rio_t */
        
        /* External variables */
        extern int h_errno;    /* Defined by BIND for DNS errors */ 
        extern char **environ; /* Defined by libc */
        
        /* Misc constants */
        #define MAXLINE  8192  /* Max text line length */
        #define MAXBUF   8192  /* Max I/O buffer size */
        #define LISTENQ  1024  /* Second argument to listen() */
        
        /* Our own error-handling functions */
        void unix_error(char *msg);
        void posix_error(int code, char *msg);
        void dns_error(char *msg);
        void gai_error(int code, char *msg);
        void app_error(char *msg);
        
        /* Process control wrappers */
        pid_t Fork(void);
        void Execve(const char *filename, char *const argv[], char *const envp[]);
        pid_t Wait(int *status);
        pid_t Waitpid(pid_t pid, int *iptr, int options);
        void Kill(pid_t pid, int signum);
        unsigned int Sleep(unsigned int secs);
        void Pause(void);
        unsigned int Alarm(unsigned int seconds);
        void Setpgid(pid_t pid, pid_t pgid);
        pid_t Getpgrp();
        
        /* Signal wrappers */
        typedef void handler_t(int);
        handler_t *Signal(int signum, handler_t *handler);
        void Sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
        void Sigemptyset(sigset_t *set);
        void Sigfillset(sigset_t *set);
        void Sigaddset(sigset_t *set, int signum);
        void Sigdelset(sigset_t *set, int signum);
        int Sigismember(const sigset_t *set, int signum);
        int Sigsuspend(const sigset_t *set);
        
        /* Sio (Signal-safe I/O) routines */
        ssize_t sio_puts(char s[]);
        ssize_t sio_putl(long v);
        void sio_error(char s[]);
        
        /* Sio wrappers */
        ssize_t Sio_puts(char s[]);
        ssize_t Sio_putl(long v);
        void Sio_error(char s[]);
        
        /* Unix I/O wrappers */
        int Open(const char *pathname, int flags, mode_t mode);
        ssize_t Read(int fd, void *buf, size_t count);
        ssize_t Write(int fd, const void *buf, size_t count);
        off_t Lseek(int fildes, off_t offset, int whence);
        void Close(int fd);
        int Select(int  n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 
               struct timeval *timeout);
        int Dup2(int fd1, int fd2);
        void Stat(const char *filename, struct stat *buf);
        void Fstat(int fd, struct stat *buf) ;
        
        /* Directory wrappers */
        DIR *Opendir(const char *name);
        struct dirent *Readdir(DIR *dirp);
        int Closedir(DIR *dirp);
        
        /* Memory mapping wrappers */
        void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
        void Munmap(void *start, size_t length);
        
        /* Standard I/O wrappers */
        void Fclose(FILE *fp);
        FILE *Fdopen(int fd, const char *type);
        char *Fgets(char *ptr, int n, FILE *stream);
        FILE *Fopen(const char *filename, const char *mode);
        void Fputs(const char *ptr, FILE *stream);
        size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
        void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
        
        /* Dynamic storage allocation wrappers */
        void *Malloc(size_t size);
        void *Realloc(void *ptr, size_t size);
        void *Calloc(size_t nmemb, size_t size);
        void Free(void *ptr);
        
        /* Sockets interface wrappers */
        int Socket(int domain, int type, int protocol);
        void Setsockopt(int s, int level, int optname, const void *optval, int optlen);
        void Bind(int sockfd, struct sockaddr *my_addr, int addrlen);
        void Listen(int s, int backlog);
        int Accept(int s, struct sockaddr *addr, socklen_t *addrlen);
        void Connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
        
        /* Protocol independent wrappers */
        void Getaddrinfo(const char *node, const char *service, 
                         const struct addrinfo *hints, struct addrinfo **res);
        void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, 
                         size_t hostlen, char *serv, size_t servlen, int flags);
        void Freeaddrinfo(struct addrinfo *res);
        void Inet_ntop(int af, const void *src, char *dst, socklen_t size);
        void Inet_pton(int af, const char *src, void *dst); 
        
        /* DNS wrappers */
        struct hostent *Gethostbyname(const char *name);
        struct hostent *Gethostbyaddr(const char *addr, int len, int type);
        
        /* Pthreads thread control wrappers */
        void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp, 
                    void * (*routine)(void *), void *argp);
        void Pthread_join(pthread_t tid, void **thread_return);
        void Pthread_cancel(pthread_t tid);
        void Pthread_detach(pthread_t tid);
        void Pthread_exit(void *retval);
        pthread_t Pthread_self(void);
        void Pthread_once(pthread_once_t *once_control, void (*init_function)());
        
        /* POSIX semaphore wrappers */
        void Sem_init(sem_t *sem, int pshared, unsigned int value);
        void P(sem_t *sem);
        void V(sem_t *sem);
        
        /* Rio (Robust I/O) package */
        ssize_t rio_readn(int fd, void *usrbuf, size_t n);
        ssize_t rio_writen(int fd, void *usrbuf, size_t n);
        void rio_readinitb(rio_t *rp, int fd); 
        ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
        ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
        
        /* Wrappers for Rio package */
        ssize_t Rio_readn(int fd, void *usrbuf, size_t n);
        void Rio_writen(int fd, void *usrbuf, size_t n);
        void Rio_readinitb(rio_t *rp, int fd); 
        ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n);
        ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
        
        /* Reentrant protocol-independent client/server helpers */
        int open_clientfd(char *hostname, char *port);
        int open_listenfd(char *port);
        
        /* Wrappers for reentrant protocol-independent client/server helpers */
        int Open_clientfd(char *hostname, char *port);
        int Open_listenfd(char *port);
        
        
        #endif /* __CSAPP_H__ */
        /* $end csapp.h */```
        

        【讨论】:

        • 请edit您的问题并将此信息添加到其中。
        • 好吧,我刚刚注册了 Stackoverflow,你们对我提出问题的方式一直很粗鲁。我只是学习系统!让我放松一下!
        • @bf3019 我相信当您单击按钮回答自己的问题时,会弹出一个警告您,如果有任何信息需要添加到问题中,请编辑它,不要发布一个答案(这样说是因为我几乎做了你现在正在做的事情)。此外,我在这里没有看到任何看似侮辱性的粗鲁或刻薄的评论,而且人们正在试图帮助您解决您的问题。我们只需要以适当方式显示的所有信息以及从哪里开始查找错误的想法(您在这几篇文章中发布了 很多 代码)来提供帮助。
        • 没有冒犯,但你已经发布了大量的代码,真的是一个完整的项目。完全不清楚(对我们来说,一目了然)一切是如何相互联系的。在您的屏幕截图中,您只需从 shell 调用 sleeper,所以问题就在那里。请尽量减少问题到那个小案例。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多