제 8 장프로세스 리눅스시스템프로그래밍 청주대학교전자공학과 한철수 1
목차 쉘과프로세스 프로그램실행 프로그램종료 프로세스 ID 프로세스이미지 2
8.1 절 프로세스 프로세스 (process) 는파일과더불어리눅스운영체제의핵심개념중하나임. 리눅스시스템을깊이있게이해하기위해서는프로세스에대해서정확히이해해야함. 프로세스는간단히실행중인프로그램이라고할수있음. 프로그램이실행되면프로세스가됨. 이장의내용 쉘이제공하는프로세스관련기능 프로그램이실행되고종료되는과정 프로세스의 ID 프로세스의내부구조 3
8.1 절 쉘 쉘은사용자와운영체제사이에창구역할을하는소프트웨어임. 사용자로부터명령어를입력받아이를처리하는명령어처리기 (command processor) 역할을수행함. Ubuntu 의기본쉘 bash, 배시쉘 (bash shell) 4
8.1 절 쉘의실행절차 * 시작파일은환경변수와같은사용자의사용환경을초기화하는데주로사용됨. 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 $ cat ^C // 강제종료됨. $ cat ^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 // 이름의파일을찾아있으면파일이름을출력함. 작업번호 후면처리되고있는작업들의표시 $ 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 [- 시그널 ] 프로세스번호 보다정확하게는수행중인특정프로세스에원하는시그널을보내는기능을수행함. 시그널번호를따로명시하지않으면종료시그널을보내해당프로세스를종료시킴. $ (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 [ 종료코드 ] 18
8.2 절 프로그램실행 사용자가프로그램을실행시키는방법은크게두가지임. 쉘프롬프트에서프로그램을지정하여실행시키는것 실행중인프로그램 ( 사용자프로세스 ) 내에서 exec() 시스템호출을이용하여다른프로그램을실행시키는것 위두방법은사실동일함. 쉘프로세스도이미실행중인프로세스이며, 사용자로부터입력받은실행할프로그램을 exec() 시스템호출을이용하여실행시킴. 모든프로그램은 exec() 시스템호출에의해서실행됨. 19
8.2 절 프로그램실행시작 exec() 시스템호출 실행될프로그램의시작루틴에게명령줄인수와환경변수를전달함. C 프로그램을컴파일하면실행파일에는 C 프로그램코드와 C 시작루틴이포함됨. C 시작루틴 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 *name); 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) main() 실행을마치고반환하면 C 시작루틴은이반환값을가지고 exit() 을호출함. 프로그램내에서직접 exit() 을호출함. 프로그램내에서직접 _exit() 을호출함. 비정상종료 (abnormal termination) abort() 시스템호출 프로세스에 SIGABRT 시그널을보내어프로세스를비정상적으로종료시킴. 시그널에의한종료 프로세스가실행중에시그널을받으면갑자기비정상적으로종료하게됨. 28
8.3 절 exit(), _exit() 시스템호출 exit() 모든열려진스트림을닫고 (fclose), 출력버퍼의내용을디스크에쓰는 (fflush) 등의뒷정리후, 프로세스를정상적으로종료시킴. 종료코드 (exit code) 를부모프로세스에게전달함. _exit() #include <stdlib.h> void exit(int status); 뒷정리를한후프로세스를정상적으로종료시킴. 뒷정리를하지않고즉시종료시킨다는점이 exit() 과다름. #include <stdlib.h> void _exit(int status); 뒷정리를하지않고프로세스를즉시종료시킴 29
8.3 절 프로그램의시작과종료과정 30
8.3 절 exit 처리기 exit() 시스템호출에의해프로세스가종료될때표준적인 I/O 뒷정리가기본적으로수행됨. 사용자는별도의뒷정리작업을하기위하여 atexit() 시스템호출을이용하여 exit 처리기함수를등록할수있음. #include <stdlib.h> int atexit(void (*func)(void)); exit 처리기로함수 func 를등록함. 성공하면 0 을, 실패하면 0 이아닌수를반환함 exit 처리기는프로세스당 32 개까지등록가능함. 등록된 exit 처리기는프로그램이종료될때등록된역순으로호출되어실행됨. 31
8.3 절 atexit.c (p.259) #include <stdio.h> #include <stdlib.h> static void my_exit1(void), my_exit2(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"); } 32
8.4 절 프로세스 ID 프로그램이실행되면프로세스가됨. 한프로그램은여러번실행될수있으므로한프로그램으로부터여러개의프로세스를만들수있음. 각프로세스는프로세스를구별하는번호인프로세스 ID 를가짐. 프로세스 ID 를종종줄여서 pid 라고함. 모든프로세스는다른프로세스에의해생성됨. 자신을생성해준프로세스를부모프로세스라고함. pid 관련시스템호출 int getpid( ); 프로세스 ID 를반환함. Int getppid( ); 부모프로세스의프로세스 ID 를반환함. 33
8.4 절 pid.c (p.261) #include <stdio.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 가 cheolsu 이고, 유효사용자 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 #include <stdio.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-uid 실행권한의설정방법 $ chmod 4755 file1 set-gid 실행권한의설정방법 $ chmod 2755 file1 41
8.4 절 예 42
8.5 절 프로세스이미지 프로그램을실행하기위해서는텍스트 ( 코드 ), 데이터, 힙, 스택등의영역을위한메모리의할당이필요함. 이러한메모리배치를프로세스이미지라고함. 프로세스이미지를구성하는각영역의역할 텍스트 (text) 또는코드 프로세스가실행하는실행코드를저장하는영역임. 데이터 (data) 전역변수 (global variable) 및정적변수 (static variable) 를위한메모리영역임. 힙 (heap) 동적메모리할당을위한영역임. C 언어의 malloc 함수를호출하면이영역에서동적으로메모리를할당해줌. 스택 (stack area) 함수호출을구현하기위한실행시간스택 (runtime stack) 을위한영역으로, 함수가호출될때마다해당함수의지역변수, 매개변수, 반환주소, 반환값등을포함하는활성레코드 (activation record) 가저장됨. U- 영역 (user-area) 열린파일디스크립터, 현재작업디렉터리등과같은프로세스의정보를저장하는영역임. ( 텍스트 ) 43
8.5 절 size 명령어 실행파일의텍스트, 데이터등의세그먼트의크기를알려줌. ( 텍스트 ) 44
마무리 질문 Q&A 45