'shell'에 해당되는 글 2건

  1. 2004/04/27 Shell 개요
  2. 2004/03/31 만우절 특집...

Shell 개요

컴퓨터/System 2004/04/27 07:05

shell의 기본적인 개념위주의 문서입니다.
심심하실때 한번씩 봐주시면 좋을거 같습니다.
Ellie Quigley의 Unix Shell을 참고했습니다.


Unix Shell의 간략한 소개


1.1 정의와 기능
shell은 kernel이라고 하는 시스템 운영체제인 유닉스와 사용자가 서로 의사소통을 할 수있도록 도와주는 인터페이스이다.

커널인 유닉스는 시스템이 부팅되어 다시 종료될때까지 메모리에 로드되어 머문다.
커널은 각종 프로세스들의 생성과 제어뿐만 아니라 메모리,파일시스템,응용프로그램등의 관리등 다양한 일을 한다.
Shell 프로그램등 다른 모든 프로그램들은 메모리가 아닌 디스크에 상주한다. 커널은 이들 프로그램들을 메모리에 로드시켜 실행하고 이들이 종료하면 시스템을 깨끗이  청소해준다.
shell 은 사용자가 시스템에 로그인하면 시작되는 유틸리티 프로그램이다. Shell은 명령줄이나 스크립트 파일에서 오는 각종 명령(command)들을 해석해서 사용자가  커널과 작업을 할수 있도록 도와준다.

사용자가 로그인하면 인터액티브 기능을 가진 shell이 시작되며 입력을 기다린다.
이때 커맨드를 하나 입력하면 shell은 해당 커맨드를 해석하고, 커맨드에 포함된 와일드 문자해석, 방향 재지정, 파이프설정 및 작업제어등을 처리하며 해당 커맨드가 시스템에 있는지 검색해서 있으면 실행시켜 준다.

유닉스를 처음 배울 때 명령 프롬프트에서 커맨드를 실행시키는데 시간을 많이 소비한다.
이때 사용하는 shell은 인터액티브기능을 가진다.

일상적으로 여러 커맨드를 같이 사용하는 경우에 이것을 자동화시키는 것이 좋다.
스크립트 파일안에 여러 커맨드들을 입력해두고 그 파일을 실행시키면 이것이 가능하다.
Shell의 스크립트는 다른 곳에서 사용하는 배치파일과 같다. 배치파일에도 여러 유닉스 명령어가 입력되어 파일로 실행된다. 그리고 조금더 섬세한 스크립트에는 각종 결정, 루프, 파일검사등의 작업을 위한 프로그래밍구조가 포함된다.

스크립트를 작성하기 위해서는 프로그래밍구조와 기법을 먼저 배워야 한다.
따라서 여기서는 사용자가 유닉스 유틸리티와 그들의 동작에 대해 잘 안다고 가정한다.

유틸리티에는 grep, sed, awk 등과 같이 커맨드출력이나 파일의 조작을 위해 스크립트에 사용되는 강력한 도구들도 있다.
이들 도구들과 더불어 특정 shell을 위한 프로그래밍구조에 어느정도 익숙해지면 아주 유용한 스크립트를 직접 작성할 수있게 된다.

스크립트안에 있는 커맨드를  실행시킨다는점에서 shell을 프로그래밍 언어라고 볼 수 있다.


1.1.1 세가지 주요 Unix Shell
대부분의 유닉스 시스템에서 지원되어 많이 사용하고 있는 세가지 주요쉘은 Bourne Shell(AT&T)과 C Shell(Berkeley shell) 그리고 Korn Shell(Bourne Shell의 확장)이다
이들 shell들은 인터액티브기능으로 사용될 때 거의 비슷한 방식으로 동작하지만 스크립트언어로 사용될때는 신택스와 효율성에 있어서 몇가지 차이를 가진다.


Bourne Shell은 표준 유닉스 shell로 시스템을 운영하기위해 사용된다.
Rc start, rc stop및 shutdown 등과 같은 대부분의 시스템운영 스크립트는 Bourne Shell 스크립트이다.
그리고 단일 사용자모드에서 시스템운영자는 루트로 이 shell을 주로 사용한다.
AT&T에서 제작한 이 shell은 간결하고 빠르다. Bourne Shell의 디폴트 프롬프트는 달러사인($)이다.

Berkeley에서 제작한 C Shell은 명령줄 히스토리, 알리아스, 내장수치처리, 파일이름완성 및 작업제어등 다수의 기능을 추가 시키고 있다.
Shell 의 인터액티브기능을 사용하는 사용자는 Bourne Shell보다는 C shell을 더 선호한다. 하지만 시스템 운영자는 Bourne shell의 스크립트들이 C shell의 스크립트들보다 더 간단하고 빠르기 때문에 Bourne shell을 더 선호한다.

C shell의 디폴트 프롬프트는 퍼센트사인(%)이다.

AT&T사의 David Korn은 Bourne shell을 확장하여 Korn shell을 제작했다.
Korn shell은 C shell보다도 더 많은 기능을 확장시켰다. Korn shell에 추가된 기능들에는 편집가능한 히스토리, 알리아스, 함수, 정규표현식 와일드카드문자, 내장된 수치계산, 작업제어, 코어프로세싱 및 특별한 디버깅기능등이 있다.
한편 Bourne shell은 Korn shell과 거의 완벽한 호환성을 제공한다. 그래서 오래된 버전의 Bourne shell 프로그램들도 Korn shell에서 잘 구동한다. Korn shell의 디폴트 프롬프트는 달러사인($)이다.



1.1.2 Linux Shell
종종 리눅스 shell이라고 불리는 Bash와 TC shell들은 자유롭게 구할 수있고 모든 유닉스시스템에서 컴파일할 수있다.
사실 이들 shell들은 이제 Solaris 8과 Sun사의 유닉스 운영체제등에 번들로 같이 제공되고 있다.
하 지만 리눅스를 인스톨하면 표준 유닉스 shell과 도구가 아닌 GNU shell과 도구를 사용하게 된다. 비록 리눅스가 많은 수의 shell들을 지원하고 있지만 Bourne Again Shell(bash)과 TC shell(tcsh)이 현재까지 리눅스에서 가장 일반적으로 사용되는 shell들이다.

또 다른 리눅스 shell인 Z shell은 Bourne Again Shell, TC shell및 Korn Shell등에서 많은 기능
을 따와 사용하고 있다.

그리고 Korn shell의 일종인 Public Domain Korn Shell(pdksh)도 리눅스에서 사용된다.
한편 AT&T의 Korn Shell을 사면 알려지지않은 많은 다른 shell도 구할수 있다.

/etc/shell 파일을 보면 자신의 리눅스버전이 지원하는 shell들이 무엇인지 알 수있다.
/etc/shell에 열거된 shell들 중에 하나로 이동하려면 chsh 커맨드와 해당 shell의 이름을 입력하면 된다.



1.1.3 Shell의 사용
shell의 주요기능들중에 하나는 명령줄프롬프트에서 입력된 명령을 해석하는 인터액티브기능이다.
Shell 은 명령줄에서 입력된 명령을 해석해서 워드(토큰)단위로 해체한다.  이때 워드들은 탭, 스페이스 및 새줄문자등의 whitespace로 구분된다. Shell은 이들 워드에 들어있는 메타문자들도 평가한다. Shell은 또한 파일 입출력 및 백그라운드프로세스도 처리한다.

명령줄입력이 처리되면 shell은 해당명령을 찾아 실행을 시작한다.


Shell이 제공하는 또다른 주요기능은 작업환경을 사용자가 정의할 수 있도록 하는것이다.

이것은 보통 shell초기화 파일에서 이루어진다.

이들 파일들에는 터미널키 설정조건, 윈도우문자설정조건, 검색경로와 허가권, 프롬프트 및 터미널형을 정의하는데 사용되는 변수들의 설정조건, 윈도우, 텍스트 프로세싱 프로그램, 프로그래밍언어 라이브러리등의 특정 응용프로그램들을 위해서 사용되는 변수들의 설정조건등이 들어간다.

Korn shell과 C shell은 히스토리, 알리아스, 사용자가 파일을 손상시키거나 실수로 로그아웃하는 것을 방지시키기위한 내장변수와 작업종료 상황을 알려주는 내장변수등을 통해서도 사용자 정의기능을 제공한다.
Shell은 해석된 프로그래밍언어로도 사용될 수있다.

스크립트라고 불리는 shell 프로그램은 파일에 열거된 커맨드리스트로 구성된다.
보통 shell프로그램은 편집기에서 작성된다(온라인 스크립팅도 허용은 된다)
Shell 프로그램은 유닉스 커맨드와 더불어 변수지정,조건식 판정 및 루프등의 기본적인 프로그래밍 구조를 혼합해서 사용한다.

한편 shell 스크립트는 컴파일을 필요로 하지않는다.
Shell은 스크립트의 각 줄이 키보드로부터 들어온 입력인것처럼 해석을 한다.
Shell이 커맨드를 해석하는 책임을 가지는 반면 사용자는 사용하는 커맨드들을 잘 이해해야할 책임이 있다.



1.1.4 Shell의 책임
프롬프트에서 들어오는 모든 커맨드가 제대로 실행하도록 하는 궁극적인 책임은 shell에 있다.
이러한 shell의 책임에는 다음과 같은 것들이 있다.


1) 입력을 읽고 해당 명령줄을 해석한다.
2) 특수문자를 평가한다.
3) 파이프, 방향재지정, 백그라운드 프로세스를 설정한다
4) 시그널을 처리한다.
5) 프로그램을 실행시킬수 있도록 설정한다.



1.2 시스템 시작과 Shell 로그인
시스템을 시작할 때 이루어지는 첫번째 작업은 초기화(init)이다.

한편 각 프로세스는 PID라고 하는 프로세스 확인번호를 갖는다. 그래서 init은 PID 1이 된다.
Init 프로세스는 시스템을 초기화시키고 터미널 라인을 열고 표준입력(stdin), 표준출력(stdout)및 해당 터미널과 연관된 모든 표준에러(stderr)를 설정하는 다른 프로세스를 시작시킨다.
표준입력은 보통 키보드를 통해서 이루어지고 표준출력과 표준에러는 스크린상에 나타난다.
여기까지 진행이 되면 로그인 프롬프트가 터미널에 나타난다.

로그인 네임과 패스워드를 각각 입력하면 /bin/login 프로그램은 passwd 파일의 첫번째 필드를 검사해서 이를 확인한다.

Passwd 파일에 입력된 사용자 이름과 일치하는 것이 있는가를 먼저 검사하고 다음에 암호해독 프로그램을 통해 입력된 패스워드가 정확한지를 검사한다. 패스워드가 확인되면 로그인 프로그램은 작업환경을 정의하는 변수들로 해서 초기화환경을 설정한다. 이들 변수들은 나중에 shell에 전달된다.
초기화 변수들인 HOME, SHELL, USER,LOGNAME  변수들은 passwd 파일에서 찾아진 정보에 의해 값이 지정된다.

HOME 변수에는 사용자의 홈디렉토리가 지정되고 SHELL변수에는 passwd파일의 마지막 엔트리인 로그인 shell의 이름이 지정되며 USER 또는 LOGNAME변수들은 사용자의 로그인네임이 지정된다.
Search path 변수는 일반적으로 사용되는 유틸리티가 특정디렉토리에서 찾아질 수 있도록 설정된다. 로그인이 종료하면 passwd파일의 마지막 엔트리에서 찾아지는 프로그램이 된다.
일반적으로 이 프로그램은 shell이다.

만약 passwd 파일의 마지막 엔트리가 /bin/csh이라면 C shell 프로그램이 실행된다.

그리고 마지막 엔트리가 /bin/sh 이거나 아무것도 없으면(널상태) Bourne Shell이 실행된다.
Shell이 시작되면 시스템 운영자가 설정한 초기화 파일들과 사용자의 홈디렉토리에 있는 shell관련 초기화 파일들의 검사가 이루어진다. 이렇게 찾아진 사용자 초기화 파일들이 다음에 실행이 된다. 이들 사용자초기화파일들은 사용자환경의 정의를 위해 사용된다. 사용자 초기화 파일에 있는 커맨드가 다 실행되면 프롬프트가 스크린상에 뜬다.
그럼 shell의 준비가 다 되어 사용자가 입력을 할수 있다.



1.2.1 명령줄 해석
프롬프트에서 커맨드를 입력하면 shell은 이를 읽은 다음 명령줄을 워드(토큰) 단위로 해체한다.
이때 각 워드는 스페이스와 탭으로 구분되어진 것들을 말하고 명령줄은 새줄문자로 끝이난다. 다음 shell은 첫번째 워드가 내장커맨드인가 아니면 디스크의 다른곳에 위치한 실행프로그램인가를 검사한다.
만약 내장커맨드이면 shell은 해당 커맨드를 내부적으로 실행시킨다.

그렇지않고 실행 프로그램이면 shell은 경로변수에 찾아지는 디렉토리들을 검사해서 해당 프로그램을 찾는다.
만약 해당프로그램이 찾아지면 shell은 새로운 프로세스를 할당해서 그 프로그램을 실행시킨다.
해당 프로그램이 실행되는동안 shell은 대기상태로 들어가는데 가끔씩 프로그램 진행상황에 대한 보고를 한다.
이와 같이 커맨드나 프로그램의 실행이 완료하면 프롬프트가 다시 나타나 새로운 커맨드를 입력할 수있다.
명령줄의 작업처리 순서는 다음과 같이 일반화 시킬수 있다.


1) 가능하면 히스토리 교체가 이루어진다.
2) 명령줄이 워드 단위로 해체된다.
3) 가능하면 히스토리 업데이트가 이루어진다.
4) 인용구들이 처리된다.
5) 가능하면 알리아스 교체와 함수의 정의가 이루어진다.
6) 방향재지정, 백그라운드 및 파이프가 설정된다.
7) 변수교체($user, $name등)가 이루어진다.
8) 커맨드교체(today에 대한 echo는 ‘date’이다)가 이루어진다.
9) 파일이름 교체(cat abc.??, rm ?f *.bak 등)가 이루어진다.
10) 프로그램이 실행된다.



1.2.2 커맨드의 종류
커맨드에는 알리아스,함수,내장커맨드 및 디스크상의 실행프로그램등이 있다.
알리아스는 기존 커맨드를 축약한 이름이다.
한 편 C, TC, Bash및 Korn shell에서 알리아스를 모두 사용할 수있다. 그리고 함수는 Bourne shell(AT&T System V 2.0이상), Bash및 Korn Shell에서 적용된다. 함수는 개별루틴으로 조직된 커맨드들의 그룹이다.

알리아스와 함수는 shell의 메모리내에서 정의된다.
내장커맨드는 Shell의 내부적인 루틴이고 실행프로그램은 디스크상에 위치한다.
Shell은 경로변수를 검사해서 실행프로그램을 디스크에서 찾고 해당 커맨드가 실행되기전에 종속프로세스를 만들어 놓는데 시간이 조금 걸린다(메모리의 입장에서)

Shell이 해당 커맨드를 실행할 준비가 다 되면 다음과 같은 순서로 커맨드의 종류를 평가한다.


1) 알리아스
2) 키워드
3) 함수(bash)
4) 내장커맨드
5) 실행 프로그램

주의) 3번과 4번은 Bourne shell과 Korm shell에서 순서가 바뀐다. 3번은 C shell과 TC shell에서 적용되지 않는다.


예를들어 xyz이라는 커맨드가 입력되면 shell은 이것이 알리아스인지 먼저 검사한다. 그리고 알리아스가 아니면 내장커맨드나 함수인지 검사한다. 만약 이것들도 아니면 디스크상에 위치하는 실행프로그램이어야 한다. 그럼 shell은 해당 커맨드를 찾기위해 경로를 검색한다.
이것도 아니면 표준에러로 스크린에 출력메세지가 나온다.



1.3 프로세스와 Shell
하나의 프로세스는 실행되는 하나의 프로그램을 말하며 고유 PID(process identification) 번호로 확인된다. 시스템 커널이 이들 프로세스를 제어하고 관리한다.

하나의 프로세스는 해당 실행프로그램, 데이터, 스택, 프로그램포인터, 스택포인터, 레지스터 및 프로그램을 구동시키기 위해 필요한 모든 정보들로 구성된다.


예를들어 shell을 시작하는 것 자체가 하나의 프로세스다.
따라서 shell도 해당 그룹의 PID 번호를 갖는다. 한편 한번에 하나만의 프로세스 그룹이 터미널을 제어한다. 그리고 이 프로세스가 포그라운드에서 구동되고 있다고 말한다.

사용자가 로그인하면 사용자의 shell이 터미널을 제어하게 되고 프롬프트에서 커맨드가 입력되기를 기다린다.


Shell은 다른 프로세스도 만들어 낼수 있다(fork)
사실 프롬프트나 shell 스크립트를 통해 커맨드를 입력하면 shell은 해당 커맨드를 내장코드나 디스크에서 찾아 실행시켜줄 책임을 갖는다.

이 작업은 커널에 호출하는것으로 이루어진다(system call)

하나의 시스템 호출은 커널의 서비스를 요청하는것으로 하나의 프로세스가 시스템의 하드웨어에 접근할수 있는 유일한 방법이다.

프로세스의 생성, 실행 및 종료를 허용하는 다수의 시스템호출이 있다.
shell은 방향재지정,파이핑,커맨드교체 및 사용자커맨드실행등을 할 때 커널을 통해 다른 서비스를 제공한다.



1.3.2 프로세스 생성

fork 시스템호출
유닉스에서 프로세스 생성은 fork 시스템 호출로 이루어진다.
fork 시스템 호출은 하면 호출하는 프로세스가 하나 다시 생성된다.
이 프로세스를 종속 프로세스(child process)라고 하고 호출하는 프로세스를 주프로세스(parent process)라고 한다.
종 속 프로세스의 구동은 fork호출이 이루어진 다음에 바로 시작화면 초기에는 주 프로세스와 CPU를 공유한다. 종속 프로세스는 주 프로세스와 같은 환경, 오픈파일, 실제확인정보, 사용자확인정보, umask, 현재의 작업디렉토리 및 시그널을 갖는다.


커맨드가 입력되면 shell은 명령줄을 해석하고 첫번째 워드가 내장 커맨드인지 아니면 디스크상에 위치하는 실행프로그램인지 결정을 한다.

이것이 내장커맨드이면 shell은 바로 처리를 하지만 디스크상에 위치하는 실행 프로그램이면 fork
시스템호출을 통해 자신의 프로세스를 복사한다.
이때 생긴 종속프로세스는 해당 커맨드를 찾는것뿐만 아니라 파일방향 재지정,파이프,커맨드교체 및 백그라운드 프로세싱등을 설정한다.

한편 종속프로세스가 진행되는 동안 주프로세스는 대기상태에 들어간다.


wait 시스템호출
주프로세스인 shell은 종속프로세스가 방향재지정,파이프설정,백그라운드설정등의 세부작업을 처리하는동안 대기상태에 들어간다.
wait 시스템호출은 종속프로세스가 종료될때까지 주프로세스의 진행을 정지시킨다. 주프로세스의 대기가 성공적으로 이루어지면 시스템은 종료된 종속프로세스의 PID번호와 exit status값을 되돌린다. 만약 종속프로세스가 대기를 하지않는 상황에서 종속프로세스가 종료되면 종속프로세스는 휴면상태(zombie state)에 들어가 종속프로세스가 대기상태에 다시들어가거나 아니면 종료할때까지 기다린다.

만약 종속프로세스보다 주프로세스가 먼저 종료되면 init 프로세스는 휴면상태에 있는 다른 프로세스를 채택한다.
그리고 wait 시스템호출은 주프로세스를 대기상태로 넣는 것이 아니라 주프로세스가 올바르게 종료할수 있도록 조치를 취한다.


exec 시스템호출
터미널에서 입력이 들어오면 shell은 보통 새로운 shell프로세스를 생성(fork)시켜 종속프로세스를 만든다.
한편 앞서서도 언급했듯이 종속프로세스는 입력된 커맨드가 실행되는것에 책임을 진다. 종속프로세스는 이 작업을 exec시스템호출을 통해서한다.

이때 사용자가 입력한 커맨드는 실행프로그램이라는 사실을 잊지 말아야한다.
Shell은 경로를 검사해서 그 실행프로그램을 찾는다.
Shell은 이 프로그램을 찾으면 해당 커맨드의 이름을 인자로해서 exec시스템호출을 한다. 그럼 커널은 호출을 했던 shell 대신에 메모리에 로드시킨다.

그리고 로드된 종속프로세스는 이 실행프로그램을 받아들여 실행 프로그램 자체가 종속프로세스가 되어 실행이 이루어진다. 종속프로세스가 자체적인 로컬변수들을 갖게 되지만 모든 환경변수, 오프파일, 시그널 및 현재의 작업디렉토리는 주프로세스부터 전달된 것을 사용한다.
마지막으로 종속프로세스가 실행을 완료하면 주프로세스인 shell이 대기상태에서 빠져나온다.


exit 시스템호출
exit시스템호출을 하면 실행프로그램을 어느때라도 종료시킬수 있다.
보통 종속프로세스가 종료하면 sigchild라는 시그널을 전송하고 주프로세스가 exit status값을 받아들일때까지 기다린다.
exit status값은 0에서 255사이에 오는 숫자가 된다.
만약 exit status가 0의 값을 가지면 프로그램 실행이 성공적으로 마친것이고 0이 아닌값이면 프로그램실행이 어떤 면에서든 실패했다는것이다.
예를들어 명령줄에서 ls 커맨드가 입력되면 주프로세스는 종속프로세스를 생성시키고 대기상태로 들어간다(fork, wait)
그리고 종속프로세스는 해당 ls 프로그램을 받아들인다(exec)
다음 ls 프로그램이 모든 환경변수,오픈파일,사용자정보 및 상태정보등을 주프로세스로부터 받아 종속프로세스로 실행이 된다.

종속프로세스의 실행이 완료되면 주프로세스가 대기상태에서 빠져나온다(exit)
그럼다시 프롬프트가 스크린상에 나타나면 shell이 다른 커맨드의 입력을 기다리게 된다.
커맨드의 종료상태는 각 shell이 마지막으로 종료된 커맨드의 exit status를 가지고 있는 내장변수를 통해 알수있다.



1.4 환경과 상속
사용자가 로그인을 하게되면 shell이 시작된다.
이때 shell은 자신을 실행시켰던 /bin/login 프로그램으로부터 여러 개의 변수,입출력 스트림 및 프로세스특징을 상속받는다.

이와 마찬가지로 주프로세스가 새로운 종속프로세스를 생성(fork)시키면 자신의 환경을 종속프로세스에 물려준다. 종속프로세스가 생성되는 이유는 여러가지가 있다.

종속프로세스는 백그라운드 프로세싱처리, 여러 커맨드 그룹의 처리, 스크립트의 실행등을 하기위해 생성될수 있다.
이때 종속프로세스는 자신의 주프로세스로부터 환경을 상속받는다. 주프로세스가 물려주는 환경에는 프로세스 허가권, 작업 디렉토리, 파일생성마스크, 특수변수, 오프파일 및 시그널등이 있다.



1.4.1 소유권
사용자가 로그인하면 shell은 확인번호를 받게된다.

이때 주어지는 번호는 실제 사용자확인(UID, user identification)번호, 하나이상의 실제그룹확인(GID, group identification)번호, 유효사용자확인(EUID, effective user identification)번호 혹은 유효그룹확인(EGID, effective group identification)번호등이 될수 있다.


여기서 EUID와 EGID는 초기에 UID와 GID와 각각 같다. 그리고 이들 ID번호들은 /etc/passwd 파일에서 찾아지고 사용자나 그룹을 확인할 때 시스템에 의해서 사용된다.
EUID, EGID는 파일의 읽기,쓰기 및 실행에 관해서 하나의 프로세스가 어떤 허가권을 갖고 있는지 결정해준다.
그 래서 만약 한 프로세스의 EUID와 해당 파일의 소유자가 갖는 실제 UID가 서로 같으면 그 프로세스는 해당파일에 대해서 소유자의 접속권한을 갖게된다. 그리고 EGID와 프로세스소유자의 실제 GID가 서로 같으면 그 프로세스는 소유자의 그룹권한을 갖는다.


/etc/passwd 파일에 있는 실제 UID는 양수의 정수로 사용자의 로그인네임과 연관이 있다.
실제 UID는 /etc/passwd 파일의 세번째 필드를 차지한다. 사용자가 로그인할 때 로그인 shell은 실제 UID를 지정받게 되고 해당 로그인 shell로부터 파생된 모든 프로세스들은 이 shell의 허가권을 상속받는다.
UID가 0인 모든 프로세스는 루트사용자(superuser)에 귀속되며 루트권한을 갖는다.
GID는 하나의 그룹을 사용자의 로그인네임과 연관을 시킨다. GID는 /etc/passwd 파일의 네번째 필드를 차지한다.

EUID와 EGID는 다른 사용자에게 지정된 번호로 변경될 수있다. 그리고 이렇게 다른 소유자로 EUID,EGID로 변경하면 그 사용자에 속하는 프로세스의 소유자가 될수 있다.
EUID,EGID를 다른 소유자로 변경시키는 프로그램들은 setuid 프로그램이나 setgid 프로그램으로 불린다.
/bin/passwd 프로그램은 setuid 프로그램의 예로 사용자에게 루트권한을 제공한다.
Setuid 프로그램은 종종 보안문제를 발생시키는 원인이 된다. Shell은 setuid 스크립트의 생성을 허용하며 shell자체가 setuid 프로그램이 될수 있다.



1.4.2 파일생성 마스크
파일이 생성되면 기본적인 디폴트 허가권이 주어진다. 그리고 이들 허가권들은 파일을 생성시킨 프로그램에 의해서 결정된다.

한편 종속프로세스는 주프로세스로부터 디폴트 마스크를 상속받는다.
사용자는 프롬프트에서 umask 커맨드를 사용하거나 umask 커맨드를 초기화파일(/etc/profile, .bashrc 등..)에 설정함으로해서 해당 shell의 마스크를 변경시킬수 있다.

umask 커맨드는 기존 마스크로부터 허가권을 제거하기위해 사용된다.

umask의 초기값은 000으로 설정되어있고 디렉토리는 777(drwxrwxrwx) 허가권을 디폴트로 주고 파일에는 666(-rw-rw-rw-) 허가권을 디폴트로 준다. 대부분의 시스템에서 umask는 /bin/login 프로그램이나 /etc/profile 초기화파일에 의해 022가 값으로 지정된다.
디렉토리와 파일을 위해 디폴트설정에서 다음과 같이 umask값을 얻을수 있다.


777(Directory) -022(umask value) => Result: drwxr-xr-x (755)
666(File) -022(umask value) => Result: -rw-r--r-- (644)


umask가 설정되면 이 프로세스에 따라 생성된 모든 디렉토리와 파일들은 새로운 디폴트허가권을 지정받는다.
이번 예에서 디렉토리에 대해 소유자는 읽기,쓰기 및 실행권한을 갖게되고 그룹은 읽기와 실행권한을 갖게되며 그 나머지 사용자들은 실행권한만 갖게된다.
그리고 디렉토리에 생성된 파일들에 대해서 소유자는 읽기와 쓰기권한을 갖게되고 그룹이나 다른 사용자들은 읽기권한만 갖게된다.

개별 디렉토리나 파일에 대한 허가권을 변경시키기 위해서는 chmod 커맨드를 사용해야한다.


작업 디렉토리
사용자가 로그인을 하면 파일시스템내에 홈디렉토리라고하는 작업디렉토리가 주어진다. 그리고 해당 shell에 의해서 파생된 모든 프로세스들이 작업디렉토리를 상속받는다.
이 shell에 의해서 생성된 모든 종속프로세스가 자신의 작업디렉토리를 변경시킬수 있지만 그 변경삿항은 종속shell에 아무런 영향도 미치지 못한다.
작업 디렉토리 변경을 위해서 사용되는 cd 커맨드는 shell에 내장된 커맨드이다. 각 shell은 모든 cd의 복사본을 갖게된다.
각 shell은 이렇게 내장된 커맨드를 자체 코드의 일부로써 실행시킨다.
따라서 내장커맨드의 실행을 위해서 shell이 fork나 exec시스템호출을 하지않는다.
한편 주shell에서 다른 shell(스크립트)을 위해 fork시스템호출을 하고나서 종속shell에서 cd커맨드를 실행시키면 디렉토리가 종속shell로 이동한다.
한편 종속shell이 존재하더라도 주shell은 종속shell이 시작되기전의 디렉토리에 그대로 머무른다.


Ex)
%> cd /   # csh이 현재 / 에 있음
%> pwd
/
%> sh   # sh 모드
$> cd /home  # sh이 현재 /home에 있음
$> pwd
/home
$> exit   # 종속프로세스종료… csh로 복귀
%> pwd   # 처음 디렉토리(/) 그대로 있음
/
%>


변수
shell은 로컬변수와 환경변수 두 종류의 변수를 정의할 수있다.
변수는 shell의 사용자정의 정보와 함께 다른 프로세스들이 적절하게 동작될 수있도록 하는 정보를 갖는다.
로컬변수는 그 변수가 생성된 shell에서만 적용이되고 그 shell로부터 파생된 다른 프로세스들에게는 적용이 되지 않는다.
반면 환경변수는 주프로세스에서 종속프로세스나 종속프로세스의 종속프로세스등의 경우에도 상속된다.
로그인 shell은 /bin/login 프로그램이나 사용자 초기화파일, 스크립트 및 명령줄로부터 환경변수를 받을 수 있다.
한편 종속shell에서 설정된 환경변수는 주shell에 전달되지 않는다.


파일기술자(File Descriptor)
파일, 파이프 및 소켓등 모든 입출력 오브젝트들은 파일설명자라고 하는 메커니즘을 통해 커널에 의해 처리된다.
파일기술자는 부호가없는 작은 정수로 커널에 의해 유지되는 파일기술자 테이블의 인덱스가 된다. 커널은 오픈파일과 입출력 스트림을 참조하기위해서 파일기술자를 사용한다.
각 프로세스는 주프로세스로부터 파일기술자 테이블을 상속받는다. 처음 세 파일기술자인 0, 1, 2는 터미널로 지정된다.
여기서 기술자 0은 표준입력(stdin)을 의미하고 1은 표준출력(stdout)을 의미하며 2는 표준에러(stderr)을 의미한다.
그리고 다음에 사용할 기술자는 3이다. 그래서 새 파일을 열면 3이 지정된다.
만약 사용 가능한 모든 파일기술자들이 이미 사용되고 있으면 다시 새로운 파일을 열 수 없다.


방향재지정(ReDirection)
하나의 파일기술자가 터미널이 아닌 다른것에 지정되는 경우 이것을 I/O 방향 재지정이라고 한다.
Shell은 터미널에 지정되는 표준출력 파일기술자 1을 파일에 재지정함으로써 출력을 파일로 방향 재지정해준다.
표준입력의 방향을 재지정하는경우에는 터미널에 지정되는 표준입력 파일기술자 0을 파일로 지정하게 된다.
Bourne shell과 Korn shell은 파일기술자 2를 파일로 지정함으로써 에러를 처리한다.
반면 C shell은 이보다 더 복잡한 과정을 거친다.


파이프(Pipe)
파이프를 사용하면 커맨드의 출력이 다른 커맨드의 입력으로 전송될 수있다.
Shell은 파일기술자의 지정을 닫고 다시 여는 방법으로 파이프를 사용한다. 하지만 파일기술자를 직접 파일에 지정하기보다는 pipe 시스템호출로 생성되는 파이프 기술자에 파일기술자를 지정한다.
주shell이 파이프 파일기술자를 생성하고 나서 파이프라인에 있는 각 커맨드를 위해 종속shell을 생성(fork)시킨다.
이때 종속shell은 파이프로 출력을 하고 다른 종속shell은 파이프로 입력을 받는다.
그래서 파이프를 양프로세스가 데이터를 공유할수 있는 커널버퍼라고 할 수 있다.
따라서 임시 파일들을 따로 사용할 필요가 없어진다.
파이프 기술자가 설정되고 나면 양 종속프로세스의 커맨드가 동시에 실행된다(exec)
한 커맨드의 출력이 버퍼로 보내지고 나서 버퍼가 가득 차게되거나 그 커맨드가 종료하게되면 파이프의 오른쪽에 있는 커맨드가 버퍼로부터 데이터를 읽는다.
이때 하나의 커맨드가 데이터를 버퍼로 쓰기를 하거나 읽기를 할 때 다른 커맨드가 대기를 할 수있도록 커널이 동기화 시켜준다. Pipe 커맨드형식은 다음과 같다.
Who | wc
Shell은 who 커맨드의 출력을 wc 커맨드의 입력으로 전송한다. 이 작업은 pipe 시스템호출로 이루어진다.
주shell이 pipe 시스템호출을 하면 두개의 파이프기술자가 생성된다.
하나는 파이프로부터 데이터를 읽기위한 것이고 다른 하나는 파이프로 데이터를 쓰기위한 것이다.
한편 파이프기술자와 연관되는 파일들은 데이터를 임시적으로 저장하기위해서 커널이 관리하는 I/O 버퍼들이다.


1.4.2  Shell과 시그널
시그널은 프로세스에 메시지를 전송해서 보통은 그 프로세스를 종료시킨다.
그 이유는 세그먼트 사용위반, 버스에러 및 정전과 같은 예측하지 못한 문제가 발생하기 때문이다. 사용자는 Break키, Delete키, Quit키 또는 Stop키를 눌러 프로세스에 시그널을 전송시킬 수 있다.
그렇게 하면 해당 터미널을 공유하고 있는 모든 프로세스가 영향을 받는다.
한편 kill 커맨드로 프로세스를 인위적으로 종료시킬수 있다. 하지만 대부분의 시그널이 디폴트로 프로그램을 종료시킨다.
하지만 shell은 이제 프로그램으로 들어오는 시그널을 사용자가 처리할 수 있도록 해준다.

즉, 사용자는 시그널을 무시하거나 아니면 특정 시그널이 들어왔을 때 특정조치를 시킬수 있다.

하지만 C shell의 경우 ^C(ctrl-C)만을 처리할 수있다.

TAG shell,

$ cat > ManUJeol

#!/bin/sh
month=`date +%D | awk -F/ '{print $1}'`
day=`date +%D | awk -F/ '{print $2}'`

while [ "$month" = "04" -a "$day" = "01" ]; do
  a_joke=("ㅋㅋㅋ" "ㅠㅠㅠ" "ㅎㅎㅎ" "ㄹㄹㄹ" "ㄹㄷ하삼")
  rand=$RANDOM
  case `expr $rand % 5` in
       0) joke=${a_joke[0]};;
       1) joke=${a_joke[1]};;
       2) joke=${a_joke[2]};;
       3) joke=${a_joke[3]};;
       4) joke=${a_joke[4]};;
  esac
  echo $joke
  sleep 1
  continue
done

^D


$ sh ./ManUJeol