프로세스
프로세스 구성요소
프로그램 코드
데이터
시스템 메모리를 사용하는 변수
프로세스에서 오픈한 파일 디스크립터
환경변수
프로세스 모델
같은 프로그램을 실행하는 서로 다른 프로세스
프로그램 코드와 라이브러리 코드를 공유하기 때문에 하나의 프로그램에 대해 여러 프로세스가 공유
프로세서 마다 고유 pid 할당
프로그램 카운터 프로세스마다 고유
데이터 공간과 스택공간은 프로세스마다 고유 할당
프로세스가 open한 파일, 환경변수도 프로세스 별로 관리
task
리눅스에서 프로세스를 task라 정의, task_struct 라는 데이터 구조체를 사용하여 표현한다.
/usr/src/linux/include/linux/sched.h에 정의
프로세스와 관련된 모든 정보를 정의한 데이터 구조체
운영체제 버전이나 플랫폼에 따라 구조체 내부 변수들이 추가되거나 삭제
struct task_struct {
volatile long state;
...
pid_t pid;
...
}
프로세스 관련 소스 파일
kernel/sched.c : 프로세스 스케쥴러
kernel/signal : 시그널 처리 함수
kernel/fork.c : 프로세스 생성 함수
kernel/exit.c : 프로세스 종료 함수
fs/exec.c : 프로그램 실행
arch/i386/kernel/entry.S : 커널 초기 함수
arch/i386/kernel/process.c : architecture 의존 프로세스
PID와 프로세스 테이블
PID
각 프로세서는 프로세스 ID라는 유일한 식별자가 할당된다. 프로세스가 시작되면 할당되지 않은 다음 번호가 자동 할당된다.
프로세스 테이블
현재 메모리에 로딩된 모든 프로세스들에 대한 정보 저장하는 구조체
프로세스들에 대한 정보를 화면에 표시하기 위한 명령어 > ps -ef 또는 ps -ax
프로세스 스케쥴링
리눅스 커널에서는 다음 time slice에 실행할 프로세스를 선발 하기 위해 우선순위 기반의 스케쥴링을 사용한다.
프로세스의 우선 순위는 nice값에 의해 결정된다.
명령어 | 설명 |
---|
nice | nice값을 10 증가 |
renice | nice값을 변경 |
ps -l | nice 값 확인 |
&을 이용하여 백그라운드 프로세스로 실행할 수 있다.
$ ./test &
프로세스 생성
system
"system" 라이브러리 함수를 사용하여 프로그램 내부에서 다른 프로그램을 실행하여 새로운 프로세스를 생성할 수 있다.
#include <stdlib.h>
int system(const char * string)
예시)
#include <stdlib.h>
#include <stdio.h>
int main() {
printf("Running ps with system\n");
system("ps ax");
printf("Done\n");
exit(0);
}
system 함수는 파라미터로 전달된 명령어 문자열을 실행한 후 종료될때까지 대기한다. 즉, "Running ps with system" 문자열 출력 후 ps ax 명령어를 실행 후 "Done"이라는 문자열이 출력된다.
fork()
현재 프로세스의 이미지 복사.
현재 프로세스의 속성을 대부분 그대로 사용하는 새로운 프로세스를 생성해서 프로세스 테이블에 등록
새로운 프로세스는 현재 프로세스와 동일한 코드를 사용하지만 고유 공간, 환경변수, 파일 디스크립터를 사용한다.
parent process는 wait(&status)를 호출하여 child process의 종료를 기다린다.
printf("pid = %d\n", getpid());
pid = fork();
switch(pid) {
case -1:
perror("fork error");
exit(1);
case 0:
printf("child process\n")
printf("getpid() = %d\n", getpid());
printf("pid = %d\n", pid);
default:
printf("parent process\n");
printf("getpid() = %d\n", getpid());
printf("pid = %d\n", pid);
}
위 코드를 실행시키면 fork함수 호출 전의 getpid() 값은 parent의 getpid()와 같음을 알 수 있다.
하지만 fork() 함수의 반환값으로 얻은 pid는 부모 프로세스에게 새로운 pid를 할당한다. 해서 부모프로세스의 pid 값과 getpid() 값은 다르다는 것을 확인할 수 있다.
자식 프로세스의 getpid() 값은 부모 프로세스의 pid와 같음을 확인할 수 있다. 자식 프로세스는 자신만의 pid를 갖게 되고, ppid는 부모 프로세스의 pid를 가지게 된다. PGID, SID 를 상속받으며, 파일지시자, 시그널등을 상속받는다. 단 파일잠금(lock)와 시그널 팬딩은 상속받지 않는다.
exec()
현재 프로세스의 이미지 변경.
char * const ps_argv[] = {"ps", "ax", 0};
char * const ps_envp[] = {"PATH=/bin:/usr/bin", "TERM=console", 0};
execl("/bin/ps", "ps", "ax", 0);
execlp("ps", "ps", "ax", 0);
execle("/bin/ps", "px", "ax", 0, ps_envp);
execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);
위 코드는 아래의 exec 함수의 접미어에 따라 전달 방법을 달리한 예시이다.
예시)
#include <stdlib.h>
#include <stdio.h>
int main() {
printf("Running ps with system\n");
execlp("ps", "ps", "ax", 0);
printf("Done\n");
exit(0);
}
위 코드는 system 함수와 비교해서 보면, system 함수와 달리 execlp 함수가 호출됨으로 인해 프로세스 이미지가 변경되어 "Done"문자열을 출력하지 않고 ps ax 쉘을 실행시키고 종료한다.
프로세스 종료 대기
wait 함수는 자식 프로세스중 하나가 종료할 때까지 부모 프로세스를 대기시킨다.
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * stat_loc);
if(pid != 0) {
int stat_val;
pid_t child_pid;
child_pid wait(&stat_val);
printf("Child has finished: PID = %d\n", child_pid);
if(WIFEXITED(stat_val))
printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
else
printf("Child terminated abnormally\n");
exit(exit_code);
}
매크로 | 정의 |
---|
WIFEXITED | 자식 프로세스가 정장석으로 종료하면 0이 아닌 값을 리턴 |
WEXITSTATUS | WIFEXITED가 0이 아니면, child exit code 값을 리턴 |
WIFSIGNALED | 자식이 어떤 신호로 인해서 종료되었다면 0이 아닌 값을 리턴 |
WTERMSIG | WIFSIGNALED가 0이 아니면, signal number 를 리턴 |
WIFSTOPPED | 자식 프로세스가 정지되었다면 0이 아닌 값을 리턴 |
WSTOPSIG | WIFSTOPPED가 0이 아니면, signal number 를 리턴 |
좀비 프로세스