pid_t waitpid(pid_t pid, int *status , int options);
status에 사용 가능한 매크로
WIFEXITED(status): 자식 프로세스가 정상적으로 종료된 경우 true
WEXITESTATUS(status): exit()의 인자에서 하위 8비트 값을 리턴
WIFSIGNALED(status): 자식 프로세스가 시그널을 받아 비정상적으로 종료된 경우 true
WIFTERMSIG(status): 시그널 번호를 리턴
WIFCOREDUMP(status): 코어 파일이 생성된 경우 true
WCOREDUMP(status): 코어
core파일
비정상적으로 종료되었슬때 현재 프로그램의 상태를 기록하는 파일을 말합니다. 이로 인해 디버깅 프로그램을 이용해서 어떤 상태였는지 확인 할수 있게 해줍니다.
WIFSTOPPED(status): 현재 중지 상태이면 true
WSTOPSIG(status): 실행을 중단시킨 시그널 번호를 리턴
WIFCONTINUED(status): 작업 제어 중지 이후 실행이 재개되었으면 trueㄴ
options
WCONTINUED: 중지되었다가 실행을 재개한 이후 상태가 아직 보고되지 않은 자식도 리턴함
WNOHANG: 종료 상태를 즉시 회수 할 수 없는 상황이라고 하여도 waitpid() 호출이 차단되지 않고 0을 리턴함
WUNTRACED: 중지되었으나 그 상태가 아직 보고되지 않은 자식도 리턴함
options 인자에 아무것도 넣지 않으면 wait()와 같은 역할
: 자식 프로세스가 종료할 때까지 blocking
optinos 인자에 WNOHANG 넣으면
: 자식 프로세스 종료될 때까지 기다리지 않고 waitpid 함수가 바로 0 리턴
존재하지 않는 자식 pid를 pid 인자에 넣으면
: No child process 에러 출력
waitpid()는 pid인자 하나로 기다리려는 자식 프로세스를 지정하는 것에 반해
waitid()는 idtype과 id인자로 기다리려는 프로세스를 지정한다.
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
idtype
int si_signo: 시그널 번호
int si_errno: errno 값
int si_code: 시그널 코드
pid_t si_pid: 프로세스 ID 보내기
uid_t si_uid: 프로세스 전송하는 real user id
int si_status: exit값 또는 시그널
clock_t si_utime: 소모된 사용자 시간
clock_t si_stime: 소모된 시스템 시간
sigval_t si_value: 시그널 값
int si_int: POSIX.1b 시그널
void si_ptr: POSIX.1b 시그널
void si_addr: 실패를 초래한 메모리 위치
int si_band: 밴드 이벤트
int si_fd: 파일 기술자
프로세스가 자식 프로세스를 생성한 다음 바로 종료를 기다린다면 이 인터페이스를 사용하는 것이 합리적
int system(const char *command);
동기식 프로세스 생성이 시스템 외부로 셸 띄우기라고 불리기 때문에 system()
command 인자로 주어진 명령을 실행
성공시 wait()과 마찬가지로 명령의 상태 반환
실행한 명령의 종료 코드는 WEXITSTATUS로 얻을 수 있다.
에러시 -1
command가 NULL이면 /bin/sh이 유효한 경우 0이 아닌 값, 그렇지 않다면 0 반환
명령을 실행하는 동안 SIGCHLD는 블록되고, SIGINT와 SIGQUIT은 무시된다.
-- 그래서 system()이 반복문 안에서 실행될 때 문제가 발생할 수 있기 때문에 자식 프로세스의 종료 상태를 적절하게 검사할 수 있도록 해야한다.
//my_system: 동기식으로 명령을 실행하고 기다린다.
//어떤 종류의 에러라도 발생할 경우 -1 반환하며 그렇지 않을 경우
//새로 실행된 프로세스의 종료 코드를 반환.
//공식 system()함수와 다르게 블록되거나 시그널을 무시하지 않는다.
int my_system (char *command){
int status;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}else if (pid == 0) { /* Child process */
const char *argv[4];
argv[0] = "sh";
argv[1] = "-c";
argv[2] = command;
argv[3] = NULL;
execv("/bin/sh", argv);
exit(-1);
}
if (waitpid(pid, &status, 0) == -1) {
perror ("wait");
return -1;
}else if (WIFEXITED (status))
return WEXITSTATUS(status);
return -1;
}
int main (void){
return my_system("ls");
}
프로세스에 연관된 사용자 ID는 사실 한개가 아니고 네 종류
real user id
그 프로세스를 최초로 실행한 사용자의 uid
effective user id
그 프로세스가 현재 영향을 미치고 있는 사용자 ID
접근 권한은 일반적으로 이 값을 기준으로 점검
예)
/usr/bin/passwd 파일은 setuid파일이고 소유자는 root이므로 일반 사용자 셸에서 이 파일을 exec하기 위해 프로세스를 생성할 때 이 프로세스는 실행하는 사용자에 상관없이 유효 사용자 ID를 root로 지정한다.
saved set user id
프로세스의 최초 effective user id
프로세스가 fork되면 자식 프로세스는 부모의 saved set user id를 상속받는다.
exec 호출을 통해 커널은 저장된 사용자 id를 유효 사용자 id로 설정하고 그렇게 함으로써 exec 시점에 유효 사용자 ID를 기록
filesystem user id
- 실제 사용자 ID : 이 프로세스의 소유자 계정입니다. 이 프로세스가 액세스 할 수있는 파일을 정의합니다.
- 유효 사용자 ID : 일반적으로 실제 사용자 ID와 동일하지만 권한이없는 사용자가 루트로만 접근 할 수있는 파일에 접근 할 수 있도록 변경되는 경우가 있습니다.
- 저장된 사용자 ID : 프로세스가 상승 된 권한 (일반적으로 루트)으로 실행 중 일부 권한이 부족한 작업을 수행해야 할 때 사용되며 일시적으로 권한이없는 계정으로 전환하여 수행 할 수 있습니다.
권한이 부족한 작업을 수행하는 동안 유효 UID는 낮은 권한 값으로 변경되고 저장된 userID (suid)에 euid가 저장되어 작업 완료시 권한있는 계정으로 다시 전환하는 데 사용할 수 있습니다.
SetUID가 설정된 파일 실행시 특정 작업 수행을 위하여 일시적으로 파일 소유자/그룹 권한을 얻게 된다.