제 8 장프로세스 리눅스시스템프로그래밍 청주대학교전자공학과 한철수
제 8 장 목차 쉘과프로세스 프로그램실행 프로그램종료 프로세스 ID 프로세스이미지 2
8.1 절 프로세스 프로세스 (process) 는파일과더불어리눅스운영체제의핵심개념중하나임. 리눅스시스템을깊이있게이해하기위해서는프로세스에대하여정확히이해해야함. 프로세스는실행중인프로그램이라고간단히말할수있음. 프로그램이실행되면프로세스가됨. 이장의내용 쉘이제공하는프로세스관련기능 프로그램이실행되고종료되는과정 프로세스의 ID 프로세스의내부구조 3
8.1 절 쉘 쉘 (shell) 은사용자와운영체제사이에창구역할을하는소프트웨어임. 사용자로부터명령어를입력받아이를처리하는명령어처리기 (command processor) 의역할을수행함. Ubuntu 터미널의기본쉘 bash( 배시쉘, bash shell) 4
8.1 절 쉘의실행절차 * * 시작파일은환경변수와같은사용자의사용환경을초기화하는데주로사용됨. 사용자시작파일 : ~/.bashrc 5
8.1 절 쉘의명령어실행과자식프로세스 쉘은사용자가입력한명령어를실행하기위해새로운자식프로세스를생성하고, 이자식프로세스로하여금입력된명령어를실행하게함. 예 $ date 2013. 12. 26. ( 목 ) 17:48:58 KST $./hello hello world! $ 쉘이새로운자식프로세스를 생성하고, 이자식프로세스가 입력된명령어를실행함. 쉘은자식프로세스의실행이 끝날때까지기다림. 자식 프로세스의 실행이 끝나면다시쉘프롬프트를 출력하고 다음 명령어를 기다림. 6
8.1 절 명령어열 명령어열은여러명령어를순차적으로실행함. 쉘은각명령어의실행을위한새로운자식프로세스를순차적으로생성하면서, 각프로세스로하여금한명령어씩순차적으로실행하게함. 명령어열의형태 $ 명령어 1; 명령어 2; ; 명령어 n 예 $ date;who;pwd 세개의명령어를순차적으로실행시키기위해 3 개의새로운자식프로세스를순차적으로생성하면서각명령어를순차적으로실행함. 7
8.1 절 명령어그룹 명령어그룹은여러명령어를순차적으로실행하는점은명령어열과같음. 명령어그룹의형태 $ ( 명령어 1; 명령어 2; ; 명령어 n) 하지만명령어그룹은모든명령어가표준입력 (stdin), 표준출력 (stdout), 표준에러 (stderr) 를공유한다는점이다름. 따라서입출력재지정과파이프를사용할때, 마치하나의명령어처럼입출력재지정및파이프처리를할수있음. 예 명령어열 $ date; who; pwd > out1.txt 명령어그룹 $ (date; who; pwd) > out2.txt pwd 의결과만 out1.txt 에저장됨. (date;who;pwd) 의모든결과가 out2.txt 에저장됨. 8
8.1 절 전면처리 전면처리란명령어를입력하면명령어가전면에서실행되며, 명령어실행이끝날때까지쉘이기다려주는명령어처리방식을말함. 전면에서실행되고있는명령어는필요에따라키보드와모니터로입출력을할수있음. 명령어를전면처리하면한순간에하나의명령어만실행할수있음. 9
8.1 절 명령어의강제종료, 정지, 계속 전면처리실행중인명령어를강제종료. ^C // 컨트롤키와 c 키를동시에누름. 전면처리실행중인명령어를정지시킨후, 후면으로보냄. ^Z // 컨트롤키와 z 키를동시에누름. 후면에서정지된명령어를전면 (foreground) 에서계속실행. $ fg 후면에서정지된명령어를후면 (background) 에서계속실행. $ bg 예 $ (sleep 100; echo done) ^C // 강제종료됨. $ (sleep 100; echo done) ^Z // 정지시킨후, 후면으로보냄. $ fg // 후면에서정지된명령어를전면에서계속실행. 10
8.1 절 후면처리 후면처리를이용하여특정명령어를후면에서처리하고, 전면에서는다른작업을수행함으로써동시에여러작업을수행할수있음. 사용법 $ 명령어 & // 후면처리할명령어뒤에 & 기호를붙여실행함. 시간이오래걸리는작업이나동시에여러작업을수행하고자할때후면처리를이용할수있음. 11
8.1 절 후면처리예제 두명령어를후면처리실행 $ (sleep 100; echo done) & // 100 초기다린후, 문자열 done 을출력함. [1] 8320 $ find. -name test.c -print & // 현재작업디렉터리에서 test.c 라는이름의 [2] 8325 파일을찾아있으면파일이름을출력함. 작업번호 PID 후면처리되고있는작업들의표시 $ jobs 후면처리에서전면처리로전환 $ fg % 작업번호 $ fg %1 후면처리입출력 후면처리의출력이전면처리의출력과뒤섞이지않도록하기위한조치 $ find. -name test.c -print > find.txt & // 후면처리의출력을파일에저장함. $ find. -name test.c -print mail chang & // 후면처리의출력을메일로전송함. 후면처리는키보드로부터입력을받을수없으므로, 텍스트파일로부터입력받아야함. $ wc < inputfile & // 텍스트파일로부터입력받음. 12
8.1 절 프로세스리스트 실행중인프로그램을프로세스 (process) 혹은작업 (job) 이라고함. 시스템은내부에서여러개의프로세스가동시에수행되고있음. 각각의프로세스는유일한프로세스번호 (PID) 를가짐. 프로세스를확인하고제어하기위한다양한명령어들 ps sleep kill wait exit 13
8.1 절 ps 명령어 ps 명령어는현재존재하는프로세스들의실행상태를요약해서출력함. 옵션을사용하지않으면, 나의프로세스에대한정보만을출력함. $ ps PID TTY TIME CMD 25435 pts/3 00:00:00 csh 25461 pts/3 00:00:00 ps $ ps aux (BSD 유닉스형식 ) a: 모든사용자의프로세스정보를출력 u: 프로세스에대한정보를자세히출력 x: 더이상제어터미널을갖지않은프로세스들도함께출력 $ ps -ef ( 시스템 V 형식 ) e: 모든사용자의프로세스정보를출력 f: 프로세스에대한정보를자세히출력 14
8.1 절 sleep 명령어 지정된시간만큼프로세스의실행을중지시킴. 사용법 $ sleep 초 예 $ (echo 시작 ; sleep 5; echo 끝 ) 시작 을출력한후, 5 초후에 끝 을출력함. sleep 명령어는여러명령어를수행할경우, 사용자의의도에따라시간적인간격을둘때유용함. 15
8.1 절 kill 명령어 kill 명령어는프로세스를강제적으로종료시키는명령어임. 프로세스번호 (PID) 또는작업번호를명령줄인수로적어실행하면해당프로세스를종료시킴. 사용법 $ kill [- 시그널 ] 프로세스번호 kill 명령어는수행중인특정프로세스에원하는시그널을보내는기능을수행함. 시그널번호를생략하면종료시그널을보내기때문에해당프로세스가종료됨. 예 $ (echo 시작 ; sleep 60; echo 끝 ) & [1] 1230 $ kill 1230 혹은 $ kill %1 후면처리시작 프로세스번호 (PID) 혹은작업번호를이용해강제종료시킬수있음. 16
8.1 절 wait 명령어 해당프로세스번호를갖는자식프로세스가종료될때까지기다리게하는명령어임. 프로세스번호를지정하지않으면모든자식프로세스가끝나기를기다림. 사용법 $ wait [ 프로세스번호 ] 예 $ (sleep 60; echo 1 번끝 ) & [1] 1231 $ echo 2 번끝 ; wait 1231; echo 3 번끝 2 번끝 1 번끝 3 번끝 $ (sleep 60; echo 1 번끝 ) & $ (sleep 60; echo 2 번끝 ) & $ echo 3 번끝 ; wait; echo 4 번끝 3 번끝 1 번끝 2 번끝 4 번끝 후면실행되는지정된자식프로세스가끝날때까지기다림. 후면실행되는두개의자식프로세스가모두끝날때까지기다림. 17
8.1 절 exit 명령어 쉘을종료하고종료코드 (exit code) 를부모프로세스에전달함. 사용법 $ exit [ 종료코드 ] 예 exit exit 1 18
8.2 절 프로그램의실행 사용자가프로그램을실행시키는방법은크게두가지임. 쉘프롬프트에서프로그램을지정하여실행시키는것 실행중인프로그램 ( 사용자프로세스 ) 내에서 exec() 시스템호출을이용하여다른프로그램을실행시키는것 위두방법은사실동일함. 쉘프로세스도이미실행중인프로세스이며, 사용자로부터입력받은실행할프로그램을 exec() 시스템호출을이용하여실행시키기때문. 모든프로그램은 exec() 시스템호출에의해서실행됨. 19
8.2 절 프로그램실행시작 C 프로그램을컴파일하면실행파일에는 C 프로그램코드와 C 시작루틴이포함됨. exec() 시스템호출 실행될프로그램의 C 시작루틴에게명령줄인수와환경변수를전달함. C 시작루틴 main 함수를호출하면서전달받은명령줄인수와환경변수를 main 함수에게다시전달함. main 함수의실행이끝나면반환값을받아 exit 함. exit( main( argc, argv) ); 20
8.2 절 명령줄인수 exec() 시스템호출은실행되는프로그램에게명령줄인수를전달함. 실행되는프로그램의 main() 함수는 argc 와 argv 를통해명령줄인수의개수와명령줄인수에대한포인터배열을전달받음. int main(int argc, char *argv[]); argc: 명령줄인수의개수 argv[]: 명령줄인수의리스트를나타내는포인터배열 21
8.2 절 args.c (p.252) #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) printf("argv[%d]: %s \n", i, argv[i]); exit(0); } 22
8.2 절 환경변수 환경변수는쉘이원래가지고있던것으로, 쉘이프로그램을실행시킬때프로세스에게넘겨줌. 전역변수 environ 을통해환경변수와값의리스트를포인터배열형태로전달함. 환경변수 = 값 23
8.2 절 environ.c (p.254) #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char **ptr; extern char **environ; for (ptr = environ; *ptr!= 0; ptr++) printf("%s \n", *ptr); exit(0); } 24
8.2 절 환경변수접근 getenv() 시스템호출을사용하면특정환경변수의값을가져올수있음. #include <stdlib.h> char *getenv(const char *name); 환경변수 name 의값을반환함. 해당이름의변수가없으면 NULL 을반환함. 예 getenv( USER ) 함수는 chang 을반환함. 25
8.2 절 printenv.c (p.254) #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char *ptr; ptr = getenv("home"); printf("home = %s \n", ptr); ptr = getenv("shell"); printf("shell = %s \n", ptr); ptr = getenv("path"); printf("path = %s \n", ptr); exit(0); } 26
8.2 절 환경변수설정, 삭제 아래의함수를이용하면특정환경변수의값을설정하거나지우는것도가능함. #include <stdlib.h> int putenv(const char *string); name=value 형태의문자열을입력받아서, 이를환경변수리스트에넣어줌. name이이미존재하면원래값을새로운값으로대체함. int setenv(const char *name, const char *value, int rewrite); 환경변수 name의값을 value로설정함. name이이미존재하는경우에는 rewrite 값이 0이아니면원래값을새로운값으로대체하고, rewrite 값이 0이면그대로둠. int unsetenv(const char *name); 환경변수 name을삭제함. 27
8.3 절 프로그램의종료방법 정상종료 (normal termination) 1. main() 함수의실행을마치고반환값을반환하면, C 시작루틴은이반환값을가지고 exit() 함수를호출함. 2. 프로그램내에서직접 exit() 함수를호출함. 3. 프로그램내에서직접 _exit() 함수를호출함. 비정상종료 (abnormal termination) 1. abort() 시스템호출함수에의한종료 프로세스에 SIGABRT 시그널을보내어프로세스를비정상적으로종료시킴. 2. 시그널에의한종료 실행중인프로세스가시그널을받으면갑자기비정상적으로종료하게됨. 28
8.3 절 exit(), _exit() 시스템호출함수 exit() 모든스트림을닫고 (fclose), 출력버퍼의내용을디스크에쓰는등의뒷정리 (fflush) 후, 프로세스를정상적으로종료시킴. 종료코드 (exit code) 를부모프로세스에게전달함. #include <stdlib.h> void exit(int status); 뒷정리를한후프로세스를정상적으로종료시킴. _exit() 뒷정리를하지않고즉시종료시킨다는점이 exit() 함수와다름. #include <stdlib.h> void _exit(int status); 뒷정리를하지않고프로세스를즉시종료시킴. 29
8.3 절 exit 처리기 exit() 시스템호출함수에의해프로세스가종료될때표준입출력의뒷정리가기본적으로수행됨. 사용자는기본적인뒷정리외에별도의뒷정리작업을하기위하여 atexit() 시스템호출함수를이용하여 exit 처리기함수를등록할수있음. #include <stdlib.h> int atexit(void (*func)(void)); exit 처리기로함수 func 를등록함. 등록이성공하면 0 을, 실패하면 0 이아닌숫자를반환함. exit 처리기는프로세스당 32 개까지등록이가능함. 등록된 exit 처리기는프로그램이종료될때등록된역순으로호출되어실행됨. 30
8.3 절 atexit.c (p.259) #include <stdio.h> #include <stdlib.h> static void exit_handler1(void); static void exit_handler2(void); int main(void) { if (atexit(exit_handler1)!= 0) perror("exit_handler1 등록할수없음 "); if (atexit(exit_handler2)!= 0) perror("exit_handler2 등록할수없음 "); printf("main 끝 \n"); exit(0); } static void exit_handler1(void) { printf(" 첫번째 exit 처리기 \n"); } static void exit_handler2(void) { printf(" 두번째 exit 처리기 \n"); } 31
8.3 절 프로그램의시작과종료과정 32
8.4 절 프로세스 ID 프로그램이실행되면프로세스가됨. 한프로그램은여러번실행될수있으므로, 한프로그램으로부터여러개의프로세스가생성될수있음. 각프로세스는프로세스를구별하는번호인프로세스 ID (PID) 를가짐. 모든프로세스는다른프로세스에의해생성됨. 자신을생성해준프로세스를부모프로세스라고함. PID 관련시스템호출 int getpid( ); 프로세스 ID 를반환함. Int getppid( ); 부모프로세스의프로세스 ID 를반환함. 33
8.4 절 pid.c (p.261) #include <stdio.h> #include <unistd.h> int main() { int pid; printf(" 나의프로세스번호 : [%d] \n", getpid()); printf(" 내부모프로세스번호 : [%d] \n", getppid()); } 34
8.4 절 chdir() 시스템호출 각프로세스는현재작업디렉터리를가짐. int chdir(char* pathname); chdir() 시스템호출은현재작업디렉터리를매개변수가지정한경로 pathname 으로변경함. chdir() 시스템호출이성공하려면그디렉터리에대한실행권한이있어야함. 35
8.4 절 프로세스의사용자 ID 와그룹 ID 프로세스는프로세스 ID 외에도다양한 ID 를가짐 프로세스 ID (PID) 프로세스사용자 ID 프로세스실제사용자 ID 프로세스유효사용자 ID 프로세스그룹 ID 프로세스실제그룹 ID 프로세스유효그룹 ID 프로세스를실행시킨사용자 ID 현재유효한사용자 ID 프로세스를실행시킨사용자의그룹 ID 현재유효한사용자의그룹 ID 이러한여러가지 ID 는프로세스가수행할수있는권한을검사하는데사용됨. 실제사용자 ID 와유효사용자 ID 는일반적으로같지만, 특별한경우에는달라질수있음. 프로세스실제사용자 ID 가 cju 이고프로세스유효사용자 ID 가 root 이면, root 소유의파일에접근이가능함. 36
8.4 절 다양한 ID 를읽어오는시스템호출 #include <sys/types.h> #include <unistd.h> uid_t getuid( ); 프로세스의실제사용자 ID를반환함. uid_t geteuid( ); 프로세스의유효사용자 ID를반환함. uid_t getgid( ); 프로세스의실제그룹 ID를반환함. uid_t getegid( ); 프로세스의유효그룹 ID를반환함. 37
8.4 절 다양한 ID 를변경하는시스템호출 #include <sys/types.h> #include <unistd.h> int setuid(uid_t uid); 프로세스의실제사용자 ID를 uid로변경함. int seteuid(uid_t uid); 프로세스의유효사용자 ID를 uid로변경함. int setgid(gid_t gid); 프로세스의실제그룹 ID를 gid로변경함. int setegid(gid_t gid); 프로세스의유효그룹 ID를 gid로변경함. 38
8.4 절 uid.c (p.263) #include <stdio.h> #include <unistd.h> #include <pwd.h> #include <grp.h> int main() { int pid; } 사용자 ID 를반환함. 사용자 ID( 숫자 ) 에해당하는사용자데이터베이스를찾아, 그속에서변수 pw_name에접근함. printf(" 나의실제사용자 ID : %d(%s) \n", getuid(), getpwuid(getuid())->pw_name); printf(" 나의유효사용자 ID : %d(%s) \n", geteuid(), getpwuid(geteuid())->pw_name); printf(" 나의실제그룹 ID : %d(%s) \n", getgid(), getgrgid(getgid())->gr_name); printf(" 나의유효그룹 ID : %d(%s) \n", getegid(), getgrgid(getegid())->gr_name); 39
8.4 절 실제사용자 ID 와유효사용자 ID 가다른경우 실행파일의 set-user-id 라는특별한실행권한을설정하면, 프로세스의유효사용자 ID 가실행파일의소유자로바뀜. 결과적으로프로세스가실행되는동안그파일의소유자권한을갖게됨. 예 /usr/bin/passwd 명령어는 set-user-id 실행권한이설정된실행파일로서, 소유자는 root 임. 일반사용자가이파일을실행하게되면이프로세스의실제사용자 ID 는사용자이지만, 프로세스의유효사용자 ID 는 root 임. 따라서이프로세스는 /etc/passwd 처럼 root 만수정할수있는파일에접근하여파일을수정하는것이가능함. 40
8.4 절 set-user-id 권한의실행파일 set-user-id 실행권한은심볼릭모드로 's' 로표현됨. $ ls l /bin/su /usr/bin/passwd 32 -rwsr-xr-x. 1 root root 32396 2011-05-31 01:50 /bin/su 28 -rwsr-xr-x. 1 root root 27000 2010-08-22 12:00 /usr/bin/passwd set-user-id 실행권한의설정방법 $ chmod 4755 file 명 set-group-id 실행권한의설정방법 $ chmod 2755 file 명 41
8.4 절 예 42
8.5 절 프로세스이미지 프로그램을실행하기위해서는텍스트 ( 실행코드 ), 데이터, 힙, 스택등의영역을위한메모리가필요하고, 이러한메모리배치를프로세스이미지라고함. 프로세스이미지를구성하는각영역의역할 텍스트 (text) 프로세스가실행하는실행코드를저장함. 데이터 (data) 전역변수 (global variable) 및정적변수 (static variable) 를저장함. 힙 (heap) 동적메모리할당을위한영역으로, malloc() 함수를호출하면이영역에서메모리를할당해줌. 스택 (stack) 함수호출의구현에이용되는영역으로, 함수가호출될때마다해당함수의지역변수, 매개변수, 반환주소, 반환값등을포함하는활성레코드 (activation record) 가저장됨. U-영역 (user-area) 열린파일의디스크립터, 현재작업디렉터리등과같은프로세스의정보를저장하는영역임. ( 텍스트 ) 43
8.5 절 size 명령어 실행파일의텍스트, 데이터등의크기를알려줌. ( 텍스트 ) 44
질문 Q&A 45