PROCESS
현재 실행 중인 프로세스로부터 새로운 프로세스를 만들고 관리하는 방법
Create New Process (system, exec, fork)
system 함수 : system(“프로그램 실행파일명”); – 전달받은 인자를 /bin/sh로 넘겨줌
① 쉘에서 실행하는 것과 동일한 효과를 나타냄. 쉘을 쓰듯이 사용
system(“date > right_now”); # date의 결과를 right_now라는 파일로..
② STD(IN, OUT, ERR)은 부모 프로세스로부터 상속받음
③ 실행결과를 리턴(0: True, Other: False, perl에서의 T/F정의와 반대)
system(“date > right_now”) && die “can’t run : $!”; # system 실패시 die
④ 함수에 전달되는 인자가 많을 때 perl은 첫번째를 명령으로, 나머지는 명령의 인자로 전달
@cfiles = (“fred.c”, “barney.c”); #
@options = (“-DHARD”, “-DGRANTE”); #
system “cc –o slate @options @cfiles”; # 쉘이 모든 것을 부담
system “cc”, “-o”, “slate”, @options, @cfiles; # 위와 동일(적은 부하)
⑤ 호출한 프로세스가 종료할 때까지 기다림
환경변수 : 프로세스의 실행 환경을 나타내는 변수. (HOME, LANG, LONNAME, PATH…)
① perl의 경우 %ENV가 환경변수를 나타냄. 키는 환경변수의 이름.
$oldpath = $ENV{“PATH”}; # ENV.PATH를 임시로 저장
$ENV{“PATH”} = “/bin:/usr/bin:/usr/ucb”; # 알려진 경로로 제한
system(“grep fred bedrock > output”); # 위에 제한된 경로에서만 grep을 실행
$ENV{“PATH”} = $oldpath; # 복구하기
역 인용부호 : `실행할 프로그램 파일명` – 실행 결과를 프로세스의 변수로 가지고 올 수 있음
① $now = “the time is now “ . `date`; # date의 결과는 출력되지 않음
일반적으로 하나의 행 만을 가지고 옴
② foreach (`who`) { # 여러 행의 출력을 가지고 오기
($who, $where, $when) = /(S+)s+(S+)s+(.*)/;
print “$who on $where at $when ”;
}
파일 핸들로 프로세스 사용하기 : open(핸들, “(|) 명령어 (|)”); 파이프(참조: popen())
① 실행할 명령의 왼쪽에 파이프가 있으면 -> 쓰기 전용, 즉 부모 프로세스에서 해당 파일핸들에 출력하는 것은 자식 프로세스의 표준입력으로 처리됨
② 오른쪽에 파이프가 있으면 -> 읽기 전용, 자식 프로세스의 표준출력을 부모 프로세스가 파일 핸들을 통해 가지고 올 수 있음
③ 생성된 파일 핸들에 대한 읽기/쓰기는 보통의 파일 핸들과 동일
④ 여러 개의 파이프가 올 수 있음
open(WHOPR, “ls | tail –r |”); # ls의 결과를 tail로, 그리고 그 결과를 읽어옴
fork : UNIX system call. 현재 프로세스와 동일한 자식 프로세스를 만들어냄.
① 예제
if (!defined($child_pid = fork())) { # 실패시 undef 반환. defined로 케치
die “can’t fork: $!”;
} elsif ($child_pid) {
# 부모 프로세스의 실행코드
} else {
# 자식 프로세스의 실행코드
}
② 반환값을 통해 부모/자식을 구분(부모 : 자식의 pid, 자식 : 0, 실패 : undef)
③ 부모와 자식은 fork의 리턴 값을 제외한 모든 것이 동일함(코드, 스택, 힙, 파일핸들 등) 하지만 공유가 아닌 복사! 자식/부모 모두 fork이후부터 실행하며 각자의 길을 걸어감
④ race condition 발생(같은 화면에 출력하려고 한다면? -> 결과 뒤섞임) -> wait!
exec : 전달받은 프로그램으로 대체됨. 새로 만들어진 프로세스는 아니므로 pid는 그대로
① exec(“프로그램파일”); 실행이 성공하면 그 뒤로 원래 프로세스는 실행되지 않음.
② fork()->자식 프로세스 exec() = system()과 같은 효과. 하지만 system의 경우 부모 프로세스는 자식이 끝날 때까지 기다림. fork->exec는 race condition 발생 가능.
③ if (!defined($kidpid = fork())) {
die “can’t fork: $!”;
} elsif ($kidpid == 0) { # 자식 프로세스
exec(“date”); # date 프로세스로 대체됨
die “can’t exec: $!”; # exec가 성공하면 실행되지 않는 부분
} else {
waitpid($kidpid, 0); # 자식 프로세스를 기다림(끝날 때 까지), 0은 옵션
}
wait(pid) : 자식 프로세스가 실행을 종료할 때까지 기다림. 차이점은 옵션의 가능 여부, 기다릴 자식 프로세스의 pid 명세
< 차이점 요약 >
CALL | STDIN | STDOUT | STDERR | WAIT? |
System | 프로그램에서 상속 | 프로그램에서 상속 | 프로그램에서 상속 | Yes |
` ` | 프로그램에서 상속 | 값에따라 capture | 프로그램에서 상속 | Yes |
open( , “|…”) | 파일핸들로 연결 | 프로그램에서 상속 | 프로그램에서 상속 | Only close() |
open(, “…|”) | 프로그램에서 상속 | 파일핸들로 연결 | 프로그램에서 상속 | Only close() |
fork,exec, wait(pid) | user 선택 | user 선택 | user 선택 | user |
signal : 가장 간단한 프로세스간 통신 방법. 특정한 값을 권한있는 프로세스에게 보낼 수 있고, 받는 쪽에서는 받은 시그널을 처리하는 함수(action)를 실행하게 됨
① 권한 : 슈퍼유저 이거나 보내는 프로세스와 받는 프로세스의 사용자 ID 가 같은 경우에만
② 생성 : 명시적으로 생성하여 보내기/OS에서 자동으로 보내기(보통 ERR시 발생)
③ 시그널의 종류 : %SIG에 있음. 키는 종류(INT, QUIT, KILL 등. man signal, man kill 보기)
④ 시그널 처리함수 정하기 : $SIG{‘해당 시그널’} = ‘함수이름’;
⑤ 보통 시그널은 특정한 상황을 알리기 위해 사용된다.
⑥ sub my_sigint_catcher { # 시그널 처리 함수
$saw_sigint = 1; # 받은 시그널에 해당하는 플래그를 On
}
$saw_sigint = 0; # 플래그 초기화
$SIG{‘INT’} = ‘my_sigint_catcher’; # 처리기 등록
foreach (@huge_array) { # 어떠한 동작
…………………… # 언제 멈춰야 하지?
if ($saw_sigint) { # SIGINT를 받으면 (거의)바로 처리기가 실행되고
last; # 플래그가 On되며 last가 실행됨
}
}
$SIG{‘INT’} = ‘DEFAULT’; # 원래의 SIGINT처리기로 복구하기
REFERENCES : SP책(APUE) Ch.8 Process, Ch 10. Signals
manpage (각종 함수…)