2014-02-11 58 views
-1

因此,我正在实现一个迷你C外壳,它支持后台进程。我的想法是,对于后台模式,父进程不会等待它的子进程完成,而是将它们注册到作业列表中,当它们完成时,我捕获SIGCHLD标签以清空它们在我的作业列表中的条目。这是代码。C迷你外壳,处理问题SIGCHLD

#include <stdlib.h> 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/time.h> 
#include <sys/resource.h> 
#include <signal.h> 
#include <wait.h> 

#define DEFAULT_PROMPT "\nLog710H2014%>" 
#define EXIT_CMD "exit" 
#define CD_CMD "cd" 
#define JOB_LIST_CMD "aptaches" 
#define HOME_ENV_VAR "HOME" 
#define NEW_LINE "\n**************************************************\n" 
#define BCG_CMD_FLAG "&" 

void cd_handler(int argc, char *argv[]); 
int lire(char *chaine, int longueur); 
char** init_command(int* size,char *str); 
int execProg(int *argc, char **argv); 
int execProgBg(int *argc, char **argv); 
void sigchldHandler(int sig_num); 
void aptachesHandler(); 
void cleanJobList(pid_t *childpid); 

struct beanProcess { 
    pid_t pid; 
    int job_num; 
    char *command; 
}; 
void ajoutProcess(struct beanProcess bp); 


struct beanProcess beans[20]; 
int jobCount = 1; 

int main() { 
    signal(SIGCHLD, sigchldHandler); 

    printf(NEW_LINE); 
    printf("Bienvenue sur le shell de l'equipe 1"); 
    printf(NEW_LINE); 

    while(1){ 
     char str[200]=""; 
     printf(DEFAULT_PROMPT); 

     lire(str, 200); 

     int commArgsC = 0, bg = 0; 
     char** comms = init_command(&commArgsC, str); 

     if(commArgsC == 0){ 
      //printf("Saisie vide, veuillez entrez une commande."); 
      continue; 
     } 

     if(strcmp(comms[commArgsC-1], BCG_CMD_FLAG) == 0){ 
      bg = 1; 
      comms[commArgsC-1] = 0; 
     } 

     if(strcmp(comms[0], CD_CMD) == 0){ 
      cd_handler(commArgsC, comms); 
      commArgsC = commArgsC -1; 
     } 

     else if (strcmp(comms[0], JOB_LIST_CMD) == 0){ 
      aptachesHandler(); 
     } 

     else if (strcmp(comms[0], EXIT_CMD) == 0){ 
      int beansVide = 1; 
      for(int i = 0; i < jobCount -1 ; i++){ 
       if(beans[i].pid != 0){ 
        beansVide = 0; 
       } 
      } 
      if(beansVide){ 
       exit(0); 
      }else{ 
       printf("\nImpossible d'arreter le programme, des processus sont encore en cours d'éxécution\n"); 
      } 
     } 
     else { 
      if(bg){ 
       execProgBg(&commArgsC, comms); 
      } 
      else{ 
       execProg(&commArgsC, comms); 
      } 
     } 
    } 
    return 0; 
} 
void cd_handler(int argc, char *argv[]){ 
    char buff[512]; 
    char * directory; 

    if(argc < 2){ 
     directory = getenv(HOME_ENV_VAR); 
    }else if (argc == 2){ 
     directory = argv[1]; 
    }else{ 
     exit(1); 
    } 

    if (chdir(directory) == -1) { 
     printf ("Erreur de changement de repertoire actif", strerror (errno)); 
    }else{ 
     if (getcwd(buff, sizeof(buff)) == NULL) 
      perror("Impossible d'afficher le repertoire courant"); 
     else 
      printf("le repertoire courant est: %s\n", buff); 
    } 
} 
//Cette fonction est adaptée a partir du code de refp sur http://stackoverflow.com/questions/11198604/c-split-string-into-an-array-of-strings 
char** init_command(int* size, char* str){ 
    char ** res = NULL; 
    char * p = strtok (str, " "); 
    int n_spaces = 0; 

    while (p) { 
     res = realloc (res, sizeof (char*) * ++n_spaces); 

     if (res == NULL){ 
      exit (-1); 
     } 
     res[n_spaces-1] = p; 
     p = strtok (NULL, " "); 
    } 
    res = realloc (res, sizeof (char*) * (n_spaces+1)); 
    res[n_spaces] = 0; 
    *size = n_spaces; 
    return res; 
} 
//cette fonction est tirée d'un exemple de http://fr.openclassrooms.com/informatique/cours/apprenez-a-programmer-en-c/recuperer-une-chaine-de-caracteres 
int lire(char *chaine, int longueur) 
{ 
    char *positionEntree = NULL; 
    //printf ("\nje suis avant fgets et char est %s", chaine); 
    if (fgets(chaine, longueur, stdin) != NULL) 
    { 
     //printf ("\nje suis apres fgets"); 
     positionEntree = strchr(chaine, '\n'); 
     if (positionEntree != NULL) 
     { 
      *positionEntree = '\0'; 
     } 
     return 1; 
    } 
    else 
    { 
     return 0; 
    } 
} 
int execProg(int *argc, char **argv){ 
    char path[30] = "/bin/"; 
    strcat(path,argv[0]); 
    //printf("\nThis is the %d process executing the code in non bg mode\n", getpid()); 
    printf("Voici le resultat de l'execution de votre commande\n"); 
    pid_t pid; 
    pid = fork(); 

    if (pid < 0) { 
     perror("Creation de processus avec fork echouee"); 
     exit(-1); 
    } 
    else if (pid == 0) { 
     if(execvp(argv[0], argv) == -1){ 
      //printf("\nthis is the child process %d executing the command in non bg mode\n", getpid()); 
      perror("execv"); 
      return EXIT_FAILURE; 
     } 
    } 
    else { 
     //printf("\nthis is the parent process %d showing the stats in non bg mode\n", getpid()); 
     struct rusage rusg; 
     long temp, tempCpu; 
     wait (NULL); 
     getrusage(RUSAGE_CHILDREN, &rusg); 
     printf("\nStatistique de la commande %s:\n", argv[0]); 

     temp = (rusg.ru_utime.tv_sec * 1000) + (rusg.ru_utime.tv_usec/1000); 
     tempCpu = (rusg.ru_stime.tv_sec * 1000) + (rusg.ru_stime.tv_usec/1000); 

     printf("\nLe temps wall-clock (ms): %ld", temp); 
     printf("\nLe temps CPU (ms) %ld", tempCpu); 
     printf("\nNB interruptions volontaires: %ld", rusg.ru_nvcsw); 
     printf("\nNB interruptions involontaires: %ld", rusg.ru_nivcsw); 
     printf("\nNB defaults de pages: %ld", rusg.ru_majflt); 
     printf("\nNB defaults de pages satifaits du noyau : %ld", rusg.ru_minflt); 
    } 
    return EXIT_SUCCESS; 
} 
int execProgBg(int *argc, char **argv){ 

    //printf("\nThis is the %d process executing the code in bg mode\n", getpid()); 

    pid_t pid; 
    pid = fork(); 

    if (pid < 0) { 
     perror("Creation de processus avec fork echouee"); 
     return EXIT_FAILURE; 
    } 
    else if (pid == 0) { 
     //printf("This is the pid %d", getpid()); 
     //printf("\nthis is the child process %d executing the command in bg mode\n", getpid()); 

     if(execvp(argv[0], argv) == -1){ 
      perror("execvp"); 
      return EXIT_FAILURE; 
     } 
    } 
    else { 
     //printf("\nthis is the parent process %d showing the queue in bg mode\n", getpid()); 

     printf("[%d] %d", jobCount, pid); 

     struct beanProcess bP; 
     bP.pid = pid; 
     bP.job_num = jobCount; 
     bP.command = argv[0]; 

     ajoutProcess(bP); 
    } 
    return EXIT_SUCCESS; 
} 
void sigchldHandler(int sig_num) 
{ 
    int status; 
    pid_t childPid; 
    childPid = waitpid(-1, &status, WNOHANG); 
    cleanJobList(&childPid); 
} 
void ajoutProcess(struct beanProcess bP){ 
    beans[jobCount-1] = bP; 
    jobCount++; 
} 
void aptachesHandler(){ 
    for(int i = 0; i < jobCount-1 ; i++){ 
     printf("[%d] %d %s\n", beans[i].job_num, beans[i].pid, beans[i].command) ; 

    } 
} 
void cleanJobList(pid_t *childpid){ 
    printf("clean performed on %d", *childpid); 
    for(int i = 0; i < jobCount-1 ; i++){ 
     if(beans[i].pid == *childpid){ 
      beans[i].pid = 0; 
      beans[i].job_num = 0; 
      beans[i].command = NULL; 
     } 
    } 
} 

有了这个代码,我有两个问题,首先是sigchldHandler工作和清理名单只有在BG命令(假设“LS &”)是被执行的非常第一个。第二个问题是,使用我的aptaches命令(相当于shell作业),所有进程的命令名称字符串始终采用最后一个命令值,为什么?这是一个执行的例子,以便你看到我在说什么。

************************************************** 
Bienvenue sur le shell de l'equipe 1 
************************************************** 

Log710H2014%>ls & 
[1] 10466 
Log710H2014%>Debug PARTIE3.c 
clean performed on 10466 
Log710H2014%>pwd & 
[2] 10467 
Log710H2014%>/home/shong/workspace/TP1_PARTIE_3 


Log710H2014%>aptaches 
[0] 0 (null) 
[2] 10467 aptaches 

Log710H2014%> 

和另一个问题:

************************************************** 
Bienvenue sur le shell de l'equipe 1 
************************************************** 

Log710H2014%>ls 
Voici le resultat de l'execution de votre commande 
Debug PARTIE3.c 
clean performed on -1 
Statistique de la commande ls: 

Le temps wall-clock (ms): 1 
Le temps CPU (ms) 0 
NB interruptions volontaires: 1 
NB interruptions involontaires: 3 
NB defaults de pages: 0 
NB defaults de pages satifaits du noyau : 315 
Log710H2014%> ls & 
[1] 10483 
Log710H2014%>Debug PARTIE3.c 


Log710H2014%>pwd & 
[2] 10484 
Log710H2014%>/home/shong/workspace/TP1_PARTIE_3 


Log710H2014%>aptaches 
[1] 10483 ptaches 
[2] 10484 aptaches 

Log710H2014%> 
+0

TL; DR!如果您还没有这样做,请阅读[帮助页面](http://stackoverflow.com/help),尤其是名为[“我可以询问什么主题?”](http:// stackoverflow .com/help/on-topic)和[“我应该避免问什么类型的问题?”](http://stackoverflow.com/help/dont-ask)。更重要的是,请阅读[Stack Overflow问题清单](http://meta.stackexchange.com/questions/156810/stack-overflow-question-checklist)。您可能还想了解[SSCCE](http://sscce.org/)是什么。 –

+0

这里的一切都是相关的,因为我没有任何线索问题来自 – user2966439

+0

'我没有任何线索问题来自何处'StackOverflow可能不是您开始的最佳位置。这里的问题应该是特定的,并且关于您花费时间进行故障排除并最终陷入困境的问题。 – admdrew

回答

1

在前台或后台运行的命令是没有什么不同 - 事实上,唯一的区别是,在一个情况下,你等待程序在重新显示提示之前退出。

儿童节目可以以任意顺序终止,如:

sleep 1 & 
sleep 2 

与您的代码,当你执行“睡眠2”时,wait(NULL)通话将实际拿起“休眠1”退出,并抛出去掉这些信息,而没有为信号处理器做任何事情。

此外,signal(2)联机帮助页指出,信号处理程序在收到信号时自动复位,除非使用BSD信号语义。您可能需要阅读该手册页的“可移植性”部分。

命令名称的问题很简单,就是您正在重新使用存储命令名称的缓冲区。您可能想要使用strdup复制命令名称,并使用free复制复制的内存当工作退出时。

+0

但我不会启动睡眠2,直到睡眠1结束,当我输入“ls&”这是一个不需要完成的命令。所以我发起第二个命令,如“pwd&”我知道第一个命令完成了 – user2966439

+0

是的,还有一个问题是信号处理程序会自动重置,这取决于您运行的是哪个系统。更新了我的答案。 –

+0

感谢Simon,我在while循环中移动了信号,现在它正在工作,所以它在第一次陷阱后被重置了。 – user2966439