수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1694231
  • Today | 582
  • Yesterday | 588

11 Articles, Search for '리눅스'

  1. 2007/07/11 리눅스 명령어 (6)
  2. 2007/06/22 시스템 관리자를 위한 기초 명령어 활용법
  3. 2007/05/04 리눅스에서의 메모리관리
  4. 2007/05/04 애플리케이션의 구현 및 분산 프로세스를 자동화하기
  5. 2007/05/04 [처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법
  6. 2007/05/04 [처음부터 다시 배우는 리눅스] ② 고급 명령과 시스템 관리
  7. 2007/05/04 [처음부터 다시 배우는 리눅스] ① 설치와 기본 명령어
  8. 2007/05/04 리눅스 시스템 콜 레퍼런스
  9. 2007/05/04 Linux 애플리케이션을 위한 DLL 작성하기
  10. 2007/05/04 리눅스에 네트워크 라우터 구현하기
Unix & Linux2007/07/11 00:11

리눅스 명령어

출처 : http://www.mireene.com/webimg/linux_tip1.htm

■ 퍼미션(권한)이란?

Owner

Group

Other

Owner와 Group은 파일소유자자신과 자신이 속한그룹. Other은 제3자, 웹사이트 방문객은 제3자로 nobody로 취급.

r

w

x

r

w

x

r

w

x

r은 파일 읽기(4), w는 파일 쓰기(2), x는 파일 실행(1)

7

5

5

파일소유자는 그것을 읽고 쓰고 실행시킬 수 있지만, 제3자는 읽고 실행만 시킬 수 있다.

7

7

7

제3자도 쓰기 권한이 주어진다.

*.html  *.cgi, *.pl *.txt등의 파일은 업로드시 반드시 ascii로 하고 나머지 그림(*.gif *.jpg)이나 자바 애플릿(*.class), 실행파일(*.exe *.zip *.rar)등은 binary mode로 업로드 할 것.

■ 리눅스 기본명령어

명령어

사 용 법

login

사용자 인증과정

리눅스 시스템은 기본적으로 multi-user 개념에서 시작하였기 때문에 시스템을 이용하기 위해서는 반드시 로그인을 하여야 합니 다. 로그인은 PC 통신에서도 많이 사용되어져 왔기 때문에 그 개 념  설정에 그다지 어려움이 없을 것입니다. 흔히 말하는 ID를 입력하는 과정입니다. 

passwd

패스워드 변경

리눅스, 특히 인터넷의 세계에서는 일반 컴퓨팅 상황에 비하여 훨씬 해킹에 대한 위험이 높습니다. 패스워드는 완성된 단어 보다는 단어 중간에 숫자나 키보드의 ^, #, ' 등과 같은 쉽게 연상 할 수 없는 기호를 삽입하여 만들어 주는 것이 좋습니다

du

하드사용량 체크(chkdsk)

자신의 하드공간을 알려면
# du
특정 디렉토리의 사용량을 알려면
# du -s diretory_name

ls

파일 리스트 보기(dir)

F : 파일 유형을 나타내는 기호를 파일명 끝에 표시
   (디렉토리는 '/', 실행파일은 '*', 심볼릭 링크는 '@'가 나타남).
l  : 파일에 관한 상세 정보를 나타냅니다.
a : dot 파일(.access 등)을 포함한 모든 파일 표시.
t  : 파일이 생성된 시간별로 표시
C : 도스의 dir/w명령과 같 이 한줄에 여러개의 정보를 표시
R : 도스의 dir/s 명령과 같이 서브디렉토리 내용까지.

(예)
# ls -al  
# ls -aC
# ls -R

cd

디렉토리를 변경

# cd cgi-bin     : 하부 디렉토리인 cgi-bin으로 들어감.
# cd  ..             : 상위디렉토리로 이동
# cd 또는 cd ~  : 어느곳에서든지 자기 홈디렉토리로 바로 이동
# cd /webker     : 현재 작업중인 디렉토리의 하위나 상위 디렉토리가
                         아닌 다른 디렉토리(webker)로 이동하려면 /로
                         시작해서 경로이름을 입력하면 된다.

cp

화일 복사(copy)

# cp index.html index.old
    : index.html 화일을 index.old 란 이름으로 복사.

# cp /home/test/*.*  .
    : test 디렉토리내의 모든 화일을 현 디렉토리로 복사.

mv

파일이름(rename) / 위치(move)변경

# mv index.htm index.html
    : index.htm 화일을 index.html 로 이름 변경

$ mv file  ../main/new_file
    : 파일의 위치변경

mkdir

디렉토리 생성

# mkdir download  : download 디렉토리 생성

rm

화일삭제

# rm test.html : test.html 화일 삭제
# rm -r <디렉토리> : 디렉토리 전체를 삭제
# rm -i a.*
    : a로 시작하는 모든 파일을 일일이 삭제할 것인지 확인하면서 삭제

rmdir

디렉토리 삭제

# rmdir cgi-bin : cgi-bin 디렉토리 삭제

pwd

현재의 디렉토리 경로를 보여주기

pico

리눅스용 에디터

put

ftp 상태에서 화일 업로드

> put  guestbook.tar.gz

get

ftp 상태에서 화일 다운로드

> get  guestbook.tar.gz

mput 또는 mget

여러개의 화일을 올리고 내릴때 (put,get과 사용법동일)

chmod

화일 permission 변경

리눅스에서는 각 화일과 디렉토리에 사용권한을 부여.

예) -rwxr-xr-x   guestbookt.html
rwx  :처음 3개 문자 = 사용자 자신의 사용 권한
r-x  :그다음 3개 문자 = 그룹 사용자의 사용 권한
r-x  :마지막 3개 문자 = 전체 사용자의 사용 권한

읽기(read)---------- 화일 읽기 권한
쓰기(write)---------- 화일 쓰기 권한
실행(execution)---------- 화일 실행 권한
없음(-)---------- 사용권한 없음

명령어 사용법
chmod [변경모드] [파일]

# chmod 666  guestbook.html
    : test.html 화일을 자신에게만 r,w,x 권한을 줌

# chmod 766  guestbook.html
    : 자신은 모든 권한을 그룹사용자와,전체사용자에게는
      읽기와 쓰기 권한만 줌

alias

" doskey alias" 와 비슷하게 이용할 수 있는 쉘 명령어 alias는 말그대로 별명입니다. 사용자는 alias를 이용하여 긴 유 닉스 명령어를 간단하게 줄여서 사용할 수도 있습니다.
이들 앨리어스는 [alias ls 'ls -al'] 같이 사용하시면 되는데, 한 번 지정한 alias를 계속해서 이용하시려면, 자신의 홈디렉토리에 있는
.cshrc(Hidden 속성)을 pico등의 에디터를 이용하여 변경시 키면 됩니다.

cat

파일의 내용을 화면에 출력하거나 파일을 만드는 명령( 도스의 TYPE명령)

# cat filename

more

cat 명령어는 실행을 시키면 한 화면을 넘기는 파일일 경우 그 내용을 모두 볼수가 없다. 하지만 more 명령어를 사용하면 한 화면 단위로 보여줄 수 있어 유용.

# more <옵션>
옵션은 다음과 같습니다.

Space bar : 다음 페이지
Return(enter) key : 다음 줄
v : vi 편집기로 전환
/str : str 문자를 찾음
b : 이전 페이지
q : more 상태를 빠져나감
h : 도움말
= : 현재 line number를 보여줌

who

현재 시스템에 login 하고 있는 사용자의 리스트를 보여줍니다.

# who

whereis

소스, 실행파일, 메뉴얼 등의 위치를 알려줍니다

# whereis perl : perl의 위치를 알려준다

vi,
touch,
cat

새로운 파일을 만드는 방법

# vi newfile :  vi 편집기 상태로 들어감
# touch newfile : 빈 파일만 생성됨
# cat > newfile  : vi 편집기 상태로 들어감, 문서 작성후 Ctrl+D로 빠져나옴

cat,
head,
tail

파일 내용만 보기

# cat filename         : 파일의 내용을 모두 보여줌
# head -n filename : n줄 만큼 위세서부터 보여줌
# tail -n filename     : n줄 만큼 아래에서부터 보여줌


■ 압축명령어 사용법

압축 명령어

사 용 법

tar .tar, _tar로 된 파일을 묶거나 풀때 사용하는 명령어
(압축파일이 아님)

# tar cvf [파일명(.tar, _tar)] 압축할 파일(또는 디렉토리): 묶을때
# tar xvf [파일명(.tar, _tar)]  :  풀 때
  (cf) cvfp/xvfp 로 하면 퍼미션 부동
compress 확장자 .Z 형태의 압축파일 생성

# compress    [파일명]     : 압축시
# uncompress [파일명]    : 해제시
gzip 확장자 .gz, .z 형태의 압축파일 생성

#  gzip     [파일명]    : 압축시
#  gzip -d [파일명]   : 해제시
기타 .tar.Z
이것은 tar로 묶은 후에 compress를 사용하여 압축한 것으로 uncompress를 사용해서 압축을 푼 다음,
다시 tar를 사용해서 원래의 파일들을 만들어내면 됩니다.
아니면 다음과 같이 한 번에 풀 수도 있다.
# zcat  [파일명].tar.Z  : 해제시

.tar.gz또는 .tar.z
# gzip -cd [파일명]    : 해제시

.tar.gz 또는 .tar.z .tgz
gzip을 사용해서 푼 다음 다시 tar를 사용해서 원래 파일을 만들어 낼 수 있으나,
하지만 다음과 같이 하면 한 번에 처리를 할 수 있다.

# gzip -cd 파일.tar.gz | tar xvf -  또는
# tar xvzf 파일.tar.gz
# tar xvzf 파일.tgz

■ 리눅스 필수명령어

Linux/Unix 명령어

설 명

MS-DOS 비교

./x

x 프로그램 실행
(현재 디렉토리에 있는 것)

x

↑/ ↓

이전에(↑) / 다음에(↓) 입력했던 명령어

doskey

cd x (또는 cd /x)

디렉토리 X로 가기

cd

cd .. (또는 cd ../ 또는 cd /..)

한 디렉토리 위로 가기

cd..

x 다음 [tab] [tab]

x 로 시작하는 모든 명령어 보기

-

adduser

시스템에 사용자 추가

/

ls (또는 dir)

디렉토리 내부 보여주기

dir

cat

터미널 상의 텍스트 파일 보기

type

mv x y

파일 x를 파일 y로 바꾸거나 옮기기

move

cp x y

파일 x를 파일 y로 복사하기

copy

rm x

파일 지우기

del

mkdir x

디렉토리 만들기

md

rmdir x

디렉토리 지우기

rd

rm -r x

디렉토리 x를 지우고 하위도 다 지우기

deltree

rm p

패키지 지우기

-

df (또는 df x)

장치 x의 남은 공간 보여주기

chkdsk ?

top

메모리 상태 보여주기(q는 종료)

mem

man x

명령어 x에 관한 매뉴얼 페이지 얻기

/

less x

텍스트 파일 x 보기
(리눅스에서는 더 많은 필터 적용 가능)

type x | more

echo

어떤 것을  echo 화면에 인쇄한다.

echo

mc

UNIX를 위한 노턴 커맨더

nc

mount

장치 연결(예: CD-ROM, 연결을 해제하려면 umount)

-

halt

시스템 종료

-

reboot ([ctrl] + [alt] +[del])

시스템  다시 시작하기

[ctrl] + [del] + [del]

■ 고급명령어

고급 명령어

chmod <권한> <파일>

파일 권한(permissions) 변경

ls -l x

파일 x의 자세한 상황을 보여줌

ln -s x y

x에서 y로 심볼릭 링크를 만들어 줌

find x -name y -print

디렉토리 x안에서 파일 y를 찾아서 화면에 그 결과를 보여줌

ps

지금 작동중인 모든 프로세스들을 보여줌

kill x

프로세스 x를 종료 (x는 ps 명령으로 알 게 된 PID)

[alt] + F1 - F7

터미널 1-7까지 바꾸기 (텍스트 터미널에서; F7은 X-윈도우(시작될때))

lilo

부트 디스크를 만듦

용어


symlink

다른 파일이나 디렉토리로 심볼릭 링크. 윈도유98의 바로가기 같은 것

shell script

여러 명령어들을 차례로 수행하게 한 것. MS-DOS의 배치 파일 같은 것

"Unix & Linux" 카테고리의 다른 글
  • 리눅스 명령어 (6)2007/07/11
  • 왜 리눅스 커널을 알아야 하는가? (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법 (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ② 고급 명령과 시... (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ① 설치와 기본 명... (0)2007/05/04
2007/07/11 00:11 2007/07/11 00:11
Posted by webdizen
Tags Linux, 리눅스, 명령어
No Trackback 6 Comments

Trackback URL : http://www.webdizen.net/blog/trackback/3072

Leave your greetings.

  1. 오상우

    난 아직도 이해가 잘 안되는데, eXecute와 Read권한이 틀린점이 정확히 뭐야?
    텍스트로 스크립트파일을 만들고 chmod -x [filename] 하는거랑 eXecute권한이랑 같은건가 생각도 했는데,
    실행파일 속성을 가지고 있으면, Readable한 것 만으로 실행 되어야하는 것 아닌가?
    예를들어 권한이 001 이면[ Read(x) Write(x) eXecute(o) ] 어떻게 되는겨?
    ## 몰라서 물어보는거임 -_-

    2007/07/11 04:59 [ Permalink : Modify/Delete : Reply ]
  2. webdizen

    chmod -x [filename]을 이용하실 경우 현재 권한에서 eXecute 권한을 모두 제거하게 됩니다.
    chmod +x [filename]을 이용하실 경우 위의 경우와 반대 상황이 됩니다.
    예를 들어 제시하신 chmod 001 [filename] 을 하실 경우에는 ---------x 이런 식으로 권한이 변경됩니다.

    Read 권한, eXecute 권한의 차이점은 일반 텍스트 파일의 경우에는 Read 권한으로 읽기가 가능하니, 별 문제가 없겠지만, 실행 파일의 경우에는 eXecute 권한이 필요하답니다.

    2007/07/11 09:57 [ Permalink : Modify/Delete : Reply ]
  3. 오상우

    또다른 질문.
    1. chmod +x [filename]은 모든 사용자분류 (Owner/Group/Other)의 eXecute 권한에(3번째 비트) 대해 +1(OR연산 -_-)
    즉, 전체 퍼미션 부분에 +001001001(2) 해주는 것과 같은건지? (논리회로... 아놔 -_-)
    2. 그렇다면 텍스트 파일에 Read권한이 없고 eXecute 권한만 있으면 cat [txt-file]을 때리면 내용이 보이는지?
    (아, 요건 이따 직접 해보면 되것구나 -_-)
    3. UNIX시스템은 MS-DOS처럼 확장자에 따라(*.exe *.com *.bat 등) 실행 파일이냐 아니냐를 따지는게 아니라,
    eXecute 퍼미션이 있으면 실행을 하는 건가?
    4. HTML, PHP, Java Script처럼 서버에 텍스트 파일로 존재하고, Client의 Parser, Interpreter등으로 동작하는
    웹언어들의 경우, Client의 Read권한만 있어도 eXecute가 가능한가?
    5. 위리플의 질문 다시하는건데, Read권한이 없고 eXecute권한만 있다고 해도 실행이 가능해?

    2007/07/11 17:05 [ Permalink : Modify/Delete : Reply ]
  4. webdizen

    오상우님의 질문에 대한 답변
    1. chmod +x [filename]와 동일한 명령어는 chmod 111 [filename]에 해당합니다. 말씀하신 것과 같이 실제적인 bit는 0001001001이 됩니다.
    2. 텍스트 파일에 Read권한이 없고 eXecute 권한만 있을 경우 cat 명령어가 Permission denied 오류를 내면서 읽어지지 않습니다. 당연한 결과로 텍스트 파일에는 Read 권한이 있어야 볼 수 있는 이치죠.
    3. UNIX 시스템은 MS-DOS와 달리 확장자 개념이 없지만, 실행파일의 경우 eXecute 권한이 없으면 실행이 당연히 안된답니다.
    4. HTML, PHP, Java Script 처럼 서버에 텍스트 파일로 존재하는 파일들은 당연히 웹 언어로 작성된 텍스트 파일이라는 것을 염두하신다면 eXecute 권한이 아니라 Read 권한이 필요하다는 것을 알 수 있습니다.
    5. Read권한이 없고, eXecute 권한만 있다면, 웹 브라우저 상에서 Permission denied 오류가 발생합니다.

    2007/07/12 00:47 [ Permalink : Modify/Delete : Reply ]
  5. 후미후

    후홧 옛날 기억이 새록새록 납니다.^^ 올만에 이수안님 블로그 들려서 웃고 갑니다.

    2008/05/07 09:50 [ Permalink : Modify/Delete : Reply ]
    • webdizen

      하핫; 웃고 가신다니 저도 즐겁네요...

      2008/05/12 20:42 [ Permalink : Modify/Delete ]
[로그인][오픈아이디란?]

Unix & Linux/System2007/06/22 14:05

시스템 관리자를 위한 기초 명령어 활용법

참고 : 시스템 로그분석 (해킹 피해와 보안 추적의 결정적 파일) - 안정철

01. 패턴을 찾아주는 grep(egrep, fgrep) 명령

grep은 입력에서 주어진 패턴을 포함하고 있는 줄을 찾아주는 명령이다. 많은 시스템 관리 명령들과 파이프(pipe)를 이용해서 사용할 수 있다. 흔히 사용하는 것은 프로세스 확인이다.

ps aux | grep httpd


위와 같이 현재 시스템의 프로세스 중 httpd만 검색해 낸다. 만일 pipe를 이용하여 검색할 경우 검색 패턴이 한개 이상일 경우엔 egrep을 이용하여 검색할 수 있다.

ps aux | egrep 'httpd|mysql'


이 밖에 grep는 file 내용 중에 특정 패턴이 들어 있는 행을 찾아서 출력해준다. option을 이용하여 보다 다양한 패턴을 선택하여 검색할 수 있다.

-i : 대소문자 구별을 하지 않는다.
-v : 패턴을 포함하지 않는 행만 출력한다.
-n : 행 번호를 출력한다.
-l : 파일명만 출력한다.
-c : 패턴과 일치하는 라인의 갯수만 보여준다.


몇 가지 예를 들어 보자.

grep [option] "pattern" 파일명


대표적인 사용 방법이다. 파일에 pattern이란 단어를 포함하는 행을 출력한다.

grep -v "^[ ^l]*$" 파일명


위는 공백을 제거한 파일 내용 살펴보기

grep -v "^#*$" 파일명


위는 주석을 제거한 파일 내용 살펴보기를 의미한다. 이 밖에 패턴과 정확히 일치하는 것만을 찾아주는 fgrep가 있다. grep, egrep, fgrep의 옵션으로는 위의 옵션을 공통적으로 사용한다.

유용한 팁 하나를 소개해 보겠다. grep는 현재 디렉토리에 존재하는 파일만을 검색한다. 만일 현재 디렉토리와 그 하위 디렉토리까지 grep 패턴 검색을 하고자 할 땐 아래와 같이 find 명령을 이용하면 된다.

find . -exec grep "pattern" {} \;



02 필터 역할을 하는 awk 명령

awk는 grep과 같이 출력된 문장에서 필요한 부분만 걸러내는 필터 역할을 하는 명령어의 일종이다. 사용하기에 따라서 매우 유용하다.

ls -al


사용자 삽입 이미지

위의 출력문은 Tab(공백)으로 각 컬럼을 구분하는데 다음과 같이 하면 원하는 컬럼값만을 선택해서 출력이 가능하다.

ls -al | awk '{print $1 $5 $9}'


사용자 삽입 이미지

위와 같이 1, 5, 9번째 컬럼값만 출력이 된다. 이 밖에 awk는 연산 기능을 가지고 있다.

find /home/webdizen -user webdizen -ls | awk '{sum+=$7}; END {print "User webdizen total disk use = " sum }'


사용자 삽입 이미지

이와 같이 webdizen 권한의 파일들의 크기를 합한 값을 보여준다.

이 명령 라인은 find로 검색한 파일을 ls -al 형식으로 출력한 후 여기서 바이트 수를 가지고 있는 7번째 열의 합계를 누적한다. 그리고 마지막 줄에 최종 결과값을 출력한다. 이 밖에 awk는 평균값도 구할 수 있다. 방법은 의외로 간단하여 END 절의 sum을 sum/NR로 대체하면 평균값이 나온다. NR은 awk내부 변수로서 현재까지의 입력 라인수를 갖는다.


03. 가장 많이 쓰이는 find 명령

find 명령어는 시스템 관리 명령 중 가장 많이 사용되는 명령어 중 하나이다. 이 명령어의 다양한 기능을 많이 알수록 관리자의 불필요한 작업량을 최대한 줄일 수 있을 것이다.

-atime n : 정확히 n일 전에 access 된 파일
-mtime n : 정확히 n일 전에 수정된 파일
-newer [file] : file 보다 최근에 수정된 파일
-size n : 정확히 n x 512byte의 길이를 갖는 파일
-type c : 파일의 종류를 기술. f : 파일 d : 디렉토리
-fstype [filesystme] : 파일 시스템 종류
-name [pattern] : 파일 이름 검색
-perm p : 파일 접근 퍼미션이 p인 경우
-user [user] : 파일 소유권이 user인 파일
-group [group] : 파일 그룹이 group인 파일
-nouser : 파일 소유자가 /etc/passwd에 없는 경우
-nogroup : 파일 소유 그룹이 /etc/group에 없는 경우
-uid n : 파일 uid가 n인 경우
-gid n : 파일 gid가 n인 경우


단순히 위의 옵션대로 사용하면 그렇게 유용해 보이지 않을 수 있을 것이다. 예를 들어 누가 정확히 3일 전에 접근한 파일들으 검색할 것인가? 하지만 +, - 기호를 이용하여 기간, 시간, 크기와 같은 수치들의 범위를 지정할 수 있다.

+ 는 ~~ 보다 큰 경우, - 는 ~~ 보다 작은 경우
-mtime +7 : 수정된 지 7일이 지난 파일
-mtime -7 : 수정된 지 7일이 안된 파일
-size +100 : 50kbyte 보다 큰 파일
-name 뒤엔 인용 부호(와일드 카드 문자)를 같이 사용할 수 있다.
-name *.dat : 확장자가 dat인 모든 파일


이런 옵션들을 차례로 기술하여 파일을 찾을 경우 AND 연산이 적용된다. 그리고 -o 옵션을 이용하여 OR을 적용할 수도 있고 ( )를 이용하여 그룹을 지어 적용할 수도 있다. 이 밖에 NOT 연산도 가능한데 옵션 앞에 ! 부호를 달아 주면 된다. 밑의 몇 가지 예를 살펴보자.

find ./ -atime +60 -mtime 120

(AND 논리 연산으로 접근한지 60일 지난 파일 중 수정한 지 120일 지난 파일)

find ./ \( -user webdizen -o -group webdizen \)

(OR 논리 연산으로 소유자나 그룹이 webdizen인 경우)

사용자 삽입 이미지


find ./ \( ! -user webdizen -o ! -group webdizen \)

(NOT 논리 연산으로 소유자나 그룹이 webdizen이 아닌 경우)

사용자 삽입 이미지


-perm 옵션은 숫자 형태의 특정 접근 모드를 이용하여 파일을 검색하는 옵션이다.

옵션별 사용 예를 들어보자.
-perm 75 : permission = 755
-perm -002 : 모든 사람들이 기록할 수 있는 파일
-perm -4000 : SUID 엑세스 설정
-perm -2000 : SGID 엑세스 설정


그 외에도 지원되는 내부 옵션들이 있다.

-print : 대응되는 파일의 경로명을 출력
-ls : 대응되는 파일에 대한 긴 디렉토리 목록을 출력
-exec commands : 대응되는 파일에 대해 commands 명령 수행
-ok commands : 파일에 대한 commands 명령 수행 전에 입력 대기 상태
-xdev : 검색 시작 디렉토리가 속해 있는 파일 시스템에 대해서만 검색 제한
-mount : IRIX와 SCO UNIX에서 -xdev 옵션
-prune : 서브 디렉토리는 검색을 하지 않음
-print 옵션은 근래는 기본으로 들어간다. 굳이 붙일 필요는 없고, -exec, -ok 옵션 사용 시에는 반드시 마지막에 \; 으로 구문을 마감해야 한다.


예를 들어서 find로 검색한 파일을 지우기 위해서는 다음과 같이 한다.

find ./ -name *.* -exec rm -f {} \;


find 명령어를 이용한 시스템 관리 방법 예를 몇 가지 더 들어 보자.

파일 크기가 20M 이상이며 30일 이상 동안 수정되지 않은 파일을 찾는다.

find / -type f -size +20480 -mtime +30 -ls
find / -type f -size +20480 -mtime +30 -exec rm -f {} \; (대응되는 파일 삭제)


보안에 관련된 팁으로서, 모든 setuid, setgid를 검색한다.

find / -type f \( -perm -4000 -o -perm -2000 \)
find / -type f \( -perm -4000 -o -perm -2000 \) | diff -setuidlist
(검색된 setuid, setgid를 기존에 작성한 목록과 비교하여 새로 추가 된 것이 있는지를 확인한다.)


"System" 카테고리의 다른 글
  • ASCII Code의 CRLF 제거 방법 (0)2009/09/04
  • Linux / Unix Command: signal (0)2007/07/18
  • 시스템 관리자를 위한 기초 명령어 활용법 (0)2007/06/22
  • 프로세스정보 얻어오기 (0)2007/05/14
  • 여러 가지 설정으로 공격으로부터 시스템을 안전하... (0)2007/05/10
2007/06/22 14:05 2007/06/22 14:05
Posted by webdizen
Tags awk, egrep, fgrep, find, grep, 리눅스, 시스템 관리자, 유닉스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3055

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/System2007/05/04 17:31

리눅스에서의 메모리관리

구현 선택, 구현의 모순, 동적 할당
Level: Intermediate


Jonathan Bartlett
기술 디렉터, New Media Worx
2004년 11월 16일

리눅스 프로그래머들이 사용할 수 있는 메모리 관리 기술을 살펴본다. C 언어 중심으로 설명 하겠지만 다른 언어들에도 적용할 수 있다. 메모리 관리가 어떻게 수행되는지, 메모리를 수동으로 관리하는 방법, 카운팅(counting) 또는 풀링(pooling)을 반-수동으로 관리하는 방법, 가비지 컬렉션을 사용하여 메모리를 자동으로 관리하는 방법을 설명한다.

메모리가 관리되어야 하는 이유
메모리 관리는 컴퓨터 프로그래밍의 가장 근본적인 분야 중 하나이다. 스크립팅 언어의 경우 메모리가 관리되는 방법을 신경 쓸 필요는 없지만 그것이 메모리 관리가 덜 중요하다는 것을 의미하는 것은 아니다. 메모리 매니저의 능력과 한계를 아는 것이 효과적인 프로그래밍에 있어 중요하다. C와 C++ 같은 시스템 언어들 경우, 메모리 관리가 필요하다. 이 글에서는 수동, 반자동, 자동 메모리 관리 방법의 기초를 설명하겠다.

Apple II에서 어셈블리 언어 프로그래밍을 하던 시절, 메모리 관리는 큰 문제가 아니었다. 기본적으로 전체 시스템을 실행했다. 시스템이 어떤 메모리를 갖고 있는지는 문제가 아니었다. 얼마나 많은 메모리가 남아있는지 파악할 필요도 없었다. 모든 컴퓨터는 같은 양의 메모리를 갖고 있었기 때문이다. 따라서 메모리 요구사항에 거의 변화가 없다면 사용할 메모리 영역을 선택하여 사용했다.

하지만 프로그램의 각 부분에 얼마나 많은 메모리가 필요한지 알지 못한다면 간단한 컴퓨터일지라도 문제가 있었다. 제한된 공간과 메모리 요구가 다양하다면 다음의 요구사항을 충족시킬 방법이 필요하다:

데이터를 처리할 메모리의 양을 결정한다.
사용할 수 있는 메모리에서 메모리 섹션을 확보한다.
메모리 섹션을 사용 가능한 메모리 풀로 리턴하여 프로그램의 다른 부분 또는 다른 프로그램에서 사용할 수 있도록 한다.

이러한 요구사항을 해결하는 라이브러리를 할당자(allocators)라고 한다. 문제가 동적일수록 메모리 관리는 더욱 중요하고 메모리 할당자의 선택이 더욱 중요해진다. 메모리 관리에 사용할 수 있는 다른 방법을들 보도록 하자.

C-스타일의 메모리 할당자(allocator)
C 프로그래밍 언어는 위 세 가지 요구사항을 수행하기 위해서 두 개의 함수를 제공한다:

malloc: 주어진 바이트 수를 할당하고 여기에 포인터를 리턴한다. 사용할 수 있는 충분한 메모리가 없다면 null 포인터를 리턴한다.
free: malloc에 의해 할당된 메모리 조각에 대한 포인터를 취해, 프로그램이나 OS가 나중에 사용할 수 있도록 이를 리턴한다. (실제로, 몇몇 malloc 구현은 메모리를 OS가 아닌 프로그램으로 리턴할 수 있다.)

물리적 메모리와 가상 메모리
자신의 프로그램 안에서 메모리가 할당되는 방법을 이해하려면 OS로 부터 프로그램으로 메모리가 어떻게 할당되는 지를 이해해야 한다. 컴퓨터 상의 각 프로세스는 모든 물리적 메모리에 액세스 했다고 생각한다. 분명, 동시에 다중의 프로그램을 실행하기 때문에 각 프로세스는 모든 메모리를 가질 수 없다. 프로세스는 가상 메모리를 사용하고 있다.

예를 들어, 프로그램이 메모리 주소 629에 액세스한다고 해보자. 하지만 가상 메모리 시스템은 629 RAM 위치에 이를 반드시 저장할 필요는 없다. 사실 RAM에 존재할 필요도 없다. 물리적 RAM이 꽉 찼다면 디스크로 옮겨질 수도 있다. 주소가 메모리가 할당된 물리적 장소를 반드시 반영할 필요는 없기 때문에 이를 가상 메모리라고 한다. OS는 가상 주소 대 물리적 주소 테이블을 관리하여 컴퓨터 하드웨어가 주소 요청에 적절히 대응할 수 있도록 한다. 그리고, 주소가 RAM이 아닌 디스크상에 있다면 OS는 일시적으로 프로세스를 중지하고 메모리를 디스크에서 언로드(unload)하고 디스크에서 그 요청된 메모리에 로딩하고 프로세스를 재시작 한다. 이러한 방식으로 각 프로세스는 고유의 주소 영역을 갖고 실행하고 물리적으로 설치된 것 보다 많은 메모리에 액세스 할 수 있다.

32-bit x86 시스템상에서, 각 프로세스는 4 GB 메모리에 액세스 할 수 있다. 요즘, 대부분의 사람들은 자신들의 시스템에 4GB 메모리를 갖추고 있지 않다. swap을 포함하더라도 프로세스 당 4GB 이하이다. 따라서, 프로세스가 로딩되면 특정 주소로 초기 메모리 할당이 이루어지고 시스템 브레이크(system break)를 호출한다. 과거에 이것은 언매핑(unmapped) 메모리이다. 즉 RAM 또는 디스크에 상응하는 물리적 위치를 할당 받지 못한 메모리이다. 따라서 프로세스가 초기 할당부터 메모리가 부족하면 OS가 더 많은 메모리를 매핑하도록 요청해야 한다. (매핑은 수학용어로 일대일(one-to-one) 대응을 의미한다-메모리는 가상 주소가 상응하는 물리적 위치를 갖고 있을 경우 "매핑"되어 그곳에 저장된다.)

유닉스 기반 시스템은 추가 메모리로 매핑되는 두 개의 기본적인 시스템 호출을 갖고 있다:

brk:brk()는 매우 간단한 시스템 호출이다. 이 프로세스를 위한 매핑된 메모리의 끝에 있는 위치인 시스템 브레이크를 기억하는가? 프로세스에 메모리를 추가 또는 제거하기 위해 brk()는 그 위치를 단순히 앞뒤로 이동한다.
mmap:mmap()또는 "memory map"은 brk() 비슷하지만 훨씬 유연하다. 우선, 프로세스의 끝 뿐만 아니라 어디에나 메모리를 매핑할 수 있다. 그리고 가상 주소를 물리적 RAM 또는 swap에 매핑할 수 있고 파일과 파일 위치로 매핑하여 읽고 쓰는 메모리 주소가 파일에서 데이터를 읽고 쓸 것이다. 하지만 여기서는 mmap의 기능에 초점을 맞춰 매핑된 RAM을 프로세스에 추가하는 것을 설명하겠다. munmap()은 mmap()와 반대되는 작동을 수행한다.

brk()또는 mmap()은 가외의 가상 메모리를 프로세스에 추가하는데 사용될 수 있다. 우리 예제에서는 보다 간단하고 일반적인 brk()를 사용할 것이다.

할당자(allocator) 구현하기
C 프로그래밍을 많이 사용했다면 malloc()과 free()를 상당히 많이 사용했을 것이다. 하지만 이들이 OS에서 어떻게 구현되는지에 대해서는 많이 생각하지 못했을 것이다. 이 섹션에서는 malloc과 free의 간단한 구현 코드를 소개하겠다.

이 예제를 실행하려면, 코드를 복사하여 malloc.c 파일에 붙인다.

대부분의 OS에서 메모리 할당은 두 개의 함수로 핸들된다:

void *malloc(long numbytes): 메모리의 numbytes를 할당하여 포인터를 파일 바이트로 리턴한다.
void free(void *firstbyte): 이전 malloc에 의해 리턴된 포인터가 있다면, 할당된 공간을 프로세스의 "유휴 공간(free space)"에 준다.
malloc_init은 메모리 할당자를 초기화 할 함수가 될 것이다. 세 가지 일을 수행한다: 초기화되는 할당자를 확인하고, 시스템 상에 유효 메모리 주소를 찾고, 관리되는 메모리의 앞에 포인터를 설정한다. 이 세 변수는 글로벌 변수이다:




앞서 언급했듯이 매핑된 메모리의 끝-마지막 유효 주소-는 시스템 브레이크 또는 커런트 브레이크로 알려져 있다. 대부분의 유닉스 시스템 상에서 커런트 시스템 브레이크를 찾으려면 sbrk(0) 함수를 사용한다. sbrk은 인자에 있는 바이트의 수 만큼 커런트 시스템 브레이크를 움직이고 새로운 시스템 브레이크를 리턴한다. 인자 0으로 이것을 호출하면 커런트 브레이크를 리턴한다. 다음은 malloc 초기화 코드이다. 커런트 브레이크를 찾고 변수를 초기화한다:




이제 메모리를 올바르게 관리하기 위해 무엇을 할당하고 무엇을 할당 해제하는지를 트래킹할 수 있어야 한다. free 가 호출된 후에 mark block 같은 사용되지 않은 것을 실행해야 한다. 그리고 malloc이 호출되면 사용되지 않은 block의 위치를 정할 수 있다. 따라서 malloc에 의해 리턴된 모든 메모리 조각의 시작에는 이 구조를 갖게 된다:




이제, malloc을 호출하는 프로그램에 문제를 일으킬 것이라고 생각할 수도 있다. 이들이 어떻게 구조를 알 수 있겠는가? 답은 '그것에 대해 알 필요가 없다' 이다; 이것을 리턴하기 전에 구조 뒤에 포인터를 움직임으로서 숨길 수 있다. 이는 리턴된 포인터가 어떤 목적으로도 사용되지 않은 메모리를 지적하도록 할 것이다. 이러한 방식으로, 호출 프로그램의 관점에서 볼 때, 그들이 얻는 모든 것은 유휴의 개방 메모리 이다. free()를 통해 포인터를 뒤로 전달하면 얼마간의 메모리 바이트를 저장하여 이 구조를 다시 찾는다.

이제 메모리 할당에 대해 논하기 전에 메모리를 비우는 것에 대해 이야기하려고 한다. 이것이 보다 간단하기 때문이다. 메모리를 비우기 위해 해야 할 일은 주어진 포인터를 취해서 sizeof(struct mem_control_block) 바이트를 백업하고 이를 사용 가능한 것으로 표시하는 것이다:




여러분도 보다시피 이 할당자에서 메모리 비우기는 매우 간단한 방식을 사용하여 일정한 시간에 수행된다. 메모리 할당은 약간 더 어렵다. 다음은 알고리즘의 아웃라인이다:




오픈 청크를 찾고 있는 링크 된 포인터를 사용하여 메모리를 검사하고 있다. 다음은 코드이다:




그리고 이것은 우리의 메모리 매니저이다. 이제 이를 구현하여 우리의 프로그램에서 실행시키도록 한다.

malloc 호환의 할당자를 구현하려면 (realloc()같은 몇몇 함수는 다루지 않았지만, malloc()과 free()도 주요 함수이다.) 다음 명령어를 실행한다:



이것은 malloc.so라는 파일을 만들어낸다. 이것이 우리 코드를 포함하고 있는 공유 라이브러리이다.

다음과 같이 유닉스 시스템에서 시스템 malloc() 대신 본인의 할당자를 사용할 수 있다:



LD_PRELOAD 환경 변수는 동적 링커가 로딩할 실행 파일에 전에 주어진 공유 라이브러리의 심볼을 로딩하게 한다. 지정된 라이브러리에서 그 심볼에 우선순위를 준다. 따라서 이 세션에서 지금부터 우리가 시작하는 모든 애플리케이션은 시스템이 아닌 우리의 malloc()을 사용할 것이다. 몇몇 애플리케이션들 malloc()을 사용하지 않지만 이들은 예외인 경우이다. realloc()같은 메모리 관리 함수를 사용하거나 malloc()의 내부 작동에 대한 어설픈 추측을 한다면 분명 충돌이 일어날 것이다. ash 쉘은 우리의 새로운 malloc()을 사용하면 작동이 잘 될 것이다.

malloc()이 사용되고 있다는 것을 확인하려면 함수의 엔트리 포인트에 있는 write()에 호출을 추가하여 이를 테스트해야 한다.

우리의 메모리 매니저는 보완되어야 할 것이 많이 있다. 다음과 같은 단점들이 있다:

시스템 브레이크(글로벌 변수)에서 작동하기 때문에 다른 할당자 또는 mmap와 공존할 수 없다.
메모리를 할당할 때, 최악의 시나리오는 모든 프로세스의 메모리를 거쳐야 한다는 것이다: 여기에는 디스크상에 위치한 많은 메모리가 포함되어 있다. 이는 OS가 데이터를 디스크에서 이동시켜야 한다는 것을 의미한다.
메모리 부족 에러를 핸들링 할 방도가 없다 (malloc은 에러가 아닌 경우만 취급한다.)
realloc() 같은 다른 많은 메모리 함수들을 구현하지 않는다.
sbrk() 요청한 것 보다 많은 메모리를 주기 때문에 힙의 끝에 메모리를 유출하게 된다.
is_available 플래그는 4-byte 단어를 사용한다. 1 bit 정보를 포함할 때에도 그렇다.
할당자는 쓰레드 보안이 되지 않는다.
할당자는 유휴 공간을 더 큰 블록으로 합체할 수 없다.
할당자의 간단한 적합(fitting) 알고리즘이 잠재적인 메모리 단편화를 만든다.
이외에도 많은 문제들이 남아있다.

기타 malloc 구현
malloc()의 여러 구현이 있고 저마다의 장단점이 있다. 할당자를 디자인할 때 다음 사항을 고려해야 한다:

할당 속도
할당 해제 속도
쓰레디드 환경에서의 작동
메모리가 파일링(filing)에 가까웠을 때의 작동
캐시 지역성
메모리 오버헤드 기록
가상 메모리 환경에서의 작동
크고 작은 객체들
실시간 게런티

각 구현 마다 장단점이 있다. 우리의 단순한 할당자의 경우 할당이 매우 느리지만 할당 해제는 매우 빠르다. 가상 메모리 시스템을 사용한 빈약한 작동 때문이다. 큰 객체에 적합하다.

이외에도 다른 할당자들을 사용할 수 있다:

Doug Lea Malloc: Doug Lea Malloc은 Doug Lea의 원래 할당자, GNU libc 할당자 ptmalloc을 포함한 전체 할당자군이다. Doug Lea의 할당자는 기본적인 구조가 우리 버전과 매우 비슷하지만 인덱스를 결합하여 검색이 훨씬 빠르고 여러 사용되지 않는 청크들을 하나의 큰 청크로 결합하는 기능도 있다. 또한 캐싱을 활용하여 최근에 비워진 메모리를 빠르게 재사용할 수 있도록 한다. ptmalloc은 다중 쓰레드를 지원하도록 확장된 Doug Lea Malloc의 한 버전이다. (참고자료)
BSD Malloc: 4.2 BSD와 함께 배포되고 FreeBSD에 포함된 구현인 BSD Malloc은 사전 결정된 크기의 객체 풀(pool)로부터 객체를 할당하는 할당자이다. 객체 사이즈를 위한 사이즈 클래스를 갖고 있다. 따라서 일정 사이즈의 객체를 요구한다면 객체에 맞는 어떤 사이즈의 클래스든지 할당할 것이다. 빠른 구현이라는 장점이 있지만 메모리를 낭비할 수 있다. (참고자료)
Hoard: Hoard는 멀티쓰레디드 환경에서 빠르게 작동할 목적으로 작성되었다. 따라서 어떤 프로세스라도 메모리 할당을 기다리지 않도록 잠금(locking)을 최대한 활용하도록 설계되었다. 많은 할당과 할당 해제를 수행하는 멀티쓰레디드 프로세스의 속도를 크게 높일 수 있다. (참고자료)

이것들은 많은 할당자들에 대해 잘 알려진 것들이다. 여러분의 프로그램이 할당에 대해 특별한 필요가 있다면 그 프로그램이 메모리를 할당하는 방식에 맞는 커스텀 할당자를 작성하는 것이 더 낫다. 하지만 할당자 디자인이 익숙하지 않다면 커스텀 할당자는 더 많은 문제를 만들어 낼 수 있다. 이 문제에 관해서는 Donald Knuth의 저서The Art of Computer Programming Volume 1: Fundamental Algorithms in section 2.5, "Dynamic Storage Allocation"을 참조하기 바란다. (참고자료)

C++ 에서, operator new()를 오버로드하여 클래스 기반 또는 템플릿 기반의 할당자를 구현할 수 있다. Andrei Alexandrescu의 Modern C++ Design의 4장("Small Object Allocation")에서는 작은 객체 할당자에 대해 설명하고 있다. (참고자료)

malloc() 기반 메모리 관리의 단점
우리의 메모리 매니저 뿐만 아니라 malloc()기반의 메모리 관리에도 단점은 있다. malloc()으로 메모리를 관리하는 것은 장기 실행하는 스토리지를 관리해야 하는 프로그램에게는 쥐약이다. 메모리 여유(floating)에 대한 레퍼런스가 많다면 언제 릴리즈 되어야 하는지 알기 힘들다. 수명주기가 현재 함수까지로 제한되어 있는 메모리는 관리가 쉽지만 그 이상의 수명주기를 가진 메모리는 훨씬 더 어렵다. 또한 많은 API들은 메모리 관리에 대한 책임이 호출 프로그램에 있는지 아니면 호출된 함수에 있는지에 대해 모호하다.

메모리를 관리할 때의 문제들 때문에 많은 프로그램들은 각자의 메모리 관리 규칙을 따르고 있다. C++의 예외 핸들링은 이 일을 더욱 복잡하게 한다. 가끔, 실제 연산 태스크를 수행 하는 것 보다 메모리 할당 및 클린업 관리에 더 많은 코드가 집중되어 있는 것 같다. 따라서 메모리 관리에 대한 다른 대안을 모색하고자 한다.

반자동 메모리 관리 전략
Reference counting
Reference counting은 반자동 메모리 관리 기술로서 어느 정도의 프로그래머 지원이 필요하다. 하지만 객체가 더 이상 사용되지 않는 다는 것 까지 알 필요는 없다. reference counting 방식은 여러분을 위한 것이다.

reference counting에서 공유된 모든 데이터 구조는 그 구조에 현재 활성화된 '레퍼런스'의 수를 포함하는 필드를 갖고 있다. 프로시저가 포인터에서 데이터 구조로 전달되면 레퍼런스 카운트(reference count)에 추가한다. 기본적으로 데이터 구조에 어떻게 위치들이 저장되는지를 명령하고 있는 것이다. 프로시저가 이것을 사용하는 것을 끝마치면 레퍼런스 카운트가 감소한다. 이와 동시에 카운트가 0으로 떨어졌는지를 확인한다. 그렇다면 메모리가 비워진 것이다.

이것의 장점은 주어진 데이터 구조가 따라야 하는 프로그램의 모든 경로를 따라갈 필요가 없다는 것이다. 이것에 대해 각 지역화된 레퍼런스는 카운트를 적절히 감소 또는 증가시킨다. 이로서 메모리를 사용중일 때 비워지게 되는 현상을 막는다. 하지만 레퍼런스 카운팅 방식의 데이터 구조를 사용할 때마다 reference counting 함수를 실행해야 한다. 또한, 빌트인 함수와 삼자 라이브러리는 reference counting 방식을 모르거나 사용할 수 없다. reference counting은 순환식 레퍼런스를 가진 구조에는 어려움을 겪는다.

reference counting을 구현하려면 두 개의 함수가 필요하다. 하나는 레퍼런스 카운트를 증가시키는 함수이고 하나는 레퍼런스 카운트를 감소시켜 0에 도달했을 때 메모리를 비우는 함수이다.

다음은 reference counting 예제이다:




무엇을 하고자 하느냐에 따라 REF와 UNREF가 상황을 복잡하게 할 수 있다. 예를 들어 멀티쓰레디드 프로그램에 잠금을 추가하고자 한다면 refcountedstruct를 확장하여 메모리를 비우기 전에 호출 할 함수에 대한 포인터를 추가하고 싶을 것이다. (객체 지향 언어의 디스트럭터(destructor)와 같다-구조에 포인터가 포함되어 있을 때에만 필요하다.)

REF와 UNREF를 사용할 때, 포인터 할당에 있어 다음 규칙을 준수해야 한다:

UNREF 할당 전에 왼쪽 포인터가 가르키는 값
REF 할당 후에 왼쪽 포인터가 가르키는 값

refcounted 구조로 전달된 함수의 경우 다음 규칙을 따라야 한다:

REF 함수의 시작에 있는 모든 포인터
UNREF 함수의 끝에 있는 모든 포인터

다음은 reference counting을 사용하는 코드 예제이다:



reference counting은 다소 간단하기 때문에 대부분의 프로그래머들은 라이브러리를 사용하기 보다는 이들 자체를 구현한다. 하지만 malloc과 free 같은 저급의 할당자들을 의존하여 메모리를 할당 및 릴리스 하고 있다.

reference counting은 Perl 같은 고급 언어에서 많이 사용된다. 그러한 언어에서 레퍼런스 카운팅은 언어에 의해 자동으로 핸들 되어 확장 모듈을 작성하는 것 외에는 걱정할 것이 없다. 모든 것이 레퍼런스 카운팅 되어야 하기 때문에 속도는 느리지만 안전성이 높고 프로그래밍이 쉽다:

간단한 구현
사용이 쉬움
레퍼런스가 데이터 구조의 일부이기 때문에 캐시 지역성이 우수함

하지만 단점도 있다:

레퍼런스 카운팅 함수를 호출하는 것을 절대 잊어서는 안된다.
순환 데이터 구조의 일부인 구조를 릴리스 하지 않는다.
거의 모든 포인터 할당이 느리다.
레퍼런스 카운팅 된 객체를 사용하는 동안 예외 핸들링 (try 또는 setjmp()/longjmp())을 사용할 때 추가 조치를 취해야 한다.
레퍼런스를 핸들 할 여분의 메모리가 필요하다.
레퍼런스 카운터는 구조 내에서 첫 번째 위치를 차지한다. 이는 대부분의 머신에서 가장 빠르게 액세스 할 수 있다.
멀티쓰레디드 환경에서 수행할 때 느리고 어렵다.

C++는 reference counting 같은 상세한 포인터 핸들링을 핸들 할 수 있는 스마트 포인터(smart pointer)를 사용하여 프로그래머 오류를 줄일 수 있다. 하지만 스마트 포인터(C 라이브러리로의 링크)를 핸들 할 수 없는 레거시 코드를 사용해야 한다면 이를 사용하지 않는 것이 낫다. 따라서 이는 C++ 전용 프로젝트에만 유용하다. 스마트 포인터를 사용하려면 Alexandrescu의 Modern C++ Design의 "Smart Pointers"를 참조하기 바란다.

메모리 풀(pool)
메모리 풀은 메모리 관리를 반자동화 할 수 있는 또 하나의 방식이다. 메모리 풀은 특정 단계를 거쳐야 하는 프로그램을 위해 메모리 관리를 자동화한다. 예를 들어, 많은 네트워크 서버 프로세스들은 커넥션 당 할당된 메모리를 갖고 있다. 최대 수명은 현재 커넥션의 수명이다. 풀링 메모리를 사용하는 Apache는 고유의 메모리 풀을 갖고 있는 단계로 나뉘어진 연결을 갖고 있다. 이 단계의 끝에, 전체 메모리 풀은 한번에 비워진다.

풀링 메모리를 관리 할 때, 각 할당은 할당되어야 하는 곳부터 메모리의 풀을 지정해야 한다. 각 풀은 다른 수명을 갖고 있다. Apache에서 서버의 수명을 지속시키는 풀이 있다. 하나는 연결의 수명을 지속시키는 것이고 하나는 요청의 수명을 지속시키는 것이다. 따라서 연결 보다 오래 지속되는 데이터를 만들지 않는 일련의 함수를 갖고 있다면 커넥션 풀에서 이 모든 것을 할당하면서 연결 끝에는 자동으로 비워진다는 것을 알 수 있다. 게다가 어떤 구현은 메모리 풀이 비워지기 비로 직전에 호출되는 레지스터링 클린업 함수로 하여금 메모리가 비워지기 전에 수행되어야 하는 추가 태스크를 수행하도록 한다. (객체 지향의 디스트럭터와 비슷하다.).

자신이 프로그램에서 풀을 사용하려면 GNU libc의 obstack 구현 또는 Apache의 Apache Portable Runtime을 사용할 수 있다. GNU obstack는 GNU 기반 리눅스 배포판에 기본적으로 포함되기 때문에 유용하다. Apache Portable Runtime은 멀티플랫폼 서버 소프트웨어를 작성하는 모든 측면들을 핸들 할 수 있는 유틸리티가 많아서 좋다.(참고자료)

다음의 가상 코드는 obstack의 사용법을 보여준다:




기본적으로 작동의 주요 단계가 끝나면 그 단계에 대한 obstack이 비워진다. 하지만 프로시저가 현재 단계 보다 더 길게 지속될 메모리를 할당하려면 커넥션 또는 글로벌 같은 장기 obstack을 사용할 수 있다. obstack_free()로 전달된 NULL은 obstack의 전체 내용을 비워야 한다는 것을 나타낸다. 다른 값들은 사용할 수 있지만 유용하지 않다.

다음은 메모리 풀링의 장점들이다:

애플리케이션의 메모리를 관리하기가 수월하다.
풀에서 한번에 되기 때문에 메모리 할당과 할당 해제가 훨씬 빠르다. 할당은 O(1) 시간에 수행될 수 있고 풀 릴리스는 끝난다. (실제로 O(n) 시간이지만 대부분의 경우 O(1)로 만드는 거대한 요소에 의해 나뉘어진다.)
에러 핸들링 풀은 사전 할당되어 정규 메모리가 소진되면 프로그램이 복구할 수 있도록 한다.
사용하기 쉬운 표준 구현들이 있다.
다음은 메모리 풀링의 단점들이다:

메모리 풀은 단계 별로 작동하는 프로그램에만 유용하다.
메모리 풀은 삼자 라이브러리로는 잘 작동하지 않는다.
프로그램 구조가 바뀌면 풀이 변경되어야 하는데, 이로 인해 메모리 관리 시스템을 재디자인 해야 한다.
할당하고 싶은 풀이 어떤 것인지를 기억해야 한다. 이것이 잘못되면 회복이 불가능하다.
가비지 컬렉션
가비지 컬렉션은 더 이상 사용하지 않는 데이터 객체들을 완전 자동으로 탐지 및 제거하는 것이다. 가비지 컬렉터는 사용 가능한 메모리가 특정 임계치로 떨어지면 자동으로 실행된다. 일반적으로 프로그램에서 사용할 수 있는 "기본" 설정-스택 데이터, 글로벌 변수, 레지스터-으로 시작한다. 그런 다음 서로 링크 된 데이터의 모든 조각들을 트레이스한다. 컬렉터가 찾는 모든 것은 좋은 데이터이다. 찾지 못하는 모든 것은 가비지(쓰레기)이고 파괴 및 재사용될 수 있다. 메모리를 효과적으로 관리하려면 많은 유형의 가비지 컬렉터들이 데이터 구조 내의 포인터 레이아웃을 알 필요가 있다. 그리고 올바로 작동하는 언어의 일부가 되어야 한다.

컬렉터 유형
복사(Copying): 메모리 스토리지를 두 부분으로 나누고 데이터가 오직 한 쪽에만 있도록 한다. "기본" 요소들을 갖고 시작하면서 주기적으로 한 쪽에서 다른 쪽으로 데이터를 복사하기 시작한다. 새롭게 채워진 메모리 섹션은 활성이 되고 다른 쪽의 모든 것은 쓰레기로 간주된다. 또한 복사가 발생하면 모든 포인터들은 각 메모리 아이템의 새로운 위치를 가르키도록 업데이트 되어야 한다. 따라서 이 방식의 가비지 컬렉션을 사용하려면 컬렉터는 프로그래밍 언어로 통합되어야 한다.
표시와 청소(Mark and sweep): 데이터의 각 조각은 태그로 표시된다. 가끔 모든 태그들은 0으로 설정되고 컬렉터는 "기본" 요소들로 시작하는 데이터부터 검사한다. 메모리를 만나면 태그를 1로 표시한다. 끝에 1로 태그가 붙지 않는 모든 것은 쓰레기로 간주되어 나중에 재사용된다.
증가(Incremental): 증가 가비지 컬렉터는 모든 데이터 객체들을 전체적으로 실행할 필요가 없다. 모든 메모리를 실행하면 모으는 기간 동안 한꺼번에 멈춰야 하고 현재의 모든 데이터에 액세스하는 것과 관련한 캐시 문제 때문이다. 증가 컬렉터는 이러한 문제들을 방지한다.
Conservative: Conservative 가비지 컬렉터는 메모리를 관리하는 데이터 구조에 대해 알 필요가 없다. 단순히 모든 데이터 바이트를 보고 이들이 모두 포인터를 갖고 있다는 것으로 간주한다. 따라서 바이트의 시퀀스가 할당된 메모리 조각에 대한 포인터가 되면 이를 레퍼런스 된 것으로 표시한다. 이는 정수 필드가 할당된 메모리의 주소인 값을 포함한다면 레퍼런스 되지 않은 메모리가 모아질 때 문제가 될 수 있다. 하지만 이는 매우 드문 경우이고 적은 메모리만을 쓴다. Conservative 컬렉터는 모든 프로그래밍 언어로 통합될 수 있는 장점이 있다.

Hans Boehm의 conservative 가비지 컬렉터는 가장 대중적인 가비지 컬렉터 중 하나이다. 무료이고 Conservative 유형이자 증가 유형이기 때문이다. --enable-redirect-malloc으로 이를 구현하여 (API 대신 Malloc/Free 사용하는)시스템 할당자 대용으로 사용할 수 있다. 사실 이렇게 하면 같은 LD_PRELOAD 트릭을 트릭을 사용할 수 있다. 프로그램이 메모리를 유출한다는 의심이 들면 이 가비지 컬렉터를 사용하여 프로세스 사이즈를 줄일 수 있다. 메모리 유출이 심했을 때 Mozilla 초기에는 많은 사람들이 이 기술을 사용했다. 이 가비지 컬렉터는 Windows®와 UNIX 모두 사용할 수 있다.

다음은 가비지 컬렉션의 장점이다:

메모리의 이중 유휴화(double-freeing)나 객체 수명을 걱정할 필요가 없다.
어떤 컬렉터의 경우 일반 할당에 사용되는 같은 API를 사용할 수 있다.
다음은 가비지 컬렉션의 단점이다:

대부분의 컬렉터를 사용할 때, 메모리가 비워져가고 있을 때 기회가 없다.
많은 경우, 가비지 컬렉션은 다른 형태의 메모리 관리 보다 느리다.
가비지 컬렉션 에러로 인한 버그는 디버그가 어렵다.
사용되지 않는 포인터를 null로 설정하지 않는다면 메모리 유출이 있다.

결론
퍼포먼스, 용이성, 구현가능성, 쓰레딩 기능 등 비교할 것이 많다. 프로젝트 요구사항에 맞는 다양한 메모리 관리 패턴이 있다. 각 패턴마다 광범위한 구현을 갖추고 있고, 이들 각각 장단점이 있다. 프로그래밍 환경을 위해 디폴트 기술을 사용하는 것은 좋은 일이지만 사용할 수 있는 옵션을 아는 것 또한 중요한 일이다. 다음 표에서는 이 글에서 다루어진 메모리 관리 전략들을 비교했다.

표 1. 메모리 할당 전략 비교
전략 할당 속도 할당 해제 속도 캐시 지역성 용이성 보편성 실시간 사용 SMP 및 쓰레드 친화력
커스텀 할당자 구현에 의존 구현에 의존 구현에 의존 매우 어려움 없음 구현에 의존 구현에 의존
일반 할당자 적은 메모리 사용에 빠름 매우 빠름 거의 없음 쉬움 매우 보편적 No No
GNU malloc 중간 빠름 중간 쉬움 Very No 중간
Hoard 중간 중간 중간 쉬움 Very No Yes
Reference counting N/A N/A 우수함 중간 중간 Yes (malloc 구현에 의존) 구현에 의존
Pooling 중간 매우 빠름 우수함 중간 중간 Yes (malloc 구현에 의존) 구현에 의존
가비지 컬렉션 중간 (slow when collection occurs) 중간 거의 없음 중간 중간 No 거의 없음
증가 가비지 컬렉션 중간 중간 중간 중간 중간 No 거의 없음
중도 증가 가비지 컬렉션 중간 중간 중간 쉬움 매우 보편적 No 거의 없음



참고자료

웹 문서
The obstacks section of the GNU C Library manual
The Apache Portable Runtime documentation

기본 할당자
Doug Lea's Malloc
BSD Malloc
ptmalloc
Hoard
GNU Memory-Mapped Malloc (part of GDB)
ElectricFence Malloc Debugger

풀링 할당자
GNU Obstacks
Apache's pooled allocator (in the Apache Portable Runtime)
Squid
NetBSD
talloc

스마트 포인터 및 커스텀 할당자
The Loki C++ Library

가비지 컬렉터
Hahns Boehm Conservative Garbage Collector

가상 메모리 문서
A New Virtual Memory Implementation for Berkeley UNIX
Mel Gorman's Linux VM Documentation

malloc 문서
Malloc in Modern Virtual Memory Environments
Hoard -- a Scalable Memory Allocator for Multithreaded Environments
Design of a General Purpose Memory Allocator for the 4.3BSD UNIX Kernel
A Memory Allocator
Memory Management for High-Performance Applications

커스텀 할당자 문서
Some Storage Management Techniques for Container Classes
Composing High-Performance Memory Allocators
Reconsidering Custom Memory Allocation

가비지 컬렉션 문서
Uniprocessor Garbage Collection Techniques
The Measured Cost of Garbage Collection
Memory Allocation Myths and Half-Truths
Space Efficient Conservative Garbage Collection

웹 참고자료
The Memory Management Reference
OOPS Group Papers on Memory Management and Memory Hierarchies
Memory Management in C++
Programming Alternatives: Memory Management
The Garbage Collection FAQ
Richard Jones's Garbage Collection Bibliography
Debugging Tools for Dynamic Storage Allocation and Memory Management

책
C++ Pointers and Dynamic Memory Management
Memory as a Programming Concept in C and C++
Garbage Collection: Algorithms for Automatic Dynamic Memory Management
Section 2.5, "Dynamic Storage Allocation" Fundamental Algorithms
Section 2.3.5, "Lists and Garbage Collection" Fundamental Algorithms
Chapter 4, "Small Object Allocation" Modern C++ Design
Chapter 7, "Smart Pointers" Modern C++ Design
Jonathan's Chapter 8, "Intermediate Memory Topics" Programming from the Ground Up

developerWorks
Self-manage data buffer memory (developerWorks, January 2004)
A framework for the user defined malloc replacement feature (developerWorks, February 2002)
Mastering Linux debugging techniques (developerWorks, August 2002)
Handling memory leaks in Java programs (developerWorks, February 2001)
developerWorks Linux zone
Speed-start your Linux app
developerWorks blogs
Linux books at discounted prices

목 차:

메모리가 관리되어야 하는 이유
C-스타일의 메모리 할당자(allocator)
반자동 메모리 관리 전략
가비지 컬렉션

결론
참고자료
필자소개
기사에 대한 평가

관련 dW 링크:
Self-manage data buffer memory
A Framework for the User Defined Malloc Replacement Feature
Mastering Linux debugging techniques
Handling memory leaks in Java programs
developerWorks newsletter 구독하기
US 원문 읽기


필자소개
Jonathan Bartlett은 리눅스 어셈블리 언어 입문서인 Programming from the Ground Up의 저자이다. New Media Worx에서 개발 팀을 이끌고 있다.

출처 : http://www-903.ibm.com/developerworks/k ··· emory%2F
"System" 카테고리의 다른 글
  • inotify를 이용한 리눅스 파일 시스템 감시 (0)2007/05/10
  • Secure programmer: 컴포넌트를 안전하게 호출하기 (0)2007/05/04
  • 리눅스에서의 메모리관리 (0)2007/05/04
  • 리눅스 시스템 콜 레퍼런스 (0)2007/05/04
  • Linux 하드웨어 안정성 가이드, Part 2 (0)2007/05/04
2007/05/04 17:31 2007/05/04 17:31
Posted by webdizen
Tags 리눅스, 메모리 관리
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2905

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Applicaton2007/05/04 17:24

애플리케이션의 구현 및 분산 프로세스를 자동화하기

구성이 잘된 디렉토리 구조와 훌륭한 스크립팅 활용하기
Level: Intermediate

Martin C. Brown
프리랜스 작가, 컨설턴트
2004년 9월 14일

한 가지 유형의 시스템을 위한 오픈 소스 애플리케이션을 구현할 때 고려해야 할 점이 많다. 하물며 호환되지 않는 머신 간 분산될 애플리케이션을 구현한다면? 약간의 훈련과 커스텀 스크립트를 사용하면 이 과정을 단순화 할 수 있다. 이 글에서는 애플리케이션들을(과도하게 커스터마이징 된 애플리케이션 포함) 구현하여 분산하는 구조를 만드는 방법과 가능한 한 쉽게, 수동 또는 자동으로, 많은 머신들 간 애플리케이션들을 분산시키는 간단한 방법을 설명한다.
나는 언제나 바이너리나 RPM 배포판을 사용하기 보다는 애플리케이션들을 시스템에 자가구현 했다. RPM을 믿지 못해서가 아니라 많은 시스템들에 커스터마이징 된 환경을 사용하기 때문이다. 디버깅을 바꾸거나 줄여야 할 때도 있을 수 있고, 특정 확장, 모듈 또는 다른 옵션이 필요할 때가 있다. 가끔은 완전히 다른 디렉토리 구조를 만들 때도 있다.

더욱 복잡한 건 Linux™, BSD, OS X, 다양한 사용 UNIX® 배포판을 포함한 오픈 소스 프로젝트를 사용하는 광범위한 플랫폼에서 작업한다는 것이다. 이들 중 어떤 것은 단 하나의 머신을 사용하지만 다른 것은 각종 유형의 10개, 20개 또는 50개 머신을 사용한다. 새로운 소프트웨어가 출시될 때 각 머신을 직접 업데이트 하는 것은 대단한 작업이다. 커스터마이징을 무시하면 실제 구현과 설치 과정은 일반적으로 쉽다. (Listing 1):



특별한 교육 없이, 수 백 개의 머신에 직접 수행할 수 있었다. 최신 버전을 섭렵하고 테스트를 목적으로 특정 패키지의 베타와 알파 릴리스로 작업하면서 내가 마치 전문가가 된 듯한 생각을 했다.

몇 년 전, 다음의 간단한 규칙을 따르고 이 프로세스에 구조를 추가하여 프로세스를 뚜렷하게 단순화하기로 결정했다.:

각 OS용 디렉토리 만들기
각 OS 디렉토리 안에 소프트웨어 패키지를 위한 디렉토리 만들기
각 릴리스용 디렉토리 만들기
각 OS를 위해 한번만 구현하기
각 머신에 여러 번 전개하기
네트워크를 통해 공유할 수 있는 모든 소프트웨어에 맞는 구조를 구현하여 시작할 수 있다.

중앙 구현 디렉토리 만들기
첫 번째 단계는 NFS를 통해 공유할 디렉토리를 만드는 것이다. NFS는 디렉토리가 구현되는 동안 애플리케이션을 저장하는데 사용될 것이다. 일단 구현되면 소스 디렉토리부터 설정 파일까지, 그리고 시스템에 필요한 기타 컴포넌트들을 저장할 때 이 디렉토리를 사용할 수 있다. 핵심은 NFS이다. 시스템이 작동하게 하려면 네트워크 상의 모든 머신에서 중앙 구현 디렉토리에 액세스할 수 있어야 한다.

상위 레벨 구조 만들기
첫 번째 단계는 네트워크에서 지원하고자 하는 각 OS을 위한 상위 레벨 구조를 만드는 것이다. 각 디렉토리는 특정 OS, 아키텍쳐, (중대한 변경일 경우) 버전 넘버의 특성에 맞춰져야 한다. 이러한 디렉토리들은 자신의 네트워크상의 머신과 맞아야 한다. 내 네트워크상에 다양한 머신들이 있기 때문에 나의 상위 레벨 디렉토리 구조는 매우 확장성이 있다. (Listing 2):


너무 많다고 걱정하지 말라. 이 OS 디렉토리들은 각 OS 개정판을 위한 관련 애플리케이션 디렉토리들을 저장하는데 사용될 것이다. 애플리케이션 구현의 커스터마이징은 각 디렉토리에서 개별적으로 다루어질 것이다.

/export/build로서 반출된 구현 디렉토리를 위해 개별 파일 시스템을 사용했다. 이 파일 시스템은 충분한 공간이 있다. 구현 디렉토리의 경우, 설정, OS, 애플리케이션의 구현 버전 등에 맞추기 위해서는 충분한 공간이 필요하다. 예를 들어, 컴파일된 Perl v5.9.0은 90 MB를 차지한다. Apache는 27 MB를 차지한다.

애플리케이션 구조 만들기
각 OS 디렉토리 내에 설치하고자 하는 애플리케이션을 위한 추가 디렉토리를 만들어야 한다. 각 OS 스팩에 맞춰야 하고 실제로 필요한 애플리케이션 디렉토리만 만들어야 한다. 예를 들어, Fedora 머신에는 Perl을 설치해야 하지만 다른 머신에는 안그래도 된다. Listing 3은 레이아웃이다:




이들 각 디렉토리 내부에, 애플리케이션과 구현의 구성 및 설정 방법에 따라 두 가지 구조 중 하나를 만들어야 한다. 여러분이 만드는 각 디렉토리는 활성 애플리케이션을 저장하는데 사용될 것이다. 두 가지 방법으로 구성할 수 있는데 하나는 버전 별 디렉토리 이고 다른 하나는 직접적인 애플리케이션 디렉토리이다:

버전 별 디렉토리: 애플리케이션의 각 버전 별 하나의 추가적인 디렉토리 레벨. 각 버전 별 다른 많은 구현 설정을 만들 곳에 유용하다. 예를 들어, perl-5.9.0/debug-build 또는 perl-5.9.0/std-build>를 만들 수 있다.
직접적인 애플리케이션 디렉토리: 이 디렉토리의 하위 디렉토리는 원래의 tarball에서 만들어진 디렉토리이다. 기존 OS에 대해 모든 머신들에 같은 설정을 사용한다면 작동한다.
애플리케이션의 구현과 전개 방법에 따라, 둘 중 하나 또는 두 가지 모두 사용할 수 있다. 다만 각 경우의 구조를 반드시 이해해야 한다. 전개를 시작하면서 디렉토리 레이아웃을 알아야 하기 때문이다. 예를 들어, Apache의 경우 디버깅을 위한 다른 설정을 저장하기 위해 각 버전 별 세 개 또는 네 개의 디렉토리를 가지고 있었고 개발, 스테이징, 제품 버전 별 설치 디렉토리도 갖추고 있었다. 레이아웃은 다음과 같다. (Listing 4):




devel-build 디렉토리에는 원래 tarball의 압축을 풀 때 만들어지는 것과 같은 디렉토리를 포함하고 있다. 비교해 보면, Listing 5는 표준 구현의 모든 Perl 버전을 갖고 있는 개발 머신 상의 Perl 레이아웃을 보여주고 있다. 보다 간단해진 구조이다.




디렉토리를 알맞은 자리에 배치하면 각 OS와 설정에 맞춰 애플리케이션 구현을 시작할 수 있다. 애플리케이션/설정이 일단 구현되면 OS들에 이들 각각을 전개하는 것은 비교적 쉽고 단순하다.

개별 구현 설정하기
다른 애플리케이션들과 다른 설정들을 지원하는 문제들 중 하나는 설정이 어떤 애플리케이션과 상황을 위한 것인지 잊기 쉽다는 점이다. 예를 들어, Apache를 구현할 때, 대안 개발 디렉토리와 지원하고자 하는 모듈과 컴포넌트 리스트를 지정한다. 처음에는 잘되었다. 하지만 세 달 후에 다시 그 프로세스로 돌아가서 특정한 설정 부분을 기억하는 것은 거의 악몽이었다. 최신 버전으로 새로운 구현을 하기로 결정했다.

이것을 해결하기 위해, 나는 설정에 일반적으로 사용하는 라인을 저장하고 있는 한 줄의 스크립트와 함께 만든 디렉토리 구조를 사용한다. 예를 들어, 내 Apache 스테이징 서버에 다음과 같은 스크립트를 사용한다. (Listing 6)



이 라인을 스크립트에 배치하고, 적절한 이름을 정하고, 이것을 애플리케이션용 디렉토리 안에 둔다. 다시, 버전에 근거하여(설정 시스템이 변경된 애플리케이션의 경우 매우 중요함. 예를 들어, Apache 1.3.x 에서 Apache 2.x으로 변경된 경우) 또는 일반적인 애플리케이션 디렉토리에 근거하여 이를 수행한다. 예를 들어, 이 파일 이름을 apache/staging-config라고 지어 어떤 버전에서라도 사용할 수 있고 Apache 스테이징 서버를 설정하는 구현 디렉토리에서 사용할 수 있음을 설명한다.

기존 OS, 애플리케이션, 설정을 위한 설정과 구현을 실제로 수행하기 위해 다음과 같이 한다:




설치 자동화
컴파일 되고 설치 준비가 된 애플리케이션 버전이 있다면 네트워크 상의 다양한 머신에 이 애플리케이션을 배포 및 설치하는 것은 쉽다. 각 머신에 개별적으로 설정/구현 프로세스를 실행할 필요가 없다. 머신이 같다면 말이다. 실행해야 할 것은 make install 뿐이다.

각 머신에 설치하고자 하는 애플리케이션 리스트를 포함하고 있는 각 OS 시스템 디렉토리에 파일을 만들어서 시스템 레벨 별로 이를 자동화한다. 어떤 경우, 다양한 설치 유형을 관리하기 위해 수 많은 다른 파일들을 만들어야 한다. 다음 파일은 애플리케이션 구현 디렉토리 리스트이다. (Listing 8):




이 파일들은 Listing 9의 스크립트로 처리된다. 보이는 그대로이다. 나열된 각 디렉토리를 실행하고 make install을 실행한다.




머신에 특정 애플리케이션을 설치하려면 이 스크립트를 실행하고 OS 폴더와 애플리케이션 선택 파일들을 제공한다. (Listing 10):




이 스크립트와 신중하게 만들어진 디렉토리 구조가 나머지를 수행한다.

요약
애플리케이션의 설치와 배포에 이 방법을 사용하려면 설정에 시간도 걸리고 익숙해져야 한다. 하지만 일단 실행하면 거의 모든 것이 자동이다. 사실, 어떤 애플리케이션을 설치할 수 있는지에 대한 정보와 함께 파일을 배치하기 때문에, 크론 잡 으로서 스크립트를 실행하여 네트워크 상에서 머신을 자동으로 업데이트 할 때 같은 시스템을 사용할 수 있다. 나는 일주일에 한 번 실행하지만 여러분은 한 달에 한 번만 실행해도 된다. 99%의 인스톨러의 경우 현재 버전과 설치되는 버전이 같다면 문제 없다. 모든 인스톨러가 하는 일은 이전에 사용되던 파일의 동일한 카피를 겹쳐 쓰는 것이다.

무엇보다도, 자동 메소드를 사용한다면 모든 머신을 업데이트하는 것은 애플리케이션 선택 파일을 최신 버전의 구현 디렉토리와 함께 업데이트 하는 경우이다. 최신 설치는 머신이 알아서 정해진 업데이트를 하는 동안 설치한다. 팬시 스크립트나 rsync, ssh, rsh가 필요 없다. 필요에 맞게 애플리케이션을 설정 및 커스터마이징 하고 다른 머신들이 다양한 애플리케이션 세트를 사용할 수 있도록 한다.

시작할 때 말했지만, 여러분이 해야 할 일은 약간의 훈련일 것이다. 디스크 공간이 많으면 더 좋다.


http://www-903.ibm.com/developerworks/k ··· ist.html
"Applicaton" 카테고리의 다른 글
  • 아파치를 기반으로 한 웹 로그 분석 - 2. 접속 로... (0)2007/07/19
  • 아파치를 기반으로 한 웹 로그 분석 - 1. 웹 로그... (0)2007/07/16
  • 애플리케이션의 구현 및 분산 프로세스를 자동화하기 (0)2007/05/04
  • RPM으로 패키지 관리하기 (0)2007/05/04
  • Apache 무단 링크 방지 (0)2007/04/12
2007/05/04 17:24 2007/05/04 17:24
Posted by webdizen
Tags 리눅스, 분산 프로세스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2904

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux2007/05/04 17:22

[처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법

연재순서
1회. 개발자를 위한 리눅스 설치와 기본 명령어
2회. 고급 리눅스 명령어와 중요 시스템 관리
3회. 개발자를 위한 vim 편집기 사용법
4회. 리눅스 환경의 새로운 변화와 적응 <끝>
손정우
2004/11/09

사용자의 입장에서 윈도우와 가장 두드러지게 눈에 띄는 리눅스, 그리고 다른 유닉스 운영체제의 차이점은 무엇일까? 여러 차이점, 특히 관습이나 문화적인 차이점도 많이 있겠지만 vi 에디터의 독특함도 중요하게 작용하고 있다고 본다.

최근 들어서는 유닉스를 처음 배우면서 에디터로 vi를 반드시 배워야 하는 상황은 많이 줄었지만 여전히 vi는 많이 쓰이고 있으며 그 편리함과 독특한 매력은 많은 사용자를 확보하고 있다. 아직까지 vi를 제대로 쓰지 않고 리눅스를 사용하고 있다면 이번 기회에 조금 귀찮더라도 반드시 vi 기본 명령어는 습득해 보도록 하고, 또 추가로 많이 쓰이는 vi 관련 설정을 살펴보기로 하자.

vi를 처음 접하는 사람에게 가장 까다롭게 느껴지는 것은 vi가 소위 명령어 모드(command mode)와 입력 모드(input mode)가 나뉘어져 있다는 특성 때문이다. vi를 처음 실행시키고 타이핑을 해보면 아무런 글자도 입력되지 않고 심지어는 비프음까지 동반하게 되는데 많은 사용자들이 이런 vi의 편집기답지 않은 황당한(?) 모습에 금방 거부감을 갖게 되는 경우가 많다. 하지만 일단 vi의 명렁어 모드와 입력 모드의 차이점을 인식하고 몇 가지 커서 이동 명령어만 학습하고 나면 vi의 매력을 금방 느낄 수 있으니 겉모습만을 보고 오해해서 vi를 버리지 말도록 하자.

vim은 정확히 말하자면 여러 vi 클론들 중의 하나이다. vim의 이름은 vi improved에서 따왔다고 하는데 vim은 기본적인 vi의 기능에 덧붙여서 여러 개의 파일을 동시에 편집한다든가 프로그램 소스코드에 색깔을 덧붙여 가독성을 높여주는 syntax highlighting과 같은 기능을 추가로 지원한다.

pico, nano
우선, vi를 배우는 동안 간단히 쓰기 좋은 에디터로는 pico나 pico의 자유 소프트웨어 클론인 nano를 권장한다(젠투 리눅스는 시스템 설치 과정에서 nano를 사용한다). pico나 nano는 어느 배포판이나 기본으로 설치되어 있는 경우가 많으며 명령어 안내가 간단하게 화면 아래쪽에 출력되기 때문에 불편하지만 쉽게 쓸 수 있다. 오래 전 PC통신 시절의 경험이 있는 사용자라면 이미 pico를 많이 사용해 보았을 수도 있다. pico의 사용 방법은 무척 간단하기 때문에 여기서는 생략하도록 한다.

명령어 모드와 입력 모드
앞에서 잠깐 얘기했듯이 vi는 명령어 모드와 입력 모드가 나뉘어서 동작한다. 예를 들어 명령어 모드에서는 h 키를 누르면 왼쪽 화살표 키와 같이 커서가 왼쪽으로 한 칸 이동하지만 입력 모드에서는 화면에 h 글자가 입력된다.

vi는 실행되었을 때 기본으로 명령어 모드가 작동된다. 따라서 vi를 실행한 뒤 아무리 키를 눌러봐야 글자가 타이핑되지 않는다. 입력 모드로 들어가기 위해 i 키(insert, 삽입)를 눌러 보자. i 키를 누른 이후로는 타이핑이 될 것이다. 참고로 a 키를 누르면 커서 다음 글자부터 입력이 된다. 추가로 대문자 I와 A의 차이점도 직접 확인해 보자.

서너 줄 정도 간단한 글을 입력한 다음 이제 다시 명령어 모드로 돌아가기 위해 esc 키를 눌러보자. esc 키는 vi에서 가장 중요한 키 중의 하나이며 esc를 누르면 vi는 항상 명령어 모드로 돌아간다. 여기서 다시 i나 a 키를 누르면 삽입 모드로 들어간다(한 줄을 비우고 삽입 모드로 들어가는 o와 O도 시험해 보자). 이제 esc 키를 눌러서 명령어 모드로 돌아간 뒤 커서를 상하좌우로 이동해보자.

요즘은 상황이 많이 달라졌지만, vi에서는 화면 상의 커서를 상하좌우로 이동하기 위해 키보드 위의 상하좌우 화살표 키를 원칙적으로 쓰지 않는다. vim에서는 화살표 키를 써도 커서 이동이 가능하지만 일단은 vi에서는 화살표 키를 쓰지 않는 습관을 들여 보자. vi에서는 커서 이동키로 h(좌), j(하), k(상), l(우)을 사용한다. 한 페이지 위로 올라갈 때는 pgup 키 대신 , 한 페이지 아래로 내려갈 때에는 pgdn 키 대신 를 사용한다. <표 1>를 참조해서 커서 이동키를 외우도록 하자.

<표 1> vi의 커서 이동 키


명령어
내용

왼쪽
h

오른쪽
l

위쪽
k

아래쪽
j

한 페이지 위로


한 페이지 아래로


vi에서는 왜 이렇게 낯선 커서 키 배열을 사용할까? 그 이유 중 하나는 vi가 커서 키로 사용하고 있는 h, j, k, l 키가 화살표 키보다 사용하기 훨씬 편리하기 때문이다. 다만 vi의 키 배열은 아무래도 처음 익숙해지고 외우기까지가 불편하다. 하지만 vi의 명령어 키는 모두 기본 자판만을 사용하기 때문에 커서를 움직이거나 페이지를 스크롤 하기 위해 오른손을 들어 화살표 키가 있는 곳까지 손을 뻗는 수고를 덜어준다. vi를 오래 쓰다보면 윈도우에서도 무심코 esc 키를 누르고 j, k 키를 눌러 커서를 무심코 이동시키려는 중독 증세가 나타나 간혹 사람을 당황하게 하는 경우도 생긴다.

일단 h, j, k, l 커서 키와 페이지 업, 페이지 다운 키로 , 에 익숙해진 다음 몇 가지 이동키를 더 익혀보자. 한 단어를 이동하는 키는 w, 한 단어를 뒤로 이동하는 키는 b이다. 시프트 키를 누르고 대문자 W, B를 누르면 띄어쓰기가 된 단어 단위로 커서가 이동한다. w와 비슷한 기능으로 e를 들 수 있는데 e 키는 커서가 단어의 맨 뒷부분에 위치하게 된다.

추가로 워드프로세서에서 Home 키에 해당하는 vi의 키는 0이고 End에 해당하는 키는 $이다. 윈도우 환경에서는 Home이나 End 키를 안 쓰는 사람도 많지만 vi에서는 많이 쓰이니 반드시 익혀두자. 여기에 커서 이동키로서의 (, ) (문장 단위 이동), {,} (문단 단위 이동), H, M, L(화면 첫줄, 가운뎃줄, 마지막줄) 키, 1G (첫줄), G(마지막줄)의 쓰임새까지 학습하고 나면 vi로 가장 기본적인 문서 편집을 하는 데는 문제가 없게 된다.

<표 2> vi의 추가 커서 이동키


명령어
내용

줄의 가장 처음(Home)
0

줄의 가장 마지막(End)
$

한 단어 오른쪽으로
w, W, e, E

한 단어 왼쪽으로
b, B

한 문장 오른쪽으로
(

한 문장 왼쪽으로
)

한 문단 오른쪽으로
}

한 문단 왼쪽으로
{

화면의 첫 줄, 가운데 줄, 마지막 줄
H, M, L

편집중인 파일의 맨 처음으로
1G

편집중인 파일의 맨 마지막으로
G



여기서 다른 에디터에서는 보기 힘든 vi의 또 다른 중요한 문법을 배워보자. w 키를 사용해 한번에 두 단어, 세 단어를 뛰어 넘어 커서를 움직이려면 어떻게 해야 할까? 방법은 그만큼의 숫자를 w 키를 타이핑하기 전에 먼저 입력해 주면 된다. 즉, 두 단어를 건너뛰고 싶으면 2w, 세 단어는 3w와 같이 타이핑을 해주면 되는 것이다. 같은 방법으로 위로 다섯줄을 올라가고 싶으면 5k, 아래로 열 줄을 내려가고 싶으면 10j와 같은 키 조합을 만들어 볼 수 있다.

vi를 종료하기 위해서는 ‘:’ 키를 눌러서 vi의 전신인 ex의 명령어 모드로 들어가야 한다. : 키를 누르면 화면 맨 아래쪽에 : 프롬프트가 뜨는 것을 볼 수 있는데 여기서 검색이라든가 새 파일 열기와 같은 좀 더 복잡한 명령을 입력할 수 있다. 종료 명령인 q를 누르고 엔터 키를 치면 vi가 종료되는데 지금은 편집 중인 파일이 있는 관계로 vi가 아직 파일이 저장되어 있지 않다는 에러 메시지를 보여준다. 강제로 종료하기 위해서는 :q! 명령을 입력하면 된다. 저장을 하려면 :w <파일이름> 명령을 실행한 뒤 q 명령으로 vi를 종료하자.

보통은 vi로 파일을 편집할 때는 $ vi 과 같이 셸에서 vi를 실행시킬 때 파일 이름을 명시해주고 종료할 때는 :wq 명령어로 저장을 하고 vi를 종료하는 방법을 쓰는 것이 편리하다.

vi에서의 텍스트 편집
삭제
이제 글자 삭제를 해볼 차례이다. 물론 vi의 입력 모드에서는 백스페이스 키가 정상적으로 작동하지만 명령어 모드에서는 백스페이스가 아닌 x 키를 사용한다. 그런데 실제 문서 편집 작업을 하다보면 글자 단위의 삭제보다는 단어나 한 줄 단위, 혹은 커서가 위치한 부분부터 줄 끝까지 삭제를 하는 경우가 많다. 이런 경우에 쓰는 vi 명령키가 d이다. d 다음에는 지울 범위를 설정해 주어야 하는데 다음과 같은 키 조합을 만들어 볼 수 있다.


◆ dw : 커서가 있는 위치부터 오른쪽으로 한 단어 삭제
◆ db : 커서가 있는 위치부터 왼쪽으로 한 단어 삭제
◆ d$ : 커서가 있는 위치부터 문장 끝까지 삭제
◆ d} : 커서가 있는 위치부터 한 문단 끝가지 삭제


앞의 경우와 마찬가지로 vi의 명령어 키 조합을 응용해 보자.


◆ d3w : 커서가 있는 위치부터 오른쪽으로 세 단어 삭제
◆ d5j : 커서가 있는 위치부터 아래로 다섯 줄 삭제


참고로 dd 명령키는 한 줄을 지울 때 사용한다. d$는 D로 간편하게 줄여 쓸 수 있다. 또한 d(delete) 대신에 c (change) 명령키를 사용하면 vi는 삭제 대신에 치환 기능을 수행한다. 치환 기능은 vi가 같은 범위의 d 명령을 수행한 다음 i 키를 알아서 눌러 입력 모드로의 전환까지 자동으로 해준다고 생각하면 된다. 텍스트 편집은 결국 어느 부분을 삭제하고 나면 그 부분을 다른 단어들로 수정하기 마련이며 vi에서는 다른 비주얼한 에디터와는 달리 삭제와 수정까지 치환 기능을 통해 한번에 간편히 해결해 주고 있다. 실제, 필자의 경우는 vi를 쓰면서 d 키보다 c 키를 더 많이 쓰는 편이다. 비슷한 기능키로 s, S와 r도 간혹 사용된다.

자르고 붙이기
윈도우나 기타 GUI 데스크탑 환경에서 자르고 붙이기(cut & paste) 기능은 이해하기도 쉽고 실제 많이 쓰는 기능이다. vi에서는 자르기(cut) 기능은 d 키로 삭제했을 때 삭제된 부분이 자동으로 기억되며, 이것을 소문자 p나 대문자 P 키를 눌러서 붙일(paste) 수 있다. 일례로 문단 하나를 cut & paste하는 경우를 생각해보자. 윈도우에서는 마우스로 해당 부분을 선택하고 풀다운 메뉴에서 「Edit | Cut」 메뉴를 선택하고 커서를 적절한 위치에 놓은 다음 풀다운 메뉴에서 「Edit | Paste」 메뉴를 선택 이런 방식으로 옮긴다.

이것을 vi에서는 d 명령으로 해당 부분을 지우고 커서를 적절한 위치에 놓은 다음 p 명령키로 붙여넣기의 방식을 따른다. 그렇다면 undo 기능은 어떻게 사용할 수 있을까? vi에서 undo 키는 단순히 u 키를 누르면 되며 vim의 경우 redo 기능이 로 추가되어 있다.

복사하고 붙이기
vi에서 복사하고 붙이기(copy & paste)에서 복사 기능을 수행하는 vi의 명령키는 y(yank)이다. d 대신에 y 키를 누르고 복사하고 싶은 부분만큼 선택한 다음 p 키를 눌러서 붙여넣기 작업을 진행하면 된다. y 키 역시 마찬가지로 yw, y$, y}, y10j와 같은 다양한 응용이 가능할 것이다. yy 명령키는 dd와 비슷한 역할을 한다. yy 명령키는 p 명령키와 같이 조합해서 특히 많이 쓰이는 편이다.

두 줄을 한 줄로 합치기
두 줄을 한 줄로 합치기 위해서는 대문자 J 명령키를 사용한다. 반대로, 한 줄을 두 줄로 나눌 때는 r 키와 엔터 키의 조합을 쓰면 편리하다. r 명령키는 커서가 위치한 곳의 문자 하나를 치환(replace) 하고 명령어 모드로 복귀한다. 여기서 잠깐 유닉스의 파이프를 이용한 팁 하나를 알아보자. vi에서는 이렇게 J 키로 두 줄을 합치다 보면 문단의 오른쪽이 들쑥날쑥해지는 것이 그다지 보기 좋지 않은 경우가 많다.

이럴 때 유닉스의 파이프(pipe) 기능을 이용해서 문단 정렬을 해볼 수 있다. 유닉스에는 기본으로 fmt 명령이 제공된다. fmt는 문단을 72컬럼 근처에서 다듬어주는 역할을 한다. 보통 다음과 같은 방식으로 쓰면 document.txt의 문단을 예쁘게 정렬해서 출력해 주는 아주 간단한 유틸리티이다.





이것을 vi에서 사용해 보자. vi에서는 ‘!’ 키를 누르면 필터(filter)가 동작한다. ! 키를 누르고 처리할 텍스트 범위를 선택한 다음 외부 명령어 이름을 적어준 뒤 엔터를 누르면 그 외부 명령어가 처리한 텍스트 결과물이 다시 vi 편집 화면에 돌아오는 것이다. 이제 한 문단 정렬을 외부 명령어 fmt를 이용해 보자. 방법은 다음과 같다.


[1] 줄이 들쑥날쑥한 문단에 커서를 위치시킨다.
[2] ! 키를 누른다.
[3] } 키를 눌러 커서가 위치한 문단 끝까지 선택한다.
[4] 외부 명령어인 fmt를 타이핑한 뒤 엔터 키를 친다.


이렇게 하면 문단이 fmt를 통해 예쁘게 정렬된다. ! 키를 누른 이후에 선택한 부분이 fmt에 들어가서 포맷팅이 되고, 그 결과가 다시 vi 편집 화면으로 돌아온 것이다. 조금 복잡해 보이지만 vi가 간단한 기능으로도 강력한 성능을 발휘할 수 있는 특징 중의 하나이므로 한번쯤 연습해 보도록 하자. 필자의 경우에는 } 대신 아예 파일 끝부분을 가리키는 G를 이용해서 편집중인 파일 전체를 fmt를 통해 문단 정렬을 자주 하는 편이다.

파일 저장 및 vi 끝내기
파일 열기, 저장, 그리고 vi 끝내기는 : 키를 눌러서 명령어 입력 모드로 전환한 다음 이루어진다. :w 명령어로 편집 중인 파일을 저장할 수 있다. :q 명령은 vi 종료 명령이다. 안전하게 파일을 저장하고 vi를 빠져 나오려면 :wq 명령을 이용한다.

vim의 추가기능
이제 vi의 확장판 클론인 vim의 유용한 기능들을 살펴보기로 하자. vim의 여러 가지 확장된 기능 중에서 우리가 중점적으로 살펴볼 부분은 다음과 같다.


◆ syntax highlighting
◆ 들여쓰기
◆ 다중 윈도우 사용


우선 vim 사용에 편리한 설정 몇 가지를 먼저 살펴보자. vim의 설정은 /etc/vimrc나 ~/.vimrc에 저장해 놓으면 된다. vimrc의 명령어는 vim의 : 명령 프롬프트에서 직접 입력해서 실행하는 것과 동일하다.

vim 사용시 한글 관련 옵션
vim에서 한글을 쓸 때 편집 도중에 한글이 깨지지 않도록 하기 위해서는 vimrc에 다음 옵션을 추가한다.


set fileencoding=korea
set encoding=korea


set fileencoding 명령은 vim이 편집 중인 파일을 저장할 때 어떤 인코딩을 사용할 것인가를 결정해 주고 set encoding은 vim 내부에서 어떤 문자 인코딩을 사용할 것인지를 결정해 준다. 이 옵션으로도 vim에서 한글 단어 단위의 커서 이동이나 한글 문자 삭제시 글자가 깨진다면 bash 셸에서 언어 환경을 한국어로 명시해 주자.





참고로 최근 들어 많은 배포판들이(특히 GNOME 데스크탑 환경에서) 한국어 환경의 한글 코드를 완성형에서 유니코드(utf-8)로 이동했다. 이런 경우는 굳이 앞의 encoding과 fileencoding 설정을 사용할 필요가 없으며 경우에 따라서 utf-8이나 korea를 상황에 맞게 설정해 주면 된다.

[로케일 설정]

로케일(locale)은 리눅스 시스템의 언어 설정을 가리킨다. 요즘은 윈도우도 윈도우 2000, XP부터 하나의 윈도우에서 여러 나라의 언어를 모두 지원하는 추세가 확립되고 있지만 아직까지도 윈도우는 한글 윈도우와 영문 윈도우가 구분되어 출시되고 있다. 이에 반해 비영어권 사용자의 숫자가 초기부터 많았던 리눅스나 유닉스 계열 운영체제에서는 처음부터 일관된 방법으로 하나의 애플리케이션이 여러 나라의 언어를 쉽게 지원할 수 있는 프레임워크를 만드는 데 신경을 많이 써 왔다. 이러한 작업을 국제화(internationalization, i18n)라고 부른다. 참고로 국제화의 줄임말로 i18n을 쓰는 이유는 국제화의 i와 n사이에 18개의 글자가 있기 때문이라고 한다.

따라서 기본적으로 리눅스 시스템이나 배포판을 한글화한다는 얘기는 일단 리눅스 시스템의 언어 설정을 한국어로 해놓고 필요한 한글 메시지 번역이라든가 폰트 추가 및 설정과 같은 작업을 추가하는 것이 된다. 리눅스 시스템의 언어는 LANG 환경 변수를 통해 설정한다. 로케일 설정에는 LANG 변수 이외에도 여러 가지 다른 설정이 있지만 사용자 입장에서는 LANG 변수만 바꾸어 주면 전체 시스템의 언어가 바뀐다. 한국어의 경우 다음과 같은 LANG 셋팅을 많이 쓴다.





여기서 ko_KR.eucKR의 의미를 짚어보고 넘어가자. 맨 앞의 ko는 언어를 가리킨다. 한국어 이외의 언어를 몇 가지 예를 들어보면 en은 영어, de는 독일어, jp는 일본어를 의미한다. 그 다음 KR은 지역을 나타낸다. 비슷하게 KR은 한국, JP는 일본, US는 미국, CA는 캐나다, DE는 독일과 같은 예를 들어볼 수 있다.

그렇다면 LANG 환경변수 설정에서 왜 ko_KR과 같이 나라와 지역을 같이 쓸까? 그 이유는 같은 나라에서도 여러 가지 언어가 쓰일 수 있으며, 같은 언어라도 여러 나라에서 쓰이는 경우가 있기 때문이다. 일례로 미국에서의 영어는 en_US, 캐나다에서의 영어는 en_CA로 표기한다. 캐나다의 경우 fr_CA의 불어도 쓸 수 있을 것이다. 따라서 우리나라의 경우 ko_KR은 대한민국에서 쓰는 한국어가 된다. 북한은 이 원칙을 따른다면 아마도 ko_KP로 쓸 수 있을 것이다.

그리고 맨 마지막으로 eucKR은 인코딩 방법을 의미한다. eucKR은 현재 우리가 쓰고 있는 완성형 코드이다. 최근 들어 리눅스 배포판은 유니코드로 넘어가고 있는 추세인데 유니코드를 지원하는 리눅스 시스템에서는 내부적으로 이제 한글을 쓸 때에도 eucKR을 쓰지 않고 유니코드를 사용하고 있다. 유니코드로 한국어를 쓰려면 다음과 같이 로케일 설정이 바뀔 것이다.





참고로, 이런 로케일 설정은 요즘은 gdm이나 kdm과 같은 그래픽 화면 로그인 스크린에서 설정하는 것이 가장 편리하다. GNOME이나 KDE 로그인 스크린에서 언어를 선택하는 옵션이 그것이다.

마지막으로 지역화(localization, l10n)에 대해서도 잠깐 알아두자. 지역화의 줄임말인 l10n도 i18n과 같은 명명법을 따른 표기이다. 지역화는 국제화와는 달리 어떠한 프로그램이나 운영체제를 아예 특정 언어 전용으로 만드는 것을 의미한다. 리눅스 환경에서는 한텀과 같은 애플리케이션이 대표적인 예가 될 것이며, 윈도우의 경우는 한글 윈도우 98이 좋은 예가 된다. 지역화는 언어마다 각기 다른 애플리케이션이 만들어져야 하기 때문에 국제화보다 범용성이 떨어지는 편이며, 장기적인 개발비용을 줄이는 관점에서 국제화가 많이 권장되고 있다.


syntax highlighting
vim에서는 vi와는 달리 프로그램 소스 파일을 편집할 때는 여러 가지 색깔을 사용해서 소스코드 가독성을 높여주는 syntax highlighting 기능을 지원한다. syntax hightlighting 기능을 켜는 명령은 다음과 같다. 이 명령 역시 vimrc에 추가해 놓으면 편리하다.


:syntax on


참고로 언어에 따른 syntax hightlighting의 구체적인 설정은 /usr/share/vim 아래쪽의 설정 파일에서 다룬다. vim의 syntax highlighting 기능의 특징 중 하나는 화면 배경색의 밝기에 따라 syntax highlighting용 색상 구성을 다르게 쓸 수 있다는 점이다. 자신이 쓰고 있는 터미널의 배경색에 따라 다음 두 가지 중의 하나를 선택해 보자.


:set background=light 또는 :set background=dark


필자의 경우 흰색 배경의 한텀을 주로 쓰는 까닭에 light 옵션을 주로 쓰고 있다.

들여쓰기 설정
소스코드에서 탭 크기를 얼마로 잡을 것인지의 문제는 종종 게시판에서 플레임까지도 잘 번지는 주제이다. 소스코드의 탭 크기를 공백 8개로 할 것인가 아니면 탭 문자 하나로 둘 것인가, 만약 공백으로 처리한다면 4개로 할 것인가 8개로 할 것인가부터 시작해서 실로 다양한 스타일이 존재한다. 어느 경우든, 사용자의 입장에서는 탭 키를 한번 눌렀을 때 사용자가 원하는 쪽으로 들여쓰기가 알아서 된다면 무척 편리할 것이다.

우선, 자동으로 들여쓰기를 할 것인지 아닐 것인지를 설정해보자. 프로그램 소스코드를 짤 때는 블럭이 바뀌면 줄이 바뀔 때 마다 탭 키를 눌러 들여쓰기를 해야 하기 때문에 이 옵션을 켜 놓고 코딩을 하는 경우가 많다. 이 기능은 다른 vi에서도 작동한다.


:set autoindent 또는 :set ai


이 옵션을 해제하려면 다음과 같은 명령을 사용한다.


:set noautoindent 또는 :set noai


파일을 편집 중에 탭 키를 눌었을 때, 이것을 탭 문자 하나로 인식할 것인지, 혹은 디폴트 8개의 공백 문자로 인식할 것인지 설정하기 위해서는 expandtab 옵션을 설정한다.


:set expandtab 또는 :set et


이 때, vim에서 탭 문자 하나를 강제로 입력하려면 , tab의 조합을 누르면 된다. 는 컨트롤 문자를 vi에서 입력할 때 쓰는 명령키이다. expandtab 옵션을 해제하려면 앞의 경우와 마찬가지로 set noexpandtab 명령을 쓰면 될 것이다.

여기서(필자의 의견으로는) expandtab 옵션은 항상 켜놓고 탭 문자 하나를 여러 개의 공백 문자로 치환해서 사용하는 것이 좋다고 생각한다. 이렇게 작성한 소스코드는 어느 에디터에서 열어보더라도 똑같은 모습으로 들여쓰기가 잘 되어 있는 상태가 유지될 것이기 때문이다. 다만, 탭 문자의 들여쓰기를 공백 8개, 4개, 혹은 2개로 할 것인지에 대해서는 사람마다 취향이 다르고 프로젝트마다 스타일이 다를 수 있으며 그때 그때 상황에 맞추는 것이 좋다고 생각한다. tabstop 옵션은 vim에서 탭 문자 하나가 몇 개의 공백으로 보일 것인지를 설정할 때 사용한다.


:set tabstop=4 디폴트 값은 8이다. 약자로 set ts=4도 사용한다.


앞의 expandtab 옵션과 연계해서 생각해 보면, tabstop이 4로 설정되어 있고 noexpandtab 옵션이 설정되어 있다면 vim에서 탭 키는 탭 키를 누를 때마다 공백 4개의 폭으로 커서가 이동하고 내부적으로 탭 키는 누를 때마다 탭 문자 하나씩으로 인식하게 된다. 만약, 이렇게 편집한 파일을 다른 tabstop이 디폴트 8로 설정되어 있는 다른 vi나 기타 에디터에서 열어본다면 에디터가 탭 문자 하나를 8개의 공백 크기로 인식하는 바람에 소스코드가 완전히 다른 모습으로 보인다는 점을 고려해야 한다.

탭과 관련된 옵션으로 shiftwidth 옵션도 살펴볼 필요가 있다. shiftwidth 옵션은 들여쓰기를 할 때마다(탭 키를 누를 때마다가 아니라는 점에 유의하자) 몇 개의 공백 크기를 쓸 것인가를 설정해 준다. cindent 옵션과 같이 많이 쓰인다. tabstop과 gpt갈릴 수 있으므로 항상 tabstop 값과 같이 놓고 쓰면 편리하다.


:set shiftwidth=4 디폴트 값은 8이다. 약자로는 set sw=4를 쓴다.


C 소스코드를 작성할 때는 C 문법 스타일을 맞추어 주는 cindent 옵션을 켜놓으면 편리하다.


:set cindent


탭에 관해서 얘기가 조금 복잡해진 감도 있지만 간단히 정리를 해 보면 이렇게 생각해 볼 수 있다. 우선, 탭 문자를 여러 개의 공백 문자로 치환할 것인지 혹은 탭 문자를 있는 그대로 하나로 쓸 것인지 결정하는 것이 중요하다. 그리고 일단 여러 개의 공백 문자를 쓰기로 결정했다면 tabstop 값과 shiftwidth 값을 일치시켜서 혼동을 없애는 편이 좋으며, 반대로 탭 문자를 그대로 사용하겠다면 다른 에디터와의 호환 여부를 고려할 필요가 있다. 필자가 쓰는 탭 관련 설정은 다음과 같다.


모든 탭을 공백 4개로 설정한다.
set tabstop=4
set shiftwidth=4
set expandtab

set autoindent


다중 윈도우 사용
원래 vi는 다중 윈도우를 연다든가, 여러 개의 파일을 동시에 편집하는 기능이 들어 있지 않았다. emacs가 에디터 내에서 에디터 이상의 기능을 수행할 수 있는 통합 환경이라면 vi는 심플한 유닉스의 초기 설계 철학을 닮은 에디터라고 볼 수 있다. 하지만 vim의 추가 기능 중 다중 윈도우나 복수 개의 파일 편집 기능은 상당히 편리하며 널리 쓰이고 있다.

다중 윈도우 기능은 요즘 아래아한글이나 MS 워드에서도 손쉽게 볼 수 있다. 마우스로 오른쪽 스크롤바 위쪽의 핸들을 아래로 주욱 잡아당기면 파일 하나에 화면이 2개 생기는 것을 볼 수 있다. vim에서는 화면을 나누기 위해서는 split 명령을 사용한다. vsplit 명령을 쓰면 화면을 세로로 나눌 수도 있다.


:split 줄여서 sp로 쓴다


이 상태에서 , w 키를 차례로 누르면 커서가 다른 화면으로 이동하게 된다. 다음에 j 키를 누르면 아래 화면으로 이동하게 되고, 다음에 k 키를 누르면 윗 화면으로 이동하게 된다(h, j, k, l 모두 사용 가능하다). 여기서 화면 하나를 닫기 위해서는 , c 명령키나 close 명령을 사용한다.


:close 줄이면 cl로 쓴다


여러 개의 파일을 동시에 편집
이제 화면을 둘로 나누면서 새로운 파일을 편집해 보자.


:new


new 명령을 내리면 같은 파일을 두 개의 윈도우에서 같이 편집하던 아까의 split 명령과는 달리, new 명령으로 열린 윈도우는 완전히 새로운 파일을 편집하게 됨을 볼 수 있다. :new 과 같이 파일명을 명시해 줄 수도 있다. 이제 vim에서 화면을 윈도우로 나누지 말고 복수 개의 파일을 편집하는 방법을 알아보자. vim에서는 여러 개의 파일을 편집하기 위해 다음과 같이 vim을 실행하는 것이 가능하다.





파일을 전환하기 위해서는 다음 명령을 사용한다. 다음과 같이 간단히 정리해 보았다.


이동 : next (:n) - previous (:prev)
저장 후 이동 : wnext (:wn) - wprevious (:wp)


현재 커서가 위치하고 있는 파일 정보와 다른 파일들의 리스트를 보기 위해서는 :args 명령을 사용한다. 현재 편집 중인 모든 파일을 저장하는 :wall 명령어도 자주 사용된다.

[bash 셸에서 vi 키 사용하기]

대부분 리눅스 배포판에서 기본 셸로 쓰고 있는 bash는 기본적으로 emacs 스타일의 편집키를 지원한다. 하지만 bash에서는 vi 편집 모드도 지원한다.

아마도 대부분의 리눅스 사용자들은 셸을 쓰면서 조금 전에 입력한 명령어로 다시 돌아가기 위해 직관적으로 위쪽 화살표 키를 누르는 습관이 있을 것이다. 이렇게 셸에서 명령어 history 기능을 이용하는데 오른쪽으로 손을 뻗어야 하는 불편이 있는 위쪽 화살표 키 말고 다른 키를 쓸 수도 있다. 이것이 bash에서는 emacs 스타일의 편집키를 쓸 수도 있고 vi 스타일의 편집키를 쓸 수도 있도록 되어 있다. bash 역시 GNU 프로젝트의 영향이 큰 까닭에 아무래도 디폴트는 emacs 스타일로 맞추어져 있다. emacs에서 위쪽 화살표에 해당하는 키는 이다. 개인적인 편차가 있겠지만 필자의 경우는 위쪽 화살표 키에 손을 뻗기보다는 그냥 를 누르는 편이 훨씬 편리하고 손목에 부담을 적게 받는다. 이제 vi 모드로 bash를 써 보자. vi 모드를 켜는 명령은 다음과 같다. .bash_profile에 넣어두고 써도 된다.


$ set -o vi


혹은 /etc/inputrc나 .inputrc에 다음 설정을 사용해도 된다.


set editing-mode vi
set keymap vi


이후로는 셸 프롬프트에서 esc 키를 누르면 vi의 명령어 모드로 진입한 것처럼 vi 편집키가 작동한다. 이제 위쪽 화살표 대신에 k 키를 쓸 수 있게 된 것이다. 추가로 자주 쓰는 cw와 같은 명령어를 사용해 보자.

굳이 vi 모드로 bash를 쓰지 않더라도, bash의 기본 설정인 emacs 스타일의 편집키는 항상 사용하는 습관을 들여 보자. emacs에서 커서 상하좌우 이동키는 각각 , , , 이다. 여기에 Home에 해당하는 와 end의 까지라도 알아두자. emacs 편집키 역시 타이핑을 할 때 손목의 피로도를 줄일 수 있을 뿐만 아니라 다른 프로그램, 특히 GNU 계열의 응용 프로그램에서 디폴트로 많이 쓰인다.

참고로 이렇게 하나의 유틸리티에서 쓰는 명령어를 다른 곳에서도 그대로 지원해서 좀 더 사용자가 새로운 프로그램을 쉽게 쓸 수 있게 해주는 방식이 유닉스에서는 특히 많이 보이는 편이다. 간단한 예로, 페이지 단위로 텍스트 파일을 출력시켜 주는 more 프로그램에서는 스페이스바를 누르면 다음 페이지로 넘어가게 되는데 유닉스 환경에서는 스페이스바를 누르면 다음 페이지로 넘어가는 프로그램들이 한둘이 아니다. 심지어는 윈도우의 대표 브라우저인 인터넷 익스플로러도 스페이스바를 누르면 다음 페이지로 넘어간다. 인터넷 익스플로러가 오래된 유닉스의 모자이크(Mosaic) 브라우저의 소스코드를 기반으로 하고 있기 때문이다. 모질라 브라우저 역시 스페이스바를 누르면 다음 페이지로 넘어간다.

이런 이유로 유닉스를 쓰면서 vi와 emacs의 편집키는 적어도 둘 중 하나는 외워두는 것이 상식이다. 이들 편집키는 vi나 emacs를 벗어난 환경에서도 많이 쓰이며, 실제 사용해보면 무척 편리하다.

자신에게 맞는 편집기를 선택하자
이번 호에서는 리눅스의 대표적인 편집기인 vim에 대해서 알아보았다. 실제, 여러 설문조사 결과를 보면 리눅스 환경에서는 emacs 사용자와 vi 사용자가 대략 50대 50의 비율을 나타내고 있다. emacs를 쓸 것인지 vi를 쓸 것인지는 사용자의 선택이다. emacs는 편집기 이상의 기능을 발휘하는 통합 환경인 반면 vi는 작으면서도 기능이 강력한 편집기로서의 특성을 보인다. 필자 개인적으로는 vi를 주로 사용하고 emacs는 잘 쓰지 않는 까닭에 이번 기사에서는 emacs를 다루지 않았지만, vi 대신에 emacs를 사용하는 것도 좋은 선택임을 강조해 보고 싶다.

다음 호에서는 오픈소스 개발에서 많이 쓰이는 diff, patch부터 시작해서 cvs의 사용법을 리뷰하고 최근의 오픈소스 환경에서 중요하게 부상하고 있는 경향을 몇 가지 추가해 보기로 하겠다. @


http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Unix & Linux" 카테고리의 다른 글
  • 리눅스 명령어 (6)2007/07/11
  • 왜 리눅스 커널을 알아야 하는가? (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법 (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ② 고급 명령과 시... (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ① 설치와 기본 명... (0)2007/05/04
2007/05/04 17:22 2007/05/04 17:22
Posted by webdizen
Tags vim, 리눅스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2903

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux2007/05/04 17:19

[처음부터 다시 배우는 리눅스] ② 고급 명령과 시스템 관리

연재순서
1회. 개발자를 위한 리눅스 설치와 기본 명령어
2회. 고급 리눅스 명령어와 중요 시스템 관리
3회. 개발자를 위한 vim 편집기 사용법
4회. 리눅스 환경의 새로운 변화와 적응 <끝>
손정우
2004/11/08

지난 호에 이어 이번 호에서는 리눅스를 설치 한 다음 숙지해야 할 리눅스 시스템의 특성을 살펴보기로 한다. 필자 개인적인 경험으로는 리눅스 시스템의 여러 토픽들 중에서 리눅스를 많이 써 보지 않은 사람들이 조금 당혹할 수 있는 부분이 리눅스의 보안 시스템이라고 생각한다. 실제, 윈도우는 최근 윈도우 2000, XP에 와서야 멀티유저의 개념이 제대로 정착되어 나가고 있는 셈이지만 리눅스는 애초부터 멀티유저의 개념에서 설계가 되었고 또 그렇게 운영되는 시스템이다.

멀티유저 운영체제에서는 자연스럽게 사용자간의 파일 접근 권한이라든가 보안 문제가 부상하게 된다. 대부분의 리눅스 사용자들이 처음에는 윈도우를 쓰는 경우가 많은 까닭에 멀티유저 시스템은 낯설게 느껴질 수밖에 없으며 웹 서버 프로그래밍이 많은 요즈음의 특성상 보안의 기초가 되는 리눅스의 파일 접근 권한(permission)과 소유권(ownership)에 대한 지식은 단단히 다져놓고 넘어가도록 하자.

파일 접근 권한과 소유권
유닉스의 심플한 설계 철학은 파일 소유권에도 반영되어 있는데 우선 유닉스의 파일은 읽기(read), 쓰기(write), 실행하기(execute) 세 가지의 기능을 수행할 수 있다. 여기서 잠깐 주의할 것은 cd 명령으로 디렉토리를 바꾸어 들어갈 수 있는 권한은 실행하기에 해당한다는 것이다.

유닉스 쪽의 파일 소유권은 우선 사용자(user), 그룹(group), 그리고 그 외의 사람들(others)의 관점으로 나누어진다. 즉, 파일 하나에 대해서 사용자, 그룹, 그리고 그 외의 사람들은 각기 다른 접근 권한을 가질 수 있도록 설계되어 있다. 다음을 보자.


r w x r w x r w x
4 2 1 4 2 1 4 2 1 (2^2, 2^1, 2^0)
O O O | O O O | O O O
user group others


유닉스의 파일 소유권은 이와 같이 간단히 도식화 해 볼 수 있다. 사용자, 그룹, 그 외의 사람들마다 3비트로 각각 read, write, execute의 스위치를 on/off 할 수 있다. 3비트는 숫자로 표현해보면 0에서 7까지의 숫자로 나타낼 수 있다. 예를 들어, 어떤 파일의 소유권자에게 읽기와 쓰기 권한을 주고 싶으면 r 스위치와 w 스위치를 올리면 되니 이것을 숫자로 표현해보면 4+2+0=6이 된다. 마찬가지로 읽기와 실행 권한만 주고 싶다면 r 스위치와 x 스위치를 올려서 4+0+1=5가 된다. 이런 방식으로 유닉스의 chmod 명령에서는 숫자를 이용, 파일의 퍼미션을 설정할 수 있다. 다음 예를 보면서 좀 더 자세히 살펴보자.



좀 더 퍼미션의 숫자 표현에 친숙해 지기 위해 한번 8가지 퍼미션의 경우를 다 적어보면 다음과 같다. 조금 귀찮아 보여도 한번 손가락으로 직접 꼽아가며 확인해 보자.


--- 0 --x 1
-w- 2 -wx 3
r-- 4 r-x 5
rw- 6 rwx 7


파일 소유권이 설정되는 과정
이제 유닉스에서 파일 소유권이 어떻게 결정되는지 알아보자. 유닉스에서 파일 소유권은 원칙적으로 그 파일을 ‘생성한’ 유저가 갖게 된다. 즉, mewmew라는 유저가 foobar.txt라는 파일을 하나 만들었다고 가정하자. 유닉스 시스템은 디폴트로 새로이 생성되는 foobar.txt의 소유자를 파일을 생성한 mewmew로 설정하게 된다.

그 다음으로 유닉스 시스템은 foobar.txt의 그룹 소유권을 결정하게 되는데 이를 위해 mewmew라는 유저가 어떤 그룹에 속해 있는지를 /etc/passwd의 패스워드 엔트리를 보고 결정하게 된다. /etc/passwd에 mewmew 유저는 다음과 같이 등록되어 있다.





혹 /etc/passwd의 문법을 잘 모르는 사람은 박스 기사를 참고해서 passwd의 매뉴얼 페이지를 잠깐 읽어보자. 여기서 mewmew은 디폴트로 1012의 uid를 가지고 1012의 gid를 가짐을 알 수 있다. 그렇다면 이제 /etc/group를 잠깐 보자.





따라서 mewmew이 생성한 foobar.txt의 그룹 소유권자의 디폴트는 1012의 gid를 갖는 mewmew 그룹이 된다. 그렇다면 이 foobar.txt의 기본 읽기/쓰기/실행 퍼미션은 어떻게 정해질까? 유닉스에서는 umask를 이용해서 생성되는 파일의 기본 퍼미션이 정해진다. umask는 masking, 즉 빼기 방식으로 파일 퍼미션을 정하는데 보통 022를 umask 값으로 많이 쓴다. /etc/profile의 쉘 설정을 보면 보통 다음과 같은 명령어를 볼 수 있다.





이 명령이 실행되면 일반 파일은 666(rw-rw-rw)의 퍼미션에서 umask의 022를 뺀 644(rw-r--r--)의 디폴트 퍼미션을 갖게 되고 디렉토리의 경우는 777(rwxrwxrwx)의 퍼미션에서 umask의 022를 뺀 755(rwxr-xr-x)를 갖게 된다.

setuid, setgid, sticky bit
setuid는 유닉스에서 볼 수 있는 특이한 파일 보안 개념이다. 일반적으로 실행 가능한 유닉스 파일을 실행할 때 유닉스에서는 그 명령을 내린 유저의 권한에 따라 실행하게 된다. 예를 들어 mewmew 유저가 다음과 같은 퍼미션의 /bin/ls 명령을 실행시키면 mewmew 유저는 root 유저도 아니고, wheel 그룹에 속해 있지도 않지만 others 퍼미션에 명시된 r-x 권한 중에서 x 권한이 허가되어 있는 까닭에 유닉스 시스템은 mewmew 유저의 /bin/ls 명령 실행을 허가해 주게 된다.





그런데 setuid 옵션이 켜진 파일은 실행될 때, 예를 들어 앞의 경우 /bin/ls 명령에 setuid가 켜져 있다면 mewmew 유저가 명령을 실행했더라도 유닉스 시스템은 /bin/ls 파일의 소유자인 root 유저가 명령을 실행한 것으로 간주해서 /bin/ls 명령을 실행한다. 즉, /bin/ls 명령이 mewmew가 아닌 root 소유로 실행되는 것이다. setuid를 켜는 방법은 다음과 같다. 두 명령 모두 동일한 명령이다. 아래 명령의 경우는 rwxr-xr-x 퍼미션이 755이고 앞의 4가 setuid 옵션이다.





두 번째 명령의 경우 숫자 표현이 조금 독특한데 한번 섹션 2의 chmod의 매뉴얼 페이지의 일부분을 인용해 보겠다. 다음의 매뉴얼 페이지 인용을 보면 유닉스의 파일에는 user, group, others이외에 디폴트로 생략되어 있는 3 비트짜리 setuid, setgid, sticky bit 옵션이 있으며, 차례대로 4, 2, 1의 숫자로 나타낼 수 있음을 알 수 있다.





이런 방식은 어디에서 응용하면 좋을까? 간단한 예가 게임에서 하이 스코어 리스트를 관리하는 경우를 들어볼 수 있다. 하이 스코어 리스트는 일반 유저들은 마음대로 고칠 수 없도록 해야 하지만 막상 서로 다른 유저가 하나의 하이 스코어 리스트 파일을 공유하는 것은 쉽지 않은 일이다. foobar, doobar 유저가 fungame을 하는 상황을 생각해보자. fungame의 파일 퍼미션은 다음과 같다. fungame 유저는 fungame 실행파일을 위해 새로 만들어진 유저이다. 그리고 fungame_high_score_list를 다음과 같이 구성했다.





setuid가 설정되어 있지 않는 경우라면 foobar 유저가 fungame을 실행하면 fungame 실행파일은 foobar 유저의 소유로 실행되기 때문에 fungame에서 fungame_high_score_list 파일을 액세스하면 others에 해당하는 읽기 권한밖에 얻을 수 없다. doobar 유저 역시 마찬가지로 fungame이 실행될 때 doobar 유저가 others로 분류되는 바람에 fungame에서 하이 스코어 리스트 파일을 읽을 수밖에 없는 상황이 벌어진다.

하지만 fungame 실행파일에 setuid를 켜 주면 상황이 달라진다. setuid가 설정된 fungame 실행파일은 어떤 사용자가 이 명령을 실행시키든지 간에 fungame 사용자가 명령을 실행한 것으로 간주하기 때문이다. 따라서 fungame 실행파일은 사용자에 상관없이 fungame_high_score_list 파일을 읽고 쓸 수 있게 된다.

이런 setuid 보안 모델은 보통 일반 유저가 시스템 관련 명령을 실행했을 때 그 명령이 잠깐 동안 관리자인 root 권한을 얻을 필요가 있을 때 많이 사용된다. 이것을 보통 setuid root라고 부른다. setuid root는 보안상의 문제를 초래하기도 하지만 유저에 따라서 케이스를 모두 구분해 주어야 하는 복잡한 경우를 피할 수 있다. 앞의 게임 하이 스코어 리스트 예를 passwd 명령과 /etc/passwd로 바꾸어 생각해 보면 setuid root의 유용함을 쉽게 알 수 있다. 참고로 /etc/passwd와 /usr/bin/passwd의 파일 퍼미션을 살펴보면 다음과 같다. 예상대로 /usr/bin/passwd는 setuid가 root 유저로 설정되어 있고 /etc/passwd 파일은 root 유저가 읽기와 쓰기 권한을 갖고 있음을 볼 수 있다.





하지만 setuid root는 보안상의 문제가 생길 수 있다는 점을 항상 염두에 두고 있어야 한다. 간단한 예로, 누군가가 root 권한을 재주껏 얻은 다음 bash 하나를 복사하고 거기에 setuid를 설정해 놓은 경우를 생각해보자.





이 셸을 실행시키는 사용자는 당연히 root 권한을 갖게 된다. 실제, 간혹 리눅스 서버가 해킹에 뚫리고 난 뒤에는 이렇게 setuid root된 셸이 만들어져 있는 경우가 많다.

setgid는 setuid 만큼 자주 발생하지는 않지만 그 원리는 setuid와 마찬가지로 setgid가 설정된 파일은 실행될 때 그룹 소유자가 그 파일에 지정된 그룹으로 설정된다. setgid는 write 명령과 같이 특정 그룹을 매개체로 액세스 권한을 공유할 때 사용된다. write 명령을 통해 다른 유저에게 메시지를 보내는 경우 setgid가 없는 경우는 다른 사람의 터미널 디바이스(/dev/tty??와 같은)에 디폴트로 쓰기를 할 수 없고 따라서 메시지를 보낼 수 없게 된다. 하지만, 유저들이 쓰는 모든 터미널 디바이스 파일을 tty와 같은 그룹으로 묶고, write 파일에 tty 그룹으로 setgid를 해 놓으면 그룹 퍼미션을 통해서 다른 유저의 터미널 화면에 쓰기를 하는 것이 가능해진다.

sticky bit은 /tmp와 /var/tmp와 같이 임시 파일이 생겨났다가 없어지는 디렉토리에 자주 사용된다. /tmp 디렉토리의 경우 모든 유저들이 마음대로 파일을 읽고 쓰는 것이 가능해야 하는데 이렇게 되면 /tmp 디렉토리는 rwxrwxrwx 퍼미션을 가져야 하고 이것은 유저들이 /tmp 디렉토리에서는 다른 유저의 파일을 지울 수도 있다는 문제를 초래할 수 있다. 하지만 sticky bit이 설정된 디렉토리에서 유저 A는 유저 B가 생성한 파일을 지울 수 없게 된다. 좀 더 정확히 말하면 /tmp와 같은 sticky bit이 설정된 디렉토리에서는 디음과 같은 경우가 아니고서는 파일을 지울 수 없다.


◆ 파일의 소유자
◆ 파일이 놓인 디렉토리의 소유자
◆ 수퍼유저


sticky bit은 다음과 같이 t가 추가된 형태로 표기된다. 숫자로 표현하면 1777이 될 것이다. 이것 역시 앞에 잠깐 언급한 chmod(2) 매뉴얼 페이지를 참고해 보자.





su
일반적으로 윈도우를 많이 쓰던 사용자가 리눅스를 쓰기 시작하면서 접하기 쉬운 실수가 수퍼유저인 root 계정으로 리눅스 시스템을 쓰는 것이다. 하지만 윈도우와는 달리 리눅스는 일반 유저 계정으로도 별다른 불편 없이 모든 작업을 할 수 있고, 필요할 때만 root 계정을 쓰면 되며, 특히 일반 유저 계정을 쓸 경우 바이러스와 같은 악성 코드가 실행 권한을 얻게 되는 것을 구조적으로 방지할 수 있기 때문에 처음부터 일반 사용자로 시스템을 사용하는 습관을 들이는 것이 중요하다. 그런 이유로 리눅스에서는 필요할 때만 su 명령으로 root shell을 획득하고 그렇지 않은 경우는 항상 일반 유저의 권한으로 컴퓨터를 사용하는 것이 좋다. 최근에 윈도우 XP에서도 도입된 Switch User 기능이 윈도우판 su라고 볼 수 있겠다.


리눅스 시스템의 환경 변수와 디렉토리
필자의 개인적인 생각이지만 리눅스 데스크탑이 아직까지 널리 쓰이지 않고 있는 이유 중의 하나는 리눅스 배포판이 전통적으로 보안에 신경을 많이 쓴 형태로 설정되어 나오기 때문이라고 본다. 따라서 윈도우를 쓰던 사용자들이 리눅스를 쓰면 보안상 이유로 디폴트로 숨겨놓은 기능들을 잘 모르고 지나치게 되고, 이것이 나중에 불편함으로 느껴지는 경우가 많은 듯 하다. 사용자 홈 디렉토리와 환경 변수에서 이런 사소한 불편함을 간단히 몇 가지 다루어 보자.

PATH 환경 변수
우선, 리눅스에서는 명령을 실행할 때 기본적으로 수퍼유저의 권한이 필요한 실행파일들의 디렉토리 위치를 PATH 환경변수에서 빼 놓고 있다. 실제, su 명령을 실행한 다음 fdisk와 같은 수퍼유저용 명령을 실행해보면 대부분의 리눅스 배포판에서 command not found라는 에러 메시지를 만나게 된다. 도대체 PATH가 어떻게 설정되어 있을까?





그렇다면 fdisk는 어디에 들어가 있을까? 다음의 결과를 보면 /sbin/fdisk에 들어가 있음을 알 수 있다. 그러나 PATH 환경 변수에는 /sbin 디렉토리가 들어가 있지 않다. 따라서 일반 유저는 su 명령으로 수퍼유저가 되더라도 PATH 환경 변수에 /sbin 디렉토리가 들어가 있지 않은 까닭에 fdisk 명령어가 실행이 되지 않는 것이다. 참고로 리눅스의 명령어 중 일반 사용자용 명령어들은 전통적으로 bin 디렉토리 아래 많이 들어가고 수퍼유저용 명령어들은 sbin 디렉토리에 많이 들어간다.





그러면 PATH 환경 변수에 /sbin 디렉토리를 추가해 보자. sbin 디렉토리는 /sbin 이외에도 /usr/sbin, /usr/local/sbin이 있을 수 있고, 보통 /usr/sbin에는 배포판이 설치될 때 /sbin 만큼 필수적이지는 않지만 중요한 시스템 관리 명령어들이 많이 들어가 있으니 /sbin과 함께 /usr/sbin도 같이 넣어보자. 참고로 bash 외의 csh이나 tcsh은 여기서는 다루지 않도록 하겠다.





이것을 전통적인 본 셸 방식으로 풀어쓰면 다음과 같이 된다.





그러나 이 명령을 셀 하나를 실행할 때마다 타이핑하기에는 귀찮을 테니 이것을 셸이 실행될 때마다 같이 실행되도록 해 보자. bash 셸은 시작하면서 다음과 같은 파일의 내용을 실행시킨다. bash의 매뉴얼 페이지를 잠깐 살펴 보면 FILES 항목 아래쪽에 설정파일에 대한 설명이 나온다.





우리의 경우는 홈 디렉토리의 ~/.bash_profile이나 ~/.bashrc를 쓰는 것이 적당해 보인다. PATH 정보는 일반적으로 로그인 셸 설정에 속하니 .bash_profile에 넣어두면 된다. 참고로 리눅스 시스템 설정을 할 때는 별다른 필요가 없으면 홈 디렉토리 아래의 .로 시작하는 설정파일에 설정해 주는 것이 좋다. 물론, 수퍼유저로 변신해서 직접 /etc 아래의 설정파일을 바꾸어 줄 수도 있지만 항상 내가 수퍼유저 권한을 갖고 있을 수도 없는 것이고, 실제 일반 유저의 권한으로도 내 입맛에 맞게 시스템 환경을 상당한 부분 맞추어 볼 수 있다.

PATH 환경 변수와 관련해서 참고로 하나 알아 둘 것은 현재 디렉토리에 들어 있는 명령을 실행시키는 방법이다. 대부분의 리눅스 배포판에서 hello.c같은 소스를 컴파일하고 다음과 같은 명령을 실행해보면 에러가 난다.





이것은 PATH 환경 변수에서 현재 디렉토리인 점(.)이 빠져 있기 때문이다. 왜 이런 복잡해 보이는 방식이 디폴트로 설정되어 있을까? 그것은 보안상의 문제 때문이다. 예를 들어, 수퍼유저가 /tmp나 /var/tmp 디렉토리에서 무심코 ls 명령을 실행시켰는데 이것이 /bin/ls가 아닌 누군가가 심어놓은 악성 /tmp/ls를 실행시키게 된다면 문제가 생길 수 있다. 이것을 아예 원천적으로 방지하기 위해 현재 디렉토리의 명령어를 실행하려면 보통 앞에 명시적으로 ./를 붙여주는 것이다.

디렉토리
리눅스의 파일 시스템 디렉토리 구조는 전통적인 방식을 지금도 따르고 있다. 루트 디렉토리 아래에 실행 파일은 /bin, 라이브러리는 /lib, 설정파일은 /etc, 수퍼유저용 명령어는 /sbin과 같은 디렉토리에 들어가게 되어 있다. 여기서 유심히 보아야 할 것은 이런 bin, lib, etc와 같은 디렉토리 명명법은 다른 디렉토리 안에서도 반복이 된다는 사실이다. 한번, 소프트웨어 패키지들이 위치하고 있는 /usr 디렉토리를 살펴보자.





여기서도 친숙한 bin, etc, lib, sbin과 같은 디렉토리가 보임을 알 수 있다. 실제, 필자가 쓰는 리눅스 시스템에서 /usr/etc 디렉토리에는 아무런 파일도 들어가 있지 않고 그냥 디렉토리 이름만 하나 만들어져 있지만 /usr 디렉토리에서도 / 디렉토리와 동일한 디렉토리 명명법이 반복되고 있음을 볼 수 있다. 이것은 /usr/local 과 /home/mewmew 홈디렉토리에서도 그대로 반복된다.





직접 손으로 소프트웨어 패키지 소스코드를 컴파일 해 보면 이러한 명명법이 상당히 쓸모가 있음을 알 수 있다. 일반적으로 ./configure; make; make install로 설치가 되는 소스 패키지의 경우 ./configure 스크립트에서 디폴트로 소스 패키지가 설치되는 디렉토리 (PREFIX)가 /usr/local이다. 이것을 필요에 따라 /usr이라든지 혹은 사용자 홈 디렉토리를 가리키는 $HOME 환경 변수로 설정해 줄 수도 있는 것이다.





다음의 경우 나중에 컴파일이 끝나고 make install을 수행하면 보통 ${HOME}/bin, ${HOME}/etc, 필요에 따라서 ${HOME}/lib나 매뉴얼 페이지가 들어가는 ${HOME}/man 까지 설치되는 경우도 있다. 특히, 관리자 권한을 얻기 힘든 일반 웹 호스팅 서비스를 이용할 때 이렇게 내 홈 디렉토리 안에 손수 필요한 패키지를 컴파일해 쓰는 경우가 흔한데 이럴 경우에는 앞의 PATH 환경 변수에 ${HOME}/bin 엔트리도 하나 넣어 주면 더욱 편리할 것이다.

그리고 리눅스 배포판에서 rpm이나 deb 같은 패키지 관리 시스템이 발전함에 따라 /usr 아래의 /usr/bin, /usr/lib와 같은 디렉토리는 직접 시스템 관리자가 필요한 소프트웨어를 컴파일해서 집어넣는 공간이라기보다는 패키지 관리자로 관리하는 공간이 되어버렸다. 실제, 손으로 컴파일하는 소프트웨어들은 /usr/local 아래쪽에 넣는 것이 나중에 시스템 유지/보수 측면에서도 편리하다. 반드시 /usr 안쪽으로 넣어야 하는 패키지들은 소스코드를 우선 컴파일해서 rpm이나 deb 패키지를 만들고 그것을 다시 설치하는 편이 의존성 유지의 측면에서 유리하다. 이제 리눅스의 전체적인 디렉토리 구조를 쉽게 넘기기 좋은 부분만 간단히 짚어보고 넘어가자.

/etc
설정파일이 들어가는 곳이다. 사용자의 입장에서는 여기서 /etc/profile.d 디렉토리를 잠깐 지켜볼 필요가 있다. /etc 아래에서 .d로 끝나는 디렉토리들은 마치 그 디렉토리 안에 들어가 있는 파일들이 하나의 설정파일을 이루고 있는듯한 역할을 하는 경우가 많다. bash는 실행되면서 먼저 /etc/profile을 참조하는데 /etc/profile.d가 있는 경우 /etc/profile.d 안의 모든 스크립트들을 실행한다. 참고로 필자가 쓰는 시스템의 /etc/profile.d에는 다음과 같은 스크립트들이 들어있다.




여기서 sh로 끝나는 파일들은 bash용, csh로 끝나는 파일들은 csh용 스크립트들인데 /etc/profile에 하나로 넣을 수도 있으나 편의상 나누어 놓은 것들이다. 비슷한 예로 /etc/xinet.d를 들 수 있다. inetd는 원래 대몬(daemon)의 대몬으로 불리는 역할을 하는데 inetd에 대몬 서비스를 등록해 놓으면 외부에서 서비스 요청이 들어올 때마다 필요한 대몬 프로세스를 실행시키고 서비스가 끝나면 종료시키는 역할을 하면서 좀 더 시스템 자원을 효율적으로 쓸 수 있도록 해 준다. 이것의 확장판이 xinetd인데 xinetd는 서비스 하나하나마다 설정파일을 따로 두고 이것들을 모아서 /etc/xinetd.d에 넣어놓도록 하고 있다.




참고로 구형 inetd의 설정파일인 /etc/inetd.conf를 잠깐 살펴보자. 어느 형태를 선호하는지는 사용자의 선택이겠지만 .d 로 끝나는 디렉토리 안에 설정 파일을 각각 분리해 놓는 아이디어가 있다는 점은 기억해 놓도록 하자.



/etc/skel 디렉토리에는 얼핏 보면 아무 파일도 없는 것처럼 보인다. 하지만 이 디렉토리에는 새로 유저 계정을 만들었을 때 그 유저의 홈 디렉토리에 디폴트로 설치될 숨겨진 설정파일들이 들어가 있다.



리눅스에서 새로 유저 계정을 추가하는 방법은 여러 가지가 있다. 전통적으로 많이 쓰는 방법이 adduser, 혹은 useradd 스크립트 명령을 이용하는 방법이고 요즘은 KDE나 GNOME에서는 그래픽 유저 인터페이스 환경에서 새로운 사용자를 추가할 수도 있다. 하지만 원래 리눅스 시스템에서는 직접 손으로 사용자를 추가하는 것도 가능하다. 간단하게 새로 사용자 계정을 만들어보자. 우선, 리눅스의 사용자는 숫자로 된 사용자 ID와 그룹 ID가 필요하다. 이것이 uid와 gid인데 su 명령 등을 이용해서 수퍼유저가 된 다음 한번 /etc/passwd 파일을 보자.



여기에 새로 hoover라는 사용자를 추가하려면 /etc/passwd 파일을 적당히 비슷하게 고쳐주면 된다. 새로 uid 1003과 gid 1003를 langel에 추가해주자. 패스워드 필드에는 *를 넣어 아직 로그인을 못하는 계정임을 명시해 주자.


마찬가지로 /etc/group 파일도 hoover 그룹을 추가해 주자. 어떤 시스템은 사용자의 그룹을 새로 만들지 않고 그냥 user 그룹에 추가하는 경우도 있다.


이렇게 한 다음 mkdir 명령으로 /home/hoover 디렉토리를 만들고 /etc/skel 디렉토리에서 기본 설정파일을 cp 명령으로 복사해준다. 그리고 chown -R hoover /home/hoover 명령과 chgrp -R hoover /home/hoover 명령으로 홈 디렉토리와 그 아래 파일들의 소유권을 명확하게 해 준 다음 마지막으로 passwd hoover 명령으로 새로운 hoover 사용자의 비밀번호를 설정해주면 직접 손으로 새 사용자 등록이 끝나게 된다. useradd와 같은 명령은 이 과정을 간단하게 자동화 시켜놓은 셈인 것이다.

여기서도 리눅스의 심플함의 매력이 묻어나는데 오래전에 필자는 솔라리스 워크스테이션을 처음 쓰면서 솔라리스에는 아예 리눅스에서 볼 수 있었던 대화식 useradd 명령어조차 없던 것을 보고 놀랬던 기억이 있다. 그래도 솔라리스에도 이런 useradd와 같은 사용자 생성 스크립트가 하나 기본으로 제공되어 있으면 좋지 않을까 했지만 오히려 이런 스크립트가 없는 것이 더 나을 수도 있다는 생각을 곧 하게 되었다. 사용자가 아닌 시스템 관리자의 입장에서는 사용자 계정 발급과 말소는 주요한 업무 중의 하나일 텐데 사용자 계정 생성과 삭제를 특정 명령어에 묶어 놓으면 좀 더 유연한 시스템을 만들기가 힘들다.

한 예로, 갑자기 전산 실습을 위해 1000명의 ID를 새로 발급해야 한다든가, 혹은 하루에도 수백 명씩 ID가 생성되고 없어지는 시스템을 유지하려면 오히려 이런 심플함이 시스템 관리자의 입장에서 좀 더 편리하고 효율적인 사용자 계정 관리 시스템을 만드는데 도움이 된다. 반복 작업을 스크립트로 처리할 수도 있고 웹 인터페이스와 연동을 할 수도 있기 때문이다. 어쨌든, 우리가 관심이 있는 시스템은 이런 대규모 시스템은 아니니 심플함이 시스템이나 레이어 간에 인터페이스를 구성하는데 도움이 될 수 있다는 점만 체크하고 넘어가도록 하자.

/dev, /proc
유닉스의 특징 중의 하나는 모든 것을 파일로 처리한다는 점이다. 이것이 유닉스 상의 프린팅과 같이 어떨 때는 자유도가 지나친 까닭에 오히려 복잡한 상황을 초래하기도 하지만 /dev 처럼 디바이스도 파일로, /proc 처럼 프로세스와 시스템 정보도 파일로 처리하는 아이디어는 한번쯤 짚고 넘어갈 필요가 있다. 여기서는 /proc 파일시스템을 위주로 살펴보자.

/proc 파일시스템에 들어가 보면 맨 처음 보이는 것이 수백MB 정도의 크기를 갖는 kcore 파일이다. 유심히 보면, kcore 파일의 사이즈가 자신의 시스템 메모리 크기와 일치한다는 사실을 알 수 있다. 이렇게, /proc 파일시스템은 가상의 파일시스템으로서 현재의 시스템 정보를 파일의 형식을 빌어 저장해 놓고 있는데 여기서 몇 가지 중요한 파일들은 다음과 같다. 대부분은 more 명령이나 vi와 같은 에디터로 직접 열어볼 수 있는 텍스트 형식의 파일들이다.


하드웨어 정보
/proc/cpuinfo - 시스템 CPU에 관한 정보가 들어가 있다.
/proc/interrupts - 시스템 하드웨어 인터럽트 사용 정보
/proc/dma - dma 정보
/proc/devices - 커널에 설정되어 있는 디바이스 정보

현재 시스템 구동 정보
/proc/net - 네트워크 정보
/proc/uptime - 시스템이 부팅 후 지금까지 계속해서 켜져 있는 시간
/proc/stat - 여러 가지 시스템 정보
/proc/loadavg - 시스템에 부하가 걸린 정도
/proc/meminfo - 시스템 메모리 사용 정보
/proc/modules - 현재 로딩되어 있는 모듈 정보


/proc 디렉토리 아래에 숫자로 만들어진 디렉토리는 모두 실행중인 프로세스이다. 이 디렉토리 안에 들어가면 그 프로세스에 관한 여러가지 정보를 알 수 있다. 참고로 윈도우의 디바이스 드라이버와 리눅스의 모듈을 혼동하는 경우가 간혹 있는 것 같아 설명을 몇 자 덧붙이면, 리눅스의 모듈은 윈도우의 디바이스 드라이버와 같다고 생각해도 되지만 리눅스 모듈은 단어 그대로 modular하다는 장점이 있다. 리눅스 모듈을 이용하면 시스템을 재부팅하지 않고도 디바이스 드라이버를 로딩(loading)/언로딩(unloading) 할 수 있는 것이다. 이런 이유로 리눅스에서는 자주 쓰이지 않는 하드웨어는 필요할 때만 모듈을 로딩하고 그 하드웨어를 사용한 다음 다시 모듈을 언로드해서 시스템 자원을 아껴 쓸 수 있다.



이러한 /proc 파일 시스템의 정보를 좀 더 사용자가 편하게 볼 수 있도록 해 주는 것들이 ps, top, uname, uptime과 같은 명령어들이다.

/usr
/usr 디렉토리 아랫쪽에는 다양한 서브디렉토리가 존재한다. X윈도우가 /usr/X11R6 디렉토리 아래쪽으로 위치하고 있으며 /usr/share, /usr/doc과 같은 디렉토리들도 존재한다. 여기서 눈여겨 볼 것은 /usr/lib 디렉토리 아랫쪽의 라이브러리들과 /usr/include 아래쪽의 헤더 파일들이다. 이곳에 들어가 있는 라이브러리와 헤더파일들은 컴파일러가 명시적으로 디렉토리를 지정하지 않아도 알아서 찾아 준다.

다음 호에서는 개발자에게 필요한 도구를
이번 호에서는 유닉스의 보안 모델의 기초가 되는 파일 퍼미션과 소유권에 관련한 내용, 그리고 리눅스 시스템을 접했을 때 쉽게 친숙해지지 않는 개념과 명시적으로 잘 기술되어 있지 않는 전통적인 관습을 명령어와 함께 알아보았다. 다음 호에서는 이번 호 기사에서 조금 더 보충할 필요가 있는 유닉스 명령어를 잠깐 추가하고 개발자들에게 필수적인 에디터와 다른 보조 도구들의 사용법을 다루어 보도록 하겠다. @

man 명령어 사용하기
유닉스에 적응하면서 겪게 되는 낯설음 중의 하나가 man 명령어의 생소함이다. 윈도우의 도움말과는 달리 man 명령어는 상당히 내용이 어렵고 형식이 지나치게 딱딱한 면이 없지 않고, 웹과 같은 하이퍼텍스트가 이미 일반화된 지금에는 그다지 매력적으로 보이지 않을 수도 있지만 유닉스 매뉴얼 페이지의 강점은 그 간결함과 통일된 형식이다. 익숙해질수록 편리해지는 유닉스와 마찬가지로 매뉴얼 페이지도 익숙해지는 노력을 들일만한 가치가 있다.

man 명령의 형식은 man 명령어 다음에 궁금한 사항을 적어주면 되는 간단한 구조를 갖고 있다. 하지만 매뉴얼 페이지는 원래 서가에 있는 여러 권의 매뉴얼의 구조를 갖고 있다. 매뉴얼 페이지는 다음의 8가지 파트로 구성되어 있다.


Commands available to users
Unix and C system calls
C library routines for C programs
Special file names
File formats and conventions for files used in Unix
Games
Word processing packages
System administration commands and procedures


여기서 눈여겨 볼 부분은 섹션 3이다. 예를 들어, 웬만한 C 함수들은 이곳에 모두 정리가 되어 있다. man 명령어는 단순히 유닉스 명령어에 관한 설명만 다루는 것이 아니고 매뉴얼화 할 필요가 있는 항목들을 모두 담고 있는 것이다. 그런데 여기서 하나 조심해야 할 것이, man 다음의 항목이 여러 섹션에 걸쳐 있는 경우이다. 예를 들어 printf 함수에 대한 매뉴얼 페이지를 보고 싶어서 man printf를 하면 다음과 같이 섹션 1의 유닉스 명령어에 관한 매뉴얼 페이지가 디폴트로 출력된다.



그러나 우리가 찾고 싶은 printf 함수의 매뉴얼 페이지는 프로그래밍에 관련된 섹션 3에 저장되어 있다. 섹션 3을 명시적으로 지정해 주려면 다음과 같이 섹션 넘버를 적어주면 된다.





그러면 우리가 원하는 printf 함수에 대한 매뉴얼 페이지가 출력된다.



이렇게 여러 섹션에 이름이 같은 항목이 존재하는 경우를 쉽게 찾기 위해서는 man -k 명령을 사용하면 좋다. (man -k 대신에 apropos명령어를 써도 된다) printf의 경우 man -k 명령을 쓰면 다음과 같은 결과를 얻을 수 있다.



시스템 설정 파일의 문법도 매뉴얼 페이지에서 다루는 항목 중의 하나이다. 예를 들어 /etc/passwd의 문법을 참조하고 싶을 때는 다음과 같이 원하는 매뉴얼 페이지를 찾을 수 있다. 참고로 섹션 5는 시스템 설정파일을 다룬다.



매뉴얼 페이지를 계속 살펴보면 알겠지만, 리눅스 시스템의 매뉴얼 페이지는 단순히 리눅스 운영체제와 프로그래밍에 관련된 부분 이외에 Perl 레퍼런스 가이드(perl reference guide)와 같은 얼핏 생각하기에는 따로 다운받아야 할 것 같은 정보도 방대한 양이 이미 들어가 있음을 알 수 있다.

참고로 GNU 시스템에서는 man보다는 info의 사용을 권장하고 있다. info는 man과 비슷한 구조를 갖고 있지만 하이퍼텍스트 기능이 들어가 있어 브라우징이 좀 더 편리하다. 여기서는 info는 다루지 않기로 한다.

지금은 많이 잊혀져가는 테크닉이지만 고전적인 유닉스 방식으로 출력용 깔끔한 메뉴얼 페이지를 만들어 보자. man 명령어에는 -t 옵션을 주면 매뉴얼 페이지를 포스트스크립트 형식으로 출력을 해 준다. 한번 고스트스크립트를 이용해서 어떤 모습으로 출력이 될 것인지 프리뷰를 해 보자. gv의 - 옵션은 포스트스크립트 파일 입력을 표준 출력에서 받겠다는 옵션이다.





이것을 pdf 파일로 만들려면 다음과 같은 간단한 명령어로 해결이 될 것이다. 심플한 명령어 한두 가지로 복잡한 기능까지 처리해 내는 유닉스의 미학이 드러나는 부분이다.






http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Unix & Linux" 카테고리의 다른 글
  • 왜 리눅스 커널을 알아야 하는가? (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법 (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ② 고급 명령과 시... (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ① 설치와 기본 명... (0)2007/05/04
  • 리눅스 데몬들의 역활 (0)2006/11/24
2007/05/04 17:19 2007/05/04 17:19
Posted by webdizen
Tags 고급 명령, 리눅스, 시스템 관리
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2902

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux2007/05/04 16:53

[처음부터 다시 배우는 리눅스] ① 설치와 기본 명령어

연재순서
1회. 개발자를 위한 리눅스 설치와 기본 명령어
2회. 고급 리눅스 명령어와 중요 시스템 관리
3회. 개발자를 위한 vim 편집기 사용법
4회. 리눅스 환경의 새로운 변화와 적응 <끝>
손정우 (필자)
2004/08/27

이제 리눅스는 더 이상 소수 마니아들만을 위한 운영체제가 아니다. 신문 방송을 통해 리눅스의 존재가 많이 알려지면서 이제 리눅스는 일반인들도 들어본 적이 있는 단어가 되었고 그 범용성 또한 계속 확장되는 추세이다. 처음 소규모 서버 분야에서 조금씩 자리를 차지하기 시작한 리눅스는 이제 엔터프라이즈, 데스크탑 그리고 임베디드 분야까지 진출하고 있는 가장 잠재력이 높은 운영체제의 하나로 성장했다.

이에 따라 리눅스를 사용하는 사람들의 모습도 다양해지고 있다. 하지만 유닉스 클론인 리눅스의 근본적인 부분은 그 모습을 그대로 유지하고 있으며 새로운 변화 속에서도 든든한 기초의 역할을 꾸준히 계속해 오고 있다.

최근 들어 리눅스 데스크탑에 대한 관심이 높아지면서 윈도우에서 리눅스로 옮겨보고 싶어 하는 개발자들이 늘고 있는 듯하다. 하지만 이들 중의 상당수가 리눅스 설치 이후 실제 개발자에게 필요한 리눅스 셸 활용법이나 개발툴 활용 그리고 윈도우와는 다른 문화를 제대로 접해보지 못하고 있는 듯하다. 이번 연재에서는 이런 리눅스의 조금은 전통적인 사용 환경과 개발 환경을 하나씩 소개해 나가면서 개발자들에게 좀 더 도움이 되는 부분들을 다뤄보기로 한다.

리눅스를 써 보고 싶다면 리눅스를 설치해 보는 것이 가장 좋은 길이다. 그렇다면 리눅스는 어떻게 설치할 것인가? 이 질문은 간단해 보이면서도 많은 리눅스 사용자들을 혼란스럽게 하는 질문이다. 특히 리눅스와 같은 오픈소스 환경은 다양성이 주요한 장점인 까닭에 자신의 필요에 맞는 배포판을 자유롭게 선택해 마음껏 설정해 쓰는 것이 가장 바람직하겠지만 오픈소스에는 자유가 많은 만큼 선택의 괴로움 또한 증가하는 것이 사실이다.

게다가 최근 들어 네트워크의 지속적인 발전과 하드웨어 가격의 하락은 굳이 리눅스를 '깔아서 쓰지' 않고서도 잘 쓸 수 있는 환경이 나타나는 추세도 보인다. 일례로 Knoppix 배포판의 경우 부팅 가능한 CD-ROM 디스크 한 장에 리눅스 배포판을 담고 있는데 Knoppix 디스크를 만들어 사용하면 Knoppix CD-ROM을 단순히 부팅하는 것만으로도 리눅스 시스템을 쓸 수 있게 해준다. 비록 파일 시스템이 읽기 전용이라는 단점이 따라오기는 하지만 리눅스를 돌리기가 예전에 비해서는 훨씬 간단해진 셈이다.

또한 최근 웹 호스팅 서비스의 가격 하락으로 PHP와 같은 웹 프로그래밍 언어에 관심이 많은 사람이라면 공들여 PHP 프로그래밍 전용 서버를 하나 설치하는 것보다는 오히려 웹 호스팅 업체에서 제공하는 리눅스 계정을 약간의 비용을 들여서 하나 장만하는 것이 직접 자신의 컴퓨터에 리눅스를 설치하는 것보다 편리할 수도 있다.

하지만 전체적인 시스템에 대한 감을 잡기 위해서는 아무래도 스스로 직접 리눅스 서버를 설치해서 사용하는 편이 가장 지름길이다. 우선, 여기서는 개발자를 위한 리눅스 서버를 하나 장만하는 것을 위주로 해서 이야기를 엮어 나가 보도록 하자.

리눅스 시스템의 선택
개발자를 위한 리눅스 하드웨어는 그다지 높은 사양을 요구하지는 않는다. 요즘 일반화되어 있는 GHz대 CPU에 256MB 정도의 메모리만 있어도 리눅스를 쓰는 데는 무리가 없다. 다만 CPU의 속도가 빠르면 아무래도 컴파일할 때 시간이 덜 걸리고, 메모리가 많으면 하드디스크 스왑이 줄어든다는 장점이 있겠다.

리눅스 하드웨어의 호환성은 지금도 꾸준히 개선되고 있으며, 최근에는 리눅스를 처음부터 지원하는 하드웨어도 많이 출시되고 있다. 하지만 리눅스 환경은 하드웨어 호환성의 측면에서 여전히 윈도우 환경에 비해 모자란 면이 없지 않다. 특히, 그래픽카드의 지원은 아직도 상대적으로 많이 부족한데 새로 리눅스용 시스템을 만드는 경우에는 그래픽카드의 지원 상황은 반드시 체크해 보도록 하자.

필자의 경우는 보통 리눅스 문서 프로젝트(The Linux Documentation Project, www.tldp.org) 페이지의 Hardware-HOWTO 문서를 우선 참조한다. 또한, 최신 하드웨어의 호환성 여부 정보는 유즈넷 뉴스그룹을 참고하거나 유즈넷에 직접 질문을 포스팅해 보는 것도 좋다. 아무래도 영문권 사용자들은 지금도 뉴스그룹을 많이 이용하는 까닭에 사용자 수가 많고 원하는 답변을 얻게 될 확률 또한 그만큼 높은 편이다.

comp.os.linux 아래쪽 뉴스그룹을 주로 참고하는 것이 괜찮으며 하드웨어 호환성은 comp.os.linux.hardware에서 많이 다루는 편이다. 혹시, 뉴스그룹이 무엇인지 잘 모르는 사람은 구글에서 제공하는 뉴스 서비스인 구글 그룹 서비스(groups.google.com)를 이용해 보자.

구입하고자 하는 하드웨어의 정확한 호환성 여부에 관한 정보를 얻지 못하는 경우는 직접 리눅스 커널 소스코드를 다운받고(ftp://ftp.kernel.org), 커널 소스코드의 압축을 푼 다음 소스코드를 뒤져 보는 것도 괜찮은 선택이다. 보통 해당 디바이스의 드라이버 소스코드에는 코멘트로 현재 드라이버의 지원 상황이 적혀 있기 마련이며, C 언어를 잘 못하더라도 코멘트 문장을 읽기란 어렵지 않다. 예를 들어 네트워크 카드 드라이버들은 리눅스 커널 소스의 drivers/net 디렉토리에 들어가 있다.

배포판의 선택
이제 시스템을 장만했다면 설치할 배포판을 고를 차례이다. 리눅스 배포판은 소스코드가 공개되어 있는 리눅스의 특성상 수많은 종류가 있다. 그 중 많이 쓰이고 유명한 배포판들이 레드햇, 수세, 데비안, 젠투, 맨드레이크, 한컴 리눅스 등이다.

리눅스 배포판의 응용 프로그램 패키징
바이너리 패키징
이들 배포판의 특성을 전체적으로 분류해 보면 우선 응용 프로그램의 패키징 방식에 따라 배포판들을 나누어 볼 수 있다. 대표적인 패키징 방식은 리눅스 배포판의 양대 산맥 격인 레드햇의 rpm 형식과 데비안의 deb 형식이다. 레드햇의 배포판, 그리고 최근의 레드햇 페도라, 맨드레이크, 수세, 한컴 리눅스와 같은 배포판이 rpm 패키지를 사용하고 있다. 이에 반해 deb 형식은 데비안 배포판에 사용되고 있으며 데비안 이외에도 여러 배포판들에서 deb 형식을 도입하고 있다.

주지하다시피 윈도우 설치 패키지와는 달리 리눅스 배포판들의 설치 패키지는 의존성(dependency) 개념이 처음부터 고려되어 있다. 즉 어떤 응용 프로그램을 설치하려고 할 때 이 패키지가 다른 패키지가 기본으로 설치되어 있어야 실행될 수 있다면 패키지 시스템은 자동적으로 이럴 때 어떤 패키지들이 추가로 필요한 지를 사용자에게 알려준다.

예를 들어 리눅스 시스템에서 사용자가 어떤 자바 기반의 응용 프로그램 패키지 설치 명령을 내리면 그 리눅스 배포판의 패키징 시스템은 현재 시스템에 자바 런타임 패키지가 설치되어 있는지를 우선 체크하고, 자바 런타임이 있지 않은 경우에는 설치 작업을 더 이상 진행하지 않는다. 이 덕분에 리눅스 시스템은 윈도우보다 응용 프로그램의 유지보수 측면에서 시스템 관리가 한결 수월하다.

여기서 조금 더 나아가면 하나의 응용 프로그램 패키지를 설치할 때 자동적으로 필요한 패키지 리스트를 생성해 낼 수 있으면 좋을 것이고 가능하다면 이 리스트를 보고 직접 다운로드 작업까지 패키지 관리 프로그램이 알아서 처리해 주면 더욱 좋을 것이다.

이렇게 패키지 사이의 의존성을 자동으로 처리해 주는 패키지 관리자의 대표적인 것이 데비안의 APT(Advanced Package Manager, www.debian.org/doc/manuals/apt-howto)이다. APT를 사용하면 사용자는 패키지 다운로드나 설치를 수동으로 할 필요가 없이 단순히 설치할 패키지의 이름만 지정해 주면 시스템이 알아서 패키지 다운로드 및 설치, 업그레이드를 해주며, 심지어는 배포판 자체의 업그레이드까지도 가능하게 해준다. 많은 데비안 사용자들이 게시판에서 APT의 편리함을 패키지 설치 명령어 한 줄 짜리 답변으로 대신하는 경우를 여러분들도 간혹 보았을 것이다.





APT의 경우에서 볼 수 있듯이 리눅스 배포판에서 이제 응용 프로그램 설치는 사용자가 원하는 패키지를 선택하면 시스템이 알아서 필요한 다른 패키지까지 알아서 다운받아 설치하는 형태로 변화되어 있다. deb뿐만 아니라 이제는 rpm에서도 마찬가지 경향이 나타나며 rpm에서는 이것이 yum이나 지미안(Ximian)의 Red Carpet과 같은 좀 더 쓰기 쉬운 패키지 관리 매니저로 나타나게 된다. 즉, 과거에는 rpm 방식과 데비안 배포판의 deb 패키징에 대한 비교가 많았지만 이제는 rpm이냐 deb이냐 혹은 소스 패키지이냐의 구분이 커다란 의미가 없는 방향으로 나아가고 있다.

리눅스 사용자의 입장에서는 어느 배포판이든 응용 프로그램을 설치하는 방법이 원하는 프로그램을 마우스로 선택하고 설치 버튼을 누르는 쪽으로 이미 통일됐기 때문이다. 다만, 설정 파일의 유지보수 측면이나 시스템을 내 마음대로 꾸미는(customization) 측면에서는 파일 단위의 의존성을 체크하는 rpm보다는 패키지 단위로 의존성을 체크하는 deb이 좀 더 우위에 있는 편이지만 실제 사용자의 입장에서 큰 차이는 존재하지 않는다.

하지만 전통적으로 rpm 기반의 배포판들은 레드햇, 수세, 맨드레이크와 같이 상용 업체들에서 만들어지는 경우가 많고 따라서 설치 작업이 쉬운 경우가 많으며 기본 데스크탑 설정도 깔끔하게 잘 되어 있다는 장점이 있다. 따라서 리눅스에 익숙하지 않은 사용자들은 아무래도 이런 상용 업체가 만든 배포판이 좀 더 쉽게 다가갈 수밖에 없다.

일단 설치가 쉽고 설정이 많이 필요하지 않는 레드햇과 같은 상용 배포판을 쓰면서 리눅스에 어느 정도 익숙해졌다고 생각하면 좀 더 다양한 가능성을 제공하는 데비안이나 여타 소스코드 패키지 기반의 시스템으로 넘어가는 것도 좋은 방법이다. 컴퓨터의 세계에서는 일반적으로 좀 더 최적화(customization)가 쉽고 동적(dynamic)인 시스템 구성이 가능할수록 사용자 입장에서 배워야 할 것이 많은 경향이 있을 수밖에 없다. 데비안의 경우 배포판 설치 작업을 좀 더 쉽게 하려는 여러 가지 노력이 계속되고 있으나 아무래도 다른 배포판에 비해 배울 것이 많은 편이다.

소스 패키지 기반의 배포판
리눅스와 같은 오픈소스 환경에서는 소스코드가 공개되어 있기에 프로그램을 설치할 때 직접 소스코드를 컴파일해서 설치하는 방식이 예전부터 자연스럽게 정착되었다. 게다가 소스코드 컴파일은 기본적으로 배포용 패키지가 플랫폼에 중립적인 경향이 강하며, 컴파일 된 바이너리가 시스템에 최적화될 수 있다는 장점을 갖고 있다.

하지만 사용자가 소스코드를 직접 손으로 컴파일하면서 수많은 응용 프로그램들을 모두 관리하는 것은 많은 시간과 노력을 들여야 할 뿐만 아니라 설치하는 패키지 숫자가 많아지면 패키지 관리가 무척 힘든 일이 될 것이다. 그러나 리눅스의 소스 패키지 기반 배포판들은 역시 바이너리 패키징과 마찬가지로 의존성 처리 기능을 갖추고 있으며, 그 대표 주자가 젠투 리눅스(gentoo.org, 이하 젠투)이다. 원래 소스코드를 가져와 컴파일을 해서 응용 프로그램을 설치하는 방식은 FreeBSD의 ports 시스템이 원조라고 할 수 있다.

ports는 소스코드를 직접 가져와 컴파일하면서도 패키지간의 의존성 정보를 알아서 처리해 주는데 리눅스에서는 젠투가 널리 쓰이고 있다. 젠투에서는 portage라는 소스 패키지 관리 프로그램을 사용하고 있는데 portage 역시 앞에서 본 데비안의 apt-get 만큼이나 패키지 관리가 편리하다. 다만 portage의 경우 소스코드 패키지를 다루기 때문에 패키지 설치 명령을 내리면 자동적으로 필요한 소스코드 패키지들을 다운받은 다음 소스코드 컴파일이 시작되는 차이점이 있다. 젠투에서 응용 프로그램 패키지를 설치하려면 다음 명령을 사용한다.





젠투의 portage 시스템은 여기서 한술 더 떠 아예 다음과 같은 명령어도 가능하다. 이 명령은 실제 젠투 설치 과정의 일부인데 최소한도의 리눅스 사용이 가능한 젠투 base system을 구성하는 과정에서 쓰는 명령이다. 이 명령 하나로 가장 최근의 젠투 base system을 구성할 수 있으며, 이미 base system이 설치된 시스템에서는 업데이트까지 해결해 준다.





여담이지만, 최근 들어 얼핏 보기에 마니아층에 좀 더 적합할 것 같은 젠투가 많은 인기를 끌고 있는 주된 이유는 무엇일까? 필자 개인적인 경험에 비추어 볼 때 젠투의 매력은 다른 무엇보다도 실제 리눅스 시스템이 어떻게 구성되는지 그 과정을 하나하나 보여준다는 데 있는 것 같다. 소스코드 컴파일은 사실 오픈소스 운영체제를 쓰지 않으면 거의 접하기 힘든 경험이다. 대부분의 소스코드는 그냥 가져다가 ./configure; make; make install 명령어만 내리면 설치가 되기는 하지만 컴파일이 안 되는 경우도 많으며, 초보 사용자의 입장에서는 컴파일 에러를 만났을 때 에러 해결이 힘든 경우가 허다하다.

하지만 젠투 리눅스는 소스코드 패키지 설치를 거의 make && make install 수준으로 쉽게 만들면서도 실제 컴파일 과정을 모두 볼 수 있다. 또한 젠투의 설치 과정은 다른 배포판과는 달리 하나하나 손으로 명령을 입력해야 한다.

예를 들어, 네트워크 설정을 할 때 다른 배포판은 일반적으로 IP 주소와 게이트웨이, 네임서버 주소 등의 정보를 설치 프로그램의 대화상자에 입력하는 방식을 많이 쓰는데 젠투에서는 다음처럼 route add, ifconfig 명령을 써서 사용자에게 실제로 네트워크 설정을 직접 명령어를 타이핑해서 설정하도록 한다. 설치 메뉴얼을 프린트해 놓고 하나하나 따라하면서 설치를 진행하다 보면 덩달아 배우는 지식도 많아지는 재미가 있는 배포판이 젠투라고 할 수 있겠다.





다만 젠투는 설치 과정이 직접 리눅스 명령어를 입력하면서 진행되기 때문에 유닉스 셸 프롬프트가 익숙하지 않은 초보자들에게는 적합하지 않으며, 패키지를 설치할 때 소스코드를 컴파일하기 때문에 시간이 많이 걸린다는 단점이 있다(참고로 젠투 최근 버전부터는 소스코드 컴파일 외에 바이너리 패키지 설치 방식도 같이 지원한다).

전체적으로(필자 개인적인 생각으로 볼 때) 젠투는 리눅스 시스템 설치 과정에서 리눅스 시스템 설정에 대해 많은 사항을 자연스럽게 배울 수 있게 해 주기 때문에 개발자들에게 가장 적합한 배포판이 아닐까 싶다. 특히 젠투에서 하드웨어 자동 인식을 갖추고 리눅스 커널 컴파일을 쉽게 해주는 genkernel(www.gentoo.org/doc/en/genkernel.xml)은 아직까지 리눅스 커널 컴파일을 해 보지 않은 사용자들에게 괜찮은 가이드가 될 수 있을 것이다. 시스템 최적화의 측면에서는 데비안도 아주 유연하지만 짧은 시간에 설치 작업만으로 시스템 관리와 설정에 관한 많은 것을 쉽게 배울 수 있게 해주는 젠투의 장점은 젠투만의 독보적인 특징이 아닐까 싶다.

리눅스 라이브 CD, Knoppix
최근 들어 CD 한 장으로 부팅해서 쓰는 리눅스 배포판이 많이 나오고 있다. 그 대표적인 것이 앞에서도 잠깐 언급했던 Knoppix(www.knoppix.net)인데 이러한 CD-ROM 부팅 방식의 배포판은 하드디스크를 비우고 채워 넣는 설치작업이 아예 필요 없다는 간편함이 큰 장점이다. 특히 하드디스크에 리눅스를 설치해 쓰는 경우라도 우선 배포판을 설치하기 전에 이런 리눅스 라이브 CD를 이용해서 쉽게 시스템 테스트를 해볼 수 있다는 점은 많은 도움이 된다.

Knoppix는 CD-ROM 방식이기 때문에 파일 시스템이 읽기 전용이고 기본적으로 시스템 설정을 고칠 수 없다는 단점이 있지만 여느 오픈소스 프로젝트들이 그렇듯이 그저 CD-ROM으로 부팅하는 리눅스 배포판이라는 기본적인 기능 외에 여기서 파생된 여러 가지 재밌는 아이디어들 또한 많이 구현되어 있다. 하드디스크의 조그만 FAT32 파티션에 Knoppix CD 이미지를 넣고 CD-ROM 대신 부팅을 해서 쓴다든가, Knoppix의 데스크탑 환경인 KDE를 빼고 GNOME을 집어넣은 Gnoppix 프로젝트라든가, 내 입맛에 맞는 설정을 플로피나 USB 메모리 드라이브에 저장해 놓고 매번 불러 쓰는 것과 같은 기능들이 이미 구현되어 있으니 관심 있는 독자들은 프로젝트 홈페이지를 한번 들러 보자.

리눅스의 설치
리눅스를 설치하기 전에, 준비한 시스템에 리눅스가 잘 작동하는지 테스트를 해보고 싶다면 방금 이야기한 Knoppix를 하나 준비하자. Knoppix CD 이미지를 다운받아 공 CD 하나에 구운 다음 Knoppix를 CD-ROM 부팅해 보면 대략 내 시스템에서 어떤 부품이 말썽을 피울 것인지 미리 짐작해 볼 수 있다.

구체적인 배포판의 설치 과정은 여기서는 생략하기로 한다. 이미 리눅스 설치의 편의성은 레드햇 구 버전부터 이미 윈도우보다 쉬워진 지 오래고 데비안이나 젠투와 같이 설치가 상대적으로 까다로운 배포판은 직접 프로젝트 홈페이지에서 문서 자료를 구하는 것이 좋기 때문에 굳이 설치까지 다룰 필요는 없을 것 같다. 다만 여기서는 배포판에 상관없이 설치 과정에서 걸리게 되는 파티션에 대한 개념과 리눅스 설치 이후 리눅스용 GUI 응용 프로그램을 원격으로 실행할 때 사용하는 X서버/클라이언트 개념에 대해 잠깐 짚어보고 넘어가도록 하자.

하드디스크 파티션 나누기
하드디스크를 왜 나누어 쓸까? 그 이유야 다양하겠지만 가장 쉽게 생각해 볼 수 있는 파티션 나누기의 이점은 분할해서 쓰기 때문에 필요한 부분만 다른 파티션에 영향을 주지 않고 내 맘대로 바꾸어 쓸 수 있다는 점이다.

예를 들어 윈도우에서 파티션을 하지 않고 하나의 하드디스크를 그냥 사용했다고 가정해보자. 윈도우 새 버전이 출시되었을 때 이 사용자가 새 버전의 윈도우를 깔끔하게 새로 설치하려면 그동안 저장된 사용자 문서나 여러 데이터 파일들을 잠시 백업받은 뒤 나중에 복구하거나, 혹은 임시 디렉토리라도 만들어서 거기에 데이터 파일들을 저장해 놓은 다음, 기존의 윈도우 디렉토리와 응용 프로그램 디렉토리들을 재주껏 지우는 과정을 거쳐야 한다.

하지만 이 사용자가 하드디스크를 이미 파티션해 놓았고, 파티션이 분리된 D: 드라이브에다가 항상 사용자 데이터를 저장하고 있었다면 얘기는 달라진다. 이 경우 새 윈도우를 설치하려면 그냥 C: 드라이브를 속 시원히 밀어버려도 D:의 사용자 데이터에는 전혀 이상이 없기 때문이다.

파티션을 나누는 또 하나의 주된 이유는 하나의 하드디스크에 2개 이상의 운영체제를 설치할 때이다. 이 경우는 운영체제마다 독립된 저장 공간이 필요할 테니 파티션을 이용하는 것이 가장 바람직한 방법이 될 것이다. 그렇다면, 2개의 운영체제가 하나의 하드디스크에 설치되어 있다고 할 때 이 하드디스크로는 어떻게 부팅을 해야 할까? 여기서 기본 파티션(primary partition)과 확장 파티션(extended partition)의 개념이 나오게 된다.

우선, 기본 파티션은 부팅이 가능한 파티션이라고 정의해 볼 수 있다. 그리고 여기서 하나 외워 둘 것은 기본 파티션은 하나의 하드디스크에 4개만 만들 수 있다는 제한 조건이다. 그렇다면 4개 이상의 기본 파티션이 필요한 경우는 어떻게 처리할까? 이 경우는 4개의 기본 파티션 중의 하나를 ‘더 쪼갤 수 있도록’ 설정한다. 이렇게 더 쪼개질 수 있도록 지정된 파티션이 확장 파티션이다.

즉, 기본 파티션은 원래 4개 밖에 만들 수 없도록 되어 있지만 그 중 하나를 맘대로 쪼갤 수 있도록 해서 실제로 4개 이상의 기본 파티션을 만들 수 있는 효과를 내는 것이다. 이 경우 우리가 쪼갤 수 있도록 지정한 파티션을 확장 파티션이라고 하며, 확장 파티션은 그 안의 조그만 파티션들을 담게 되는 큰 그릇 역할을 하게 되는 셈이다.

그런데 요즘은 윈도우 fdisk의 영향으로 기본 파티션을 보통 하나만 만들고 하드 디스크의 남은 부분은 자동으로 확장 파티션으로 지정해 버리는 방법이 많이 사용된다. 즉, 기본 파티션을 4개까지 만들다가 4개 이상의 파티션을 생성해야 할 일이 생기면 그 중 하나를 더 쪼개 쓸 수 있도록 지정하지 않고, 처음부터 만들어진 파티션 중 두 번째 파티션을 바로 확장 파티션으로 지정해 놓고 이 안에서 숫자의 제한 없이 파티션의 개수를 계속 늘려 나가겠다는 얘기이다. 하지만 이 방식이 리눅스를 설치하는 사용자의 입장에서는 약간의 혼동을 일으키게 된다는 문제가 있다.

우선, 기본 파티션만을 이용해서 리눅스를 설치하는 과정을 생각해 보자. 리눅스는 윈도우와는 달리 가상 메모리를 쓰기 위해 스왑 파티션이 필요하니 리눅스 전용 파티션 하나, 스왑 파티션 하나, 이렇게 2개의 파티션이 최소한으로 필요하다. 윈도우 듀얼 부팅을 위해 윈도우에도 파티션을 하나 줘 도합 3개의 파티션을 만들어 본다고 가정하자. 리눅스는 파일 방식으로 모든 하드웨어 장치를 가리키는데 일반적인 IDE 방식 하드디스크가 하나 설치되어 있다고 가정하면 이 하드디스크는 /dev/hda에 해당하며 여기서 한번 /dev/hda에 리눅스 fdisk로 파티션을 나누어 보자.



앞에서 볼 수 있듯이 생성되는 파티션은 /dev/hda2, /dev/hda3로 끝남을 알 수 있다. 윈도우용 파티션은 /dev/hda1으로 설정해 주었다. 그런데 여기서 하드디스크를 /dev/hda1, /dev/hda2 둘로 쪼갠 다음, 두 번째 기본 파티션인 /dev/hda2를 확장 파티션으로 지정해 보자.



그 다음, 이 그릇 역할을 하는 /dev/hda2 파티션을 잘게 쪼개어 보자. 쪼개진 파티션 각각을 논리 파티션(logical partition)이라고 한다.



즉, 확장 파티션 그릇인 /dev/hda2 안에 생성된 자식 파티션들을 가리키는 파일 이름은 /dev/hda5, /dev/hda6, /dev/hda7 등이 됨을 볼 수 있다. 중간의 hda3, hda4가 생략된 조금 got갈리는 숫자 배열 방식이기는 하지만 한번 알아두면 편하니 머릿속에 꼭 넣어 놓도록 하자. <그림 1>에서는 하드디스크 파티션을 만들면서 기본 파티션만 쓴 경우와 확장 파티션을 쓴 경우를 다이어그램으로 간략화시켜 표현해 보았다.


사용자 삽입 이미지

<그림 1> 기본 파티션과 확장 파티션의 하드디스크 분할 예



몇 개의 파티션을 만들 것인가?
앞에서 잠깐 얘기했듯이 리눅스를 설치하기 위해서는 리눅스 전용 파티션 하나, 그리고 스왑 파티션 하나, 이렇게 최소 2개의 파티션이 필요하다. 하지만 파티션은 적절히 나누어 놓으면 나중에 업그레이드나 시스템을 변경할 일이 생길 때 무척 쉽게 대응할 수 있다.

유닉스 시스템의 재미있는 특징 중의 하나는 하드디스크를 마운트할 때 윈도우와 같이 C: D: E: 드라이브 이름을 주지 않고 디렉토리에 바로 마운트시켜 버린다는 점이다. 아래 df 명령 출력을 잠깐 보자. df는 남아있는 디스크 용량을 보여주는 명령어이고 -h는 MB, GB 단위로 용량을 출력하라는 옵션이다.


$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/hda1 3.9G 2.5G 1.3G 66% /
/dev/hdb1 39G 33G 4.3G 89% /home
/dev/hda2 15G 13G 1.4G 91% /home/foobar/backup
/dev/hdb2 18G 16G 1.2G 94% /usr


앞의 리눅스 시스템의 경우 물리적으로 2대의 하드디스크를 리눅스에서 가져다 쓰고 있으며, 첫 번째 하드디스크의 첫 번째 파티션은 리눅스의 루트(root) 파티션, 두 번째 파티션은 백업용인 /home/foobar/backup으로 쓰이고 있음을 알 수 있다. 두 번째 하드디스크의 첫 번째 파티션은 사용자 계정이 들어가는 /home 디렉토리에 그리고 두 번째 파티션은 응용 프로그램들이 주로 들어가는 /usr 디렉토리에 사용되고 있음을 볼 수 있다.

우선, 리눅스 시스템을 만들 때 사용자 계정이 들어가는 /home 디렉토리는 별도의 파티션으로 나누어 주는 것이 차후의 시스템 확장을 대비해 무척 편리하다. 리눅스를 완전히 새로 설치하는 경우가 생기더라도 /home 디렉토리가 별도의 파티션으로 빠져 있으면 리눅스 루트 파티션을 새로 포맷하고 리눅스를 설치한 다음 /home 디렉토리만 그냥 마운팅해 버리는 것으로 사용자 파일은 전혀 손상 없이 유지되기 때문이다.

그 다음으로 생각해 볼 것이 /boot 파티션이다. /boot 파티션은 굳이 안 만들어도 상관은 없지만, 리눅스 커널을 넣어두는 곳이라 하나쯤 분리해 놓는 편이 좋다. /boot 파티션이 분리되어 있으면 루트 파일 시스템에 오류가 생기더라도 커널 차원의 부팅에는 문제가 없기 마련이다.

/boot 파티션의 크기는 커널 이미지가 1MB가 안되는 편이니 수십MB 정도만 잡아도 충분하다. /var 디렉토리를 파티션으로 분리하면 어떤 점이 좋을까? /var 디렉토리 안에는 여러 종류의 로그(log) 파일과 메일 데이터가 저장된다. /var/log와 /var/spool/mail이 대표적인 예이다.

만약 /var 디렉토리가 파티션 분리되어 있지 않다면 특정 서버 프로세스의 로그 파일이 갑자기 넘치거나 덩치 큰 메일이 하드디스크를 채워버리는 경우 시스템의 루트 파티션에 더 이상 쓰기가 불가능하게 될 것이고 이것은 서버 동작의 정지로 이어진다. 하지만 /var 디렉토리가 분리되어 있으면 이 경우 /var 파티션이 가득 차기는 하지만 루트 파티션은 영향을 받지 않기 때문에 서버는 여전히 제 동작할 수 있다.

마찬가지로 응용 프로그램이 많은 경우 /usr 쪽을 분리해 내는 것도 생각해 볼 수 있을 것이다. 하지만 대규모 서버를 운영하기 전에는 지나치게 파티션을 많이 만들 필요 없다. 일반적인 개인 사용자의 입장에서 리눅스 파티션은 스왑 파티션, /, /home, /boot 정도만 해주면 충분하다고 보면 되겠다.

참고로 파일 시스템은 자동 에러 체킹 기능이 있는 저널링 파일 시스템을 사용하도록 하자. 일반적으로 많이 쓰이는 ext3, xfs, Reiserfs 정도를 사용하면 된다. 단, /boot 파티션은 심플한 것이 좋으니 어디서나 지원하는 ext2 파일 시스템으로 포맷해서 쓰는 것이 좋다.

세컨드 PC에서 리눅스
요즘은 세컨드 PC라는 말이 의외로 자주 등장하고 있는 것 같다. 새 PC를 장만하는 데 드는 비용이 지속적으로 하락하고 있고, VGA가 내장된 microATX 크기의 메인보드가 10만원이 채 안되는 가격을 형성하고 있으니 이제는 세컨드 PC도 사치가 아닌 시대가 찾아온 셈이다.

게다가 구형 취급을 당하는 1GHz CPU들의 성능은 사실 리눅스를 돌리기에는 남아도는 사양이다. 이런 이유로 여유가 닿는 대로 간단히 세컨드 PC로 리눅스 서버를 구성해서 개발 전용으로 쓰는 것도 괜찮은 아이디어이다. 그리고 리눅스 데스크탑이 비약적인 발전을 이루고 있긴 하지만 인터넷 익스플로러에 지나치게 최적화되어 버린 국내 인터넷 환경은 이미 윈도우 PC를 어느 정도는 쓰지 않을 수 없을 정도까지 상황이 악화되어 버리고 말았다. 아는 사람의 미니 홈페이지에 접속하기 위해 신용카드로 물건을 하나 주문하는 데도 윈도우와 인터넷 익스플로러가 있어야 하는 상황은 결코 정상은 아니지만, 이것이 짧은 시간 내에 해결될 것 같지는 않다.

리눅스에서는 X윈도우가 클라이언트/서버 구조이기 때문에 윈도우의 터미널 서버보다 쓰기 편한 터미널 서버/클라이언트 환경을 이용할 수 있다. 바로 옆의 리눅스 서버의 GUI 응용 프로그램의 디스플레이를 윈도우 PC로 당겨 쓰는 것은 무척 쉬운 일이며, 리눅스는 윈도우 XP 프로페셔널처럼 윈도우 시스템을 현재 누군가가 쓰고 있는 경우 터미널 클라이언트에서의 로그인 접속을 막아버리는 제한도 없다.

사용자 입장에서는 윈도우와 리눅스 두 환경을 한 데스크탑에서 쓸 수 있으니 상당히 편리하다. <화면 1>은 윈도우 XP에서 X윈도우 서버인 Xmanager를 설치하고 리눅스용 응용 프로그램들의 디스플레이를 윈도우 데스크탑에서 쓰고 있는 모습이다. 인터넷 익스플로러와 나란히 리눅스용 한텀, gnuplot, xlogo가 실행되어 있는 모습을 볼 수 있다.


사용자 삽입 이미지

<화면 1> 인터넷 익스플로러, 한텀, gnuplot, xlogo가 실행중인 윈도우 데스크탑



많은 독자들이 이미 이 방법을 잘 사용하고 있겠지만 이제 어떻게 하면 리눅스용 GUI 애플리케이션들을 윈도우 데스크탑으로 끌어와서 디스플레이할 수 있는지 살펴보도록 하자. 게다가 X윈도우의 클라이언트/서버 구조는 일반적인 클라이언트/서버 구조를 이해하는 데 많은 도움을 준다.

클라이언트/서버 구조는 중간에 서비스라는 매개체를 넣고 생각하면 쉽다. 어떠한 서비스를 제공하는 쪽은 서버가 되는 것이고 그 서비스를 받는 쪽은 클라이언트가 되는 것이다. 어떤 사람들은 그냥 쉽게 메인프레임과 터미널의 확장, 혹은 웹 서버와 클라이언트 정도로 생각을 하는데 여기에 한번 X윈도우 서버와 클라이언트의 작동을 살펴보도록 하자.

우선 리눅스의 모든 X윈도우 응용 프로그램들은(KDE나 Gnome 응용 프로그램들을 모두 포함하여) X클라이언트이다. 그리고 윈도우 PC에서 작동하는 Xmanager나 XFree86과 같은 프로그램은 X서버이다. X서버는 X클라이언트의 디스플레이 서비스를 제공한다.

즉, X서버는 X클라이언트의 요청에 따라 X클라이언트의 모습을 화면에 그려내는 서비스를 제공하는 것이다. 그리고 X클라이언트는 X서버의 이런 디스플레이 서비스를 이용해서 자신의 모습을 화면에 표시할 수 있게 되는 것이다. 이 모든 구조가 클라이언트/서버 방식으로 구현되어 있으니 클라이언트와 서버는 완전히 분리될 수 있는 것이다.

그렇다면 사용자가 리눅스용 GUI 응용 프로그램들을 지금 쓰고 있는 컴퓨터에 디스플레이하려면 어떻게 해야 할까? 답은 X윈도우 서버를 현재 쓰고 있는 컴퓨터에 설치해서 사용한다는 것이다. 클라이언트/서버 환경에서 보통은 웹 브라우저와 같이 사용자는 클라이언트 프로그램을 쓰게 되는 경우가 많기에 이런 경우 왜 X클라이언트가 아니고 X서버를 내 데스크탑에 깔아야 하는지 잘 이해하지 못하는 경우가 많은 것 같다.

하지만 X서버는 X클라이언트에게 디스플레이 서비스를 제공하는 서버가 맞으며, 사용자에게는 데스크탑에 X클라이언트의 모습을 출력시켜 주는 기능을 제공하는 응용 프로그램인 것이다. 따라서 사용자는 X서버만 설치하면 OS에 상관없이 어디서나 리눅스 GUI 응용 프로그램들을 불러 쓸 수 있다는 얘기가 된다. 보통, 윈도우에서 많이 쓰이는 X서버로는 Exceed, Xmanager 그리고 공개인 XFree86이 있다.

Xmanager(www.netsarang.co.kr)의 경우 국산 프로그램으로 속도도 빠르고 평가판 다운로드가 가능하며 한글 폰트까지 내장되어 있어 편리하다. 윈도우용 XFree86은 공개라는 장점이 있지만 속도가 상대적으로 느리다는 단점이 있다. 이제 직접 리눅스용 X클라이언트를 윈도우 데스크탑에 띄워 보자. X클라이언트는 DISPLAY 환경 변수를 참조해서 X서버와의 연결을 시도한다. 즉, DISPLAY 환경 변수는 우리가 알아서 설정해 줘야 한다. 서버와 클라이언트의 구분에 유의하면서 다음 명령어들을 하나하나 실행시켜 보자.

윈도우즈 데스크탑에서 엑스 서버 실행

telnet이나 ssh로 리눅스 컴퓨터 접속

DISPLAY 환경변수에 윈도우즈 데스크탑의 아이피 주소와 디스플레이
번호를 설정. 보통 디스플레이 번호는 0번을 쓰면 된다.

$ export DISPLAY=<윈도우즈 데스크탑 아이피 주소>:0.0
원하는 엑스 클라이언트 실행

$ xterm &
이렇게 하면 윈도우 데스크탑에 떠 있는 리눅스용 X터미널을 볼 수 있을 것이다. 경우에 따라서는 X서버에서 네트워크를 통한 클라이언트의 접속을 수동으로 열어줘야 하는 경우도 있다(이럴 때 X서버 쪽에서 xhost <클라이언트의 IP 주소>와 같은 명령어를 쓰게 된다).

여기서 ssh를 이용한 X11 forwarding 기능에 대해 잠깐 알아보자. 앞의 과정에서 DISPLAY 환경 변수를 세팅하기가 의외로 번거로울 수 있는데 X11 forwarding 기능을 이용하면 ssh로 접속을 시도할 때마다 이 DISPLAY 정보가 알아서 자동으로 세팅된다.

윈도우에서 cygwin에 포함된 ssh와 XFree86을 X서버로 쓰는 경우 /etc/ssh_config나 홈 디렉토리의 .ssh_config에 다음 2줄을 추가한다. Xmanager와 같은 X서버를 쓸 때는 연결 방식을 ssh로 설정하는 것으로 보통 충분하다.


Host <리눅스 서버의 IP 혹은 *>
ForwardX11 yes


이렇게 하면 DISPLAY 환경 변수 설정이 필요 없게 되며 특히 윈도우 데스크탑이 사설 IP를 쓰는 IP 공유기 뒤에 있고, 리눅스 서버가 IP 공유기 바깥에 있는 경우와 같이 DISPLAY 환경 변수 설정이 곤란한 상황에서 아주 유용하게 쓰인다.

리눅스의 사용, 유닉스 셸에서의 파일 관리
KDE와 GNOME과 같은 리눅스 데스크탑 환경의 발전의 영향으로 요즘은 리눅스를 쓰면서 처음부터 유닉스 셸을 접하는 사람들의 비율이 예전처럼 많지는 않은 것 같다. 아마도 터미널 방식의 커맨드 라인 인터페이스 (command line interface)가 불편할 것이라는 선입관이 많이 작용하는 이유도 상당할 것 같은데 개발자 입장에서 기본적인 유닉스 명령어는 필수이며 유닉스 셸은 조금만 익숙해지면 무척이나 쓰기 편한 인터페이스 환경이다. 파일 관리를 중심으로 가장 기본적인 유닉스 명령어들을 한번 더 체크해 보도록 하자.

파일 리스트 보기 - ls
가장 먼저 배우게 되는 유닉스 명령어의 대표격이다. 긴 파일 리스트를 보여주는 ls -l, 숨겨진 파일(리눅스에서 .로 시작하는 파일은 모두 숨겨진 파일이다)까지 보여주는 ls -al에 추가해서 디렉토리 뒤에는 /, 실행 파일 뒤에는 *, 심볼릭 링크(윈도우의 ‘바로가기’에 해당한다)에는 @를 붙여주는 -F 옵션까지를 기억해 두자. GNU ls에서는 한글 파일명이 잘 보이지 않는 경우 간혹 --show-control-chars 옵션을 쓰기도 한다. pwd는 현재 디렉토리를 알려주는 명령어이다.


$ pwd
/home/jwsohn
$ ls
Mail dragon.txt public_html storage www
bin hangul_fonts share swarm_for_fedora_core_1
doc man src temp
$ ls -F
Mail/ dragon.txt public_html@ storage/ www/
bin/ hangul_fonts/ share/ swarm_for_fedora_core_1/
doc/ man/ src/ temp/
$ ls -F bin
ex@ rview@ rvim@ vi@ view@ vim* vimdiff@ vimtutor* xxd*


파일 이름 자동 완성 기능, 디렉토리 옮겨 다니기 - cd
리눅스에서 많이 쓰는 bash와 tcsh에는 파일 이름 자동 완성(filename autocompletion) 기능이 있다. 파일 이름이 상당히 긴 경우 그냥 탭(tab) 키만 눌러 주면 쉘이 알아서 파일 이름을 사람 대신 타이핑해 주는 기능인데 여기에 일단 맛들이고 나면 심지어는 윈도우에도 bash를 깔게 될 정도로 그 마수에서 벗어나기가 쉽지 않다. 혹 아직까지 이 기능을 써 보지 않은 독자들을 위해서 간단한 예를 들어 보자. 앞의 파일 리스트에서 swarm_for_fedora_core_1 디렉토리에 들어가기 위해서는 다음과 같은 명령을 입력해야 한다.


$ cd swarm_for_fedora_core_1

모두 다 타이핑하려면 귀찮으니 이 경우 자동완성 기능을 이용하면,


$ cd sw
$ cd sw [TAB] # 탭 키를 누른다.
$ cd swarm_for_fedore_core_1 # bash가 파일 이름을 대신 타이핑해 준다.

s로 시작하는 디렉토리가 3개 있는데 만약 s만 타이핑하고 탭 키를 누르면 어떻게 될까?


$ cd s
$ cd s [TAB] # 탭 키를 누른다. bash가 비프음으로 파일명 중복을 알려준다.
$ cd s [TAB] # 탭을 한번 더 누른다. 중복 파일명이 보인다.
share storage
src swarm_for_fedora_core_1
$ cd sw # w를 하나 더 타이핑해 준다.
$ cd sw [TAB] # 이후로는 앞의 경우와 같다.

참고로 윈도우와는 달리 리눅스에서는 디렉토리 이름에도 와일드카드를 쓸 수 있다.


$ cd ~ # ~를 생략하고 cd만 입력해도 된다.
$ pwd
/home/jwsohn
$ cd sw*
$ pwd
/home/jwsohn/swarm_for_fedora_core_1

cd 명령과 관련해서 점 1개(.)는 현재 디렉토리, 점 2개(..)는 한 단계 위의 디렉토리(parent directory), 그리고 ~ 는 사용자의 홈 디렉토리(보통은 /home/<사용자 계정>이다)임을 꼭 외워두자.

디렉토리 생성과 삭제 - mkdir, rmdir
말 그대로이다. mkdir, rmdir 뒤에는 복수 개의 디렉토리가 올 수 있다.

파일 복사 - cp
리눅스 명령어 중 복수 파라미터가 필요한 명령어는 반드시 두 번째 파라미터를 생략해서는 안 된다는 원칙이 있다. 파일 복사 cp는 원본 파일 하나, 복사된 파일 하나 해서 두 개의 파라미터를 가지는 대표적인 명령인데 우선 다음 예를 보면서 그 의미를 살펴보자. 다운받은 한글 폰트의 백업본을 만드는 과정이다.


$ cd hangul_fonts/
$ ls
iyagi.pcf.gz johabg16.pcf.gz johabp16.pcf.gz johabsm16.pcf.gz
iyagis16.pcf.gz johabm16.pcf.gz johabsg16.pcf.gz johabsp16.pcf.gz
$ cp iyagi.pcf.gz iyagi.pcf.gz.backup # cp 명령의 일반적인 형식
$ ls iyagi.pcf.gz*
iyagi.pcf.gz iyagi.pcf.gz.backup
$ cp iyagi.pcf.gz iyagis16.pcf.gz .. # 복수 개의 파일 복사. ..에 유의하자
$ cd ..
$ ls
Mail dragon.txt iyagis16.pcf.gz share swarm_for_fedora_core_1
bin hangul_fonts man src temp
doc iyagi.pcf.gz public_html storage www

mv


mv는 move의 약자라고는 하나 여러 가지 기능을 갖고 있다. 유닉스에서 파일 이름을 바꾸는 명령이 무엇이냐는 질문에 mv라고 대답하면 novice에서 user로 승급하게 된다는 농담도 있는데 실로 간단하면서도 여러 응용을 보여주는 참으로 재미있는 명령어이다. 역시 사용 예를 보자.


◆ 파일이나 디렉토리 이름 변경
$ mkdir backup
$ mv backup storage # backup을 storage로 변경

◆ 디렉토리를 통째 이동
$ ls
Mail bin dragon.txt man share storage temp
backup doc hangul_fonts public_html src swarm_for_fedora_core_1 www
$ cd storage
$ mv ../hangul_fonts ../doc . # 상위 디렉토리 두개를 현재 디렉토리로 옮김
$ ls -F
doc/ hangul_fonts/


별로 쓰일 일이 없을 것 같던 현재 디렉토리 기호 (.)가 유용하게 쓰이고 있음을 볼 수 있다. 윈도우 데스크탑에서 마우스 드래그앤드롭으로 폴더를 옮기는 것과 아주 흡사하다.

파일 지우기 - rm
파일을 삭제할 때는 rm 명령을 쓴다. -r 옵션을 쓰면 디렉토리도 지울 수 있다. 리눅스에서 rm 명령으로 한번 삭제한 파일은 복구가 되지 않으니 rm 명령을 쓰기 전에는 항상 여러 번 확인하는 습관을 들이는 것이 좋다.

심볼릭 링크 만들기 - ln -s
ln 명령에는 아예 심볼릭 링크를 의미하는 -s 옵션을 붙여 외워버리자. 심볼릭 링크는 윈도우 파일 매니저의 바로가기에 해당한다. ln -s 명령을 쓸 때는 항상 원본 파일/디렉토리 이름이 앞에, 만들어지는 심볼릭 링크가 뒤에 간다는 점을 잘 외워두자.


$ ln -s storage my_stuff
$ ls -ld storage my_stuff
lrwxrwxrwx 1 jwsohn jwsohn 8 5월 20 08:21 my_stuff -> storage
drwxr-xr-x 2 jwsohn jwsohn 4096 5월 20 08:08 storage
참고로 심볼릭 링크를 지워도 원본에는 영향이 없다.
$ rm my_stuff
$ ls -ld storage
drwxr-xr-x 2 jwsohn jwsohn 4096 5월 20 08:08 storage


개발자 입장에서 계속 살펴볼 리눅스
지금까지 간략히 개발자를 위한 리눅스 시스템과 배포판 선택시 유의할 사항, 기본 유닉스 명령어에 관해 알아보았다. 다음 글부터는 이번 글에 다루지 못한 고급 수준의 중요 유닉스 명령어와 시스템 관리 기초를 다지고 리눅스의 다양한 개발 환경을 어떻게 활용하는 것이 좋은지 개발자의 입장에서 계속 살펴보기로 한다. @

VMWare, 가상 시스템

VMWare(http://www.vmware.com)는 소프트웨어적으로 PC 한 대를 에뮬레이션해 주는 상용 프로그램이다. VMWare는 윈도우용과 리눅스용 두 가지가 있으며 어느 경우에든 VMWare를 실행하면 조그만 윈도우 하나에서 부팅시 비프음부터 시작해서 바이오스가 뜬 다음 완전한 시스템이 하나 에뮬레이션으로 실행된다. VMWare는 처음 출시되었을 때, 특히 리눅스와 윈도우를 동시에 사용하기를 원하는 사용자들에게 인기가 높았던 솔루션이었다.

원래 VMware는 소프트웨어 방식으로 하드웨어를 에뮬레이션하는 까닭에 속도가 느리고 높은 하드웨어 사양을 요구한다는 단점이 있었다. 하지만 오랜 개발 과정을 거치면서 최적화가 상당히 많이 진행된 까닭에 최근의 VMWare는 속도도 빠르고 안정성도 높다고 한다. 실제, 윈도우용 VMWare의 가상 하드웨어에 리눅스를 설치한 다음 리눅스 상의 아파치 웹 서버로 인터넷 서비스를 운영하는 엽기적인(?) 경우도 제법 있다.

윈도우와 리눅스의 듀얼 부팅이나 새로운 시스템을 하나 만들지 않고 굳이 VMWare를 선택해서 시스템을 구성할 때의 장점은 무엇일까? 우선, VMWare는 다양한 시스템 구성을 사용자 마음대로 만들어 볼 수 있다. 각 가상 머신마다 메모리 크기, 하드디스크 크기와 같은 설정을 내 마음대로 구성할 수 있으며, 여러 종류의 OS를 마음대로 깔아볼 수 있다. 이렇게 다르게 구성한 시스템은 만들어진 VMWare 하드디스크 이미지 파일만 갖고 있으면 언제든지 다른 구성의 시스템을 부팅해 볼 수 있는 것이다. 즉, 물리적인 하드웨어로 친다면 배포판 A를 깔아놓은 컴퓨터, 배포판 B를 깔아놓은 컴퓨터, 배포판 B를 깔고 여기에 오라클과 같은 DB 셋팅을 한 컴퓨터, FreeBSD를 설치한 컴퓨터 등 이런 식으로 VMWare 사용자는 VMWare를 통해 컴퓨터 여러 대를 갖고 있는 효과를 볼 수 있는 것이다.

아직 VMWare의 이런 동적(dynamic)인 시스템 구성을 굳이 활용할 정도는 아니지만 당장 새로운 배포판이 출시되었다든가 새로운 OS를 테스트해 보기에는 테스트 머신을 새로 구성하기보다는 그냥 VMWare 가상 머신을 하나 더 만드는 것도 괜찮은 방법임을 알 수 있다. 실제, 여러 종류의 리눅스 배포판을 테스트하는 전문 리뷰어들의 경우, 새 배포판을 깔면서 하드디스크를 매번 파티션하기도 귀찮고 하니 VMware를 많이 사용한고 한다. 그리고 소규모의 클러스터링을 할 경우 VMWare를 쓴다면 한 기계에 VMware 가상 머신을 노드 숫자대로 서너 개 띄워놓고 각각의 노드가 제 역할을 잘 해 내는지 테스트해 볼 수도 있을 것이다. 예를 들어 웹 서버와 데이터베이스 서버를 물리적으로 분리했을 때 원래 의도했던 대로 전체 시스템이 잘 동작하는지 체크하는 경우 VMware를 쓰면 물리적인 시스템을 2개 구성하고 직접 케이블링을 하는 것보다 좀 더 간편하게 테스트가 가능할 수도 있는 것이다.

아직은 일반 개발자나 사용자의 입장에서 이런 가상 머신의 활용도는 그다지 중요하지는 않지만 최근 들어 서버 쪽에서는 이런 식으로 가상 머신을 구성하고자 하는 경향이 강해지는 것 같다. 관심 있는 독자는 재미삼아 VMWare를 설치해 보자. 리눅스에서 VMWare를 쓰면 굳이 윈도우로 부팅을 하지 않고서도 윈도우 머신을 리눅스 상에서 그냥 쓸 수 있다는 장점도 있으니 말이다. VMware 이외에 bochs와 같은 오픈소스 하드웨어 에뮬레이터도 있지만 아직은 최적화에서 VMWare에 많이 밀리는 듯하다.


http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Unix & Linux" 카테고리의 다른 글
  • [처음부터 다시 배우는 리눅스] ③ vim 편집기 활용법 (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ② 고급 명령과 시... (0)2007/05/04
  • [처음부터 다시 배우는 리눅스] ① 설치와 기본 명... (0)2007/05/04
  • 리눅스 데몬들의 역활 (0)2006/11/24
  • 리눅스 문자열 검색 명령어 (0)2006/11/24
2007/05/04 16:53 2007/05/04 16:53
Posted by webdizen
Tags 기본 명령어, 리눅스, 설치
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2901

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/System2007/05/04 15:14

리눅스 시스템 콜 레퍼런스

Linux_Syscall_quickref.pdf


시스템콜은 운영체제에 명령을 내리기 위해서 사용자에게 제공되는 인터페이스다. 이 문서는 리눅스에서 제공하는 거의 대부분의 시스템콜에 대한 번호와 간단한 설명을 제공한다. 프로그래밍시 빠르게 시스템콜을 참조하고자 할때 유용할 것이다.

이 문서는 http://tiger.la.asu.edu/quick_ref/linux ··· kref.pdf 의 번역 문서입니다. 참고하시기 바랍니다.

리눅스 시스템 콜 퀵 레퍼런스
윤 상배
yundream@joinc.co.kr


고친 과정
고침 0.8 2004년 3월 19일 23시  
최초 번역

--------------------------------------------------------------------------------

차례

1. 소개
2. 시스템 콜 예제
3. 시스템 콜 레퍼런스


1. 소개
시스템 콜이란 리눅스 커널에 의해 제공되는 서비스이다. 예를 들어 파일에 쓰는 서비스를 이용하길 원한다면 프로그래머는 리눅스에서 제공하는 해당 시스템콜을 이용해서 프로그램을 작성한다. C를 이용해서 프로그래밍을 할경우 대부분의 시스템콜은 libc를 통한 포장(wrapper)함수형태로 제공받을 수 있다.

시스템 콜 함수에 대한 정보는 메뉴얼 페이지(man page)의 섹션 2번을 통해서 얻어올 수 있다. 예를 들어 read()시스템콜에 대한 정보를 얻기를 원한다면 man 2 read 하면 된다. 시스템콜에 대한 소개를 원한다면 man 2 intro를 이용하기 바란다. # man 2 intro


시스템 콜을 사용하기 위해서 libc를 통한 포장함수를 호출하는 외에도 syscall()함수를 이용해서 직접 실행시키는 방법도 있다. 각각의 시스템콜은 고유한 번호를 가지고 있는데, syscall에 이 시스템 콜의 번호를 입력하는 방식으로 호출한다. 내부적으로 syscall은 0x80 인터럽트를 이용해서 커널에 명령을 전달한다.

시스템 콜함수들은 syscall.h 와 unistd.h 에 정의되어 있으며, 시스템 콜 테이블은 "arch/i386/kernel/entry.S"리눅스 커널 소스파일에 정의되어 있다.


--------------------------------------------------------------------------------

2. 시스템 콜 예제
#include <syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

int main()
{
   long ID1, ID2;

   // 시스템콜의 직접 사용  
   // 시스템콜 번호 : 20
   ID1 = syscall(SYS_getpid);
   printf("%ld\n", ID1);

   // libc를 이용한 시스템 콜
   // 시스템콜 번호 : 20
   ID2 = getpid();
   printf("%ld\n", ID2);

   return (0);
}
               




--------------------------------------------------------------------------------

3. 시스템 콜 레퍼런스

표 1. 시스템 콜 레퍼런스

번호 함수 이름 설명 소스
1 exit() 현재 프로세스의 종료 kernel/exit.c
2 fork() 자식 프로세스의 생성 arch/i385/kernel/process.c
3 read() 파일 지정자로 부터 읽기 fs/read_write.c
4 write() 파일 지정자로 쓰기 fs/read_write.c
5 open() 파일이나 장치열기 fs/open
6 close() 파일 지정자 닫기 fs/open.c
7 waitpid() 프로세스의 종료를 기다린다 kernel/exit.c
8 creat() 파일이나 장치의 생성 fs/open.c
9 link() 파일을 위한 새로운 이름 만들기 fs/namei.c
10 unlink() 파일 혹은 참조된 이름을 삭제한다 fs/namei.c
11 execv() 프로그램의 실행 arch/i386/kernel/process.c
12 chdir() 작업디렉토리의 변경 fs/open.c
13 time() 초단위의 시간 얻기 kernel/time.h
14 mknod() 일반 혹은 특수파일의 생성 fs/namei.c
15 chmod() 파일의 권한 바구기 fs/open.c
16 chown() 파일의 소유자 변경 fs/open.c
18 stat() 파일의 상태 얻기 fs/stat.c
19 lseek() 파일에서의 위치 변경 fs/read_write.c
20 getpid() 프로세스의 ID를 얻어온다 kernel/sched.c
21 mount() 파일 시스템의 마운트 fs/super.c
22 umount() 파일 시스템 마운트 해제 fs/super.c
23 setuid() 실제 유저 아이디 설정 kernel/sys.c
24 getuid() 실제 유저 아이디 얻어오기 kernel/sched.c
25 stime() 시스템의 시간과 날짜 설정 kernel/time.c
26 ptrace() 부모프로세스가 자식프로세스의 실행을 제어하도록 허가 arch/i386/kernel/ptrace.c
27 alarm() 실정시간후 alarm시그널이 전달되도록 한다. kernel/sched.c
28 fstat() 파일 상태 얻기 fs/stat.c
29 pause() 시그널이 전달될때까지 대기한다. arch/i386/kernel/sys_i386.c
30 utime() 파일의 엑세스시간과 수정시간을 수정한다. fs/open.c
33 access() 파일의 권한을 검사한다. fs/open.c
34 nice() 프로세스의 우선순위를 번경한다. kernel/sched.c
36 sync() 슈퍼블럭을 업데이트 한다. fs/buffer.c
37 kill() 프로세스에 시그널을 전송한다. kernel/signal.h
38 rename() 파일의 이름과 위치를 변경한다. fs/namei.c
39 mkdir() 디렉토리를 생성한다. fs/namei.c
40 rmdir() 디렉토리를 제거한다. fs/namei.c
41 dup() 열린 파일 지정자를 복사한다. fs/fcntl.c
42 pipe() 내부통신을 위한 채널을 생성한다. arch/i386/kernel/sys_i386.c
43 times() 프로세스 시간을 얻는다. kernel/sys.c
45 brk() 프로세스의 데이터 세그먼트 크기를 변경한다. mm/mmap.c
46 setgid() real 그룹 아이디를 설정한다. kernel/sys.c
47 getgid() real 그룹 아이디를 얻어온다. kernel/sched.c
48 sys_signal() ANSI C 시그널 제어 kernel/signal.c
49 geteuid() effective 유저 아이디 가져오기 kernel/sched.c
50 getegid() effective 그룹 아이디 가져오기 kernel/sched.c
51 acct() 프로세스 측정을 켜거나 끈다. kernel/acct.c
52 umount2() 파일시스템 unmount fs/super.c
54 ioctl() 장치 제어 fs/ioctl.c
55 fcntl() 파일 제어 fs/fcntl.c
56 mpx 사용되지 않음  
57 setpgid() 프로세스의 그룹 아이디 설정 kernel/sys.c
58 ulimit() 사용되지 않음  
59 olduname 구식의 uname 시스템콜 arch/i386/kernel/sys_i386.c
60 umaks() 파일 마스크의 생성 kernel/sys.c
61 chroot() 루트디렉토리의 변경 fs/open.c
62 ustat() 파일시스템의 통계 얻기 fs/super.c
63 dup2() 파일 지정자 복사 fs/fcntl.c
64 getppid() 부모 프로세스의 PID 얻기 kernel/sched.c
65 getpgrp() 프로세스의 그룹 아이디 얻기 kernel/sys.c
66 setsid() 세션과 프로세스 그룹 아이디 설정 kernel/sys.c
67 sigaction() POSIX 시그널 제어 함수 arch/i386/kernel/signal.c
68 sigmask() ANSI C 시그널 제어 kernel/signal.c
69 ssetmask() ANSI C 시그널 제어 kernel/signal.c
70 setreuid() 실제 혹은 유효사용자 아이디의 설정 kernel/sys.c
71 setregid() 실제 혹은 유효그룹 아이디의 설정 kernel/sys.c
72 sigsuspend() 시그널 마스크를 일시적으로 대체한후 시그널을 기다린다. arch/i386/kernel/signal.c
73 sigpending() 시그널을 블럭하고 검사를 수행한다. kernel/signal.c
74 sethostname() 호스트이름 설정 kernel/sys.c
75 setrlimit() 자원의 제한값을 설정한다. kernel/sys.c
76 getrlimit() 자원의 제한값을 얻어온다. kernel/sys.c
77 getrusage() 자원의 제한값을 얻어온다. kernel/sys.c
78 gettimeofday() 날짜와 시간을 얻는다. kernel/time.c
79 settimeofday() 날짜와 시간을 설정한다. kernel/time.c
80 getgroups() 포함된 그룹아이디의 목록을 얻는다. kernel/sys.c
81 setgroups() 포함될 르룹아이디의 목록을 설정한다. kernel/sys.c
82 old_select() 오래된 버젼의 입출력다중화 arch/i386/kernel/sys_i386.c
83 symlink() 파일에 대한 심볼릭링크 생성 fs/namei.c
84 lstat() 파일의 상태 얻기 fs/stat.c
85 readlink() 심볼릭 링크의 연결된 파일 이름을 읽는다. fs/stat.c
86 uselib() 공유라이브를 선택한다. fs/exec.c
87 swapon() 파일과 장치의 스와핑을 시작한다. mm/swapfile.c
88 reboot() 리붓 시키거나 Ctrl-Alt-Del을 활성화/비활성화 시킨다. kernel/sys.c
89 old_readdir() 오래된 버젼의 디렉토리 내용읽기 fs/readdir.c
90 old_mmap() 오래된 버젼의 메모리 파일 대응 arch/i386/kernel/sys/i386.c
91 mnunmap() 메모리 페이지 해제 mm/mmap.c
92 truncate() 파일의 길이 결정 fs/open.c
93 ftruncate() 파일의 길이 결정 fs/open.c
94 fchmod() 파일의 권한 변경 fs/open.c
95 fchown() 파일의 그룹및 소유자 변경 fs/open.c
96 getpriority() 프로그램의 우선순위 얻어오기 kernel/sys.c
97 setpriority() 프로그램의 우선순위 설정 kernel/sys.c
98 profile() execution time profile  
99 statfs() 파일시스템 정보 얻기 fs/open.c
100 fstatfs() 파일시스템 정보 얻기 fs/open.c
101 ioperm() set port input/output permissions arch/i386/kernel/ioport.c
102 socketcall() 소켓 시스템콜 net/socket.c
103 syslog() 커널 메시지 버퍼의 내용을 읽거나 클리어한다. kerne/printk.c
104 setitimer() 내부 타이머 설정 kernel/itimer.c
105 getitimer() 내부 타이머 값 가져오기 kernel/itimer.c
106 sys_newstat() 파일의 상태 얻기 fs/stat.c
107 sys_newlstat() 파일의 상태 얻기 fs/stat.c
108 sys_newfstat() 파일의 상태 얻기 fs/stat.c
109 olduname() 최근 커널의 정보얻기 arch/i386/kernel/sys_i386.c
110 iopl() I/O privilege 레벨 변경 arch/i386/kernel/ioport.c
111 vhangup() 가상으로 현재 tty를 중지시킨다. fs/open.c
112 idle() 0번 프로세스를 idel상태로 한다. arch/i386/kernel/process.c
113 vm86old() 가상 8086모드로 들어가기 arch/i386/kernel/vm86.c
114 wait4() 프로세스의 종료를 기다린다. BSD 스타일 kernelk/exit.c
115 swapoff() 파일/장치의 스와핑 끝내기 mm/swapfile.c
116 sysinfo() 시스템의 정보 얻어오기 kernel/info.c
117 ipc() System V IPC 시스템 콜 arch/i386/kernelk/sys_i386.c
118 fsync() 파일의 내부상태와 디스크상의 상태를 동기화 한다. fs/buffer.c
119 sigreturn() 시그널 핸들러와 클린업 스택 프레임으로 부터 반환 arch/i386/kernel/signal.c
120 clone() 자식 프로세스의 생성 arch/i386/kernel/process.c
121 setdomainname() 도메인 이름 설정 kernel/sys.c
122 uname() 최근 커널의 정보 얻어오기 kernel/sys.c
123 modify_ldt() ldt를 가져오거나 설정한다. arch/i386/kernel/ldt.c
124 adjtmex() 커널 클럭을 조율한다. kernel/time.c
125 mprotect() 메모리 영역에 대한 접근을 제어한다. mm/mprotect.c
126 sigprocmask() POSIX 시그널 제어 관련 함수 kernel/signal.c
127 create_module() 적재가능한 모듈엔트리 생성 kernel/module.c
128 init_module() 적재가능한 모듈 엔트리 초기화 kernelk/module.c
129 delete_module() 적재 모듈의 삭제 kernel/module.c
130 get_kernel_syms() retrieve exported kernel and module symbols kernel/module.c
131 quotactl() 디스크 쿼터 수정 fs/dquot.c
132 getpgid() 프로세스 그룹아이디 가져오기 kernel/sys.c
133 fchdir() 작업 디렉토리 변경 fs/open.c
134 bdflush() start, flush, buffer-dirty-flush 데몬을 조정한다 fs/buffer.c
135 sysfs() 파일시스템 타입정보 가져오기 fs/super.c
136 personality() 프로세스 실행 도메인 설정 kernel/exec_domain.c
137 afs_syscall() 사용하지 않음  
138 setfsuid() 파일 시스템 검사를 위해 사용되는 사용자 실별자를 설정 kernel/sys.c
139 setfsgid() 파일 시스템 검사를 위해 사용되는 그룹 식별자를 설정  
140 sys_llseek() 읽기/쓰기 파일의 위치 이동 fs/read_write.c
141 getdents() 디렉토리 내용을 읽어들인다. fs/readdir.c
142 select() 입출력 다중화 fs/select.c
143 flock() 열린파일에 대한 권고잠금 적용및 제거 fs/locks.c
144 msync() 메모리 맵과 파일의 동기화 mm/filemap.c
145 readv() 벡터를 읽는다 fs/read_write.c
146 writev() 벡터를 쓴다 fs/read_write.c
147 sys_getsid() 세션리더의 프로세스 아이디를 가져온다 kernel/sys.c
148 fdatasync() 파일의 디스크에 있는 in-core 데이터를 동기화 fs/buffer.c
149 sysctl() 시스템 파라메터를 읽고 쓴다  
150 mlock() 메모리의 페이지 잠금 mm/mlock.c
151 munlock() 메모리의 페이지 잠금 풀기 mm/mlock.c
152 mlockall() 호출한 프로세스의 페이징을 금지시킨다 mm/mlock.c
153 munlockall() 호출한 프로세스에 대한 페이징을 다시 가능하도록 한다. mm/mlock.c
154 sched_setparam() 스케줄 파라메터 설정 kernel/sched.c
155 sched_getparam() 스케쥴 파라메터 설정값 가져오기 kernel/sched.c
156 sched_setscheduler() 스케쥴 알고리즘 파라메터 설정 kernel/sched.c
157 sched_getscheduler() 스케쥴 알고리즘 파라메터 값 가져오기 kernel/sched.c
158 sched_yield()   kernel/sched.c
159 sched_get_priority_max() 정적 선행 범위를 가진다 kernel/sched.c
160 sched_get_priority_mix()   kernel/sched.c
161 sched_rr_get_interval() 프로세스의 SCHED_RR간격을 가져온다. kernel/sched.c
162 nanosleep() 지정한 시간에 실행을 잠시 멈춘다 kernel/sched.c
163 mremap() 가상 메모리 주소를 재대응시킨다 mm/mremap.c
164 setresuid() set real, effective and saved user or group ID kernel/sys.c
165 getresuid() get real, effective and saved user or group ID kernel/sys.c
166 vm86() 8086가상 모드로 진입 arch/i386/kernel/vm86.c
167 query_module() query the kernel for various bits pertaining to modules kernel/module.c
168 poll() 파일 지정자로 부터 이벤트를 기다린다 fs/select.c
169 nfsservctl() 커널 nfs 데몬을 위한 인터페이스 fs/filesystems.c
170 setresgid() set real, effective and saved user or group ID kernel/sys.c
171 getresgid() get real, effective and saved user or group ID kernel/sys.c
172 prctl() 프로세스상에서의 실행 kernel/sys.c
173 rt_sigreturn   arch/i386/kernel/signal.c
174 rt_sigaction   kernel/signal.c
175 rt_sigprocmask   kernel/signal.c
176 rt_sigpending   kernel/signal.c
177 rt_sigtimedwait   kernel/signal.c
178 rt_sigqueueinfo   kernel/signal.c
179 rt_sigsuspend   arch/i386/kernel/signal.c
180 pread() 파일 지정자로 부터 위치를 가져오거나 읽는다 fs/read_write.c
181 sys_pwrite() 파일 지정자로 부터 위치를 가져오거나 쓴다 fs/read_write.c
182 chown() 파일 소유자 변경 fs/open.c
183 getcwd() 최근 작업 디렉토리 가져오기 fs/dcache.c
184 capget() 프로세스 기능의 설정값 가져오기 kernel/capability.c
185 capset 프로세스 기능 설정하기 kernle/capability.c
186 sigaltstack() 시그널 스택 문맥을 가져오가나 설정 arch/i386/kernel/signal.c
187 sendfile() 파일 지정자 사이의 데이터 교환 mm/filemap.c
188 getpmsg() 사용하지 않음  
189 putpmsg() 사용하지 않음  
190 vfork() 자식 프로세스 생성과 부모 프로세스 블럭 arch/i386/kernel/process.c

출처 : http://www.joinc.co.kr/modules.php?name ··· 3Dnested
"System" 카테고리의 다른 글
  • Secure programmer: 컴포넌트를 안전하게 호출하기 (0)2007/05/04
  • 리눅스에서의 메모리관리 (0)2007/05/04
  • 리눅스 시스템 콜 레퍼런스 (0)2007/05/04
  • Linux 하드웨어 안정성 가이드, Part 2 (0)2007/05/04
  • Linux 하드웨어 안정성 가이드, Part 1 (0)2007/05/04
2007/05/04 15:14 2007/05/04 15:14
Posted by webdizen
Tags 리눅스, 콜 레퍼런스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2899

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Develop2007/05/04 14:25

Linux 애플리케이션을 위한 DLL 작성하기

plugin 작성으로 전체 애플리케이션 구현 효과 얻기  

Allen Wilson
e-business architect (IBM)
2001년 10월

Plugin과 DLL은 전체 애플리케이션을 새로 작성하지 않고 기능을 추가 할 수 있는 좋은 방법이다. Linux에서 plugin과 DLL은 동적 라이브러리로서 구현된다. e-business 컨설턴트이자 아키텍트인 Allen Wilson은 동적 라이브러리를 소개하고 그것들을 사용하여 애플리케이션이 실행된 후 애플리케이션을 변경하는 방법을 설명한다.
plugin은 인터넷 사용자들에게 친숙할 것이다. plugin은 웹에서 다운로드를 받아, 오디오, 비디오, 또는 브라우저 내의 특별한 효과를 지원한다. 일반적으로 plugin은 기존 애플리케이션을 변경하지 않고 기존 애플리케이션에 새로운 함수들을 제공한다.

DLL은 프로그램 함수이다. 애플리케이션의 주요 프로그램은 dll이 디스크상에서 주요 애플리케이션과 분리된 파일에 존재하고 있으면서 런타임시 요청된 dll을 선택적으로 로딩하는 프레임웍이나 backplan과 함께 구현된다. 이러한 패키징(packaging)과 동적 로딩으로 인해 업그레이드, 유지관리, 라이센싱 전략이 유연성을 갖추게 된다.

Linux에는 적어도 libc 라이브러리 함수에 필요한 수 천개의 명령어와 애플리케이션이 있다. libc 함수가 모든 프로그램과 함께 패키징된다면 수천 카피의 같은 함수들이 디스크상에 존재하게 된다. Linux는 디스크 공간을 낭비하지 않고 이러한 단일 시스템 규모의 카피를 사용하기 위해서 애플리케이션을 구현한다. 게다가 일반적인 시스템 라이브러리 함수를 필요로 하는 각 프로세스는 단일 시스템 규모의 카피를 사용하는데 이것은 메모리로 로딩되어 공유된다.

Linux 에서, plugin과 dll은 동적 라이브러리로서 구현된다. 이 글의 나머지 부분은 애플리케이션이 실행된 후에 애플리케이션을 변경하기 위해 동적 라이브러리를 사용하는 예제들이다.

Linux 동적 링크
Linux의 애플리케이션들은 한 가지 이상의 방법으로 외부 함수와 연결된다: 정적 라이브러리 (lib*.a)를 이용하고, 라이브러리 코드를 애플리케이션의 실행 파일에 포함시켜 구현할 때 정적으로 링크되는 방법이 그 중 하나이고 또 다른 하나는 공유 라이브러리(lib*.so)를 이용하여 런타임 시 동적으로 링크하는 것이다. 동적 라이브러리는 동적 링크 로더에 의해 애플리케이션 실행 메모리로 매핑된다. 애플리케이션이 시작되기 전에, 동적 링크 로더는 필요한 공유 객체 라이브러리를 애플리케이션의 메모리로 매핑하거나 시스템 공유 객체를 사용하거나 또는 애플리케이션에 필요한 외부 레퍼런스를 분석한다. 이제, 애플리케이션은 실행 할 준비가 되었다.

다음 예제는 Linux에서 동적 라이브러리의 기본적인 사용에 대한 작은 프로그램이다:




gcc hello.c로 컴파일 될 때, a.out 라는 이름의 실행파일 이름이 만들어진다. 공유 라이브러리를 프린트하는 Linux 명령어 ldd a.out 을 사용할 때에는, 필요한 공유 라이브러리는 다음과 같다:


        libc.so.6 => /lib/libc.so.6 (0x4001d000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)



같은 동적 링크 로더가 실행된 후에 dll을 애플리케이션의 메모리로 매핑하는 데 사용된다. 애플리케이션은 어떤 동적 라이브러리가 로딩되는지 또 라이브러리에서 어떤 함수가 호출되는 지를 제어한다. 로딩과 링크를 수행하고 필요한 엔트리 포인트의 주소를 리턴하기 위해서 Linux 동적 로더를 사용한다.

Linux dll 함수
Linux는 동적 링크 로더를 지원하기 위해 네 가지의 라이브러리 함수(dlopen, dlerror, dlsym, dlclose), 한 개의 include file (dlfcn.h), 그리고 두 개의 공유 라이브러리 (정적 library libdl.a, 동적 library libdl.so)를 제공한다. 다음은 라이브러리 함수에 대한 설명이다:

dlopen 은 오픈되어 메모리를 공유 객체 파일로 매핑하고 핸들을 리턴한다.
dlsym 는 포인터를 요청된 엔트리 포인트로 리턴한다.
dlerror 는 NULL 또는 포인터를 최신의 에러를 설명하는 an ASCII 스트링으로 리턴한다.
dlclose 는 핸들을 닫고 공유 객체의 매핑을 푼다.

동적 링크 로더 루틴인 dlopen은 파일을 열고 핸들을 만들기 위해서 파일시스템에서 공유 객체를 찾아야 한다. 파일 위치를 지정하는 네 가지의 방법이 있다:

dlopen call 에서 절대적 파일 경로
LD_LIBRARY_PATH 환경 변수에서 지정된 디렉토리
/etc/ld.so.cache에 지정된 라이브러리 리스트
/usr/lib 그리고 /lib

dll 예제: C 프로그램과 dlTest
동적 링크 로더 예제 프로그램은 dl 루틴들을 시험하기 위한 C 프로그램이다. 모든 사람이 작성한 C 프로그램에 기초하여 콘솔에 "Hello World" 을 프린트한다. 프린트 된 원래의 메시지는 "HeLlO WoRlD" 이다. 이 테스트는 메시지를 다시 프린트 하는 두 개의 함수에 연결된다: 첫 번째는 대문자로 그 다음은 소문자로 프린트 된다.

다음은 프로그램의 아웃라인이다:

dll은 dlfcn.h 파일을 포함하고 있고 필요한 변수가 정의된다. 필요한 변수는 다음과 같다:
공유 라이브러리 파일을 위한 핸들
매핑된 함수 엔트리 포인트를 위한 포인터
에러 스트링을 위한 포인터


원래 메시지인 "HeLlO WoRlD"가 프린트 된다.


UPPERCASE dll 용 공유 객체 파일은 dlopen 에 의해 열리고 핸들은 absolute path인 "/home/dlTest/UPPERCASE.so"과 RTLD_LAZY 옵션을 사용하여 리턴된다.
RTLD_LAZY 옵션은 dll의 외부 레퍼런스 변환을 dll이 실행될 때까지 지연한다.
Option RTLD_NOW 옵션은 dlopen이 리턴되기 전에 모든 dll 외부 레퍼런스를 변환한다.


엔트리 포인트 printUPPERCASE 어드레스는 dlsym에 의해 리턴된다.


printUPPERCASE는 호출되고 변경된 메시지인 "HELLO WORLD"가 프린트된다.


UPPERCASE.so에 대한 핸들은 dlclose에 의해 닫히고 dll은 메모리에서 매핑이 해지된다.


lowercase dll을 위한 공유 객체 파일인 lowercase.so는 dlopen에 의해 열리고 공유 객체를 검색하기 위해 환경 변수인 LD_LIBRARY_PATH를 사용하여 핸들은 리턴된다.


엔트리 포인트 printLowercase 어드레스는 dlsym에 의해 리턴된다.


printLowercase가 호출되고 변경된 메시지인 "hello world"가 프린트된다.


lowercase.so로의 핸들은 dlclose 에 의해 닫히고 dll은 메모리에서 매핑이 해지된다 .
dlopen, dlsym 또는 dlclose 로의 각 호출 후에 마지막 에러와 에러 스트링을 프린트하기 위해서 dlerror가 호출된다. 다음은 dlTest의 실행 테스트이다:


       dlTest  2-Original message
HeLlO WoRlD
       dlTest  3-Open Library with absolute path return-(null)-
       dlTest  4-Find symbol printUPPERCASE return-(null)-
HELLO WORLD
       dlTest  5-printUPPERCASE return-(null)-
       dlTest  6-Close handle return-(null)-
       dlTest  7-Open Library with relative path return-(null)-
       dlTest  8-Find symbol printLowercase return-(null)-
hello world
       dlTest  9-printLowercase return-(null)-
       dlTest 10-Close handle return-(null)-



dlTest.c, UPPERCASE.c, lowercase.c에 대한 소스리스트는 Listings 을 참조하라 .

dlTest 구현하기
런타임 동적 링크를 만드는 데에는 다음 세 단계 절차를 거친다:

dll을 위치 독립 코드로서 컴파일
dll 공유 객체 파일 구현
주요 프로그램의 컴파일 및 dl 라이브러리와 링크
UPPERCASE.c와 lowercase.c를 컴파일하는 gcc 명령어에는 -fpic 옵션이 포함되어 있다. -fpic 옵션과 -fPIC 옵션은 코드 생성이 위치 독립적이 되도록 한다. 이것은 공유 객체 라이브러리를 다시 만드는 데 필요하다. -fPIC 옵션은 위치 독립적인 코드를 만들고 이것은 큰 오프셋을 위해 실행된다. UPPERCASE.o 와 lowercase.o 를 위한 두 번째 gcc 명령어는 -shared 옵션으로 전달된다. 이것은 동적 링크에 적합한 *.so 공유 객체 파일을 만든다.

dltest를 컴파일 하고 실행하는 데 사용되는 ksh 스크립트이다:






요약
Linux 시스템에서 런타임 시 애플리케이션으로 동적 링크될 수 있는 공유 객체를 만드는 것은 간단하다. 애플리케이션은 dlopen, dlsym, dlclose같은 함수 호출을 동적 링크 로더에 사용함으로서 공유 객체에 액세스 할 수 있다. 모든 에러는 dlerror에 의해 스트링으로 리턴된다. 런타임 시, 주요 애플리케이션은 absolute path 또는 LD_LIBRARY_PATH에 path relative를 사용하여 공유 객체 라이브러리를 찾고 필요한 dll 엔트리 포인트의 어드레스를 요청한다. 필요할 경우 간접적인 함수 호출이 dll에 이루어지고, 마지막으로 공유 객체로의 핸들은 종료되고 객체들은 메모리에서 매핑이 해지되어 사용할 수 있게 된다.

공유 객체는 추가 옵션인 -fpic 또는 -fPIC으로 컴파일 되어 위치 독립적인 코드를 만들고 -shared 옵션으로 공유 객체 라이브러리에 위치한다.

Linux에서 사용할 수 있는 공유 객체 라이브러리와 동적 링크 로더는 애플리케이션에 추가적인 기능을 제공한다. 디스크와 메모리에서 실행파일의 사이즈를 줄인다. 선택적인 애플리케이션 함수들은 필요할 경우 로딩 될 수 있고, 전체 애플리케이션을 다시 구현하지 않고도 결함이 픽스될 수 있고 third-party plugin도 애플리케이션에 포함될 수 있다.

Listings (애플리케이션과 dll)





원문 : http://www-903.ibm.com/developerworks/k ··· dll.html
"Develop" 카테고리의 다른 글
  • GDB를 이용한 Linux 소프트웨어의 디버깅 (0)2007/05/04
  • 객체 비지향(object disoriented)을 위한 공유 객체 (0)2007/05/04
  • Linux 애플리케이션을 위한 DLL 작성하기 (0)2007/05/04
  • 리눅스에 네트워크 라우터 구현하기 (0)2007/05/04
  • GNU Coding Standards (0)2007/04/30
2007/05/04 14:25 2007/05/04 14:25
Posted by webdizen
Tags Dll, 리눅스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2892

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Develop2007/05/04 14:23

리눅스에 네트워크 라우터 구현하기

Cisco 라우터의 강력한 대안-Zebra Level: Intermediate

Dominique Cimafranca , IT Specialist, IBM
Rex Young, Network Manager, C-Cube

2003년 10월 8일

Zebra는 오픈 소스 TCP/IP 라우팅 소프트웨어이다. Cisco의 Internetworking Operating System (IOS)와 비슷하다. 유연하고 강력하여 Routing Information Protocol (RIP), Open Shortest Path First (OSPF), Border Gateway Protocol (BGP) 등의 라우팅 프로토콜을 핸들 할 수 있다.
동적이고 강력한 라우팅은 인터넷 작동에 필수적이다. 인터네트워킹(internetworking)의 초보 엔지니어는 이것의 개념을 알아야 할 뿐만 아니라 실제 상황에 적용할 수 있어야 한다.

우리는 TCP/IP 라우팅에 클래스를 조직할 때 어떤 문제에 직면했다. 작은 테스트 네트워크로 작업하면서 Routing Information Protocol (RIP)과 Open Shortest Path First (OSPF)로 다양한 로드 밸런싱 시나리오를 설명 할 필요를 느꼈다. 하지만 사용할 수 있는 Cisco 라우터의 수는 제한되어 있었다. 사용할 수 있는 몇 개의 PC를 갖고 있어서 리눅스를 사용하여 Cisco 라우터의 대안을 찾기 시작했다.

처음에는 전통적인 라우팅 및 게이팅 데몬을 사용하여 테스트 네트워크를 설정하려고 했지만 이것의 어색한 설정과 제한된 기능은 가치보다 장애가 더 많이 만들어진다는 것을 빠르게 깨달았다. 네트워크를 완료할 보다 현대적인 수단을 찾기로 결정했는데 운좋게도 Zebra가 우리에게 왔다.

Zebra
Zebra는 TCP/IP 라우팅 소프트웨어로서 BGP-4, BGP-4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng를 지원한다. GNU General Public License로 배포되며 리눅스는 물론 유닉스 계열 머신에서도 실행된다. Zebra는 대부분의 배포판에 라우팅 소프트웨어로서 포함되어 있다. 최신 버전(문서 포함)은 GNU Zebra 웹 사이트에서 사용할 수 있다. (참고자료).

원래의 Zebra 패키지는 1966년에 Kunihiro Ishiguro와 Yoshinari Yoshikawa가 작성했다. 요즘 이 패키지는 IP Infusion이 관리하고 있고 네트워킹 엔지니어와 오픈 소스 자원자들이 도움을 주고있다.

Zebra는 디자인이 독특하다. 이것이 관리하는 프로토콜에 모듈식의 접근 방식을 취하기 때문이다. 프로토콜들은 네트워크의 요구사항 명령에 따라 실행 가능 또는 불가가 될 수 있다.

지금까지, Zebra에서 찾아낸 가장 유용한 기능은 Cisco IOS 설정 포맷과의 긴밀한 유사성이다. IOS와는 약간 차이점이 있지만 IOS에 익숙한 네트워크 엔지니어가 이 환경에 안정감을 느낄 정도로 유사하다.

Zebra 설치
Zebra 테스트를 위한 플랫폼은 오래된 것이지만 강력한 ThinkPad X20(Red Hat Linux 9)로 정했다. ThinkPad에는 빌트인 Ethernet 포트가 있어서 또 다른 Ethernet PCMCIA 카드를 추가했다. 이것은 라우터로서 작동할 수 있었다. Zebra 설치 전에 두 개의 네트워크 카드를 리눅스가 인식하는지와 작동하는지를 확인했다.

RPM for Zebra-0.93b가 Red Hat 9에 이미 포함되어 있었다. Zebra RPM은 바이너리, 스크립트, 설정 파일, 매뉴얼, 예제, 문서 파일 등을 설치한다.

Zebra 기본 설정
Zebra 데몬은 실제 라우팅 매니저로서 다른 모듈들을 제어한다. 또한 사용자와의 인터랙션 기본 포인트도 제공한다. 이것이 설정할 때 필요한 첫 번째이고 /etc/zebra/zebra.conf 파일을 통해 이를 수행했다.

Zebra RPM 패키지에는 완성된 샘플 설정 파일이 포함되어 있다. 하지만 최소한 다음 라인을 포함하는 /etc/zebra/zebra.conf 파일을 만들기만 해도 된다:

Listing 1. Zebra 설정 파일

hostname speedmetal
password zebra
enable password zebra



hostname 명령어는 인터랙티브 설정 모드로 들어갈 때 라우터 이름을 정의한다. 어떤 레이블도 가능하며 머신의 호스트네임에 상응하지 않아도 된다.

password 명령어는 인터랙티브 Zebra 터미널에 로그인할 때의 패스워드를 지정한다.

enable password 명령어는 고급 Zebra 액세스를 위한 패스워드를 정의한다. 설정을 변경할 때 쓰인다.

/etc/zebra/zebra.conf 파일을 만들면 다음을 실행하여 Zebra를 시작할 수 있다:

# service zebra start

이제 우리 머신의 port 2601의 텔넷팅을 통해 Zebra 인터랙티브 세션에 들어갈 수 있다.

Listing 2. Zebra 세션 샘플

[root@speedmetal zebra]# telnet 127.0.0.1 2601
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

Hello, this is zebra (version 0.93b).
Copyright 1996-2002 Kunihiro Ishiguro.

User Access Verification

Password: zebra
speedmetal> enable
Password: zebra
speedmetal# ?
 configure  Configuration from vty interface
 copy       Copy configuration
 debug      Debugging functions (see also 'undebug')
 disable    Turn off privileged mode command
 end        End current mode and change to enable mode.
 exit       Exit current mode and down to previous mode
 help       Description of the interactive help system
 list       Print command list
 no         Negate a command or set its defaults
 quit       Exit current mode and down to previous mode
 show       Show running system information
 terminal   Set terminal line parameters
 who        Display who is on vty
 write      Write running configuration to memory, network, or terminal
speedmetal#



인터랙티브 터미널 내부를 돌아다니기란 쉽지 않다. 사용가능한 명령어 힌트를 보려면 언제라도 ?를 누르면 된다. 그러면 스크린이 나타날 것이다. 자신의 Zebra 라우터를 설정한다면 이 설정은 Cisco와 매우 유사하게 될 것이다.

이 지점에서 Zebra만이 설정 및 실행되었다. 다른 프로토콜들은 그렇지 않았다.

MRLG 설정 및 사용
Multi-Router Looking Glass(MRLG)는 EnterZone의 John Frazier가 작성한 것으로 웹 기반 유틸리티이다. Zebra에 의해 인식되는 인터페이스와 라우터를 디스플레이하는데 사용된다. MRLG는 제한된 명령어를 갖춘 Zebra 쉘에 대한 웹 인터페이스 그 이상도 이하도 아니다. 하지만 테스팅 과정 중에 라우트를 빠르고 유용하게 디스플레이 할 방법을 발견했다. Zebra 프로토콜을 설정하기 전에 설치 과정을 설명하겠다.

MRLG가 Zebra 쉘과 통신하기 위해서는 Net::Telnet Perl 패키지가 필요하다. 안타깝게도 Red Hat 9 에는 포함되지 않았다. 따라서 다운로드 할 수 밖에 없었다. (참고자료).

MRLG가 CGI 애플리케이션으로서 실행되기 때문에 웹 서버를 설치해야 했다. 이를 스스로 해보려면 httpd RPM을 사용해도 된다.

/usr/share/doc/zebra-0.93b/tools 에서 /var/www/cgi-bin 으로 mrlg.cgi 파일을 복사했다. 그런다음 36번 행을 다음과 같이 수정했다:

$url="http://www.sample.com/mrlg.cgi";

를

$url="http://127.0.0.1/cgi-bin/mrlg.cgi"; 로

168에서 174행 까지의 블록을 수정했다. 다음과 같이 읽힌다:


if ($Form{'router'} eq 'router1')
       {
$server = '127.0.0.1';
$login_pass = 'zebra';
$bgpd = "2605";
$zebra = "2601";
$full_tables=1;



MRLG에 액세스 하려면 브라우저를 http://127.0.0.1/cgi-bin/mrlg.cgi에 둔다.

그림 1. Multi-Router Looking Glass
사용자 삽입 이미지


기본 lab 설정
우리의 lab 설정은 두 개의 Cisco 3620 라우터와 빌트인 Ethernet 포트와 Home-and-Away PCMCIA Ethernet 카드를 갖춘 ThinkPad X20으로 구성되었다. 이 두 개의 라우터는 시리얼 커넥션을 통해 서로 통신하고 각 라우터는 Ethernet을 통해 ThinkPad로 연결된다. 다음이 이러한 연결 다이어그램이다. :

그림 2. Lab 연결 다이어그램
사용자 삽입 이미지


Zebra로 인터페이스 설정하기
RIP로 시작되는 Zebra를 사용하여 라우팅에 첫 번째로 개입했다. ThinkPad에 Zebra를 설치했다. ThinkPad에 또 다른 네트워크 인터페이스가 필요했기 때문에 다음과 같은 더미 네트워크 디바이스를 설치했다:

# modprobe dummy
# ifconfig dummy0

설정을 시작하기 위해 Zebra 포트에 텔넷팅을 했다. Zebra와의 다이얼로그는 다음 순서와 같다:

Listing 3. IP 인터페이스 설정하기

User Access Verification

Password: zebra
speedmetal> enable
Password: zebra
speedmetal# configure terminal
speedmetal(config)# interface eth0
speedmetal(config-if)# ip address 192.168.2.1/30
speedmetal(config-if)# quit
speedmetal(config)# interface eth1
speedmetal(config-if)# ip address 192.168.1.1/30
speedmetal(config-if)# quit
speedmetal(config)# interface dummy0
speedmetal(config-if)# ip address 10.0.2.1/24
speedmetal(config-if)# write
Configuration saved to /etc/zebra/zebra.conf
speedmetal(config-if)# end
speedmetal# show run

Current configuration:
!
hostname speedmetal
password zebra
enable password zebra
!
interface lo
!
interface eth0
ip address 192.168.2.1/30
!
interface dummy0
ip address 10.0.2.1/24
!
interface eth1
ip address 192.168.1.1/30
!
!
line vty
!
end



일반적인 방법으로 ThinkPad에 IP 어드레스를 설정하지 않았다는 것을 기억하라. 대신 Zebra를 통해 설정했다. 이 설정은 설정파일인 /etc/zebra/zebra.conf에서 유지되어 Zebra 서비스가 시작할 때 마다 이 설정이 발효된다.

zebra.conf 파일의 내용은 다음과 같다 :

Listing 4. /etc/zebra/zebra.conf-Zebra에 의해 변경

!
! Zebra configuration saved from vty
!   2003/08/20 00:07:51
!
hostname speedmetal
password zebra
enable password zebra
!
interface lo
!
interface eth0
ip address 192.168.2.1/30
!
interface dummy0
ip address 10.0.2.1/24
!
interface eth1
ip address 192.168.1.1/30
!
!
line vty
!



"router1"을 선택하여 MRLG를 사용하는 인터페이스의 상황을 점검할 수 있다. "show interface" 라디오 버튼을 누르고 "Execute"를클릭한다.

Zebra를 이용하여 RIP 라우팅 설정하기
ThinkPad/라우터에 네트워크 인터페이스를 설정했기 때문에 RIP 업데이트와 작동할 것을 설정한 것이다. 앞서 언급했지만 Zebra는 개별 데몬을 사용하여 라우팅 프로토콜들을 구현하기 때문에 /etc/zebra에 있는 RIP 데몬을 위해 우선 간단한 설정파일인 ripd.conf를 만들어야 했다. .

Listing 5. 기본적인 /etc/zebra/ripd.conf 파일

hostname speedmetal-rip
password zebra
enable password zebra



ripd 데몬을 시작한다:

# service ripd start

이것이 실행되면 Zebra 라우터의 포트 2602로 텔네팅하여 RIP 데몬을 설정할 수 있었다.

Listing 6. RIP 설정하기

User Access Verification

Password: zebra
speedmetal-rip> enable
Password: zebra
speedmetal-rip# configure terminal
speedmetal-rip(config)# router rip
speedmetal-rip(config-router)# network 10.0.0.0/8
speedmetal-rip(config-router)# network 192.168.0.0/16
speedmetal-rip(config-router)# end
speedmetal-rip# show run

Current configuration:
!
hostname speedmetal-rip
password zebra
enable password zebra
!
interface lo
!
interface eth0
!
interface dummy0
!
router rip
network 0.0.0.0/0
network 192.168.0.0/16
!
line vty
!
end
speedmetal-rip# write
Configuration saved to /etc/zebra/ripd.conf
speedmetal-rip#



결과 ripd.conf 설정 파일은 다음과 같다:

Listing 7. /etc/zebra/ripd.conf 파일

!
! Zebra configuration saved from vty
!   2003/08/19 13:50:30
!
hostname speedmetal-rip
password zebra
enable password zebra
!
interface lo
!
interface eth0
!
interface eth1
!
interface dummy0
!
router rip
network 10.0.0.0/8
network 192.168.0.0/16
!
line vty
!



Cisco 라우터에 RIP 라우팅 설정하기
두 개의 Cisco 3620 (각각 이름을 A와 B로 지었다) 라우터 설정을 이용하기 위해 라우터가 적절히 실행되는데 필요한 최소한의 설정만 하였다. 인터페이스 IP 어드레스, 루프백(loopback) 어드레스, 적절한 시리얼 포트 통신을 위한 시리얼 clockrate를 설정하였다.

Listing 8. 라우터 A 설정하기

Router#config terminal
Router(config)#hostname RouterA
RouterA(config)#int s0/0
RouterA(config-if)#ip address 192.168.0.1 255.255.255.252
RouterA(config-if)#no shut
RouterA(config-if)# interface fastEthernet 0/0
RouterA(config-if)#ip address 192.168.2.2 255.255.255.252
RouterA(config-if)#no shut
RouterA(config-if)#int loopback 0
RouterA(config-if)#ip address 10.0.0.1 255.255.255.0
RouterA(config-if)#end
RouterA#write



라우터 B 설정도 비슷하다.

Listing 9. 라우터 B 설정하기

Router#configure terminal
Router(config)#hostname RouterB
RouterB(config)#int s0/0
RouterB(config-if)#ip address 192.168.0.2 255.255.255.252
RouterB(config-if)#no shut
RouterB(config-if)#int fastEthernet0/0
RouterB(config-if)#ip address 192.168.1.2 255.255.255.252
RouterB(config-if)#no shut
RouterB(config-if)#int loopback 0
RouterB(config-if)#ip address 10.0.1.1 255.255.255.0
RouterB(config-router)#end
RouterB#write



3620 라우터에 RIP을 설정하는 것은 Zebra의 명령어와 매우 비슷하다. 콘솔 케이블을 통해 두 개의 3620에 액세스 하여 다음의 명령어를 실행시켰다:

Listing 10. RIP에 라우터 A 설정하기

RouterA#conf t
Enter configuration commands, one per line. End with CNTL/Z.
RouterA(config)#router rip
RouterA(config-router)#network 10.0.0.0
RouterA(config-router)#network 192.168.0.0
RouterA(config-router)#network 192.168.2.0
RouterA(config-router)#version 2
RouterA(config-router)#end
RouterA#write



그리고 B는:

Listing 11. RIP에 라우터 B 설정하기

RouterB#conf t
Enter configuration commands, one per line. End with CNTL/Z.
RouterB(config)#router rip
RouterB(config-router)#network 10.0.1.0
RouterB(config-router)#network 192.168.0.0
RouterB(config-router)#network 192.168.1.0
RouterB(config-router)#version 2
RouterB(config-router)#end
RouterB#write



router rip 명령어는 RIP 프로세스를 실행한다. network 명령어는 RIP에 의해 선전될 네트워크가 어떤 것인지를 라우터에게 말해준다.

RIP을 이용하여 선전된 라우트
Cisco 라우터와 Zebra가 모두 설정되었으므로 선전되고 있는 라우트를 점검한다. MRLG에서 "show ip route"를 선택하고 "Execute"를 클릭한다. 다음과 같은 리포트가 나온다:

Listing 12. RIP 라우트를 반영하는 Zebra

Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
      B - BGP, > - selected route, * - FIB route

R>* 10.0.0.0/24 [120/2] via 192.168.2.2, eth0, 00:11:05
R>* 10.0.1.0/24 [120/2] via 192.168.1.2, eth1, 00:02:08
C>* 10.0.2.0/24 is directly connected, dummy0
K * 127.0.0.0/8 is directly connected, lo
C>* 127.0.0.0/8 is directly connected, lo
R>* 192.168.0.0/30 [120/2] via 192.168.2.2, eth0, 00:11:05
C>* 192.168.1.0/30 is directly connected, eth1
C>* 192.168.2.0/30 is directly connected, eth0



RIP을 통해 알려진 라우트들은 R로 표시된다.

Zebra는 10.0.0.0/24와 10.0.1.0/24 네트워크에 대해 알고 있다. ThinkPad Zebra 라우터에서 10.0.0.1과 10.0.1.1을 PING 하여 이것을 테스트했다.

라우트 페일오버(failover)를 테스트하기 위해 라우터 A에서 네트워크 연결을 끊었다. 약 2분 정도의 타임아웃 뒤에 Zebra는 10.0.0.0/24 네트워크의 대안 라우트를 알게되고 라우터 B를 통해 갔다.

Listing 13. RIP 라우트를 반영하는 Zebra

Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
      B - BGP, > - selected route, * - FIB route

R>* 10.0.0.0/24 [120/3] via 192.168.1.2, eth0, 00:00:26
R>* 10.0.1.0/24 [120/2] via 192.168.1.2, eth1, 00:06:02
C>* 10.0.2.0/24 is directly connected, dummy0
K * 127.0.0.0/8 is directly connected, lo
C>* 127.0.0.0/8 is directly connected, lo
R>* 192.168.0.0/30 [120/2] via 192.168.1.2, eth1, 00:00:26
C>* 192.168.1.0/30 is directly connected, eth1
C>* 192.168.2.0/30 is directly connected, eth0



총 타임아웃이 2분 이상이 된 이유는? RIP의 디폴트 타임아웃은 30초이다. 하지만 RIP 프로토콜은 라우트가 무효라는 것과 무효 라우트를 플러쉬하는데 필요한 시간(별도의 240초)를 결정하기 전에 세 번의 재시도(총 90초)를 지정해놓았다. RIP은 연결 실패에 대한 응답이 느린 것으로 알려졌는데 이유가 있었다.

Listing 14. 라우터 A의 라우팅 테이블(페일오버 전)

RouterA#show ip route
Codes: C - connected, S - static, I - IGRP, R - RIP, M - mobile, B - BGP
      D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
      N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
      E1 - OSPF external type 1, E2 - OSPF external type 2, E - EGP
      i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
      * - candidate default, U - per-user static route, o - ODR
      P - periodic downloaded static route

Gateway of last resort is not set

    10.0.0.0/24 is subnetted, 3 subnets
R       10.0.2.0 [120/1] via 192.168.2.1, 00:00:11, FastEthernet0/0
C       10.0.0.0 is directly connected, Loopback0
R       10.0.1.0 [120/1] via 192.168.0.2, 00:00:18, Serial0/0
    192.168.0.0/30 is subnetted, 1 subnets
C       192.168.0.0 is directly connected, Serial0/0
    192.168.1.0/30 is subnetted, 1 subnets
R       192.168.1.0 [120/1] via 192.168.0.2, 00:00:18, Serial0/0
                   [120/1] via 192.168.2.1, 00:00:11, FastEthernet0/0
    192.168.2.0/30 is subnetted, 1 subnets
C       192.168.2.0 is directly connected, FastEthernet0/0




그리고 페일오버 후는,

Listing 15. 라우터 A의 라우팅 테이블 (페일오버 후)

RouterA#show ip route
Codes: C - connected, S - static, I - IGRP, R - RIP, M - mobile, B - BGP
      D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
      N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
      E1 - OSPF external type 1, E2 - OSPF external type 2, E - EGP
      i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
      * - candidate default, U - per-user static route, o - ODR
      P - periodic downloaded static route

Gateway of last resort is not set

    10.0.0.0/24 is subnetted, 3 subnets
R       10.0.2.0 [120/2] via 192.168.0.2, 00:00:09, Serial0/0
C       10.0.0.0 is directly connected, Loopback0
R       10.0.1.0 [120/1] via 192.168.0.2, 00:00:09, Serial0/0
    192.168.0.0/30 is subnetted, 1 subnets
C       192.168.0.0 is directly connected, Serial0/0
    192.168.1.0/30 is subnetted, 1 subnets
R       192.168.1.0 [120/1] via 192.168.0.2, 00:00:09, Serial0/0
    192.168.2.0/30 is subnetted, 1 subnets
R       192.168.2.0 [120/2] via 192.168.0.2, 00:00:10, Serial0/0



Zebra를 이용하여 OSPF 라우팅 설정하기
이제 OSPF 라우팅이다. OSPF와 RIP이 함께 사용되기는 하지만 보다 단순한 설정을 더욱 선호하기 때문에 OSPF를 주로 사용했다. Zebra에서 RIP을 실행불가로 하려면 ripd 서비스를 끈다.

# service ripd stop

OSPF용 기본 설정 파일을 시작한다. 이 경우, /etc/zebra에 있는 ospfd.conf 이다.

Listing 16. 기본 /etc/zebra/ospfd.conf 파일

hostname speedmetal-ospf
password zebra
enable password zebra



OSPF 서비스를 시작했다:

# service ospfd start

OSPF 설정은 RIP 설정 보다 단순하다. OSPF에게 알고있는 모든 라우트를 방송하라고 명령하면 된다.

OSPF 설정 포트는 2604 이다.

OSPF 설정 다이얼로그는 다음과 같다.

Listing 17. OSPF 설정 다이얼로그

[root@speedmetal zebra]# telnet 127.0.0.1 2604
User Access Verification

Password: zebra
speedmetal-ospf> enable
Password: zebra
speedmetal-ospf# configure terminal
speedmetal-ospf(config)# router ospf
speedmetal-ospf(config-router)# network 0.0.0.0/0 area 0
speedmetal-ospf(config-router)# end
speedmetal-ospf# write
Configuration saved to /etc/zebra/ospfd.conf
speedmetal-ospf# show run

Current configuration:
!
hostname speedmetal-ospf
password zebra
enable password zebra
!
!
router ospf
network 0.0.0.0/0 area 0
!
line vty
!
end
speedmetal-ospf#



결과 설정 파일인 ospfd.conf는 다음과 같다:

Listing 18. /etc/zebra/ospfd.conf-Zebra에 의해 변경

!
! Zebra configuration saved from vty
!   2003/08/19 14:22:17
!
hostname speedmetal-ospf
password zebra
enable password zebra
!
!
!
interface lo
!
interface eth0
!
interface eth1
!
interface dummy0
!
router ospf
network 0.0.0.0/0 area 0
!
line vty
!



Cisco 라우터에 OSPF 설정하기
라우터에서 RIP을 제거하고 OSPF를 추가하기 위해 다음의 명령어를 실행했다:

Listing 19. RIP을 제거하고 OSPF 추가하기

RouterA#conf term
RouterA(config)no router rip
RouterA(config)#router ospf 100
RouterA(config-router)#network 0.0.0.0 255.255.255.255 area 0
RouterA(config-router)end



라우터 A와 라우터 B 모두 수행 단계는 같다.

OSPF로 선전된 라우트
MRLG 리포트는 다음과 같다:

Listing 20. OSPF 라우트를 반영하는 Zebra

Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
      B - BGP, > - selected route, * - FIB route

O>* 10.0.0.0/32 [110/11] via 192.168.2.2, eth0, 00:00:01
O>* 10.0.1.1/32 [110/11] via 192.168.1.2, eth1, 00:02:53
O   10.0.2.0/24 [110/10] is directly connected, dummy0, 00:03:31
C>* 10.0.2.0/24 is directly connected, dummy0
K * 127.0.0.0/8 is directly connected, lo
C>* 127.0.0.0/8 is directly connected, lo
O>* 192.168.0.0/30 [110/58] via 192.168.2.2, eth0, 00:00:01
                           via 192.168.1.2, eth1, 00:00:01
O   192.168.1.0/30 [110/10] is directly connected, eth1, 00:03:21
C>* 192.168.1.0/30 is directly connected, eth1
O   192.168.2.0/30 [110/10] is directly connected, eth0, 00:03:31
C>* 192.168.2.0/30 is directly connected, eth0



10.0.0.1/32와 10.0.1.1/32로의 라우트는 O로 표시된다.

Zebra 라우터에서 라우터 A 까지 연결을 끊을 때, 이 라우트는 자동으로 업데이트 되었다. MRLG는 다음을 리포팅했다:

Listing 21. OSPF 라우트를 반영하는 Zebra(패일오버 후)

Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
      B - BGP, > - selected route, * - FIB route

O>* 10.0.0.1/32 [110/59] via 192.168.2.2, eth0, 00:01:10
O>* 10.0.1.1/32 [110/11] via 192.168.1.2, eth1, 00:09:46
O   10.0.2.0/24 [110/10] is directly connected, dummy0, 00:10:24
C>* 10.0.2.0/24 is directly connected, dummy0
K * 127.0.0.0/8 is directly connected, lo
C>* 127.0.0.0/8 is directly connected, lo
O>* 192.168.0.0/30 [110/58] via 192.168.1.2, eth1, 00:01:10
O   192.168.1.0/30 [110/10[ is directly connected, eth1, 00:10:14
C>* 192.168.1.0/30 is directly connected, eth1
O   192.168.2.0/30 [110/10] is directly connected, eth0, 00:10:24
C>* 192.168.2.0/30 is directly connected, eth0

OSPF의 링크 특성 상 패일오버는 훨씬 빠르다. 새로운 라우트가 30초 이내에 선전되었다.


http://www-903.ibm.com/developerworks/k ··· emu.html
"Develop" 카테고리의 다른 글
  • 객체 비지향(object disoriented)을 위한 공유 객체 (0)2007/05/04
  • Linux 애플리케이션을 위한 DLL 작성하기 (0)2007/05/04
  • 리눅스에 네트워크 라우터 구현하기 (0)2007/05/04
  • GNU Coding Standards (0)2007/04/30
  • Linux System Call Table (0)2007/04/30
2007/05/04 14:23 2007/05/04 14:23
Posted by webdizen
Tags 네트워크 라우터, 리눅스
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2891

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1 2  Next»

RSS HanRSS
Blog Image
webdizen
이곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (3009)
Webdizen (141)
Life (6)
Diary (16)
Blog (9)
IDEA (2)
Travel (10)
Book (16)
Photo (7)
Movie (8)
Music (14)
Leisure Sports (10)
Funny (6)
Hardware (121)
Software (120)
Windows (5)
Unix & Linux (120)
Installation (5)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (804)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (2)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (4)
Network (66)
Web (79)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • 춘천캠퍼스
  • 소스코드
  • 난지원
  • Handling
  • 레퍼런스
  • DirectDraw
  • 자연과학대학
  • Performance
  • GPO
  • Recovery
  • 프로젝트 관리
  • 행정본관
  • Modeling
  • 확장성
  • 에펠탑
  • 열망
  • 몽마르뜨언덕
  • 엔터티빈
  • Worker
  • 영어속독

Recent Articles

  • 트위터(Twitter)의 시작!.
  • 청년 리더의 조건.
  • 애플의 타블렛 PC - 아이패드....
  • 미래의 인터페이스 - 육감 기....
  • 기초발성법 동영상 강좌.

Recent Comments

  • 학교 과제물중 쓰레드에 대하....
    장진혁 03/17
  • 관리자만 볼 수 있는 댓글입....
    비밀방문자 03/12
  • 상대방의 이야기를 열심히 경....
    DoNuts 03/03
  • Lots of students know techn....
    Bobbi35Shannon 02/25
  • 좋은글 잘 보고 갑니다..
    Und_hacker 01/08

Recent Trackbacks

  • printf,scanf를 이용한 형식....
    yundream의 프로그래밍 이야기 03/10
  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 2009
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007

Archive

  • 2010/02 (1)
  • 2010/01 (6)
  • 2009/12 (5)
  • 2009/09 (3)
  • 2009/08 (1)

Calendar

«   2010/03   »
일 월 화 수 목 금 토
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.