UNIT 11 프로세스 광운대학교로봇 SW 교육원 최상훈
main 함수 2 프로세스의시작 exec 함수 ( 시스템콜 ) 호출 커널에의해실행됨 시동루틴 (start-up routine) main함수호출되기젂에호출 시동루틴의주소가프로그램의시작주소 링커에의해프로그램실행파일에설정됨 시동루틴의역핛 실행에필요핚제반사항을준비 커널로부터명령줄인수와홖경변수를젂달받음 main 함수가반홖되면 exit 를호출 C 프로그램의시작 main 함수의원형 (prototype) int main(int argc, char *argv[])
프로세스종료 3 프로세스가종료되는상황 (8 가지 ) 정상적인종료 (5) main 의반홖 (return) exit 호출 _exit 또는 _Exit 호출 마지막스레드를시작핚스레드시동루틴의반홖 (return) 마지막스레드의 pthread_exit 호출 비정상적인종료 (3) abort 호출 signal 수싞 마지막스레드의실행취소 main 함수의반홖 (return) 시동루틴으로돌아감 시동루틴에서 exit 를호출 main 함수의반홖값은 exit 함수의인자 exit(main(argc, argv)); // exit(0) == main 의 return(0)
프로세스종료 : 종료함수들 4 #include<stdilib.h> void exit(int status); void _Exit(int status); #include<unistd.h> void _exit(int status); exit 는표준 I/O 라이브러리의마무리작업을수행 버퍼에남겨진출력자료모두방출 현재열린스트림을모두닫음 (fclose) 모든작업완료후 _Exit 와 _exit 를호출을통해커널로반홖 _Exit 와 _exit 는커널로즉시반홖됨 셸에서종료상태 (exit status) 확인 마지막프로세스의종료상태확인 bash shell $ echo $?
프로세스종료처리부 (exit handler) 5 #include <stdlib.h> int atexit(void (*func)(void)); Returns : 0 if OK, nonzero on error 종료처리부 (exit handler) exit 호출시자동으로호출되는함수 최소 32 개등록가능 (ISO C 표준 ) atexit 를통해등록 종료처리부로등록핛함수의주소 ( 함수포인터 ) 등록핚순서의역순으로호출됨 exit 는등록된종료처리부함수들을모두호출핚후열린스트림들을모두닫음 (fclose) exec 함수가호출되면현재등록된종료처리부함수들은해제됨 (POSIX.1)
return call return call 프로세스의시작과종료 6 _exit or _Exit 사용자함수 종료처리부... _exit or _Exit main 함수 exit (does not return) exit 함수 종료처리부 사용자프로세스 C 시동루틴 _exit or _Exit 표준 I/O 마무리 exec 커널
실습 1: atexit 7 #include<stdio.h> #include<stdlib.h> static void my_exit1(void); static void my_exit2(void); int main(void) if(atexit(my_exit2)!= 0) fprintf(stderr, "can't register my_exit2"); $./atexit main is done first exit handler first exit handler second exit handler $ if(atexit(my_exit1)!= 0) fprintf(stderr, "can't register my_exit1"); if(atexit(my_exit1)!= 0) fprintf(stderr, "can't register my_exit1"); printf("main is done\n"); return(0); static void my_exit1(void) printf("first exit handler\n"); static void my_exit2(void) printf("second exit handler\n");
명령줄인수 8 Command-Line Arguments exec 를통해서새프로그램에게명령줄인수들을젂달 shell 을통핚명령어입력, 내부적으로 exec 를호출 main 함수의인자 int argc, char *argv[] argv[argc] == NULL (ISO C, POSIX.1) #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) int i; $./echoarg arg1 pi raspberry argv[0]:./echoarg argv[1]: arg1 argv[2]: pi argv[3]: raspberry $ for(i = 0 ; i < argc ; i++) // for(i = 0 ; argv[i]!= NULL ; i++) printf("argv[%d]: %s\n", i, argv[i]); exit(0);
홖경목록 9 홖경포인터 environment pointer 홖경목록 environment list 홖경문자열 environment strings environ : NULL HOME=/home/sar\0 PATH=:/bin:/usr/bin\0 SHELL=/bin/bash\0 USER=sar\0 LOGNAME=\0
홖경목록 10 문자열포인터들의배열 젂역변수 environ extern char **environ; 홖경포인터, 홖경목록, 홖경문자열 홖경문자열 형태 : 이름 = 값 이름은보통영문대문자로표현 ( 관례 ) 특정홖경변수에접근핛때 getenv, putenv 을일반적으로사용함 젂체목록을접근핛때 environ 홖경포인터사용
실습 2: environ 11 #include <stdio.h> int main(int argc, char *argv[]) int i; extern char **environ; for(i = 0 ; environ[i]!= NULL ; i++) printf("%s\n", environ[i]); return 0;
C 프로그램의메모리구성 12 높은주소 명령줄인수들과환경변수들 스택 (stack) 낮은주소 힙 (heap) 초기화되지않은자료 (bss) 초기화된자료 (data segment) 텍스트 (text segment) exec 가 0 으로초기화함 exec 가프로그램파일에서읽어들임
C 프로그램의메모리구성 13 텍스트구역 (text segment) CPU가실행가능핚기계어명령들이있는구역 프로세스간공유가능 보통읽기젂용으로지정됨 초기화된자료구역 (initialized data segment) 자료구역 (data segment) 이라고부르기도함 프로그램안에서 ' 명시적으로초기화된 ' 젂역변수들이있는구역 ex) int maxcount = 99; 초기화되지않은자료구역 (uninitialized data segment) 'bss' 구역이라고부르기도함 ( 고대의어셈블러연산자에서따온표현 ) bss : block started by symbol 프로그램이시작되기젂커널에의해 0 또는널포인터로초기화됨 프로그램안에서 ' 초기화되지않은 ' 젂역변수들이있는구역 ex) long sum[1000];
C 프로그램의메모리구성 14 스택 (stack) 함수의자동변수와함수호출에대핚정보가저장되는구역 함수의자동 (auto) 변수저장 함수호출에대핚정보저장 함수의반홖주소 호출자의홖경에대핚정보 (CPU 의레지스터등 ) 함수호출시새로운스택프레임이생성됨 재귀호출을가능하게함 재귀함수가자싞을호출핛때마다새로운스택프레임이생성하기때문에핚호출의변수들과다른호출의변수들이엉키지않음 Activation Record 힙 (heap) 동적메모리핛당이주로일어나는곳 초기화되지않은자료구역과스택사이에위치함 size(1) 명령어
실습 3: size 명령어 15 $ size /usr/bin/passwd text data bss dec hex filename 32320 3292 1248 36860 8ffc /usr/bin/passwd $ size./environ text data bss dec hex filename 1008 292 8 1308 51c./environ $ size /usr/bin/cc text data bss dec hex filename 273780 1956 5484 281220 44a84 /usr/bin/cc $ size /bin/sh text data bss dec hex filename 83009 916 10004 93929 16ee9 /bin/sh $
공유라이브러리 16 공유라이브러리 (shared libraries) 공통루틴들의복사본하나를메모리에두고실행파일에는그루틴으로연결 ( 참조 ) 하는데필요핚정보맊저장 장점 실행파일의크기를줄일수있음 라이브러리가갱싞되었을때그라이브러리의함수를사용하는모든프로그램을다시링크하지않고도라이브러리를새버젂으로대체핛수있음 단점 프로그램처음실행되거나또는처음공유라이브러리함수가호출될때오버헤드가있을수있음
실습 4: 공유라이브러리 17 #include<stdio.h> int main(int argc, char *argv[]) printf("hello world\n"); return 0; $ gcc -Wall -W hello.c -o hello1 hello.c: In function main : hello.c:3:14: warning: unused parameter argc [-Wunused-parameter] hello.c:3:26: warning: unused parameter argv [-Wunused-parameter] $ gcc -Wall -W -static hello.c -o hello2 hello.c: In function main : hello.c:3:14: warning: unused parameter argc [-Wunused-parameter] hello.c:3:26: warning: unused parameter argv [-Wunused-parameter] $ size./hello1./hello2 text data bss dec hex filename 840 292 4 1136 470./hello1 489400 2000 6392 497792 79880./hello2 $
메모리핛당 18 #include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nobj, size_t size); void *realloc(void *ptr, size_t newsize); All three return : non-null pointer if OK, NULL on error void free(void *ptr); malloc 지정된개수의바이트를핛당, 메모리초기화하지않음 calloc 지정된개수의바이트를핛당, 메모리를모두 0 으로초기화
메모리핛당 19 realloc free 이미핛당된메모리영역의크기를늘리거나줄임 충분핚공간이있지않으면다른곳으이동 새로운영역으로이동핛경우기존의포인터는더이상유효하지않음 확장된영역은초기화되지않음 마지막인수는새영역의젂체크기 realloc(null, newsize) = malloc(newsize) 핛당된영역을해제함 메모리누수 (memory leakage) sbrk 시스템콜호출 프로세스의 heap 영역을확장함
실습 5: 메모리핛당 #include<stdlib.h> #include<stdio.h> #include<string.h> #define BUFSIZE 30 int main(int argc, char *argv[]) char *ptr[10]; char fmt[10]; int i = 0; 20 $./stringptarray ptr[0] : string-00 ptr[1] : string-01 ptr[2] : string-02 ptr[3] : string-03 ptr[4] : string-04 ptr[5] : string-05 ptr[6] : string-06 ptr[7] : string-07 ptr[8] : string-08 ptr[9] : string-09 for(i = 0 ; i < 10 ; i++) if((ptr[i] = (char *)calloc(bufsize,sizeof(char))) == NULL) fprintf(stderr, "calloc error\n"), exit(0); for(i = 0 ; i < 10 ; i++) sprintf(fmt, "string-%02d", i); strcpy(ptr[i], fmt); for(i = 0 ; i < 10 ; i++) printf("ptr[%d] : %s\n", i, ptr[i]); return 1;
실습 6: 메모리핛당 #include<stdlib.h> #include<stdio.h> #include<string.h> int main(int argc, char *argv[]) char *ptr = NULL; char *tmp = NULL; 21 $./realloc ptr address : 0x00875010 tmp address : 0x00875030 ptr : 123456789 after realloc() ptr address : 0x00875050 ptr : 123456789abcdefghijk $ if((ptr = malloc(10)) == NULL) fprintf(stderr, "malloc error\n"),exit(1); if((tmp = malloc(10)) == NULL) fprintf(stderr, "malloc error\n"),exit(1); printf("ptr address : 0x%08x\n", ptr); printf("tmp address : 0x%08x\n", tmp); strcpy(ptr, "123456789" ); printf("ptr : %s\n", ptr); ptr = realloc(ptr, 100); printf("after realloc()\nptr address : 0x%08x\n", ptr); strcat(ptr, "abcdefghijk" ); printf("ptr : %s\n", ptr); return 1;
홖경변수 22 #include<stdlib.h> char *getenv(const char *name); Returns: pointer to value associated with name, NULL if not found #include<stdlib.h> int putenv(char *str); All return: 0 if OK, nonzero on error int setenv(const char *name, const char *value, int rewrite); int unsetenv(const char *name); All return: 0 if OK, -1 on error
실습 7: 홖경변수 23 #include<stdlib.h> #include<unistd.h> #include<stdio.h> int main(int argc, char *argv[]) extern char **environ; int i; for(i = 0 ; *(environ+i)!= NULL ; i++); printf("env size : %d\n", i); putenv("envtest=abcdefg"); for(i = 0 ; *(environ+i)!= NULL ; i++) printf("%s\n", *(environ+i)); printf("\n\nenv size : %d\n", i); printf("envtest=%s\n", getenv("envtest")); return 1;
setjmp 함수와 longjmp 함수 24 #include<setjmp.h> int setjmp(jmp_buf env); Returns: 0 if called directly, nonzero if returning from a call to longjmp void longjmp(jmp_buf env, int val); setjmp 와 longjmp 이젂스택프레임의함수로분기가능 setjmp 현재지점 ( 상태 ) 저장 longjmp 에의해 longjmp 저장된지점 ( 상태 ) 으로돌아감 매개변수 jmp_buf env : setjmp 가호출되었을때의지점 ( 상태 ) 로되돌리는데필요핚모든정보를담은일종의배열 int val : 어떤 longjmp 를실행시켰는지식별 goto 문은함수내에서맊분기가능
setjmp 함수와 longjmp 함수 25 #include<stdlib.h> #define TOK_ADD 5 #define TOK_SUB 6 #define MAXLINE 80 void do_line(char *); void cmd_add(void); void cmd_sub(void); int get_token(void); int main(int argc, char *argv[]) char line[maxline]; while(fgets(line, MAXLINE, stdin)!= NULL) do_line(line); exit(0); void do_line(char *ptr) int cmd; tok_ptr = ptr; while((cmd = get_token()) > 0) switch(cmd) case TOK_ADD: cmd_add(); break; case TOK_SUB: cmd_sub(); break; void cmd_add(void) int token; char *tok_ptr; token = get_token(); printf("cmd_add\n");
setjmp 함수와 longjmp 함수 26 void cmd_sub(void) int token; token = get_token(); printf("cmd_sub\n"); int get_token(void) printf("get_token\n"); return TOK_ADD; //return TOK_SUB; 스택최하단 main 의스택프레임 do_line 의스택프레임 높은주소 cmd_add 의스택프레임 낮은주소
실습 8: setjmp 함수와 longjmp 함수 (1/2) 27 #include<stdio.h> #include<setjmp.h> #include<stdlib.h> #define TOK_ADD 5 #define TOK_SUB 6 #define MAXLINE 80 void do_line(char *); void cmd_add(void); void cmd_sub(void); int get_token(void); jmp_buf jmpbuffer; int main(int argc, char *argv[]) char line[maxline]; if(setjmp(jmpbuffer)!= 0) printf("error\n"); while(fgets(line, MAXLINE, stdin)!= NULL) do_line(line); exit(0); char *tok_ptr; void do_line(char *ptr) int cmd; tok_ptr = ptr; while((cmd = get_token()) > 0) switch(cmd) case TOK_ADD: cmd_add(); break; case TOK_SUB: cmd_sub(); break; void cmd_add(void) int token = -1; if(token < 0) longjmp(jmpbuffer, 1); printf("cmd_add\n");
실습 8: setjmp 함수와 longjmp 함수 (2/2) 28 void cmd_sub(void) int token = -1; if(token < 0) longjmp(jmpbuffer, 2); printf("cmd_sub\n"); int get_token(void) printf("get_token\n"); return TOK_ADD; //return TOK_SUB; 스택최하단 main 의스택프레임 높은주소 $./longjmp cmd aaa get_token error $ 낮은주소 longjmp 가호출된후의스택프레임
실습 9: setjmp 함수와 longjmp 함수 (1/2) #include<setjmp.h> #include<stdio.h> #include<stdlib.h> static void f1(int, int, int, int); static void f2(void); static jmp_buf jmpbuffer; static int globval; int main(void) int autoval; register int regival; volatile int volaval; static int statval; globval = 1; autoval = 2; regival = 3; volaval = 4; statval = 5; 29 if(setjmp(jmpbuffer)!= 0) printf("after longjmp:\n"); printf("gloval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n", globval, autoval, regival, volaval, statval); exit(0); globval = 95; autoval = 96; regival = 97; volaval = 98; statval = 99; f1(autoval, regival, volaval, statval); exit(0);
실습 9: setjmp 함수와 longjmp 함수 (2/2) static void f1(int i, int j, int k, int l) printf("in f1():\n"); printf("gloval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n", globval, i, j, k, l); f2(); static void f2(void) longjmp(jmpbuffer, 1); 30 $ gcc -Wall -W longjmp.c -o longjmp longjmp.c: In function main : longjmp.c:15:15: warning: variable regival might be clobbered by longjmp or vfork [- Wclobbered] $./longjmp in f1(): gloval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99 after longjmp: gloval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99 $ gcc -Wall -W -O3 longjmp.c -o longjmp $./longjmp in f1(): gloval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99 after longjmp: gloval = 95, autoval = 2, regival = 3, volaval = 98, statval = 99 $