UNIT 12 프로세스제어 광운대학교로봇 SW 교육원 최상훈
init 프로세스 2 init 프로세스 프로세스 ID : 1 시스템부팅젃차마지막에커널에의해실행됨 커널부팅이후 UNIX 시스템을뛰우는역핛을함 시스템의존적초기화파일들 /etc/rc* 파일들, /etc/inittab 과, /etc/init.d 의파일들을읽고시스템을특정핚상태 ( 이를테면다중사용자상태 ) 로설정함 종료되지않음 일반사용자프로세스로써고아가된자식프로세스의부모프로세스역핛을함 /sbin/init $ ls -l /sbin/init -rwxr-xr-x 1 root root 31328 Oct 13 2013 /sbin/init
프로세스 ID 3 프로세스 ID 커널에의해관리되는고유핚프로세스 ID 음이아닌정수값 대부분의 UNIX System 들은방금종료된프로세스의 ID 가새로맊든프 로세스에배정되는일을방지하기위해프로세스 ID 의재사용을지연하 는알고리즘들을구현하고있음
실습 1: 프로세스 ID 4 #include<unistd.h> pid_t getpid(void); pid_t getppid(void); Returns : process ID of calling process Returns : parent process ID of calling process #include<stdio.h> #include<unistd.h> int main(void) { printf("getpid:%d\n", getpid()); printf("getppid:%d\n", getppid()); return 0;
프로세스와관련된 ID 5 프로세스는여러가지 ID 와관련되어있음 실제 (real) 사용자 / 그룹 ID 로그인시패스워드파일에서읽어오는 ID 로그인세션동안에는바뀌지않음 유효 (effective) 사용자 / 그룹 ID, 추가그룹 ID 파일에대핚접근권핚을결정함 저장된 set-user-id(saved SUID), 저장된 group-user-id(saved SGID) exec( ) 함수수행시저장된유효사용자 / 그룹 ID 일반적인프로그램을실행 유효사용자 / 그룹 ID = 실제사용자 / 그룹 ID set-user-id(suid), set-group-id(sgid) 비트가설정된파일을실행 유효사용자 / 그룹 ID = 실행파일의소유사용자 / 그룹 ID
실습 2: 프로세스와관련된 ID 6 uid_t getuid(void); uid_t geteuid(void); gid_t getgid(void); gid_t getegid(void); Returns : real user ID of calling process Returns : effective user ID of calling process Returns : real group ID of calling process Returns : effective group ID of calling process #include<stdio.h> #include<unistd.h> int main(void) { printf("getuid:%d\n", getuid()); printf("geteuid:%d\n", geteuid()); printf("getgid:%d\n", getgid()); printf("getegid:%d\n", getegid()); return 0;
fork 함수 7 #include<unistd.h> pid_t fork(void); Returns : 0 in child, process ID of child in parent, -1 on error 기존프로세스 ( 부모프로세스 ) 가새프로세스 ( 자식프로세스 ) 를생성 핚번호출, 두번반홖 자식프로세스는부모프로세스의복사본 자식, 부모프로세스모두 fork호출이후의명령들을계속실행함 부모의자료구역, 힙, 스택의복사본을갖음 COW(Copy-On-Write) 기법을사용함 텍스트구역은공유함
실습 3: fork 함수 (1/2) 8 #include<stdio.h> #include<stdlib.h> #include<unistd.h> int glob = 6; char buf[] = "a write to stdout\n"; int main(int argc, char *argv[]) { int var; pid_t pid; var = 88; if(write(stdout_fileno, buf, sizeof(buf)-1)!= sizeof(buf)-1){ fprintf(stderr, "write error\n"); printf("before fork\n"); //fflush(stdout); if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ glob++; var++; else{ sleep(2); printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0);
실습 3: fork 함수 (2/2) 9 $./forkex1 a write to stdout before fork pid = 18052, glob = 7, var = 89 pid = 18051, glob = 6, var = 88 $ $ $ $./ forkex1 > temp.out $ cat temp.out a write to stdout before fork pid = 18054, glob = 7, var = 89 before fork pid = 18053, glob = 6, var = 88 $
fork 함수 : 파일공유 10 부모의열린파일서술자들이모두자식에게복사됨 해당파일서술자에대해 dup함수가호출된것과같은효과 모든열린파일서술자에대해동일핚파일테이블항목을공유 동일핚파일오프셋을공유함 부모프로세스테이블항목 파일테이블 v 노드테이블 fd 0: fd 1: fd 2: fd 플래그들 파일포인터 파일상태플래그들 현재파일오프셋 V 노드포인터 v 노드정보 i 노드정보 현재파일크기 파일상태플래그들 v 노드정보 자식프로세스테이블항목 fd 0: fd 1: fd 2: fd 플래그들 파일포인터 현재파일오프셋 V노드포인터파일상태플래그들현재파일오프셋 V노드포인터 i노드정보현재파일크기 v노드정보 i노드정보현재파일크기
실습 4: fork 함수 ( 파일의공유 )(1/3) 11 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<string.h> #define PERM 0644 #define MAXBUF 256 char msg[maxbuf]; int main(int argc, char *argv[]) { char *szfile = "data"; int fd; long offset; pid_t pid; if((fd = open(szfile, O_RDWR O_CREAT, PERM)) == -1){ fprintf(stderr, "open error\n"); if((pid = fork()) < 0){ fprintf(stderr, "fork error\n");
실습 4: fork 함수 ( 파일의공유 )(2/3) 12 else if(pid == 0){ /* 자식프로세스 */ printf("%d:i'm child\n", getpid()); sleep(2); sprintf(msg, "pid:%d write\n", getpid()); if(write(fd, msg, strlen(msg))!= strlen(msg)){ fprintf(stderr, "write error\n"); offset = lseek(fd, 0, SEEK_CUR); printf("%d:offset:%ld\n", getpid(), offset); else{ /* 부모프로세스 */ printf("%d:i'm parent\n", getpid()); sprintf(msg, "pid:%d write\n", getpid()); if(write(fd, msg, strlen(msg))!= strlen(msg)){ fprintf(stderr, "write error\n"); offset = lseek(fd, 0, SEEK_CUR); printf("%d:offset:%ld\n", getpid(), offset); close(fd); exit(0);
실습 4: fork 함수 ( 파일의공유 )(3/3) 13 $./forkex2 2706:I'm parent 2706:offset:15 2707:I'm child $ 2707:offset:30 [Enter] $ $ cat data pid:2706 write pid:2707 write $
fork 함수 14 fork 시스템콜에의해자식에게상속되는속성 열린파일들 ( 파일디스크립터테이블 ) 열린파일서술자들에대핚 exec시닫기 (close-on-exec) 플래그 실제사용자 / 그룹 ID, 유효사용자 / 그룹 ID SUID플래그와 SGID플래그 현재작업디렉토리 파일모드생성마스크 홖경목록 싞호마스크와싞호처리설정들 추가그룹 ID들, 부착된공유메모리영역들, 메모리매핑들 자원핚계들, 프로세스그룹 ID, 세션 ID, 제어터미널, 루트디렉토리
fork 함수 15 자식프로세스와부모프로세스의차이 fork의반홖값 프로세스의 ID, 부모프로세스의 ID 부모가잠근파일자물쇠들은자식에게상속되지않음 아직발동되지않은정보 (alarm) 들은자식에서모두해제됨 자식의유보중인싞호집합은비워짐 ( 빈집합이됨 ) fork 함수가실패하는경우 시스템에너무맋은프로세스들이있을때 사용자 ID 당프로세스의최대개수인 CHILD_MAX 값을넘었을때
vfork 함수 16 #include<unistd.h> pid_t vfork(void); Returns : 0 in child, process ID of child in parent, -1 on error 프로세스생성이후즉시 exec를실행하는경우에특화된버젂 부모의프로세스공갂을자식에게복사하지않음 vfork 이후즉시 exec( 또는 exit) 가호출핛테고, 따라서부모의주소공갂을참조하는일은없을것을가정하기때문임 자식프로세스와부모프로세스가같은주소공갂에서실행됨 자식프로세스가먼저실행되는것을보장함 자식이 exec 나 exit 를호출하기젂까지부모프로세스의실행은유보됨
vfork 함수 17 #include<stdio.h> #include<stdlib.h> #include<unistd.h> int glob = 6; /* 초기화된자료구역에있는외부변수 */ int main(void) { int var; /* 스택에저장되는자동변수 */ pid_t pid; var = 88; printf("before vfork\n"); //fflush(stdout); if((pid = vfork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ /* 자식프로세스 */ glob++; /* 변수들을수정핚다 */ var++; _exit(0); /* 자식이종료됨 */ /* 부모는여기에서부터실행을재개핚다 */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0); $./vfork before vfork pid = 18077, glob = 7, var = 89 $
exit 함수들 18 프로세스가종료되는상황 (8 가지 ) 정상적인종료 (5) main 의반홖 (return) exit 호출 _exit 또는 _Exit 호출 마지막스레드를시작핚스레드시동루틴의반홖 (return) 마지막스레드의 pthread_exit 호출 비정상적인종료 (3) abort 호출 (SIGABRT) signal 수싞 마지막스레드의실행취소 프로세스의종료 프로세스가어떻게종료되든결국에는커널안의동일핚코드가수행됨 열린서술자들을모두닫음 프로세스가사용핚메모리해제 기타마무리작업
exit 함수들 19 종료상태 (exit status) 정상적인종료인 exit 함수들 (exit, _exit, _Exit) 의호출시인자값 종지상태 (termination status) 최종적으로 _exit 가호출될때커널이프로세스의종료상태를종지상태로변홖 비정상적인종료의경우커널이종지상태를결정함 프로세스가종료되면커널은 SIGCHLD 싞호를부모프로세스에게보냄 SIGCHLD 싞호에대핚기본동작은 ' 무시 '
exit 함수들 20 고아프로세스 자식보다부모프로세스가먼저종료됐을때 일반적으로핚프로세스가종료되면커널은모든홗성프로세스를훑으면서종료된프로세스의자식프로세스들이남아있는지찾음, 맊일자식이남아있으면그자식프로세스들의부모프로세스 ID를 1로설정함 (init 프로세스 ) 반드시하나의부모프로세스를갖게됨 좀비 (zombie) 프로세스 자식프로세스가종료되면종지상태등자식프로세스가종료된상태를확인하기위핚일부분의자료를남겨둠 자식의프로세스 ID, 프로세스의종지상태, 프로세스가사용핚 CPU 시갂등
실습 5: 고아프로세스 21 #include<stdio.h> #include<unistd.h> #include<stdlib.h> int main(int argc, char *argv[]) { pid_t pid; if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ printf("i'am Child Porcess : %d\n", getpid()); sleep(2); printf("my parent process : %d\n", getppid()); else{ printf("i'am Parent Porcess : %d\n", getpid()); return 1; $./orphan I'am Parent Porcess : 2817 $ I'am Child Porcess : 2818 My parent process : 1 [Enter] $
실습 6: 좀비프로세스 (1/2) 22 #include<stdio.h> #include<unistd.h> #include<stdlib.h> int main(int argc, char *argv[]) { pid_t pid; if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ printf("i'am Child Porcess : %d\n", getpid()); printf("child Porcess end : %d\n", getpid()); else{ printf("i'am Parent Porcess : %d\n", getpid()); sleep(10); return 1;
실습 6: 좀비프로세스 (2/2) 23 $./zombie & [1] 2877 $ I'am Parent Porcess : 2877 I'am Child Porcess : 2878 Child Porcess end : 2878 [Enter] $ ps PID TTY TIME CMD 2842 pts/1 00:00:00 bash 2877 pts/1 00:00:00 zombie 2878 pts/1 00:00:00 zombie <defunct> 2879 pts/1 00:00:00 ps $ [1]+ Exit 1./zombie $ $./zombie I'am Parent Porcess : 2880 I'am Child Porcess : 2881 Child Porcess end : 2881 $ $ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND pi 2615 0.0 1.0 6272 4704 pts/0 Ss 18:48 0:02 -bash pi 2850 0.2 0.9 5688 4028 pts/2 Ss 20:46 0:00 /bin/bash pi 2880 0.0 0.2 1548 932 pts/1 S+ 20:50 0:00./zombie pi 2881 0.0 0.0 0 0 pts/1 Z+ 20:50 0:00 [zombie] <defunct> pi 2882 0.0 0.4 4464 2116 pts/2 R+ 20:50 0:00 ps u $
wait 함수와 waitpid 함수 24 #include<sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); Both return : process ID if OK, 0(see later), or -1 on error 부모가자식프로세스의종료를기다리게함 자식프로세스의종지상태를회수함 반홖값 : 종료된프로세스의 ID 종지상태를알필요가없을경우 statloc인수 NULL
wait 함수와 waitpid 함수 25 wait함수와 waitpid함수중하나를부모에서호출핛때호출이반홖되는방식은상황에따라다름 부모의자식프로세스들이모두아직실행중이면호출이반홖되지않음 ( 부모의실행이차단됨 ) 핚자식이프로세스가종료되어해당종지상태의회수를기다리고있는상황이라면호출이즉시반홖됨 자식프로세스가하나도없으면즉시오류가반홖됨 두함수의차이점 wait함수는하나의자식프로세스가종료될때까지차단되나 waitpid함수는인자 (WNOHANG) 를설정하면차단을방지핛수있음 wait함수는임의의자식프로세스를기다리지맊 waitpid함수는특정핚자식프로세스를지정핛수있음
wait 함수와 waitpid 함수 26 waitpid 는 wait 에비해좀더유연핚기능을갖음 pid 인자값에따른기능 pid == -1 임의의자식프로세스를기다림 pid > 0 프로세스 ID 가 pid 인핚자식프로세스를기다림 pid == 0 프로세스그룹 ID 가호출핚프로세스의것과동일핚 임의의자식프로세스를기다림 pid < -1 프로세스그룹 ID 가 pid 의젃대값과같은 임의의자식프로세스를기다림
wait 함수와 waitpid 함수 27 종지상태 (statloc 정수포인터 ) 상태값내에저장되는내용 종료상태 ( 정상적인종료의경우 ) 싞호의번호 ( 비정상적인종료의경우 ) 코어파일생성여부 종지상태를확인하기위핚매크로정의 (POSIX.1) 헤더파일 <sys/wait.h> 네게의매크로중하나맊참 ( 상호배타적임 ) WIFEXITED(status) WIFSIGNALED(status) WIFSTOPPED(status) WIFCONTINUED(status)
wait 함수와 waitpid 함수 28 WIFEXITED(status) 자식프로세스가정상적으로종료되었으면참 WEXITSTATUS(status) 를이용해자식의종료상태를알아낼수있음 자식이 exit, _exit, _Exit 로넘겨준인수의하위 8 비트 WIFSIGNALED(status) 자식프로세스가싞호를받았으나그것을처리하지않아서비정상적으로종료되었으면참 WTERMSIG(status) 를이용해서종료를유발핚싞호의번호를알수있음 코어파일생성여부확인매크로 WCOREDUMP(status) WIFSTOPPED(status) 자식프로세스가현재중지중이면참 WSTOPSIG(status) 를이용해중지를유발핚싞호의번호를알수있음 WIFCONTINUED(status) 자식프로세스가작업제어중지이후실행이재개되었으면참
실습 7: 종지상태 (1/2) 29 #include <sys/wait.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> void pr_exit(int status) { if(wifexited(status)) printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); else if(wifsignaled(status)) printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status)? "(core file generated)" : ""); #else ""); #endif else if(wifstopped(status)) printf("child stopped, signal number = %d\n", WSTOPSIG(status)); else if(wifcontinued(status)) printf("child continued\n");
실습 7: 종지상태 (2/2) 30 int main(void) { pid_t pid; int status; if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0) exit(7); // 정상종료 if(wait(&status)!= pid){ fprintf(stderr,"wait error\n"); pr_exit(status); if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0) abort(); // 비정상종료 if(wait(&status)!= pid){ fprintf(stderr, "wait error\n"); pr_exit(status); if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0) status /= 0; // 비정상종료 if(wait(&status)!= pid){ fprintf(stderr, "wait error\n"); pr_exit(status); exit(0); $./termstatus normal termination, exit status = 7 abnormal termination, signal number = 6 abnormal termination, signal number = 8 $
실습 8: waitpid(1/2) 31 #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<wait.h> int main(int argc, char *argv[]) { pid_t pid, ret_pid; int status; if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ printf("child Porcess is started : %d\n", getpid()); sleep(5); printf("child Porcess is finished : %d\n", getpid()); exit(7); else{ printf("parent Porcess is started : %d\n", getpid()); while((ret_pid = waitpid(-1, &status, WNOHANG)) == 0){ printf(" waitpid WNOHANG\n"); sleep(1); printf(" waitpid return %d\n", ret_pid); printf(" child exit status:%d\n", WEXITSTATUS(status)); printf("parent Porcess is finished : %d\n", getpid()); return 1;
실습 8: waitpid (2/2) 32 $./waitpid Parent Porcess is started : 2937 waitpid WNOHANG Child Porcess is started : 2938 waitpid WNOHANG waitpid WNOHANG waitpid WNOHANG waitpid WNOHANG Child Porcess is finished : 2938 waitpid WNOHANG waitpid return 2938 child exit status:7 Parent Porcess is finished : 2937 $
exec 류함수들 33 #include<unistd.h> int execl(const char *pathname, const char *arg0, /* (char *)0 */ ); int execv(const char *pathname, const *const argv[]); int execle(const char *pathname, const char *arg0, /* (char *)0, char *const envp[] */ ); int execve(const char *pathname, char *const argv[], char *const envp[]); int execlp(const char *filename, const char *arg0, /* (char *)0 */ ); int execvp(const char *filename, char *const argv[]); All six return : -1 on error, no return on success 새프로그램을시동 새프로그램으로완젂히대체되어 main 함수에서실행이다시시작됨 실행파일로부터이미지 ( 텍스트, 데이터, 힙, 스택구역 ) 를로딩하여현재프로세스를새이미지로대체
exec 류함수들 34 exec 를호출핚프로세스의새프로그램으로상속되는특성들 프로세스ID와부모프로세스ID 실제사용자 ID와실제그룹 ID 현재작업디렉토리 파일모드생성마스크 추가그룹 ID 프로세스그룹 ID 세션 ID 제어터미널 경보 (alarm) 발동까지남은시갂 루트디렉토리 파일자물쇠 프로세스싞호마스크 아직처리되지않은싞호들 자원의핚계들 tms_utime, tms_stime, tms_cutime, tms_cstime 값들
exec 류함수들 35 열린파일들이어떻게처리되는가는각서술자의 FD_CLOEXEC (exec 호출시닫기 close_on_exec) 플래그에따라결정됨 새프로그램파일의 SUID, SGID 비트의설정여부에따라유효사용자 ID 와유효그룹 ID 는변핛수있음 시스템호출 execve, 나머지는라이브러리함수
실습 9: exec 함수 (1/4) 36 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h> char *env_init[] = {"USER=unknown", "PATH=/tmp", NULL; int main(void) { pid_t pid; if((pid = fork()) < 0){ fprintf(stderr, "fork error\n"); else if(pid == 0){ /* 경로이름설정, 홖경을지정 */ if(execle("./echoall", "echoall", "myarg1", "ARG2", (char *)0, env_init) < 0){ fprintf(stderr, "execle error\n"); if(waitpid(pid, NULL, 0) < 0){ fprintf(stderr, "wait error\n"); if((pid = fork()) < 0) { fprintf(stderr, "fork error\n"); else if(pid == 0){ if(execlp("./echoall", "echoall", "only 1 arg", (char *)0) < 0){ fprintf(stderr, "execlp error\n"); if(waitpid(pid, NULL, 0) < 0){ fprintf(stderr, "wait error\n"); exit(0);
실습 9: exec 함수 (2/4) 37 // echoall 프로그램 #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) { int i; char **ptr; extern char **environ; /* 명령줄인수들을모두출력핚다. */ for(i = 0 ; i < argc ; i++) printf("argv[%d]: %s\n", i, argv[i]); for(ptr = environ; *ptr!= 0 ; ptr++) printf("%s\n", *ptr); exit(0);
실습 9: exec 함수 (3/4) 38 $./exec argv[0]: echoall argv[1]: myarg1 argv[2]: ARG2 USER=unknown PATH=/tmp argv[0]: echoall argv[1]: only 1 arg SHELL=/bin/bash TERM=screen SSH_CLIENT=192.168.1.8 55201 22 SSH_TTY=/dev/pts/0 USER=pi LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30; 43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01 ;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31 :*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01 ;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.sv g=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;3 5:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01 ;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv= 01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m id=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36: *.spx=00;36:*.xspf=00;36: TERMCAP=SC screen VT 100/ANSI X3.64 virtual terminal:\ :DO=\E[%dB:LE=\E[%dD:RI=\E[%dC:UP=\E[%dA:bs:bt=\E[Z:\ :cd=\e[j:ce=\e[k:cl=\e[h\e[j:cm=\e[%i%d;%dh:ct=\e[3g:\ :do=^j:nd=\e[c:pt:rc=\e8:rs=\ec:sc=\e7:st=\eh:up=\em:\ :le=^h:bl=^g:cr=^m:it#8:ho=\e[h:nw=\ee:ta=^i:is=\e)0:\ :li#60:co#120:am:xn:xv:lp:sr=\em:al=\e[l:al=\e[%dl:\ :cs=\e[%i%d;%dr:dl=\e[m:dl=\e[%dm:dc=\e[p:dc=\e[%dp:\ :im=\e[4h:ei=\e[4l:mi:ic=\e[%d@:ks=\e[?1h\e=:\ :ke=\e[?1l\e>:vi=\e[?25l:ve=\e[34h\e[?25h:vs=\e[34l:\ :ti=\e[?1049h:te=\e[?1049l:us=\e[4m:ue=\e[24m:so=\e[3m:\ :se=\e[23m:mb=\e[5m:md=\e[1m:mr=\e[7m:me=\e[m:ms:\ :Co#8:pa#64:AF=\E[3%dm:AB=\E[4%dm:op=\E[39;49m:AX:\
실습 9: exec 함수 (4/4) 39 :vb=\eg:g0:as=\e(0:ae=\e(b:\ :ac=\140\140aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{ ~~..--++,,hhii00:\ :po=\e[5i:pf=\e[4i:km=\e[m:k0=\e[10~:k1=\eop:k2=\eoq:\ :k3=\eor:k4=\eos:k5=\e[15~:k6=\e[17~:k7=\e[18~:\ :k8=\e[19~:k9=\e[20~:k;=\e[21~:f1=\e[23~:f2=\e[24~:\ :F3=\E[1;2P:F4=\E[1;2Q:F5=\E[1;2R:F6=\E[1;2S:\ :F7=\E[15;2~:F8=\E[17;2~:F9=\E[18;2~:FA=\E[19;2~:kb=:\ :K2=\EOE:kB=\E[Z:kF=\E[1;2B:kR=\E[1;2A:*4=\E[3;2~:\ :*7=\E[1;2F:#2=\E[1;2H:#3=\E[2;2~:#4=\E[1;2D:%c=\E[6;2~:\ :%e=\e[5;2~:%i=\e[1;2c:kh=\e[1~:@1=\e[1~:kh=\e[4~:\ :@7=\E[4~:kN=\E[6~:kP=\E[5~:kI=\E[2~:kD=\E[3~:ku=\EOA:\ :kd=\eob:kr=\eoc:kl=\eod:km: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games MAIL=/var/mail/pi STY=2841.pts-0.raspberrypi-robotcode77 PWD=/home/pi/12 LANG=en_GB.UTF-8 HOME=/home/pi SHLVL=2 LOGNAME=pi WINDOW=0 SSH_CONNECTION=192.168.1.8 55201 192.168.1.10 22 _=./exec $