目录
分析和计算(evaluate)用户输入,执行内部命令或可执行文件
建立argv数组,返回命令属性(foreground / background)
头文件
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <signal.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXARGS 128
#define MAXLINE 8192 /* Max text line length */
void eval(char* cmdline);
int builtin_command(char** argv);
int parseline(char* buf, char** argv);
void print_working_directory();
void list(char* filename);
void concatenate(char* filename);
void remove_file(char* filename);
void touch(char* filename);
void directly_systemcall_exit();
实验步骤
主函数main
确定shell的运行模式
- 使用signal系统调用设置SIGCHLD信号的处理函数(回收僵尸进程)
- 打印出提示符
- 从键盘(标准输入)获取一条命令
- 分析和运行该命令
代码
int main() {
char cmdline[MAXLINE];
if (signal(SIGCHLD, sigchld_handler) == SIG_ERR)
printf("signal error\n");
while (1) {
printf("> ");
fgets(cmdline, MAXLINE, stdin);
if (feof(stdin)) exit(0);
eval(cmdline);
}
}
核心函数eval
分析和计算(evaluate)用户输入,执行内部命令或可执行文件
- 首先对输入的命令进行语法分析(parse),并建立argv数组
- 然后检查argv数组的第一项argv[0],检查其是否为内部命令(builtin command)
- 如果是内部命令,那么执行该命令
- 如果不是内部命令,那么将其视作可执行文件,调用fork系统调用创建一个子进程,然后在子进程中调用execve系统调用来为该可执行文件创建进程上下文
- 对于为可执行文件生成的进程,检查其为前台进程(foreground)还是后台进程(background),如果是前台进程,那么在父进程中调用waitpid系统调用来等待其终止;如果是后台进程,输出其进程号和当前命令行,回到主函数
代码
void eval(char* cmdline)
{
char* argv[MAXARGS]; //argument list execve()
char buf[MAXLINE]; //holds modified command line
int bg; //should the job run in bg or fg?
pid_t pid; //process id
strcpy(buf, cmdline);
bg = parseline(buf, argv);
if (argv[0] == NULL) return; //ignore empty lines
if (!builtin_command(argv)) {
if ((pid = fork()) == 0) { //child runs user job
if (execve(argv[0], argv, NULL) < 0) {
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
/* parent waits for foreground job to terminate */
if (!bg) {
int status;
if (waitpid(pid, &status, 0) < 0)
printf("waitfg: waitpid error\n");
}
else printf("%d %s", pid, cmdline);
}
return;
}
命令行解析函数parseline
建立argv数组,返回命令属性(foreground / background)
- 用空格作为分隔符,来划分用户输入的字符串,每个子串首地址作为argv数组的一项,并将argv数组尾元素的下一个位置设为NULL
- 检查argv数组的最后一个字符串是否为“&”,如果是,那么该命令生成一个后台进程(bg=1);否则该命令生成一个前台进程(bg=0)
代码
/* parseline - Parse the command line and build the argv array */
int parseline(char* buf, char** argv)
{
char* delim; //points to first space delimiter
int argc; //number of args
int bg; //background job?
buf[strlen(buf) - 1] = ' '; //replace trailing '\n' with space
while (*buf && (*buf == ' ')) ++buf; //ignore leading spaces
/* build the argv list */
argc = 0;
while ((delim = strchr(buf, ' '))) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
whi