교재 : 리눅스 시스템 원리와 실제 (창병모, 생능출판)
13.1 프로세스 이미지
1) 프로세스
- 실행 중인 프로그램
- 프로그램 실행을 위해서는 프로그램 코드, 데이터, 스택, 힙, U-영역 등이 필요하다.
- 프로세스 이미지(구조)는 메모리 내의 프로세스 레이아웃
- 프로그램 자체가 프로세스는 아니다.
2) 프로세스 이미지
- 프로세스 구조
(1) 텍스트(코드) : 프로세스가 실행되는 실행 코드를 저장하는 영역
(2) 데이터 : 프로그램 내에 선언된 전역 변수(global variable) 및 정적 변수(static variable) 등을 위한 영역
(3) 힙 : 동적 메모리 할당을 위한 영역
(4) 스택 : 함수 호출을 구현하기 위한 실행시간 스택(runtime stack)을 위한 영역
(5) U-영역 : 열린 파일의 파일 디스크립터, 현재 작업 디렉터리 등과 같은 프로세스의 내부 정보
3) size 명령어
$ size [실행파일]
: 실행파일의 각 영역의 크기를 알려준다. 실행파일을 지정하지 않으면 a.out를 대상으로 한다.
13.2 프로세스 ID
1) 쉘의 명령어 처리과정
$ 명령어 &
[1] 프로세스번호
2) 프로세스 ID
- 각 프로세스는 프로세스를 구별하는 번호인 프로세스 ID를 갖는다.
#include <unistd.h>
int getpid(); // 프로세스의 id를 반환한다
int getppid(); // 부모 프로세스의 id를 반환한다
13.3 프로세스 생성
1) 프로세스 생성
- fork() 시스템 호출
- 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스 생성
- 자기복제(自己複製)
#include <unistd.h>
pid_t fork(void);
/*
새로운 자식 프로세스를 생성한다.
자식 프로세스에게는 0을 반환한다.
부모 프로세스에게는 자식 프로세스 ID를 반환한다.
*/
- fork()는 한 번 호출되면 두 번 리턴한다.
- 부모 프로세스와 자식 프로세스는 병행적으로 각각 실행을 계속한다.
2) 부모-자식 프로세스
: fork() 호출 후에 리턴 값이 다르므로 이 리턴값을 이용하여 부모 프로세스와 자식 프로세스를 구별하고 서로 다른 일을 할수 있도록 할 수 있다.
...
pid = fork();
if (pid == 0) {
// 자식 프로세스의 실행 코드
}
else {
// 부모 프로세스의 실행 코드
}
...
3) 프로세스 기다리기 : wait()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
/*
자식 프로세스중 하나가 종료할 때까지 기다린다.
자식 프로세스가종료되면 종료코드가 *status에 저장된다.
종료한 자식 프로세스의 ID를 반환한다.
*/
13.4 프로그램 실행
1) 프로그램 실행
- fork() 후 : 자식 프로세스는 부모 프로세스와 똑같은 코드 실행
- 자식 프로세스에게 새로운 프로그램을 실행시키기 : exec() 시스템 호출 사용
- 프로세스 내의 프로그램을 새 프로그램으로 대치
- 보통 fork() 후 exec()
2) exec()
- 프로세스가 exec() 호출을 하면 그 프로세스 내의 프로그램은 완전히 새로운 프로그램으로 대치
- 자기대치(自己代置)
- 새 프로그램의 main()부터 실행이 시작된다.
- exec() 호출이 성공하면 리턴할 곳이 없어진다.
- 성공한 exec() 호출은 절대 리턴하지 않는다.
#include <unistd.h>
// path : 절대 경로
int execl(char* path, char* arg0, char* arg1, ... , char *argn, NULL);
int execv(char* path, char* argv[]);
// file : 파일 이름
int execlp(char *file, char* arg0, char* arg1, ... , char *argn, NULL);
int execvp(char *file, char* argv[]);
/*
호출한 프로세스의 코드, 데이터, 힙, 스택 등을 path(혹은 file)가 나타내는 새로운 프로그램으로 대치 후 새로운 프로그램을 실행한다.
성공한 exec() 호출은 반환하지 않으며, 실패하면 -1을 반환한다.
*/
3) 쉘의 명령어 처리 원리
- 보통 fork() 호출 후 exec() 호출 : 새로 실행할 프로그램에 대한 정보를 arguments로 전달한다.
- exec() 호출이 성공하면
- 자식 프로세스는 새로운 프로그램을 실행
- 부모 프로세스는 다음 코드를 실행하게 된다.
ex) 자식 프로세스를 생성하여 echo 명령어를 수행한다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int pid, child, status;
printf("부모 프로세스 시작\n");
pid = fork();
if (pid == 0) {
execl("/bin/echo", "echo", "hello", NULL);
fprintf(stderr, "첫 번째 실패");
exit(1);
}
else {
child = wait(&status)
printf("자식 프로세스 %d 끝\n",child);
printf("부모 프로세스 끝\n");
}
return 0;
}
4) 프로그램 실행 시작
- exec 시스템 호출 : C 시작 루틴에 명령줄 인수와 환경 변수를 전달하고 프로그램을 실행시킨다.
- C 시작 루틴(start-up routine)
- main 함수를 호출하면서 명령줄 인수, 환경 변수를 전달
- 실행이 끝나면 반환값을 받아 exit
exit(main(argc, argv));
5) 명령줄 인수/환경 변수
int main(int argc, char *argv[]);
- argc : 명령줄 인수의 수
- argv[] : 명령줄 인수 리스트를 나타내는 포인터 배열
13.5 시스템 부팅
- 시스템 부팅은 fork/exec 시스템 호출을 통해 이루어진다.
- swapper(스케줄러 프로세스) : 커널 내부에서 만들어진 프로세스, 프로세스 스케줄링을 한다.
- init(초기화 프로세스)
- 실제로는 systemd에 대한 링크
- /lib/sysetemd/system 파일에 기술된 대로 시스템을 초기화
- 서비스 데몬 프로세스 : 서비스들을 위한 데몬 프로세스들이 생성된다. (ex.ftpd)
- getty 프로세스 : 로그인 프롬프트를 내고 키보드 입력을 감지함.
- login 프로세스 : 사용자의 로그인 아이디 및 패스워드 검사
- shell 프로세스 : 시작 파일을 실행 후에 사용자로부터 명령어를 기다린다.
- 프로세스 트리 출력 : 실행 중인 프로세스들의 부모, 자식 관계를 트리 형태로 출력한다.
$ pstree
'전공과목 정리 > 리눅스시스템 + 시스템프로그래밍' 카테고리의 다른 글
[리눅스시스템🐧] 14장 시스템 관리 (2) | 2024.01.07 |
---|---|
[리눅스시스템🐧] 12장 파일 시스템과 파일 입출력 (2) | 2024.01.07 |
[리눅스시스템🐧] 11장 프로그래밍 환경 (2) | 2024.01.06 |
[리눅스시스템🐧] 10장 Bash 쉘 스크립트 (2) | 2024.01.06 |
[리눅스시스템🐧] 9장 유틸리티 (4) | 2024.01.05 |