所以我正在实现一个迷你C shell,它支持后台进程。我的想法是,对于后台模式,父进程不等待其子进程完成,而是将它们注册到作业列表中,当它们完成时,我捕获SIGCHLD sig以清空其在我的作业列表上的条目。这是代码。
#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&amp;”)是第一个被执行的命令时。第二个问题是,使用我的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 :(得分:1)
在前台或后台运行命令并没有什么不同 - 实际上,唯一的区别是在一种情况下,在重新显示提示之前等待程序退出。
子程序可以以任何顺序终止,例如:
sleep 1 &
sleep 2
使用你的代码,当你执行“sleep 2”时,wait(NULL)
调用实际上会接收退出的“sleep 1”,并丢弃该信息,不会为信号处理程序做任何事情。< / p>
此外,signal(2)联机帮助页指出信号处理程序在接收到信号时会自动重置,除非使用信号的BSD语义。您可能需要阅读该手册页的“可移植性”部分。
命令名称的问题只是您正在重用存储命令名称的缓冲区。您可能希望使用strdup
复制命令名称,free
在作业退出时回收副本的内存。