【操作系统】操作系统课后作业-第三章项目一代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <sys/types.h>
#include <wait.h>

#define MAX_LINE 80
#define MAX_CMDS_CNT 12//history保存10条命令 

typedef struct History {
	char command_line[MAX_LINE];
	int usage_cnt;
} history;
history h_lst[MAX_CMDS_CNT];

int head = 0, tail = 0;
// head指向循环队列首部,tail指向队列尾部的下一个编号
//故判断Head和tail的值,可以得出队列是否为空。
int cmd_cnt = 0;//此变量代表所有保存过的命令的个数,可以大于10 
int should_run = 1;

void save_command(char *command_buffer){
	strcpy(h_lst[tail].command_line, command_buffer);
	h_lst[tail].usage_cnt ++;
	tail = (tail + 1) % MAX_CMDS_CNT;//MAX_CMDS_CNT参加运算, 防止tail = 0时, tail - 1 = -1,负数索引
	if(tail == head) head = (head + 1) % MAX_CMDS_CNT;//调整链表首部
	//printf("%d	%d\n",head,tail);
	cmd_cnt++;
}

void delete_last_elem(){
	if(head == tail)
		return ;
	tail = (tail - 1 + MAX_CMDS_CNT) % MAX_CMDS_CNT;
	if(cmd_cnt >= 10){
		if(tail == head) head = (head + 1) % MAX_CMDS_CNT;//调整链表首部
	}
	cmd_cnt --;
}

void display_commands_history(){
	if(head == tail) {
		printf("No commands in history.\n");
		return ;
	}

	//从队尾,往队首遍历
	int i = (tail - 1 + MAX_CMDS_CNT) % MAX_CMDS_CNT;
	int endi = (head - 1 + MAX_CMDS_CNT) % MAX_CMDS_CNT;
	//printf("%d	%d\n",i,endi);
	int t_cnt = cmd_cnt;
	while(i != endi) {//由于条件原因,MAX_CMDS_CNT加二
		printf("%3d %s\n", t_cnt--, h_lst[i].command_line);
		i = (i - 1 + MAX_CMDS_CNT) % MAX_CMDS_CNT;
	}
}


int process_command(char command_buffer[], char *args[], int *background){
	save_command(command_buffer);

	char *delim = " \r\t\n";
	int tcnt = 0;
	args[tcnt++] = strtok(command_buffer, delim);
	//分解字符串,command_buffer为要被分解的字符串,delim为用作分隔符的字符(可以是一个,也可以是集合),该函数返回被分解的第一个子字符串,若无可检索的字符串,则返回空指针 
	while(args[tcnt++] = strtok(NULL, delim)) ;//不需要再传入command_buffer,strtok会持续分割之前传入的command_buffer 
	//当args[n]为null时退出循环,所以数组中最后一个值为null,即args[tcnt - 1]为null,然后tcnt自加 

	if(strcmp(args[tcnt-2], "&") == 0) { 
		args[tcnt-2] = NULL;
		*background = 1;
	}
	if(strcmp(args[0], "!!") == 0 && args[1] == NULL) {
		delete_last_elem();//删掉保存好的'!!'命令 
		if(cmd_cnt == 0) {
			printf("No commands in history!\n");
			return 0;
		} 
		else{
			int indx = (tail - 1 + MAX_CMDS_CNT) % MAX_CMDS_CNT;
			strcpy(command_buffer, h_lst[indx].command_line);
			return process_command(command_buffer, args, background);
		}
	}
	// N should [cmd_cnt - MAX_CMDS_CNT + 1, cmd_cnt] and > 0.
	if(args[0][0] == '!' && args[1] == NULL) {
		delete_last_elem();
		int is_int = 1;
		for(int j = 1; args[0][j]; ++j) {//判断参数是否为数字
			if(!isdigit(args[0][j])) {
				is_int = 0;
				break;
			}
		}
		if(is_int) {
			int ret_n = atoi(&args[0][1]);//把字符串nptr转换为int
			int min_cmd_ind = 1 > cmd_cnt - MAX_CMDS_CNT + 1 ? 1 : cmd_cnt - MAX_CMDS_CNT + 1;
			int max_cmd_ind = cmd_cnt;
			if( ! (ret_n >= min_cmd_ind && ret_n <= max_cmd_ind) ) {
				printf("No such command in history.\n");
				return 0;
			}
			int offset = (cmd_cnt - ret_n);
			int indx = (tail - 1 - offset + MAX_CMDS_CNT) % MAX_CMDS_CNT;
			strcpy(command_buffer, h_lst[indx].command_line);
			return process_command(command_buffer, args, background);
		}
	}
	if (strcmp(args[0], "exit") == 0 && args[1] == NULL) {
		should_run = 0;//结束父进程 
	} 
	else if (strcmp(args[0], "history") == 0 && args[1] == NULL) {
		delete_last_elem();
	}
	return 1;

}


int get_command(char command_buffer[], char *args[], int *background){
	int length;
	length = read(STDIN_FILENO, command_buffer, MAX_LINE);
	//STDIN_FILENO:接收键盘的输入,MAX_LINE是请求读取的最大字节数,读上来的数据保存在缓冲区command_buffer中,同时文件的当前读写位置向后移。
	//read():成功则返回读取的字节数,出错则返回-1并设置errno,如果在调read()之前已到达文件末尾,则返回0
	
	if (length == 1) // \n,说明只按了一个回车键,则重新接收命令 
		return 0;
	if (length < 0) {
		printf("Command reading failure!\n");
		exit(-1);//退出当前运行的程序,并将-1返回给主调进程
	}

	command_buffer[length-1] = 0;//将回车替换为'\0',表示字符串结束
	return process_command(command_buffer, args, background);
}


int execute_command(char *args[], int *background)
{
	if (strcmp(args[0], "history") == 0) {
		display_commands_history();
		return 1;
	}
	if(strcmp(args[0], "exit") == 0) {
		exit(0);//退出的是子进程 
	}

	execvp(args[0], args);
}



int main(int argc, char *argv[]){
	char command_buffer[MAX_LINE];//存储输入的整个字符串 
	int background;//判断是否输入了'&'符号,若在命令最后使用了"&"符号,父进程与子进程并发执行,即父进程是否等待子进程 
	char *args[MAX_LINE / 2 + 1];//command line arguments
	pid_t pid, tpid;

	while (should_run) {
		background = 0;
		printf("osh>");
		fflush(stdout);//调用printf()时,输出的结果一般会被标准库缓存起来,可能不会及时打印写出到输出设备上面,此时就可以用fflush(stdout)强制把缓存内容进行输出 

		int ret_flag = get_command(command_buffer, args, &background);
		
		if (ret_flag > 0) {
			pid = fork();
			if (pid < 0) {
				printf("Failed to fork a process!\n");
				exit(1);
			} 
			else if (pid == 0) {
				//child process
				if (execute_command(args, &background) == -1)
					printf("Failed to execute the command!\n");
				printf("child over!\n");
				break;
			} 
			else {
				printf("%d\n",background);
				// father process
				if (background == 0) {
					//解决输入&之后,父进程先于子进程结束的问题
					//原因:前一个并发执行时,子进程执行完exit后,由于父进程是并发执行,因此并没有调用wait进行“收尸”
 					//所以下一次非并发执行时,父进程wait“收尸”的是上一次并发执行子进程的“尸体”
 					//以往,会导致整个程序的父进程总是在“收尸”上一个子进程
					while(wait(NULL) != pid); 
				}
				printf("father over!\n");
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值