三十五、minishell(3)
35.1 内容
在当前的 minishell 中,如果执行 date clear 命令等,minishell 会停止:
这是因为引入进程组的时候,mshell 放置在前台进程组,同时之后在子进程中又创建了一个进程组,在代码中,第二个进程组在没有将其设置为前台进程组之前,一直是后台进程组。那么后台进程组读写 minishell 的时候,会产生 SIGTTIN 和 SIGTTOU 这两个信号。
SIGTTIN:后台进程组的成员读控制终端
SIGTTOU:后台进程组的成员读控制终端
产生这两个信号默认的操作就是停止进程。
minishell 被停止的原因就是对这两个信号未作处理。我们处理的有些命令是通过 FORK 执行的,必须处理这两个信号。
所以我们必须对作业控制信号进行处理。
35.2 修改部分
其他部分参考第 29 节:https://www.cnblogs.com/kele-dad/p/9201411.html
35.2.1 signal 部分
mshell_signal.h
1 #ifndef INCLUDE_MSHELL_SIGNAL_H_ 2 #define INCLUDE_MSHELL_SIGNAL_H_ 3 4 #include "mshell_common.h" 5 #include <signal.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/wait.h> 9 10 extern void mshell_signal_Ign(void); 11 extern void mshell_signal_Catch(void); 12 extern void mshell_signal_Default(void); 13 #endif /* INCLUDE_MSHELL_SIGNAL_H_ */
mshell_signal.c
1 #include "mshell_signal.h" 2 3 /** ================ parent handler handler ==================== */ 4 /** 忽略某些信号 */ 5 void mshell_signal_Ign(void) 6 { 7 signal(SIGTTIN, SIG_IGN); 8 signal(SIGTTOU, SIG_IGN); 9 signal(SIGINT, SIG_IGN); 10 signal(SIGTSTP, SIG_IGN); 11 } 12 13 /** mshell 的信号处理函数 */ 14 static void mshell_signal_Handler(int signo) 15 { 16 /** 子进程终止信号捕获 */ 17 if(signo == SIGCHLD){ 18 /** 回收进程组中的子进程, 非阻塞模式 */ 19 waitpid(-1, NULL, WNOHANG); 20 /** 将 minishell 所在的组调度为前台进程组 */ 21 tcsetpgrp(0, getpgid(getpid())); 22 } 23 } 24 25 /** 捕获信号 */ 26 void mshell_signal_Catch(void) 27 { 28 signal(SIGCHLD, mshell_signal_Handler); 29 } 30 31 /** =============== child process handler =================== */ 32 void mshell_signal_Default(void) 33 { 34 signal(SIGTTIN, SIG_DFL); 35 signal(SIGTTOU, SIG_DFL); 36 signal(SIGINT, SIG_DFL); 37 signal(SIGTSTP, SIG_DFL); 38 signal(SIGCHLD, SIG_DFL); 39 }
35.2.1 mshell_handler 修改
mshell_handler.h
1 #ifndef __MSHELL_HANDLER_H__ 2 #define __MSHELL_HANDLER_H__ 3 4 5 #include "mshell_common.h" 6 #include "mshell_signal.h" 7 #include "mshell_process.h" 8 #include "mshell_cmd_fun.h" 9 #include "mshell_program.h" 10 #include "mshell_job.h" 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <malloc.h> 14 #include <unistd.h> 15 #include <string.h> 16 #include <memory.h> 17 #include <sys/wait.h> 18 19 #define MSHELL_PROMPT "mshell =>" 20 #define MSHELL_COMMAND_LEN 256 21 #define MSHELL_ARGS_SIZE 100 22 23 mshell_error_t mshell_Handler(); 24 25 #endif
mshell_handler.c
1 #include "mshell_handler.h" 2 3 static mshell_error_t mshell_cmd_Parsed(mshell_job_t *job, char *line, mshell_process_Flag_t *process_flag) 4 { 5 /** Create args secondary pointer stored in program */ 6 char **args_tmp = (char **)malloc(MSHELL_ARGS_SIZE * sizeof(char *)); 7 if(NULL == args_tmp) { 8 return MSHELL_ERROR_MALLOC; 9 } 10 11 /** Split the command line */ 12 char *cmd = strtok(line, " "); 13 14 /** The first parameter is the command itself */ 15 args_tmp[0] = (char *)calloc(strlen(cmd + 1), sizeof(char)); 16 if(NULL == args_tmp[0]) { 17 return MSHELL_ERROR_MALLOC; 18 } 19 strcpy(args_tmp[0], cmd); 20 21 /** Start with the second parameter */ 22 int i = 1; 23 char *cmd_param; 24 mshell_redirection_t *redirections[MSHELL_REDIRECTION_NUMBER]; 25 int redirection_num = 0; 26 27 while(NULL != (cmd_param = strtok(NULL, " "))) { 28 29 /** 对命令行进行解析,判断是否为后台进程 */ 30 int process_ret; 31 *process_flag = mshell_process_BackParse(cmd_param, &process_ret); 32 if(0 == process_ret) { 33 continue; 34 } 35 36 /** 分析重定向 */ 37 mshell_error_t redirection_ret; 38 redirections[redirection_num] = 39 mshell_redirection_Parse(redirections, redirection_num, cmd_param, &redirection_ret); 40 if(NULL != redirections[redirection_num] && MSHELL_ERROR_NONE == redirection_ret){ 41 redirection_num++; 42 continue; 43 } 44 else if (NULL == redirections[redirection_num] && MSHELL_ERROR_REDIRECION_PARAM == redirection_ret) { 45 printf("need param\n"); 46 continue; 47 } 48 else if(NULL == redirections[redirection_num] && MSHELL_ERROR_REDIRECTION_CREATE == redirection_ret) 49 { 50 perror("error create redirection"); 51 return MSHELL_ERROR_REDIRECTION_CREATE; 52 } 53 54 /** 分析命令 */ 55 args_tmp[i] = (char *)calloc(strlen(cmd_param + 1), sizeof(char)); 56 if(NULL == args_tmp[i]) { 57 return MSHELL_ERROR_MALLOC; 58 } 59 strcpy(args_tmp[i], cmd_param); 60 i++; 61 } 62 args_tmp[i] = NULL; 63 64 /** Store all command line parameters in program and free args_tmp*/ 65 mshell_prog_t *prog = mshell_prog_Create(args_tmp); 66 if(NULL == prog) { 67 return MSHELL_ERROR_PROG_CREATE; 68 } 69 mshell_args_Free(args_tmp); 70 71 /** Add redirection to job and free redirections*/ 72 for(i = 0; i < redirection_num; i++){ 73 mshell_prog_RedirectionAdd(prog, redirections[i]); 74 } 75 mshell_redirections_Free(redirections, redirection_num); 76 77 /** Add program to the job*/ 78 if(MSHELL_ERROR_NONE != mshell_job_AddProg(job, prog)) { 79 return MSHELL_ERROR_JOB_PROGADD; 80 } 81 82 return 0; 83 } 84 85 static void mshell_cmd_ExcuProcess(mshell_job_t job, int order, mshell_process_Flag_t process_flag) 86 { 87 pid_t pid; 88 if((pid = fork()) < 0) { 89 perror("fork error"); 90 } 91 else if(pid == 0) { 92 /** child process */ 93 94 /** 信号处理 */ 95 mshell_signal_Default(); 96 97 if(order == 0) { 98 /** order = 0, 则为 minishell 当中启动的第一个子进程,设置其为组长进程 */ 99 job.pgid = mshell_process_GroupGet(getpid(), getpid()); 100 } 101 else { 102 /** order > 0, 则为启动的第二个进程,将其设置进程组的成员进程 */ 103 job.pgid = mshell_process_GroupGet(getpid(), job.pgid); 104 } 105 mshell_process_GroupSet(process_flag, getpid()); 106 /** 对便准输入、标准输出和追加进行重定向 */ 107 mshell_prog_RedirectionExcu(job.progs[order]); 108 109 /** 调用 exec 函数执行系统中的其他命令 */ 110 if(MSHELL_ERROR_CMD_NONECMD == mshell_Cmd_ExcuOther(job.progs[order].args)) 111 exit(1); 112 } 113 else { 114 /** parent process */ 115 if(order == 0) { 116 job.pgid = mshell_process_GroupGet(pid, pid); 117 } 118 else { 119 job.pgid = mshell_process_GroupGet(pid, job.pgid); 120 } 121 mshell_process_GroupSet(process_flag, job.pgid); 122 123 mshell_process_Wait(process_flag, job.pgid); 124 } 125 126 } 127 128 static mshell_error_t mshell_cmd_Excu(mshell_job_t *job, mshell_process_Flag_t process_flag) 129 { 130 int ret = MSHELL_ERROR_NONE; 131 int i; 132 for(i = 0; i < job->progs_num; i++) 133 { 134 ret = mshell_Cmd_ExcuFun(job->progs[i].args); 135 if(MSHELL_ERROR_NONE == ret || MSHELL_ERROR_PARAM == ret) { 136 return MSHELL_ERROR_NONE; 137 } 138 139 /** 执行其他命令 */ 140 mshell_cmd_ExcuProcess(*job, i, process_flag); 141 } 142 143 return 0; 144 } 145 146 mshell_error_t mshell_Handler() 147 { 148 /**创建一个进程组,将 minishell 进程设置为进程组的组长 */ 149 setpgid(getpid(), getpid()); 150 151 /** 信号处理 */ 152 mshell_signal_Ign(); 153 mshell_signal_Catch(); 154 155 char buffer[MSHELL_COMMAND_LEN]; 156 memset(buffer, 0, MSHELL_COMMAND_LEN); 157 158 159 ssize_t size = strlen(MSHELL_PROMPT) * sizeof(char); 160 write(STDOUT_FILENO, MSHELL_PROMPT, size); 161 162 mshell_process_Flag_t process_flag; ///< 设置前台和后台进程的标志 163 ssize_t len; 164 while(1) { 165 166 len = read(STDIN_FILENO, buffer, MSHELL_COMMAND_LEN); ///< 从命令行读取内容到 buffer 167 buffer[len - 1] = 0; 168 if(strlen(buffer) > 0){ 169 mshell_job_t *job = mshell_job_Create(buffer); 170 if(NULL == job) { 171 return MSHELL_ERROR_JOB_CREATE; 172 } 173 174 mshell_cmd_Parsed(job, buffer, &process_flag); 175 mshell_cmd_Excu(job, process_flag); 176 //mshell_job_Destroy(job); 177 } 178 179 write(STDOUT_FILENO, MSHELL_PROMPT, size); 180 memset(buffer, 0, MSHELL_COMMAND_LEN); 181 } 182 }