쉘 프로그램의 구조
쉘(Shell)은 Command Interpreter 로 명령어 해석기라는 뜻이다. 명령어라는 것은 기본적으로 실행프로그램의 이름이고 실행프로그램을 실행시켜주는 역할을 한다. 사용자는 모니터에서 입력을 주거나 출력을 받는 작업을 한다.
쉘이라는 프로그램은 커널, 운영체제의 일부로 작동하는 것은 아니고 일반 프로세스로 작동한다. 화면에 나타나는 프롬프트(%)를 보고 실행되고 있다는 것을 알 수 있다.
명령어라는 것은 앞에서 말했듯이 실행프로그램의 이름인데 크게 두 가지로 나눌 수 있다.
1. UNIX 시스템에서 미리 만들어 놓은 프로그램들
2. 사용자가 프로그램을 짜서 실행 파일을 만들어 놓았다.
쉘이 명령을 받으면 자식 프로세스를 만들어서 프로그램을 실행하게 되고 작업이 완료되면 쉘이 그 다음 프롬프트를 내준다.(%)
쉘(Shell)을 사용자와 interact 하면서 사용자의 명령(Command)를 해석해서 처리해준다 해서 command interpreter라고 한다.
쉘 프로그램의 구조는 다음과 같다.
/* read command line until "end of file" */
while(read(stdin, buffer, numchars)) {
/* parse command line */
if(/* command line contains & */)
amper = 1;
else
amper = 0;
/* for command not part of the shell command language */
// 자식이 시작되는 시점
if(fork() == 0) {
/* redirection of IO? */
if(/* redirect output */){
fd = creat(newfile, fmask);
close(stdout);
dup(fd);
close(fd);
}
/* student is now redirected */
if(/* piping */) {
pipe(fildes);
if(fork() == 0) {
/*first component of command line */
close(stdout);
dup(fildes[1]);
close(fildes[1]);
close(fildes[0]);
/* stdout now goes to pipe */
/* child process does command */
execlp(command1, command1, 0);
}
/* 2nd command component of command line */
close(stdin);
dup(fildes[0]);
close(fildes[0]);
close(fildes[1]);
/* stadard input now comes from pipe */
}
execve(command2, command2, 0);
}
/* parent continues over here...
* wait for child to exit if required
*/
if(amper == 0)
retid = wait(&status);
}
먼저 while문 내부에 사용자의 명령을 읽어들이는 부분이 있다. 이후 명령라인을 해독하면서 명령문 내에 &가 포함되어 있는지 확인한다. 명령문 내에 &가 포함되어 있다는 것은 명령을 백그라운드로 수행하라는 것으로 자식프로세스가 수행되는 동안에 바로 프롬프트를 내보내게 되고, 백그라운드가 아니라면 쉘은 작업이 진행되는 동안 대기하다가 작업을 끝난 후에 다시 수행되게 된다. 여기서는 amper 라는 변수를 통해 코드의 마지막 부분에서 자식 프로세스를 기다릴지 아닐지를 결정하는 것을 볼 수 있다.
fork() 함수를 통해 자식프로세스를 생성하고, 부모프로세스는 amper 변수의 값이 1( 백그라운드 )라면 자식프로세를 기다리지 않고 반복문을 돌아 다음 프롬프트의 입력을 받고, 0이라면 자식프로세스의 종료를 기다린다.
자식프로세스는 그자체가 명령어로 준 프로그램이 되는데 이때 execve() 함수를 사용한다. 자식의 경우 exec계열 함수를 사용하기 전에 몇가지를 체크한다.
1. Redirect 가 있는가.
UNIX 에서의 파일은 입출력장치도 파일로 받게 된다. 그 이유는 UNIX에 크게 두가지의 컴포넌트가 있기 때문인데 바로 프로세스와 파일이다. 프로세스라는 것은 실행중인 프로그램, 파일이라는 것은 프로세스가 실행될 때 필요한 입력을 제공하거나 또는 프로세스의 결과를 받는 것을 말한다.
파일이라는 것이 입/출력 장치로 본다는 것을 숙지하고, 입출력의 파일을 바꾸는 것을 output/input Redirect 라고 한다. (1) > (2), (1) < (2) 와 같이 사용한다. 이러한 Redirect 가 있으면 명령어를 수행하기 전에 처리를 해주어야 한다.
2. pipe 명령이 있는가
'|' 를 사용해서 command를 여러 개 주게 되는데, 파이프 앞에 있는 것을 생산자, 뒤에 있는 것을 소비자 라고 한다. 앞에 있는 명령의 출력이 뒤에 있는 명령의 입력이 된다. 프로세스를 동시에 생성하고 생산자와 소비자의 관계가 이어진다.
예를 들어, x | y | z 와 같이 입력했을 때 파이프마다 파이프 파일을 만들어서 앞 명령의 출력이 뒤 명령의 입력이 되도록 한다. 이때 파이프 파일은 임시로 만든 파일이기 때문에 파일 시스템 상에 등록되지는 않는다.