mysh.h
//header for myshell by 이창민, ckdals0115@naver.com
//mysh.h
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<wait.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<signal.h>
#include<errno.h>
#define MAX 100
//static int child_pid = 0;
//static int COUNT = 0;
//static char* COMMAND_PATH = "Desktop/Sys/mysh/";
void redirection_com(char** commands, int i);
void background_com(char** commands, int i);
char** get_tokens(char* CommandLine);
void execute_com(char** command);
mysh.c
//myshell by 이창민, ckdals0115@naver.com
//mysh.c
#include"mysh.h"
void main(){
write(STDOUT_FILENO,"+------------------------------------------------------------------------------------+\n",88);
write(STDOUT_FILENO,"|MYSHELL |\n",88);
write(STDOUT_FILENO,"|------------------------------------------------------------------------------------|\n",88);
write(STDOUT_FILENO,"|QUIT : quit |\n",88);
write(STDOUT_FILENO,"+------------------------------------------------------------------------------------+\n",88);
while(1){
//write시 쓰레기값 없게 초기화
char CommandLine[MAX]={0};
char PWD[MAX]={0};
char** command;
int read_size;
int errno;
/*char *getcwd(char *buf, int maxlen); => get pwd "unistd.h*/
getcwd(PWD,MAX);
/////////////////////////////////////////////////////////////////
//화면에 출력, write가 printf보다 빠르므로 write를 써봣음..
write(STDOUT_FILENO, "@myshell:~", 20);
write(STDOUT_FILENO, PWD, MAX);
write(STDOUT_FILENO, "$ ",3);
/////////////////////////////////////////////////////////////////
//읽기
/*입력받은 커맨드 command로 읽음*/
read_size = read(STDIN_FILENO, CommandLine, MAX);
//input enter
if(read_size == 1){
continue;}
////////////////////////////////////////////////////////////////
//명령어 구분하고 실행
command = get_tokens(CommandLine);
if(!strcmp(command[0],"quit")){
printf("+------------------------------------------------------------------------------------+\n");
printf("|BASH |\n");
printf("+------------------------------------------------------------------------------------+\n");
exit(1);}
else{
execute_com(command);
}
}
}
mysh_funcs.c
//functions for myshell by 이창민, ckdals0115@naver.com
//mysh_funcs.c
/*
token구하기
명령수행
-shell bulitin
-/bin
-redirection
-background
*/
#include"mysh.h"
/////////////////////////////////////////////////////////////////////////////////////
////////CommandLine 쪼갬
char** get_tokens(char* CommandLine) {
char **tokens;//토큰들을 저장할 배열
char *token;
int COUNT=0;
//\n 대신 \0넣음
CommandLine[strlen(CommandLine) - 1] = '\0';
//토큰 저장할 배열 크기
tokens = malloc((sizeof(char*))*MAX);
//" "을 구분자로 자름
token = strtok(CommandLine, " ");
while(token != NULL){
//\0포함해야하므로 +1
tokens[COUNT] =malloc(strlen(token)+1);
//동적할당 후 내용복사
strncpy(tokens[COUNT], token, strlen(token)+1);
token = strtok(NULL, " ");
COUNT++;
}
//토큰들 입력 끝나면 null
tokens[COUNT] = NULL;
return tokens;
}
//////////////////////////////////////////////////////////////////////////////////////
/////명령을 받으면 수행
void execute_com(char **commands){
int fork_process;
int status;
int i=0;
char *dir;
///////////////////////////////////////////////
/////////shell builtin command
if(!strcmp(commands[0], "cd")){
if(commands[1] == NULL){
printf("USEAGE : cd directory\n");
}
chdir(commands[1]);
}
else if(!strcmp(commands[0], "pwd")){
printf("Present Working Directory: %s\n",getcwd(dir, MAX));
}
///////////////////////////////////////////////
///////////저장되어있는 명령어(/bin안에 존재)
else {
if((fork_process = fork())==0){
//execv'P' 는 PATH에 등록된 모든 디렉토리에 있는 프로그램을 실행
//명령어 끝까지 읽어서 각각 상황에 따라 실행
while(commands[i]){
if(!strcmp(commands[i],">")){
redirection_com(commands, i);
}
else if(!strcmp(commands[i],"&")){
background_com(commands, i);
}
i++;
}
//잘못된 commands[0]가 들어오면 -1 반환함
if(execvp(commands[0],commands)==-1){
printf("%s : command not found\n",commands[0]);
exit(1);
}
}
else if(fork_process < 0){
printf("fork error\n");
exit(1);
}
else
wait(&status);
}
}
//////////////////////////////////////////////////////////////////////
///////////명령어에 리다이렉션이 있을경우 수행
//ex) cat a.txt > b.txt
void redirection_com(char** commands, int i){
char* to = commands[i+1];
char* from = commands[i-1];
int to_fd;
to_fd = open(to, O_WRONLY | O_CREAT, 0641);
if(to_fd < 0){
printf("open error\n");
exit(1);
}
//STDOUT_FILENO이 to_fd를 가리키게함
dup2(to_fd, STDOUT_FILENO);
//>자리를 NULL로
commands[i] = NULL;
//명령어 수행
if(execvp(commands[0], commands)==-1){
printf("No such command : %s\n",commands[0]);
exit(1);
}
}
/////////////////////////////////////////////////////////////////////////
////////명령어에 &이 있을경우 background processing 수행
void background_com(char** commands, int i){
commands[i]=NULL;
int fork_process;
if((fork_process = fork())==0){
if(execvp(commands[0], commands)==-1){
printf("No such command : %s\n",commands[0]);
exit(1);
}
}
else if(fork_process<0){
printf("fork error\n");
exit(1);
}
//기다리지 않음
else{
exit(1);
}
};
while1.c (단순 무한 루프)
#include<stdio.h>
int main(){
while(1){
}
return 0;
}
Makefile
mysh: mysh.o mysh_funcs.o
gcc -o mysh mysh.o mysh_funcs.o
mysh.o: mysh.h mysh.c
gcc -c mysh.c
mysh_funcs.o: mysh.h mysh_funcs.c
gcc -c mysh_funcs.c
현재 ~디렉토리 $를 해주기 위해 getcwd를 사용하여 현재 디렉토리를 가져와 터미널에 출력하였다.
read systemcall을 사용하여 STDIN을 읽었다. 그리고 만약 엔터 \n만 치게된다면 다시 커맨드를 입력받을 수 있게 하였다.
명령어가 들어오면 execute_com를 실행하여 명령어를 실행하게 하였고, quit를 입력하면 myshell이 종료되게 하였다.
execute_com을 하려면 일단 입력받은 커맨드를 쪼개서 구분을 하여 명령을 처리해야한다. 따라서 get_tokens라는 토큰을 얻는 함수를 생성하였다. strtok함수를 이용하여 “ ”을 구분자로 하여 token화 하였다.
커멘드를 token으로 쪼개어 명령을 수행하는데, shell builtin 명령어들은 /bin에 저장되어있는 것이 아니라 bash가 동작할 때 수행하므로 각 명령어에 맞는 함수들을 사용하여 구현하였다.
shell builtin명령어들은 종류가 많이 있지만 각각 경우들을 비교하려면 수행시간이 길어지므로 cd와 pwd만 구현을 하였다.
따라서, strcmp함수를 사용하여 shell builtin명령어가 들어오면 builtin명령어인지 구분하여 실행을 한다.
그 외의 명령어들은 /bin에 있으므로 execvp를 사용하여 명령어를 실행한다. 환경변수 path에 디렉토리가 있으면 파일명만 입력하면 되므로 commands[0]을 인자로 넘겨준다.
리다이렉션의 경우, “from_file > to_file”의 형태이므로 STDOUT_FILENO가 to_file fd를 가리키게 한다.
그리고 “>” 앞의 명령어들을 실행하여 to_file에 그 결과들이 수행되게 한다.
백그라운드의 경우, fork를 한 후 부모프로세스는 자식프로세스를 wait하지 않게 하여 자식 프로세스가 백그라운드 프로세스로 돌아가도록 한다.
나머지 잘못된 사용을 하였을 시 잘못되었다고 예외처리들을 해주었다.
bash의 builtin 명령어들을 모두 구현하기 위해선 모든 builtin 명령어에 해당하는지 검사를 하고, 또 그에 따른 적절한 함수들을 사용해야하므로 코드가 길어지고 명령어 수행시간이 길어질 것 같았다.
따라서 cd, pwd와 같은 간단한 명령어만 수행하였다.
여기서 굳이 shell에서 builtin 명령어, 그 외 명령어로 구분지어 하나는 파일로 저장을 하고 하나는 쉘 내부에서 작동하게 하였는지 궁금하여 찾아보니, builtin 명령어들은 자주 사용되어 파일로 디스크에 저장해두면 매번 디스크에서 불러와야하므로 효율을 높이기 위해 램에 상주시키는 것을 알게 되었다.
'공부 > 운영체제&시스템프로그래밍' 카테고리의 다른 글
New Architectural state structure (0) | 2021.06.21 |
---|---|
mycp (0) | 2020.07.16 |
EXT2 file system 실습 (6번 디렉토리안의 82번 파일 찾기) (0) | 2020.07.16 |