三十五、minishell(3)

时间:2023-03-09 09:58:02
三十五、minishell(3)

35.1 内容

  在当前的 minishell 中,如果执行 date clear 命令等,minishell 会停止:

  三十五、minishell(3)

  这是因为引入进程组的时候,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

 #ifndef INCLUDE_MSHELL_SIGNAL_H_
#define INCLUDE_MSHELL_SIGNAL_H_ #include "mshell_common.h"
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> extern void mshell_signal_Ign(void);
extern void mshell_signal_Catch(void);
extern void mshell_signal_Default(void);
#endif /* INCLUDE_MSHELL_SIGNAL_H_ */

  mshell_signal.c

 #include "mshell_signal.h"

 /** ================ parent handler handler ==================== */
/** 忽略某些信号 */
void mshell_signal_Ign(void)
{
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
} /** mshell 的信号处理函数 */
static void mshell_signal_Handler(int signo)
{
/** 子进程终止信号捕获 */
if(signo == SIGCHLD){
/** 回收进程组中的子进程, 非阻塞模式 */
waitpid(-, NULL, WNOHANG);
/** 将 minishell 所在的组调度为前台进程组 */
tcsetpgrp(, getpgid(getpid()));
}
} /** 捕获信号 */
void mshell_signal_Catch(void)
{
signal(SIGCHLD, mshell_signal_Handler);
} /** =============== child process handler =================== */
void mshell_signal_Default(void)
{
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
}

35.2.1 mshell_handler 修改

  mshell_handler.h

 #ifndef __MSHELL_HANDLER_H__
#define __MSHELL_HANDLER_H__ #include "mshell_common.h"
#include "mshell_signal.h"
#include "mshell_process.h"
#include "mshell_cmd_fun.h"
#include "mshell_program.h"
#include "mshell_job.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <sys/wait.h> #define MSHELL_PROMPT "mshell =>"
#define MSHELL_COMMAND_LEN 256
#define MSHELL_ARGS_SIZE 100 mshell_error_t mshell_Handler(); #endif

  mshell_handler.c

 #include "mshell_handler.h"

 static mshell_error_t mshell_cmd_Parsed(mshell_job_t *job, char *line, mshell_process_Flag_t *process_flag)
{
/** Create args secondary pointer stored in program */
char **args_tmp = (char **)malloc(MSHELL_ARGS_SIZE * sizeof(char *));
if(NULL == args_tmp) {
return MSHELL_ERROR_MALLOC;
} /** Split the command line */
char *cmd = strtok(line, " "); /** The first parameter is the command itself */
args_tmp[] = (char *)calloc(strlen(cmd + ), sizeof(char));
if(NULL == args_tmp[]) {
return MSHELL_ERROR_MALLOC;
}
strcpy(args_tmp[], cmd); /** Start with the second parameter */
int i = ;
char *cmd_param;
mshell_redirection_t *redirections[MSHELL_REDIRECTION_NUMBER];
int redirection_num = ; while(NULL != (cmd_param = strtok(NULL, " "))) { /** 对命令行进行解析,判断是否为后台进程 */
int process_ret;
*process_flag = mshell_process_BackParse(cmd_param, &process_ret);
if( == process_ret) {
continue;
} /** 分析重定向 */
mshell_error_t redirection_ret;
redirections[redirection_num] =
mshell_redirection_Parse(redirections, redirection_num, cmd_param, &redirection_ret);
if(NULL != redirections[redirection_num] && MSHELL_ERROR_NONE == redirection_ret){
redirection_num++;
continue;
}
else if (NULL == redirections[redirection_num] && MSHELL_ERROR_REDIRECION_PARAM == redirection_ret) {
printf("need param\n");
continue;
}
else if(NULL == redirections[redirection_num] && MSHELL_ERROR_REDIRECTION_CREATE == redirection_ret)
{
perror("error create redirection");
return MSHELL_ERROR_REDIRECTION_CREATE;
} /** 分析命令 */
args_tmp[i] = (char *)calloc(strlen(cmd_param + ), sizeof(char));
if(NULL == args_tmp[i]) {
return MSHELL_ERROR_MALLOC;
}
strcpy(args_tmp[i], cmd_param);
i++;
}
args_tmp[i] = NULL; /** Store all command line parameters in program and free args_tmp*/
mshell_prog_t *prog = mshell_prog_Create(args_tmp);
if(NULL == prog) {
return MSHELL_ERROR_PROG_CREATE;
}
mshell_args_Free(args_tmp); /** Add redirection to job and free redirections*/
for(i = ; i < redirection_num; i++){
mshell_prog_RedirectionAdd(prog, redirections[i]);
}
mshell_redirections_Free(redirections, redirection_num); /** Add program to the job*/
if(MSHELL_ERROR_NONE != mshell_job_AddProg(job, prog)) {
return MSHELL_ERROR_JOB_PROGADD;
} return ;
} static void mshell_cmd_ExcuProcess(mshell_job_t job, int order, mshell_process_Flag_t process_flag)
{
pid_t pid;
if((pid = fork()) < ) {
perror("fork error");
}
else if(pid == ) {
/** child process */ /** 信号处理 */
mshell_signal_Default(); if(order == ) {
/** order = 0, 则为 minishell 当中启动的第一个子进程,设置其为组长进程 */
job.pgid = mshell_process_GroupGet(getpid(), getpid());
}
else {
/** order > 0, 则为启动的第二个进程,将其设置进程组的成员进程 */
job.pgid = mshell_process_GroupGet(getpid(), job.pgid);
}
mshell_process_GroupSet(process_flag, getpid());
/** 对便准输入、标准输出和追加进行重定向 */
mshell_prog_RedirectionExcu(job.progs[order]); /** 调用 exec 函数执行系统中的其他命令 */
if(MSHELL_ERROR_CMD_NONECMD == mshell_Cmd_ExcuOther(job.progs[order].args))
exit();
}
else {
/** parent process */
if(order == ) {
job.pgid = mshell_process_GroupGet(pid, pid);
}
else {
job.pgid = mshell_process_GroupGet(pid, job.pgid);
}
mshell_process_GroupSet(process_flag, job.pgid); mshell_process_Wait(process_flag, job.pgid);
} } static mshell_error_t mshell_cmd_Excu(mshell_job_t *job, mshell_process_Flag_t process_flag)
{
int ret = MSHELL_ERROR_NONE;
int i;
for(i = ; i < job->progs_num; i++)
{
ret = mshell_Cmd_ExcuFun(job->progs[i].args);
if(MSHELL_ERROR_NONE == ret || MSHELL_ERROR_PARAM == ret) {
return MSHELL_ERROR_NONE;
} /** 执行其他命令 */
mshell_cmd_ExcuProcess(*job, i, process_flag);
} return ;
} mshell_error_t mshell_Handler()
{
/**创建一个进程组,将 minishell 进程设置为进程组的组长 */
setpgid(getpid(), getpid()); /** 信号处理 */
mshell_signal_Ign();
mshell_signal_Catch(); char buffer[MSHELL_COMMAND_LEN];
memset(buffer, , MSHELL_COMMAND_LEN); ssize_t size = strlen(MSHELL_PROMPT) * sizeof(char);
write(STDOUT_FILENO, MSHELL_PROMPT, size); mshell_process_Flag_t process_flag; ///< 设置前台和后台进程的标志
ssize_t len;
while() { len = read(STDIN_FILENO, buffer, MSHELL_COMMAND_LEN); ///< 从命令行读取内容到 buffer
buffer[len - 1] = 0;
if(strlen(buffer) > ){
mshell_job_t *job = mshell_job_Create(buffer);
if(NULL == job) {
return MSHELL_ERROR_JOB_CREATE;
} mshell_cmd_Parsed(job, buffer, &process_flag);
mshell_cmd_Excu(job, process_flag);
//mshell_job_Destroy(job);
} write(STDOUT_FILENO, MSHELL_PROMPT, size);
memset(buffer, , MSHELL_COMMAND_LEN);
}
}