/* * tsh - 一个支持作业控制的微型shell程序 * * 功能特性: * - 支持内置命令:jobs, fg, bg * - 支持前台/后台作业控制 * - 支持作业状态管理(运行中/停止) * - 处理Ctrl-C(中断)、Ctrl-Z(停止)等信号 * - 支持最多16个并发作业 * * 主要数据结构: * - job_t: 表示一个作业,包含PID、作业ID、状态和命令行 * - jobs数组: 保存所有作业的列表 * * 作业状态说明: * - UNDEF: 未定义 * - FG: 前台运行 * - BG: 后台运行 * - ST: 已停止 * * ChengJingyu 202302723005 */ /* 最大命令行长度 */ #define MAXLINE 1024 /* max line size */ /* 每条命令最大参数数量 */ #define MAXARGS 128 /* max args on a command line */ /* 最大并发作业数 */ #define MAXJOBS 16 /* max jobs at any point in time */ /* 最大作业ID值 */ #define MAXJID 1 << 16 /* max job ID */ /* 作业状态常量定义 */ #define UNDEF 0 /* 未定义状态 */ #define FG 1 /* 前台运行状态 */ #define BG 2 /* 后台运行状态 */ #define ST 3 /* 停止状态 */ #include #include #include #include // #include #include #include #include #include #include /* Misc manifest constants */ #define MAXLINE 1024 /* max line size */ #define MAXARGS 128 /* max args on a command line */ #define MAXJOBS 16 /* max jobs at any point in time */ #define MAXJID 1 << 16 /* max job ID */ /* Job states */ #define UNDEF 0 /* undefined */ #define FG 1 /* running in foreground */ #define BG 2 /* running in background */ #define ST 3 /* stopped */ /* * Jobs states: FG (foreground), BG (background), ST (stopped) * Job state transitions and enabling actions: * FG -> ST : ctrl-z * ST -> FG : fg command * ST -> BG : bg command * BG -> FG : fg command * At most 1 job can be in the FG state. */ /* 全局变量定义 */ extern char **environ; /* 环境变量数组,定义在libc中 */ char prompt[] = "tsh> "; /* 命令行提示符(请勿修改) */ int verbose = 0; /* 调试模式标志,为真时打印额外信息 */ int nextjid = 1; /* 下一个要分配的作业ID */ char sbuf[MAXLINE]; /* 用于构造sprintf消息的缓冲区 */ /* 作业结构体定义 */ struct job_t { pid_t pid; /* 进程ID */ int jid; /* 作业ID [1, 2, ...] */ int state; /* 状态: UNDEF(未定义), BG(后台), FG(前台), ST(停止) */ char cmdline[MAXLINE]; /* 命令行字符串 */ }; struct job_t jobs[MAXJOBS]; /* 作业列表数组 */ /* 全局变量定义结束 */ /* 函数原型声明 */ /* 需要实现的核心功能函数 */ void eval(char *cmdline); /* 评估并执行命令行 */ int builtin_cmd(char **argv); /* 执行内置命令 */ void do_bgfg(char **argv); /* 处理fg/bg命令 */ void waitfg(pid_t pid); /* 等待前台作业完成 */ /* 信号处理函数 */ void sigchld_handler(int sig); /* 子进程状态改变处理 */ void sigtstp_handler(int sig); /* Ctrl-Z处理 */ void sigint_handler(int sig); /* Ctrl-C处理 */ /* 提供的辅助函数 */ int parseline(const char *cmdline, char **argv); /* 解析命令行 */ void sigquit_handler(int sig); /* 退出信号处理 */ /* 作业管理函数 */ void clearjob(struct job_t *job); /* 清除作业条目 */ void initjobs(struct job_t *jobs); /* 初始化作业列表 */ int maxjid(struct job_t *jobs); /* 获取最大作业ID */ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); /* 添加作业 */ int deletejob(struct job_t *jobs, pid_t pid); /* 删除作业 */ pid_t fgpid(struct job_t *jobs); /* 获取前台作业PID */ struct job_t *getjobpid(struct job_t *jobs, pid_t pid); /* 通过PID获取作业 */ struct job_t *getjobjid(struct job_t *jobs, int jid); /* 通过JID获取作业 */ int pid2jid(pid_t pid); /* PID转JID */ void listjobs(struct job_t *jobs); /* 列出所有作业 */ void listjob(struct job_t *job); /* 列出单个作业 */ /* 工具函数 */ void usage(void); /* 打印用法信息 */ void unix_error(char *msg); /* Unix风格错误处理 */ void app_error(char *msg); /* 应用风格错误处理 */ typedef void handler_t(int); handler_t *Signal(int signum, handler_t *handler); /* 信号处理包装函数 */ /* * main - shell的主循环函数 * * 功能: * 1. 解析命令行参数 * 2. 安装信号处理程序 * 3. 初始化作业列表 * 4. 进入读取-评估循环 * * 参数: * argc - 参数个数 * argv - 参数数组 * * 返回值: * 正常情况不会返回,若返回则退出码为0 */ int main(int argc, char **argv) { char c; char cmdline[MAXLINE]; int emit_prompt = 1; /* 是否显示提示符,默认为1(显示) */ /* 将标准错误重定向到标准输出,确保所有输出都通过管道传递 */ dup2(1, 2); /* 解析命令行参数 */ while ((c = getopt(argc, argv, "hvp")) != EOF) { switch (c) { case 'h': /* 打印帮助信息 */ usage(); break; case 'v': /* 启用详细输出模式 */ verbose = 1; break; case 'p': /* 不显示提示符(用于自动化测试) */ emit_prompt = 0; break; default: usage(); } } /* 安装信号处理程序 */ /* 需要实现的信号处理函数 */ Signal(SIGINT, sigint_handler); /* 处理Ctrl-C中断信号 */ Signal(SIGTSTP, sigtstp_handler); /* 处理Ctrl-Z停止信号 */ Signal(SIGCHLD, sigchld_handler); /* 处理子进程状态变化 */ /* 提供一个干净终止shell的方式 */ Signal(SIGQUIT, sigquit_handler); /* 初始化作业列表 */ initjobs(jobs); /* shell主循环:读取-评估-执行 */ while (1) { /* 读取命令行输入 */ if (emit_prompt) { printf("%s", prompt); /* 显示提示符 */ fflush(stdout); } if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin)) app_error("fgets error"); /* 读取错误处理 */ if (feof(stdin)) /* 处理文件结束(Ctrl-D) */ { fflush(stdout); exit(0); } /* 评估并执行命令行 */ eval(cmdline); fflush(stdout); fflush(stdout); } exit(0); /* 控制流不会到达这里 */ } /* * eval - 评估并执行用户输入的命令行 * * 功能: * 1. 如果是内置命令(quit/jobs/bg/fg),直接执行 * 2. 否则fork子进程执行命令 * 3. 如果是前台作业,等待其完成 * 4. 设置独立的进程组,防止后台作业接收终端信号 * * 参数: * cmdline - 用户输入的命令行字符串 */ void eval(char *cmdline) { char *argv[MAXARGS]; /* execve()的参数列表 */ char buf[MAXLINE]; /* 存储修改后的命令行 */ int bg; /* 作业是否应该在后台运行 */ pid_t pid; /* 子进程ID */ sigset_t mask_all, mask_one, pre; /* 初始化信号屏蔽集合 */ sigfillset(&mask_all); /* 屏蔽所有信号 */ sigemptyset(&mask_one); /* 初始化空信号集 */ sigaddset(&mask_one, SIGCHLD); /* 添加SIGCHLD信号 */ /* 解析命令行 */ strcpy(buf, cmdline); bg = parseline(buf, argv); /* 解析命令行并判断是否后台运行 */ if (argv[0] == NULL) /* 忽略空行 */ return; /* 处理非内置命令 */ if (!builtin_cmd(argv)) { /* 阻塞SIGCHLD信号,防止addjob前收到SIGCHLD */ sigprocmask(SIG_BLOCK, &mask_one, &pre); /* 创建子进程执行命令 */ if ((pid = fork()) == 0) { /* 子进程执行用户作业 */ setpgid(0, 0); /* 设置新的进程组,防止后台作业接收终端信号 */ sigprocmask(SIG_SETMASK, &pre, NULL); /* 恢复信号屏蔽 */ /* 执行命令 */ if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found.\n", argv[0]); exit(0); } } /* 父进程添加作业到作业列表 */ addjob(jobs, pid, bg ? BG : FG, cmdline); /* 添加作业到作业列表 */ sigprocmask(SIG_SETMASK, &pre, NULL); /* 恢复信号屏蔽 */ /* 处理前台/后台作业 */ if (!bg) { waitfg(pid); /* 等待前台作业完成 */ } else { listjob(getjobpid(jobs, pid)); /* 列出后台作业信息 */ } } return; } /* * parseline - 解析命令行并构建argv数组 * * 功能: * 1. 解析命令行参数,处理单引号内的内容作为一个参数 * 2. 判断是否为后台作业(&结尾) * * 参数: * cmdline - 输入的命令行字符串 * argv - 输出的参数数组 * * 返回值: * 如果是后台作业返回1,否则返回0 */ int parseline(const char *cmdline, char **argv) { static char array[MAXLINE]; /* 存储命令行副本 */ char *buf = array; /* 遍历命令行的指针 */ char *delim; /* 参数分隔符位置 */ int argc; /* 参数个数 */ int bg; /* 是否为后台作业 */ /* 初始化处理 */ strcpy(buf, cmdline); buf[strlen(buf) - 1] = ' '; /* 替换末尾的换行符为空格 */ while (*buf && (*buf == ' ')) /* 跳过前导空格 */ buf++; /* 构建argv参数列表 */ argc = 0; if (*buf == '\'') { /* 处理单引号参数 */ buf++; delim = strchr(buf, '\''); } else { delim = strchr(buf, ' '); } /* 循环解析所有参数 */ while (delim) { argv[argc++] = buf; /* 添加参数到数组 */ *delim = '\0'; /* 终止当前参数 */ buf = delim + 1; while (*buf && (*buf == ' ')) /* 跳过空格 */ buf++; /* 查找下一个参数分隔符 */ if (*buf == '\'') { buf++; delim = strchr(buf, '\''); } else { delim = strchr(buf, ' '); } } argv[argc] = NULL; /* 参数列表以NULL结尾 */ if (argc == 0) /* 忽略空行 */ return 1; /* 检查是否为后台作业 */ if ((bg = (*argv[argc - 1] == '&')) != 0) { argv[--argc] = NULL; /* 移除&符号 */ } return bg; } /* * builtin_cmd - 执行内置命令 * * 功能: * 1. 处理quit命令: 退出shell * 2. 处理jobs命令: 列出所有作业 * 3. 处理fg/bg命令: 转发给do_bgfg处理 * 4. 忽略单独的&符号 * * 参数: * argv - 命令参数数组 * * 返回值: * 如果是内置命令返回1,否则返回0 */ int builtin_cmd(char **argv) { if (!strcmp(argv[0], "quit")) /* 退出命令 */ exit(0); if (!strcmp(argv[0], "&")) /* 忽略单独的& */ return 1; if (!strcmp(argv[0], "jobs")) { /* 列出作业 */ listjobs(jobs); return 1; } if (!strcmp(argv[0], "fg") || !strcmp(argv[0], "bg")) { /* 前台/后台命令 */ do_bgfg(argv); return 1; } return 0; /* 不是内置命令 */ } /* * do_bgfg - 执行bg和fg内置命令 * * 功能: * 1. 解析PID或%JID参数 * 2. 将作业转为前台或后台运行 * 3. 发送CONT信号继续作业 * * 参数: * argv - 命令参数数组 */ void do_bgfg(char **argv) { struct job_t *job; char *name = argv[0]; /* 命令名(fg/bg) */ char *num = argv[1]; /* PID或%JID参数 */ /* 参数验证 */ if (num == NULL) { printf("%s command requires PID or %%jobid argument\n", name); return; } /* 处理%JID格式 */ if (*num == '%') { num = num + 1; /* 跳过% */ if (strspn(num, "0123456789") != strlen(num)) { /* 检查数字 */ printf("%s: argument must be a PID or %%jobid\n", name); return; } job = getjobjid(jobs, atoi(num)); /* 通过JID获取作业 */ if (job == NULL) { printf("%%%s: No such job\n", num); return; } } /* 处理PID格式 */ else { if (strspn(num, "0123456789") != strlen(num)) { /* 检查数字 */ printf("%s: argument must be a PID or %%jobid\n", name); return; } job = getjobpid(jobs, (pid_t)atoi(num)); /* 通过PID获取作业 */ if (job == NULL) { printf("(%s): No such process\n", num); return; } } /* 执行fg/bg命令 */ if (!strcmp(argv[0], "fg")) { /* 前台命令 */ kill(job->pid, SIGCONT); /* 发送继续信号 */ job->state = FG; /* 设为前台作业 */ waitfg(job->pid); /* 等待前台作业完成 */ } else if (!strcmp(argv[0], "bg")) { /* 后台命令 */ kill(job->pid, SIGCONT); /* 发送继续信号 */ job->state = BG; /* 设为后台作业 */ listjob(job); /* 列出作业信息 */ } } /* * waitfg - 等待前台作业完成 * * 功能: * 1. 阻塞当前进程,直到指定的前台作业完成 * 2. 使用sigsuspend安全地等待信号,避免竞争条件 * 3. 处理SIGCHLD信号,确保前台作业结束时能及时检测到 * * 参数: * pid - 要等待的前台作业的进程ID * * 实现说明: * 1. 使用信号屏蔽确保原子性操作 * 2. 通过检查作业列表中的前台作业状态来判断是否继续等待 * 3. 使用sigsuspend替代pause,避免竞争条件 */ void waitfg(pid_t pid) { /* * 示例说明(trace06.txt): * ./myspin 4 # 子进程前台执行,主进程进入pause * INT # * (1) * CTRL_C发送INT信号给主进程,中断pause状态,进入INT处理函数, * 该函数把INT信号转发给前台子进程,处理完成后返回上次中断的位置(2)。 * (2) * INT中断处理函数返回后,继续执行下一条指令, * 发现while循环条件依然成立又进入pause. * (3) * 前台子进程被INT信号终止结束生命发送CHLD信号给主进程, * 中断pause状态,进入CHLD处理函数, * 该函数回收僵尸进程,删除对应的job,处理完成后返回上次中断的位置。 * (4) * CHLD中断返回后,继续执行下一条指令, * 发现while循环条件不再成立,跳出while循环。 * * 注意:如果(3)的中断位置在while条件执行后,pause执行前, * 那么使用pause会导致中断处理函数返回后一直pause, * 而sigsuspend可以避免这种情况。 */ sigset_t mask, prev; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); /* 屏蔽SIGCHLD信号 */ sigprocmask(SIG_BLOCK, &mask, &prev); /* 设置信号屏蔽 */ /* 循环等待前台作业完成 */ while (fgpid(jobs)) { if (verbose) printf("%ld pausing...\n", time(NULL)); sigsuspend(&prev); /* 原子地解除屏蔽并等待信号 */ if (verbose) printf("%ld interrupt return\n", time(NULL)); } sigprocmask(SIG_SETMASK, &prev, NULL); /* 恢复原始信号屏蔽 */ } /***************** * Signal handlers *****************/ /* * sigchld_handler - 处理子进程状态变化的信号处理函数 * * 功能: * 1. 当子进程终止(成为僵尸进程)或停止时,内核会发送SIGCHLD信号 * 2. 回收所有可用的僵尸子进程 * 3. 不等待其他正在运行的子进程终止 * * 参数: * sig - 信号编号 */ void sigchld_handler(int sig) { if (verbose) printf("%ld interrupt by SIGCHLD(%d)\n", time(NULL), sig); int olderrno = errno; pid_t pid; int status; /* 循环处理所有终止或停止的子进程 */ while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED)) > 0) { if (WIFEXITED(status)) { /* 子进程正常退出 */ deletejob(jobs, pid); /* 从作业列表中删除作业 */ } else if (WIFSIGNALED(status)) { /* 子进程被信号终止 */ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); /* 从作业列表中删除作业 */ } else if (WIFSTOPPED(status)) { /* 子进程被信号停止 */ printf("Job [%d] (%d) stopted by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); getjobpid(jobs, pid)->state = ST; /* 更新作业状态为停止 */ } else if (WIFCONTINUED(status)) { /* 子进程被继续 */ /* 目前不做特殊处理 */ } } /* 错误处理 */ if (pid == -1 && errno != ECHILD) { unix_error("sigchld_handler error"); } errno = olderrno; /* 恢复errno */ } /* * sigint_handler - 处理Ctrl-C中断信号的信号处理函数 * * 功能: * 1. 当用户在键盘输入Ctrl-C时,内核会发送SIGINT信号 * 2. 将信号转发给前台作业 * * 参数: * sig - 信号编号 */ void sigint_handler(int sig) { if (verbose) printf("%ld interrupt by SIGINT(%d)\n", time(NULL), sig); int olderrno = errno; pid_t pid = fgpid(jobs); /* 获取前台作业的PID */ if (kill(pid, SIGINT) == -1) { /* 向前台作业发送SIGINT信号 */ unix_error("sigint_handler"); } errno = olderrno; /* 恢复errno */ } /* * sigtstp_handler - 处理Ctrl-Z停止信号的信号处理函数 * * 功能: * 1. 当用户在键盘输入Ctrl-Z时,内核会发送SIGTSTP信号 * 2. 将信号转发给前台作业使其停止 * * 参数: * sig - 信号编号 */ void sigtstp_handler(int sig) { if (verbose) { printf("%ld interrupt by SIGSTP(%d)\n", time(NULL), sig); } int olderrno = errno; pid_t pid = fgpid(jobs); /* 获取前台作业的PID */ if (kill(pid, SIGTSTP) == -1) /* 向前台作业发送SIGTSTP信号 */ unix_error("sigtstp_handler"); errno = olderrno; /* 恢复errno */ } /********************* * End signal handlers *********************/ /*********************************************** * Helper routines that manipulate the job list **********************************************/ /* clearjob - Clear the entries in a job struct */ void clearjob(struct job_t *job) { job->pid = 0; job->jid = 0; job->state = UNDEF; job->cmdline[0] = '\0'; } /* initjobs - Initialize the job list */ void initjobs(struct job_t *jobs) { int i; for (i = 0; i < MAXJOBS; i++) clearjob(&jobs[i]); } /* maxjid - Returns largest allocated job ID */ int maxjid(struct job_t *jobs) { int i, max = 0; for (i = 0; i < MAXJOBS; i++) if (jobs[i].jid > max) max = jobs[i].jid; return max; } /* * addjob - 添加作业到作业列表 * * 功能: * 1. 将新作业添加到第一个空闲位置 * 2. 分配作业ID并自动递增 * 3. 设置作业状态和命令行 * * 参数: * jobs - 作业列表数组 * pid - 进程ID * state - 作业状态(UNDEF/FG/BG/ST) * cmdline - 命令行字符串 * * 返回值: * 成功返回1,失败返回0 */ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) { int i; if (pid < 1) /* 检查PID有效性 */ return 0; /* 查找空闲位置并添加作业 */ for (i = 0; i < MAXJOBS; i++) { if (jobs[i].pid == 0) { /* 找到空闲位置 */ jobs[i].pid = pid; jobs[i].state = state; jobs[i].jid = nextjid++; /* 分配作业ID并递增 */ if (nextjid > MAXJOBS) /* 作业ID循环使用 */ nextjid = 1; strcpy(jobs[i].cmdline, cmdline); /* 保存命令行 */ if (verbose) { /* 调试输出 */ printf("%ld Added job [%d] %d %s\n", time(NULL), jobs[i].jid, jobs[i].pid, jobs[i].cmdline); } return 1; /* 添加成功 */ } } printf("Tried to create too many jobs\n"); /* 作业列表已满 */ return 0; } /* * deletejob - 从作业列表删除指定PID的作业 * * 功能: * 1. 根据PID查找并删除作业 * 2. 更新下一个可用的作业ID * * 参数: * jobs - 作业列表数组 * pid - 要删除的进程ID * * 返回值: * 成功返回1,失败返回0 */ int deletejob(struct job_t *jobs, pid_t pid) { int i; if (pid < 1) /* 检查PID有效性 */ return 0; /* 查找并删除作业 */ for (i = 0; i < MAXJOBS; i++) { if (jobs[i].pid == pid) { /* 找到匹配的作业 */ if (verbose) { /* 调试输出 */ printf("%ld deleted job [%d] %d %s\n", time(NULL), jobs[i].jid, jobs[i].pid, jobs[i].cmdline); } clearjob(&jobs[i]); /* 清除作业条目 */ nextjid = maxjid(jobs) + 1; /* 更新下一个可用的作业ID */ return 1; /* 删除成功 */ } } return 0; /* 未找到匹配的作业 */ } /* * fgpid - 获取当前前台作业的PID * * 功能: * 遍历作业列表,查找状态为FG的作业 * * 参数: * jobs - 作业列表数组 * * 返回值: * 找到返回PID,否则返回0 */ pid_t fgpid(struct job_t *jobs) { int i; for (i = 0; i < MAXJOBS; i++) if (jobs[i].state == FG) return jobs[i].pid; return 0; } /* * getjobpid - 通过PID查找作业 * * 功能: * 在作业列表中查找指定PID的作业 * * 参数: * jobs - 作业列表数组 * pid - 要查找的进程ID * * 返回值: * 找到返回作业指针,否则返回NULL */ struct job_t *getjobpid(struct job_t *jobs, pid_t pid) { int i; if (pid < 1) return NULL; for (i = 0; i < MAXJOBS; i++) if (jobs[i].pid == pid) return &jobs[i]; return NULL; } /* * getjobjid - 通过JID查找作业 * * 功能: * 在作业列表中查找指定JID的作业 * * 参数: * jobs - 作业列表数组 * jid - 要查找的作业ID * * 返回值: * 找到返回作业指针,否则返回NULL */ struct job_t *getjobjid(struct job_t *jobs, int jid) { int i; if (jid < 1) return NULL; for (i = 0; i < MAXJOBS; i++) if (jobs[i].jid == jid) return &jobs[i]; return NULL; } /* * pid2jid - 将PID转换为JID * * 功能: * 查找指定PID对应的作业ID * * 参数: * pid - 进程ID * * 返回值: * 找到返回JID,否则返回0 */ int pid2jid(pid_t pid) { int i; if (pid < 1) return 0; for (i = 0; i < MAXJOBS; i++) if (jobs[i].pid == pid) { return jobs[i].jid; } return 0; } /* * listjobs - 打印所有作业信息 * * 功能: * 遍历作业列表,打印所有非空作业的信息 * 包括JID、PID、状态和命令行 * * 参数: * jobs - 作业列表数组 */ void listjobs(struct job_t *jobs) { int i; for (i = 0; i < MAXJOBS; i++) { if (jobs[i].pid != 0) { printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid); switch (jobs[i].state) { case BG: printf("Running "); break; case FG: printf("Foreground "); break; case ST: printf("Stopped "); break; default: printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state); } printf("%s", jobs[i].cmdline); } } } /* * listjob - 打印单个作业信息 * * 功能: * 打印指定作业的信息 * 包括JID、PID和命令行 * * 参数: * job - 要打印的作业指针 */ void listjob(struct job_t *job) { if (job->pid != 0) { printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline); } } /****************************** * end job list helper routines ******************************/ /*********************** * Other helper routines ***********************/ /* * usage - 打印帮助信息 * * 功能: * 打印shell的使用说明和命令行选项 */ void usage(void) { printf("Usage: shell [-hvp]\n"); printf(" -h print this message\n"); printf(" -v print additional diagnostic information\n"); printf(" -p do not emit a command prompt\n"); exit(1); } /* * unix_error - Unix风格错误处理 * * 功能: * 打印Unix系统调用错误信息并退出程序 * * 参数: * msg - 自定义错误消息 */ void unix_error(char *msg) { fprintf(stdout, "%s: %s\n", msg, strerror(errno)); exit(1); } /* * app_error - 应用风格错误处理 * * 功能: * 打印应用程序错误信息并退出程序 * * 参数: * msg - 错误消息 */ void app_error(char *msg) { fprintf(stdout, "%s\n", msg); exit(1); } /* * Signal - 信号处理包装函数 * * 功能: * 1. 为指定信号安装信号处理程序 * 2. 设置SA_RESTART标志,使被信号中断的系统调用自动重启 * 3. 返回旧的处理程序 * * 参数: * signum - 信号编号 * handler - 信号处理函数 * * 返回值: * 旧的信号处理程序 */ handler_t *Signal(int signum, handler_t *handler) { struct sigaction action, old_action; action.sa_handler = handler; sigemptyset(&action.sa_mask); /* 屏蔽正在处理的信号类型 */ action.sa_flags = SA_RESTART; /* 如果可能,重启被中断的系统调用 */ if (sigaction(signum, &action, &old_action) < 0) unix_error("Signal error"); return (old_action.sa_handler); } /* * sigquit_handler - SIGQUIT信号处理函数 * * 功能: * 1. 处理SIGQUIT信号,优雅终止shell * 2. 打印终止信息并退出 * * 参数: * sig - 信号编号 */ void sigquit_handler(int sig) { printf("Terminating after receipt of SIGQUIT signal\n"); exit(1); }