操作系统lab1-shell
内容与设计思想
实现一个基本的Shell,能够实现以下功能:
1. 带参数的程序运行功能。
2. 重定向功能,将文件作为程序的输入/输出。
3. 管道符号“|”,在程序间传递数据。
4. 后台符号& ,表示此命令将以后台运行的方式执行。
5. 工作路径移动命令cd。
6. 程序运行统计mytop。
7. shell退出命令exit。
8. history n显示最近执行的n条指令
实验过程
常用函数封装
封装了带有错误处理的open(),fopen(),execvp()函数,make64()函数合并高低周期,sleep_ms()通过select()函数实现毫秒级的sleep()
/*
* 合并高低周期
*/
static inline u64_t make64(unsigned long lo, unsigned long hi) {
return ((u64_t)hi << 32 | (u64_t)lo);
}
/*
* 实现毫秒级的sleep
*/
static void sleep_ms(unsigned int secs) {
struct timeval tval;
tval.tv_sec=secs/1000;
tval.tv_usec=(secs*1000)%1000000;
select(0,NULL,NULL,NULL,&tval);
}
/*
* 封装带有错误处理的open函数
*/
int Open(char *path, int param) {
int file_fd;
if ((file_fd = open(path, param)) < 0) {
fprintf(stderr, "OPEN FILE ERROR.\n");
exit(1);
} else return file_fd;
}
/*
* 封装带有错误处理的fopen函数
*/
FILE* Fopen(char *path, char *mode) {
FILE* fp;
if ((fp = fopen(path, mode)) == NULL) {
fprintf(stderr, "FOPEN ERROR.\n");
exit(1);
}
return fp;
}
/*
* 封装带有错误处理的execvp函数
*/
void Execvp(char *path, char **params) {
if (execvp(path, params) < 0) {
fprintf(stderr, "EXECVP ERROR.\n");
exit(1);
}
}
命令行预处理和解析
preparse()函数在重定向符号前后添加一个空格,便于后续解析。
/*
* 在重定向前后加空格,便于后续解析
*/
void preparse(char *cmdline) {
char newcmdline[MAXLEN];
int len = strlen(cmdline), j = 0;
for (int i = 0; i < len; i++) {
if (cmdline[i] == '<') newcmdline[j++] = ' ', newcmdline[j++] = '<', newcmdline[j++] = ' '; // '<'
else if (cmdline[i] == '>') {
if (cmdline[i + 1] == '>') { // '>>'
newcmdline[j++] = ' ';
newcmdline[j++] = '>';
newcmdline[j++] = '>';
newcmdline[j++] = ' ';
i++;
} else { // '>'
newcmdline[j++] = ' ';
newcmdline[j++] = '>';
newcmdline[j++] = ' ';
}
} else newcmdline[j++] = cmdline[i];
}
newcmdline[j] = '\0';
strcpy(cmdline, newcmdline);
}
parseline()函数将一行字符串命令解析为多个参数,存放在argv中,并返回argc
/*
* 解析参数,把参数存入argv,并返回argc
*/
int parseline(const char *cmdline, char **argv) {
int len = strlen(cmdline), argc = 0, temp = 0;
for (int i = 0; i < len; i++) {
if (cmdline[i] == ' ') {
if (i != 0 && cmdline[i - 1] != ' ') {
argv[argc][temp] = '\0';
argc++;
temp = 0;
}
continue;
}
if (!temp) argv[argc] = (char *)malloc(sizeof(char) * MAXLEN);
argv[argc][temp++] = cmdline[i];
}
if (temp == 0) argc--;
return argc + 1;
}
带参数的程序运行功能,在最后加&符号,表示此命令后台执行
先利用fork()创建子进程,在子进程中通过execvp()函数执行程序,父进程通过waitpid挂起,等待子进程结束后继续运行。
如果检测到后台执行&符号,则将子进程的标准输入、输出、错误流重定向到/dev/null,并通过signal(SIGCHLD, SIG_IGN)使得minix接管该进程,即不产生僵死进程。
pid_t pid;
if ((pid = fork()) == 0) {
if (!wait) {
direct_null();
signal(SIGCHLD, SIG_IGN); // 使得minix接管该进程,即不产生僵死进程
} else redirect(argv, &argc);
Execvp(argv[0], argv);
}
int status;
if (wait) waitpid(pid, &status, 0);
重定向功能,将文件作为程序的输入/输出
解析参数,检查是否存在<,>,>>,若有则利用dup()进行重定向
<表示文件写入,重定向输入流,文件打开方式为O_RDONLY,即只读
>表示覆盖写,重定向输出流,文件打开方式为O_WRONLY | O_CREAT | O_TRUNC,即只写,若文件不存在则创建新文件,清空原文件
>>表示追加写,重定向输出流,文件打开方式为O_WRONLY | O_CREAT | O_APPEND,即只写,若文件不存在则创建新文件,在原文件后追加
/*
* 根据>, >>, <重定向标准输入输出
*/
void redirect(char **argv, int *argc) {
for (int i = 0; i < *argc; i++) {
if (strcmp(argv[i], "<") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDIN_FILENO);
int file_fd = Open(argv[i + 1], O_RDONLY);
dup(file_fd);
*argc -= 2; // 去掉这两个参数 并把后面的参数都往前移两位
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
if (strcmp(argv[i], ">") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDOUT_FILENO);
int file_fd = Open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC);
dup(file_fd);
*argc -= 2;
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
if (strcmp(argv[i], ">>") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDOUT_FILENO);
int file_fd = Open(argv[i + 1], O_WRONLY | O_CREAT | O_APPEND);
dup(file_fd);
*argc -= 2;
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
}
argv[*argc] = NULL; // 保证argv以NULL为结尾
}
管道符号“|”,在程序间传递数据。
利用pipe()和dup()实现管道。根据”|”的位置,将一行命令分割为前后两部分,前一部分由重定向输出后的子进程执行,后一部分由重定向输入后的父进程执行。多管道的情况也可以正常递归处理。
/*
* 子进程执行cmdline1,将输出通过管道作为主进程cmdline2的输入
*/
void pipeline(char *cmdline1, char *cmdline2) {
int fd[2];
pipe(&fd[0]);
if (fork() == 0) { // 子进程,重定向输出
close(fd[0]);
close(STDOUT_FILENO);
dup(fd[1]);
close(fd[1]);
eval(cmdline1, 1);
exit(0);
} else { // 父进程,重定向输入
close(fd[1]);
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
eval(cmdline2, 1);
}
}
工作路径移动命令cd。
利用chdir()函数实现
if (!strcmp(argv[0], "cd")) {
chdir(argv[1]);
return 1;
}
程序运行统计mytop
/*
* 统计内存和cpu使用情况
*/
void mytop() {
int totSize, freeSize, cachedSize, total_proc, proc_num_prev = 0, proc_num_now = 0;
u64_t total_ticks_pre = 0, total_ticks_now = 0, idle_ticks_pre = 0, idle_ticks_now = 0;
get_meminfo(&totSize, &freeSize, &cachedSize);
printf("main memory: ");
printf(" %dK total,", totSize);
printf(" %dK free,", freeSize);
printf(" %dK cached\n", cachedSize);
get_kinfo(&total_proc);
printf("Total procs: %d\n", total_proc);
get_pidinfo(prev, &proc_num_prev, &total_ticks_pre, &idle_ticks_pre);
sleep_ms(500);
get_pidinfo(now, &proc_num_now, &total_ticks_now, &idle_ticks_now);
double CPU_util = 100 - (double)100 * (idle_ticks_now - idle_ticks_pre) / (total_ticks_now - total_ticks_pre);
printf("CPU utilization : %.4f%%\n", CPU_util);
}
读取/proc/meminfo获取内存使用情况
/*
* 读取/proc/meminfo
*/
void get_meminfo(int *totSize, int *freeSize, int *cachedSize) {
FILE *fp;
fp = Fopen("/proc/meminfo", "r");
int pagesize, total, free, largest, cached;
fscanf(fp, "%d %d %d %d %d", &pagesize, &total, &free, &largest, &cached);
*totSize = pagesize * total / 1024;
*freeSize = pagesize * free / 1024;
*cachedSize = pagesize * cached / 1024;
fclose(fp);
}
读取/proc/kinfo获取进程总数
/*
* 读取/proc/kinfo
*/
void get_kinfo(int *total_proc) {
FILE *fp;
fp = Fopen("/proc/kinfo", "r");
int proc, task;
fscanf(fp, "%d %d", &proc, &task);
*total_proc = proc + task;
fclose(fp);
}
读取/proc/pid/psinfo获取每个进程的cpu占用时间
/*
* 读取/proc下所有文件夹
*/
void get_pidinfo(struct proc *p, int *num, u64_t *total, u64_t *idle) {
DIR *p_dir;
char *end;
if ((p_dir = opendir("/proc/")) == NULL) {
fprintf(stderr, "OPENDIR ERROR.\n");
exit(1);
}
struct dirent *p_ent;
*num = 0;
for (p_ent = readdir(p_dir); p_ent != NULL; p_ent = readdir(p_dir)) {
int pid = strtol(p_ent->d_name, &end, 10);
if (!pid) continue;
(*num)++;
parse_file(pid, &p[*num], total, idle);
}
closedir(p_dir);
}
/*
* 读取/proc/pid/psinfo
*/
void parse_file(int pid, struct proc *p, u64_t *total, u64_t *idle) {
char path[MAXPATH];
sprintf(path, "/proc/%d/psinfo", pid);
FILE *fp = Fopen(path, "r");
fscanf(fp, "%d", &(p->version));
fscanf(fp, " %c", &(p->type));
fscanf(fp, " %d", &(p->endpoint));
fscanf(fp, " %s", p->name);
fscanf(fp, " %c", &(p->state));
fscanf(fp, " %d", &(p->blocked));
fscanf(fp, " %d", &(p->priority));
fscanf(fp, " %d", &(p->user_time));
fscanf(fp, " %d", &(p->ticks));
fscanf(fp, " %lu", &(p->cycle_high));
fscanf(fp, " %lu", &(p->cycle_low));
fscanf(fp, " %llu", &(p->memory));
fscanf(fp, " %lu %lu", &(p->effuid), &(p->nice));
p->cpucycles[0] = make64(p->cycle_low, p->cycle_high);
*total += p->cpucycles[0];
if (p->endpoint == -4)
*idle = p->cpucycles[0];
// printf("PID : %d CYCLE : %llu low : %lu high : %lu ticks : %d %c %c\n", pid, p->cpucycles[0], p->cycle_low, p->cycle_high, p->ticks, p->type, p->state);
fclose(fp);
}
其中psinfo中的每个参数对应的含义如下:
为了计算cpu占用率,需要连续两次读取cpu使用情况并计算差值,这里选择间隔为500ms,利用select()函数实现。通过所有进程cpu占用时间累加获得total_ticks,通过/proc/-4/psinfo获得空闲的idle_ticks,cpu占用率为(1-idle_ticks/total_ticks)*100%
shell退出命令exit。
利用exit(0)实现退出
if (!strcmp(argv[0], "exit")) {
exit(0);
}
history n显示最近执行的n条指令
利用全局变量char *record[MAXHISTORY]记录最近执行的指令,rec_size记录指令条数。
if (!strcmp(argv[0], "history")) {
redirect(argv, &argc);
if (argc == 1) {
for (int i = 0; i <= rec_size; i++) {
printf(" %d %s", i + 1, record[i]);
}
} else {
int number;
// fprintf(stderr, "%s\n", argv[1]);
sscanf(argv[1], "%d", &number);
for (int i = MAX(0, rec_size - number + 1); i <= rec_size; i++) {
printf(" %d %s", i + 1, record[i]);
}
}
return 1;
}
编译和测试
总结
实现了一个基本的shell,包含重定向和管道功能。实现过程中对一些常用的函数以及系统调用进行了错误处理的封装。实现mytop()时参考了MINIX原生top命令的实现,一些系统调用的使用方法参考了《Unix环境高级编程》第3和第8节。
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
typedef unsigned long long u64_t;
#define debug(x) fprintf(stderr, "%d ", x)
#define debugl(x) fprintf(stderr, "%d\n", x)
#define MAXLINE 1024
#define MAXLEN 32
#define MAXARGS 128
#define MAXPATH 128
#define MAXHISTORY 1000
#define PROC_NAME_LEN 16
#define PROC_MAX 1024
#define MAX(a, b) (a) > (b) ? (a) : (b)
/*
* 记录进程信息
*/
struct proc {
int version;
char type;
int endpoint;
char name[PROC_NAME_LEN + 1];
char state;
int blocked;
int priority;
int user_time;
int ticks;
unsigned long cycle_high;
unsigned long cycle_low;
u64_t memory;
unsigned long effuid;
unsigned long nice;
u64_t cpucycles[1];
}prev[PROC_MAX], now[PROC_MAX];
/*
* 记录历史指令
*/
char *record[MAXHISTORY];
int rec_size = -1;
void eval(char *cmdline, int inPipe);
void preparse(char *cmdline);
int parseline(const char *cmdline, char **argv);
int builtin(char **argv, int argc);
void redirect(char **argv, int *argc);
void direct_null();
void direct_restore(int STDIN_FD_COPY, int STDOUT_FD_COPY);
char* get_pipepos(char *cmdline);
void pipeline(char *cmdline1, char *cmdline2);
void mytop();
void get_meminfo(int *totSize, int *freeSize, int *cachedSize);
void get_kinfo(int *total_proc);
void get_pidinfo(struct proc *p, int *num, u64_t *total, u64_t *idle);
void parse_file(int pid, struct proc *p, u64_t *total, u64_t *idle);
static void sleep_ms(unsigned int secs);
static inline u64_t make64(unsigned long lo, unsigned long hi);
int Open(char *path, int param);
FILE* Fopen(char *path, char *mode);
void Execvp(char *path, char **params);
/*
* 解析一行命令
*/
void eval(char *cmdline, int inPipe) {
if (!inPipe) { // 记录history
record[++rec_size] = (char *)malloc(sizeof(char) * (strlen(cmdline) + 1));
strcpy(record[rec_size], cmdline);
}
char *argv[MAXLINE];
cmdline[(int)strlen(cmdline) - 1] = ' '; // 把最后的回车改成空格
preparse(cmdline);
int argc = parseline(cmdline, argv);
if (argc == 0) return; // 遇到空行直接返回
char* cmdline2 = get_pipepos(cmdline);
if (cmdline2 != NULL) {
*cmdline2 = '\0';
pipeline(cmdline, cmdline2 + 1);
} else if (!builtin(argv, argc)) {
int wait = argv[argc - 1][0] != '&';
// fprintf(stderr, "%d\n", wait);
if (!wait) argv[argc - 1] = NULL, argc--;
pid_t pid;
if ((pid = fork()) == 0) {
if (!wait) {
direct_null();
signal(SIGCHLD, SIG_IGN); // 使得minix接管该进程,即不产生僵死进程
} else redirect(argv, &argc);
Execvp(argv[0], argv);
}
int status;
if (wait) waitpid(pid, &status, 0);
}
}
/*
* 在重定向前后加空格,便于后续解析
*/
void preparse(char *cmdline) {
char newcmdline[MAXLEN];
int len = strlen(cmdline), j = 0;
for (int i = 0; i < len; i++) {
if (cmdline[i] == '<') newcmdline[j++] = ' ', newcmdline[j++] = '<', newcmdline[j++] = ' '; // '<'
else if (cmdline[i] == '>') {
if (cmdline[i + 1] == '>') { // '>>'
newcmdline[j++] = ' ';
newcmdline[j++] = '>';
newcmdline[j++] = '>';
newcmdline[j++] = ' ';
i++;
} else { // '>'
newcmdline[j++] = ' ';
newcmdline[j++] = '>';
newcmdline[j++] = ' ';
}
} else newcmdline[j++] = cmdline[i];
}
newcmdline[j] = '\0';
strcpy(cmdline, newcmdline);
}
/*
* 解析参数,把参数存入argv,并返回argc
*/
int parseline(const char *cmdline, char **argv) {
int len = strlen(cmdline), argc = 0, temp = 0;
for (int i = 0; i < len; i++) {
if (cmdline[i] == ' ') {
if (i != 0 && cmdline[i - 1] != ' ') {
argv[argc][temp] = '\0';
argc++;
temp = 0;
}
continue;
}
if (!temp) argv[argc] = (char *)malloc(sizeof(char) * MAXLEN);
argv[argc][temp++] = cmdline[i];
}
if (temp == 0) argc--;
return argc + 1;
}
/*
* 判断是否为内置命令
*/
int builtin(char **argv, int argc) {
if (!strcmp(argv[0], "cd")) {
chdir(argv[1]);
return 1;
}
if (!strcmp(argv[0], "history")) {
redirect(argv, &argc);
if (argc == 1) {
for (int i = 0; i <= rec_size; i++) {
printf(" %d %s", i + 1, record[i]);
}
} else {
int number;
// fprintf(stderr, "%s\n", argv[1]);
sscanf(argv[1], "%d", &number);
for (int i = MAX(0, rec_size - number + 1); i <= rec_size; i++) {
printf(" %d %s", i + 1, record[i]);
}
}
return 1;
}
if (!strcmp(argv[0], "exit")) {
exit(0);
}
if (!strcmp(argv[0], "mytop")) {
redirect(argv, &argc);
mytop();
return 1;
}
return 0;
}
/*
* 根据>, >>, <重定向标准输入输出
*/
void redirect(char **argv, int *argc) {
for (int i = 0; i < *argc; i++) {
if (strcmp(argv[i], "<") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDIN_FILENO);
int file_fd = Open(argv[i + 1], O_RDONLY);
dup(file_fd);
*argc -= 2; // 去掉这两个参数 并把后面的参数都往前移两位
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
if (strcmp(argv[i], ">") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDOUT_FILENO);
int file_fd = Open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC);
dup(file_fd);
*argc -= 2;
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
if (strcmp(argv[i], ">>") == 0) {
if (i + 1 == *argc) {
fprintf(stderr, "REDIRECT ERROR.\n");
exit(0);
}
close(STDOUT_FILENO);
int file_fd = Open(argv[i + 1], O_WRONLY | O_CREAT | O_APPEND);
dup(file_fd);
*argc -= 2;
for (int j = i; j < *argc; j++) strcpy(argv[j], argv[j + 2]);
}
}
argv[*argc] = NULL; // 保证argv以NULL为结尾
}
/*
* 把输入输出重定向到/dev/null
*/
void direct_null() {
int null_fd = Open("/dev/null", O_WRONLY);
// fprintf(stderr, "%d\n", file_fd);
/*
if (file_fd < 0) {
fprintf(stderr, "OPEN /dev/null ERROR.\n");
exit(0);
}
*/
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup(null_fd);
dup(null_fd);
dup(null_fd);
}
/*
* 复原重定向
*/
void direct_restore(int STDIN_FD_COPY, int STDOUT_FD_COPY) {
close(0); close(1);
dup(STDIN_FD_COPY); dup(STDOUT_FD_COPY);
}
/*
* 检查是否存在管道,若存在则返回管道地址
*/
char* get_pipepos(char *cmdline) {
return strchr(cmdline, '|');
}
/*
* 子进程执行cmdline1,将输出通过管道作为主进程cmdline2的输入
*/
void pipeline(char *cmdline1, char *cmdline2) {
int fd[2];
pipe(&fd[0]);
if (fork() == 0) { // 子进程,重定向输出
close(fd[0]);
close(STDOUT_FILENO);
dup(fd[1]);
close(fd[1]);
eval(cmdline1, 1);
exit(0);
} else { // 父进程,重定向输入
close(fd[1]);
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
eval(cmdline2, 1);
}
}
/*
* 统计内存和cpu使用情况
*/
void mytop() {
int totSize, freeSize, cachedSize, total_proc, proc_num_prev = 0, proc_num_now = 0;
u64_t total_ticks_pre = 0, total_ticks_now = 0, idle_ticks_pre = 0, idle_ticks_now = 0;
get_meminfo(&totSize, &freeSize, &cachedSize);
printf("main memory: ");
printf(" %dK total,", totSize);
printf(" %dK free,", freeSize);
printf(" %dK cached\n", cachedSize);
get_kinfo(&total_proc);
printf("Total procs: %d\n", total_proc);
get_pidinfo(prev, &proc_num_prev, &total_ticks_pre, &idle_ticks_pre);
sleep_ms(500);
get_pidinfo(now, &proc_num_now, &total_ticks_now, &idle_ticks_now);
double CPU_util = 100 - (double)100 * (idle_ticks_now - idle_ticks_pre) / (total_ticks_now - total_ticks_pre);
printf("CPU utilization : %.4f%%\n", CPU_util);
}
/*
* 读取/proc/meminfo
*/
void get_meminfo(int *totSize, int *freeSize, int *cachedSize) {
FILE *fp;
fp = Fopen("/proc/meminfo", "r");
int pagesize, total, free, largest, cached;
fscanf(fp, "%d %d %d %d %d", &pagesize, &total, &free, &largest, &cached);
*totSize = pagesize * total / 1024;
*freeSize = pagesize * free / 1024;
*cachedSize = pagesize * cached / 1024;
fclose(fp);
}
/*
* 读取/proc/kinfo
*/
void get_kinfo(int *total_proc) {
FILE *fp;
fp = Fopen("/proc/kinfo", "r");
int proc, task;
fscanf(fp, "%d %d", &proc, &task);
*total_proc = proc + task;
fclose(fp);
}
/*
* 读取/proc下所有文件夹
*/
void get_pidinfo(struct proc *p, int *num, u64_t *total, u64_t *idle) {
DIR *p_dir;
char *end;
if ((p_dir = opendir("/proc/")) == NULL) {
fprintf(stderr, "OPENDIR ERROR.\n");
exit(1);
}
struct dirent *p_ent;
*num = 0;
for (p_ent = readdir(p_dir); p_ent != NULL; p_ent = readdir(p_dir)) {
int pid = strtol(p_ent->d_name, &end, 10);
if (!pid) continue;
(*num)++;
parse_file(pid, &p[*num], total, idle);
}
closedir(p_dir);
}
/*
* 读取/proc/pid/psinfo
*/
void parse_file(int pid, struct proc *p, u64_t *total, u64_t *idle) {
char path[MAXPATH];
sprintf(path, "/proc/%d/psinfo", pid);
FILE *fp = Fopen(path, "r");
fscanf(fp, "%d", &(p->version));
fscanf(fp, " %c", &(p->type));
fscanf(fp, " %d", &(p->endpoint));
fscanf(fp, " %s", p->name);
fscanf(fp, " %c", &(p->state));
fscanf(fp, " %d", &(p->blocked));
fscanf(fp, " %d", &(p->priority));
fscanf(fp, " %d", &(p->user_time));
fscanf(fp, " %d", &(p->ticks));
fscanf(fp, " %lu", &(p->cycle_high));
fscanf(fp, " %lu", &(p->cycle_low));
fscanf(fp, " %llu", &(p->memory));
fscanf(fp, " %lu %lu", &(p->effuid), &(p->nice));
p->cpucycles[0] = make64(p->cycle_low, p->cycle_high);
*total += p->cpucycles[0];
if (p->endpoint == -4)
*idle = p->cpucycles[0];
// printf("PID : %d CYCLE : %llu low : %lu high : %lu ticks : %d %c %c\n", pid, p->cpucycles[0], p->cycle_low, p->cycle_high, p->ticks, p->type, p->state);
fclose(fp);
}
/*
* 合并高低周期
*/
static inline u64_t make64(unsigned long lo, unsigned long hi) {
return ((u64_t)hi << 32 | (u64_t)lo);
}
/*
* 实现毫秒级的sleep
*/
static void sleep_ms(unsigned int secs) {
struct timeval tval;
tval.tv_sec=secs/1000;
tval.tv_usec=(secs*1000)%1000000;
select(0,NULL,NULL,NULL,&tval);
}
/*
* 封装带有错误处理的open函数
*/
int Open(char *path, int param) {
int file_fd;
if ((file_fd = open(path, param)) < 0) {
fprintf(stderr, "OPEN FILE ERROR.\n");
exit(1);
} else return file_fd;
}
/*
* 封装带有错误处理的fopen函数
*/
FILE* Fopen(char *path, char *mode) {
FILE* fp;
if ((fp = fopen(path, mode)) == NULL) {
fprintf(stderr, "FOPEN ERROR.\n");
exit(1);
}
return fp;
}
/*
* 封装带有错误处理的execvp函数
*/
void Execvp(char *path, char **params) {
if (execvp(path, params) < 0) {
fprintf(stderr, "EXECVP ERROR.\n");
exit(1);
}
}
int main() {
char cmdline[MAXLINE];
int STDIN_FD_COPY = dup(STDIN_FILENO); //保存标准输入输出文件符,用于复原
int STDOUT_FD_COPY = dup(STDOUT_FILENO);
while(1) {
printf(" myshell> ");
fgets(cmdline, MAXLINE, stdin);
eval(cmdline, 0);
direct_restore(STDIN_FD_COPY, STDOUT_FD_COPY);
}
return 0;
}