【 압축 파일 】 [OS] Shell
45532 단어 압축 파일
작업 링크: 홈 워 크: 셸
이번 작업 은 주로 셸, 시스템 호출, 셸 의 작업 원 리 를 더욱 잘 알 게 하 는 것 이다.6.828 Shell 을 통 해 확장 하면 유 닉 스 API 를 지원 하 는 운영 체제 에서 실행 할 수 있 습 니 다. 예 를 들 어 Linux, MacOS 등 입 니 다.
xv 6 북 의 Chapter 0 을 읽 고 운영 체제 의 인 터 페 이 스 를 알 아 볼 수 있 습 니 다.
첫 번 째 단계: 6.828 셸 의 소스 코드
sh.c
를 다운로드 합 니 다. 코드 는 주로 두 부분 을 포함 합 니 다. 셸 명령 과 실행 명령 을 해석 하고 간단 한 명령 만 고려 합 니 다. 다음 과 같 습 니 다.ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls | sort | uniq | wc
rm y
나중에 사용 할 수 있 도록 위의 명령 을
t.sh
파일 에 저장 합 니 다.주: 컴 파일
sh.c
은 C 컴 파일 러 를 사용 해 야 합 니 다. 설치 가 필요 하지 않 으 면 아래 명령 으로 컴 파일 해 야 합 니 다.$ gcc sh.c
그리고 같은 폴 더 에서 실행 가능 한 파일 을 생 성 합 니 다
a.out
.현재 디 렉 터 리 구조:
.
├── a.out
├── sh.c
└── t.sh
0 directories, 3 files
방금 컴 파일 된 실행 가능 한 파일 실행:
$ ./a.out < t.sh
redir not implemented
exec not implemented
pipe not implemented
exec not implemented
exec not implemented
pipe not implemented
exec not implemented
실행 한 후에 잘못 보고 할 수 있 기 때문에 우 리 는 이곳 의 일부 기능 을 실현 해 야 한다.
STEP 2:
sh.c
에 뭐라고 쓰 여 있 는 지 알 아 보 세 요.먼저 보기
main()
함수:int main(void)
{
// ( )
static char buf[100];
int fd, r;
// Read and run input commands.
// while , while
while (getcmd(buf, sizeof(buf)) >= 0)
{
// [cd fileName] ,
//
if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ')
{
// Clumsy but will have to do for now.
// Chdir has no effect on the parent if run in the child.
buf[strlen(buf) - 1] = 0; // chop
if (chdir(buf + 3) < 0)
fprintf(stderr, "cannot cd %s
", buf + 3);
continue;
}
// fork ,
// fork() http://www.cnblogs.com/jeakon/archive/2012/05/26/2816828.html
// fork() :
// fork() : , ,
// pid
// 0
//
//
if (fork1() == 0)
runcmd(parsecmd(buf));
// wait http://www.jb51.net/article/71747.htm
// wait() ( wait() ) ,
// 。
// ,
// wait() 。
// pid_t wait (int * status);
wait(&r);
}
exit(0);
}
터미널 에 입력 이 있 으 면 함수
getcmd()
를 실행 합 니 다.// , ,
int getcmd(char *buf, int nbuf)
{
//
if (isatty(fileno(stdin)))
fprintf(stdout, "6.828$ ");
//
memset(buf, 0, nbuf);
//
if (fgets(buf, nbuf, stdin) == 0)
return -1; // EOF
return 0;
}
그 다음 에
while
순환 에 들 어 갑 니 다. 먼저 디 렉 터 리 전환 명령 cd
을 입력 하면 디 렉 터 리 전환 작업 을 직접 수행 하고 그렇지 않 으 면 fork()
키 프로 세 스 를 입력 하여 처리 해 야 합 니 다.if (fork1() == 0)
runcmd(parsecmd(buf));
wait(&r);
함수
fork1()
는 하위 프로 세 스 를 fork 하고 부모 / 하위 프로 세 스 의 pid 를 되 돌려 줍 니 다.main()
함수 에서 돌아 오 는 pid 를 판단 하여 현재 실행 중인 프로 세 스 가 어떤 프로 세 스 인지 판단 하고 하위 프로 세 스에 서 해당 하 는 명령 을 수행 합 니 다.부모 프로 세 스 에서 wait(&r)
를 사용 하여 차단 하고 하위 프로 세 스 가 돌아 오 기 를 기다 린 후에 계속 실행 합 니 다.함수
parsecmd()
는 입력 한 명령 을 해석 하기 위해 서 입 니 다. 너무 많은 관심 을 가지 지 않 아 도 되 지만 함수 parsecmd
의 정의 에서struct cmd *parsecmd(char *);
구조 체
cmd
의 정의// All commands have at least a type. Have looked at the type, the code
// typically casts the *cmd to some specific cmd type.
struct cmd
{
int type; // ' ' (exec), | (pipe), '' for redirection
};
이 함수 의 기능 은 주로 입력 한 명령 의 종 류 를 판단 하 는 것 임 을 알 수 있다.
그리고 함수
runcmd()
안에 무엇이 있 는 지 보 겠 습 니 다.void runcmd(struct cmd *cmd)
{
int p[2], r;
struct execcmd *ecmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if (cmd == 0)
_exit(0);
switch (cmd->type)
{
default:
fprintf(stderr, "unknown runcmd
");
_exit(-1);
case ' ':
ecmd = (struct execcmd *)cmd;
if (ecmd->argv[0] == 0)
_exit(0);
fprintf(stderr, "exec not implemented
");
// Your code here ...
break;
case '>':
case ':
rcmd = (struct redircmd *)cmd;
fprintf(stderr, "redir not implemented
");
// Your code here ...
runcmd(rcmd->cmd);
break;
case '|':
pcmd = (struct pipecmd *)cmd;
fprintf(stderr, "pipe not implemented
");
// Your code here ...
break;
}
_exit(0);
}
이 함 수 는 구조 체
cmd
의 매개 변 수 를 받 아들 이 고 이 구조 체 중의 type
값 을 통 해 진일보 한 처 리 를 한다.switch case
문장의 판단 조건 을 통 해 알 수 있 듯 이 명령 의 유형 을 세 가지 로 나 누 었 는데 그것 이 바로 case ''
명령 을 집행 할 수 있 고 case ' case '>'
리 셋 명령 과 case '|'
파이프 명령 이다.우리 가 해 야 할 일 은 서로 다른 유형의 명령 에서 구체 적 으로 명령 을 실행 하 는 코드 를 보완 하 는 것 이다.
세 번 째 단계: 실행 가능 한 명령 실현 (Executing simple commands)
우선 함수 int access(const char *path, int mode);
를 소개 하고 터미널 에서 명령 man access
을 사용 하여 상세 한 소 개 를 볼 수 있 습 니 다.
이 함수 의 기능 은 파일 이나 폴 더 의 접근 권한 을 확인 하 는 것 입 니 다. 예 를 들 어 읽 기, 쓰기 등 입 니 다. :
path: 。
mode: , 。
R_OK //
W_OK //
X_OK //
F_OK // /
반환 값 형식 은 int
이 며, 해당 권한 이 있 으 면 0
으로 되 돌아 갑 니 다. 그렇지 않 으 면 함수 가 되 돌아 갑 니 다 -1
.
리 눅 스에 서 '모든 것 이 파일' 이기 때문에 우리 가 수행 하 는 리 눅 스 의 명령 도 파일 로 존재 합 니 다.일부 시스템 기본 명령 은 /bin/
디 렉 터 리 에 존재 합 니 다. ls /bin
디 렉 터 리 에 존재 하 는 명령 도 있 습 니 다. /usr/bin/
디 렉 터 리 에 다른 명령 은 해당 프로그램 디 렉 터 리 bin
디 렉 터 리 에 존재 하기 때문에 이 명령 을 실행 하기 전에 access()
함 수 를 사용 하여 이 명령 (파일) 이 존재 하 는 지 확인 하 십시오. (F OK)다음 작업 을 진행 하 다.$ ls /bin
[ date expr ln pwd sync
bash dd hostname ls rm tcsh
cat df kill mkdir rmdir test
chmod domainname ksh mv sh unlink
cp echo launchctl pax sleep wait4path
csh ed link ps stty zsh
그 다음 에 우 리 는 함수 int exec(const char *path, char *const argv[]);
를 하나 더 보고 터미널 에서 명령 man 3 exec
을 사용 하여 상세 한 소 개 를 볼 수 있다.
이 함수 의 기능 은 path
경로 의 파일 을 사용 하여 존재 하 는 명령 을 수행 하 는 것 입 니 다.
매개 변수 설명: argv
파일 을 실행 할 경로 입 니 다.path
은 하나의 배열 로 배열 에 실행 할 명령 이 저장 되 어 있 고 배열 의 요소 간 에 빈 칸 으로 연결 되 어 실행 할 명령 이 형성 되 었 다.
반환 값: 실행 에 성공 하면 되 돌아 오지 않 습 니 다. 실행 에 실패 하면 되 돌아 갑 니 다 argv
. 실패 원인 은 -1
에 있 습 니 다.
사용 errno
헤더 파일 도입 errno
함수 #include
를 사용 하여 오류 코드 에 대응 하 는 설명 정 보 를 얻 을 수 있 습 니 다.
다음은 '명령 집행' 을 시작 하 겠 습 니 다!먼저 생각 을 말 하고 strerror(errno)
함 수 를 사용 하여 실행 할 명령 파일 이 존재 하 는 지 확인 하고 존재 하면 직접 실행 합 니 다. 그렇지 않 으 면 시스템 access()
디 렉 터 리 와 /bin/
디 렉 터 리 에서 해당 하 는 명령 을 찾 습 니 다. 있 으 면 실행 합 니 다. 그렇지 않 으 면 오 류 를 버 립 니 다.코드 세그먼트 다음 /usr/bin/
부분의): case ' ':
ecmd = (struct execcmd *)cmd;
if (ecmd->argv[0] == 0)
_exit(0);
// fprintf(stderr, "exec not implemented
");
// Your code here ...
if (access(ecmd->argv[0], F_OK) == 0)
{
execv(ecmd->argv[0], ecmd->argv);
}
else
{
const char *bin_path[] = {
"/bin/",
"/usr/bin/"};
char *abs_path;
int bin_count = sizeof(bin_path) / sizeof(bin_path[0]);
int found = 0;
for (int i = 0; i < bin_count && found == 0; i++)
{
int pathLen = strlen(bin_path[i]) + strlen(ecmd->argv[0]);
abs_path = (char *)malloc((pathLen + 1) * sizeof(char));
strcpy(abs_path, bin_path[i]);
strcat(abs_path, ecmd->argv[0]);
if (access(abs_path, F_OK) == 0)
{
execv(abs_path, ecmd->argv);
found = 1;
}
free(abs_path);
}
if (found == 0)
{
fprintf(stderr, "%s: Command not found
", ecmd->argv[0]);
}
}
break;
수정 이 끝 난 후 컴 파일 하여 실행 할 수 있 습 니 다. 효 과 는 다음 과 같 습 니 다.$ gcc sh.c
$ ./a.out
6.828$ ls
a.out sh.c t.sh
6.828$
case ' '
를 사용 하면 프로그램 을 종료 할 수 있 습 니 다.
STEP 4: IO 리 디 렉 션 실현 (I / O redirection)
우선, 구조 체 Ctrl/Command + D
의 정 의 를 살 펴 보 자.struct redircmd
{
int type; // < or >
struct cmd *cmd; // the command to be run (e.g., an execcmd)
char *file; // the input/output file
int flags; // flags for open() indicating read or write
int fd; // the file descriptor number to use for the file
};
주로 이 구조 체 안의 이러한 속성 을 사용한다.
설명 이 필요 한 것 은 redircmd
파일 설명자 입 니 다.
파일 설명자: 보통 작은 비 마이너스 정수 입 니 다. 커 널 은 특정한 프로 세 스 가 접근 하고 있 는 파일 을 표시 합 니 다.커 널 이 기 존 파일 을 열거 나 새 파일 을 만 들 때 파일 설명 자 를 되 돌려 줍 니 다.파일 을 읽 고 쓸 때 이 파일 설명 자 를 사용 할 수 있 습 니 다.
그 다음 에 생각 을 정리 하고 코드 를 쓰기 시작 할 수 있 습 니 다. 현재 의 표준 입 출력 을 닫 고 지정 한 파일 을 열 어 새로운 표준 입 출력 으로 명령 을 실행 합 니 다.코드 세그먼트 다음 int fd;
부분: case '>':
case ':
rcmd = (struct redircmd *)cmd;
// fprintf(stderr, "redir not implemented
");
// Your code here ...
close(rcmd->fd);
if (open(rcmd->file, rcmd->flags, 0644) < 0)
{
fprintf(stderr, "Unable to open file: %s
", rcmd->file);
exit(0);
}
runcmd(rcmd->cmd);
break;
함수 case ''
의 사용 방법 은 파일 IO 상세 설명 (5) - open 함수 상세 설명 참조
수정 이 끝 난 후 컴 파일 하여 실행 할 수 있 습 니 다. 효 과 는 다음 과 같 습 니 다.$ gcc sh.c
$ ./a.out
6.828$ ls > ls.tmp
6.828$ cat < ls.tmp
a.out
ls.tmp
sh.c
t.sh
6.828$
위의 두 명령 은 각각 int open(const char * pathname, int flags, mode_t mode);
열 거 된 파일 이름 을 파일 ls
에 저장 하고 ls.tmp
명령 을 사용 하여 파일 cat
의 내용 을 읽 고 표시 하 는 것 이다.
다섯 번 째 단계: 파이프 명령 실현 (Implement pipes) * *
파 이 프 는 두 프로 세 스 (예 를 들 어 fork 에서 나 온 부자 프로 세 스) 간 의 표준 입력 과 표준 출력 을 연결 하 는 메커니즘 으로 여러 프로 세 스 간 의 통신 방법 을 제공 합 니 다. 프로 세 스 가 파 이 프 를 만 들 때 매번 두 개의 파일 설명 자 를 제공 하여 파 이 프 를 만들어 야 합 니 다.그 중 하 나 는 파 이 프 를 쓰 고 다른 하 나 는 파 이 프 를 읽 는 작업 을 한다.파이프 에 대한 읽 기와 쓰 기 는 일반적인 IO 시스템 함수 와 일치 합 니 다. write () 함수 로 데 이 터 를 기록 하고 read () 로 데 이 터 를 읽 습 니 다.
마찬가지 로 구조 체 ls.tmp
의 정 의 를 살 펴 보 자.struct pipecmd
{
int type; // |
struct cmd *left; // left side of pipe
struct cmd *right; // right side of pipe
};
파이프 명령 의 표 지 는 기호 pipecmd
이 고 |
의 왼쪽 과 오른쪽 은 각각 다른 명령 이 므 로 우 리 는 이러한 명령 을 점차적으로 집행 해 야 한다.
그리고 우 리 는 함 수 를 하나 알 아 보 겠 습 니 다. |
이 함수 의 역할 은 두 프로 세 스 사이 에 파 이 프 를 만 들 고 두 개의 파일 설명자 int pipe(int fd[2]);
와 fd[0]
를 생 성 하 는 것 입 니 다. 각각 파이프 의 읽 기와 쓰기 단 에 대응 합 니 다. 이 두 프로 세 스 는 이 두 파일 설명 자 를 사용 하여 읽 기와 쓰기 작업 을 할 수 있 습 니 다. 즉, 이 두 프로 세 스 간 의 통신 을 실현 합 니 다.
반환 값: 성공 하면 반환 fd[1]
, 그렇지 않 으 면 반환 0
, 오류 원인 은 -1
에 저 장 됩 니 다.
다른 함수: errno
:
이 함수 의 기능 은 기 존 파일 설명 자 를 복사 하 는 것 입 니 다.
반환 값: 성공 하면 같은 파일 을 가리 키 는 새 파일 설명자 (현재 사용 가능 한 파일 설명자 의 최소 값) 를 되 돌려 줍 니 다. 실패 하면 되 돌려 줍 니 다 dup(int old_fd)
.
코드 세그먼트 -1
부분): case '|':
pcmd = (struct pipecmd *)cmd;
// fprintf(stderr, "pipe not implemented
");
// Your code here ...
//
if (pipe(p) < 0)
fprintf(stderr, "pipe failed
");
// fork ,
//
if (fork1() == 0)
{
// dup
close(1);
// dup p[1] ,
dup(p[1]);
//
close(p[0]);
close(p[1]);
// left ,
runcmd(pcmd->left);
}
// fork ,
// ,
//
if (fork1() == 0)
{
// dup
close(0);
// dup p[0] ,
dup(p[0]);
//
close(p[0]);
close(p[1]);
// right ,
runcmd(pcmd->right);
}
close(p[0]);
close(p[1]);
wait(&r);
wait(&r);
break;
Linux 명령 case '|'
(Word Count): 파일 의 줄 수, 글자 수, 바이트 수 를 통계 하 는 데 사 용 됩 니 다.
예 를 들 어 파일 wc
에는 다음 과 같은 내용 이 포함 되 어 있다.a.out
sh.c
t.sh
y
명령 사용 y
결 과 는 다음 과 같 습 니 다. 4 4 18 y
첫 번 째 wc y
는 파일 중 4 줄, 두 번 째 4
는 4 글자 (분할 문자 로 빈 칸 사용), 세 번 째 4
는 18 바이트 (주의, 줄 바 꿈 문자 도 하나의 바이트 로 계산), 네 번 째 18
는 통 계 를 나타 내 는 파일 이름 을 나 타 냈 다.
마지막 으로 컴 파일 하여 실행 할 수 있 습 니 다:$ gcc sh.c
# t.sh
$ ./a.out < t.sh
#
4 4 18
4 4 18
Bingo~
참고 자료:
Homework: shell
6.828 운영 체제 Homework: Shell
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【 압축 파일 】 [OS] Shell이번 작업 은 주로 셸, 시스템 호출, 셸 의 작업 원 리 를 더욱 잘 알 게 하 는 것 이다.6.828 Shell 을 통 해 확장 하면 유 닉 스 API 를 지원 하 는 운영 체제 에서 실행 할 수 있 습 니 다....
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.