01. Process API
- OS가 제공하는 프로세스와 관련된 함수로, 프로세스 생성, 종료와 같은 제어를 다룬다.
02. Process Creation
- Child proc에는 자원이 필요 -> OS가 주거나 Parent가 share
- Resource sharing 종류
- Parent와 Child가 모든 자원을 공유
- Parent의 자원 일부만 Child가 공유받음
- Parent와 Child가 자원 공유하지 않음
- Execution 종류
- Parent와 Child가 동시 실행 ("$cmd &"의 경우 백그라운드에서 동시 실행: cmd가 child, $(쉘)이 parent / 만약 &이 아니었다면 cmd(child)가 끝날 때까지 $이 안 뜸(동시 실행 x))
- Parent가 Child가 종료되길 기다림 (wait)
- Address space 종류
- Child가 Parent의 것을 그대로 복사 (fork: Parent의 PCB를 복제 후 메모리 공간 할당함)
- Child가 완전히 새로운 프로그램을 disk로부터 적재하여 그것으로 대체함 (exec: Disk에서 실행 파일 load후 초기화)
- Process creation 요약
- OS kernel 내에 PCB 생성
- Memory space 할당
- Load binary program (프로그램 실행 파일 적재)
- Program 초기화 -> main()부터 실행
03. Process Termination
- 정상 종료 exit (child가 먼저 종료)
- Process가 마지막 명령문 실행 -> 자식 프로세스는 부모 프로세스에게 상태값 반환 (wait) -> OS가 프로세스 자원 할당 해제 시킴 (delete)
- 비정상 종료 abort
- Child proc가 자원을 너무 많이 쓸 때
- Child proc가 더이상 필요하지 않을 때
04. Process Creation & Termination
05. System Call - fork()
★fork() 반환 값
-child: 0
-parent: child ID
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
printf("start (pid: %d)\n", (int) getpid());
/* Parent 그대로 복사해서 Child 생성
adress space 복사 -> code, stack(실행 위치) */
int rc = fork(); //child는 여기서부터 실행(stack copy)
if (rc < 0) { // fork failed
exit(1);
}
else if (rc == 0) { // child proc
printf("child (pid: %d)\n", (int)getpid());
}
else { // rc == child ID => parent proc
printf("parent of %d (pid: %d)\n", rc, (int)getpid());
}
return 0;
}
$ ./forkTest
start (pid: 1220) // start == parent proc
child (pid: 1221) // parent와 child 순서는 달라질 수 있음(OS 스케쥴러에 따라)
parent of 1221 (pid: 1220)
$
06. System Call - wait()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
printf("start (pid: %d)\n", (int)getpid());
int rc = fork(); //child는 여기서부터 실행(stack copy)
if (rc < 0) { // fork failed
exit(1);
}
else if (rc == 0) { // child proc
printf("child (pid: %d)\n", (int)getpid());
}
else { // rc == child ID => parent proc
int wc = wait(NULL); // child 종료까지 block
printf("parent of %d (wc: %d / pid: %d)\n", rc, wc, (int)getpid());
}
return 0;
}
$ ./waitTest
start (pid: 1220) // start == parent proc
child (pid: 1221) // parent는 block이라 child 먼저 수행
parent of 1221 (wc: 1221 / pid: 1220) // child wait값 같이 출력
$
07. System Call - exec()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
printf("start (pid: %d)\n", (int)getpid());
int rc = fork(); //child는 여기서부터 실행(stack copy)
if (rc < 0) { // fork failed
exit(1);
}
else if (rc == 0) { // child proc
printf("child (pid: %d)\n", (int)getpid());
char* myargs[3];
myargs[0] = strdup("wc"); // wc: word count cmd
myargs[1] = strdup("execTest.c"); // arg: file to count
myargs[2] = NULL; // make cmd array
/* "wc execTest.c" -> cmd 실행, child proc가 wc 실행파일로 완전 대체 */
execvp(myargs[0], myargs);
printf("따라서 이 라인은 실행 안 됨!! (child proc가 바뀌었으니까)");
}
else { // rc == child ID => parent proc
int wc = wait(NULL); // child 종료까지 block
printf("parent of %d (wc: %d / pid: %d)\n", rc, wc, (int)getpid());
}
return 0;
}
$ ./execTest
start (pid: 1220) // start == parent proc
child (pid: 1221) // parent는 block이라 child 먼저 수행
29 107 1030 execTest.c // wc 프로그램의 결과(execvp)
parent of 1221 (wc: 1221 / pid: 1220) // child는 wc로 대체되었기 때문에 그다음 줄은 출력 안 됨
$
☆strdup(str): null값을 포함하여 str의 길이만큼 동적할당하여 str을 복붙 시킨 후 그 메모리 공간의 주소를 포인터로 반환한다.
08. Redirection
$ cmd ... > a // 결과값이 a파일에 저장
$ cmd ... < b // 키보드가 아니라 b 파일로부터 입력받음
=> 기본 입력(키보드)이나 출력(터미널)이 다른 것으로 전환(redirection)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
int rc = fork();
if (rc < 0) { // fork failed
exit(1);
}
else if (rc == 0) { // child proc
close(STDOUT_FILENO); // 1 돌려 받음
open("./reTest.output", O_CREATE | O_WRONLY | O_TRUNC, S_IRWXU); // 얘가 1 됨
char* myargs[3];
myargs[0] = strdup("wc"); // wc: word count cmd
myargs[1] = strdup("reTest.c"); // arg: file to count
myargs[2] = NULL; // make cmd array
/* "wc execTest.c" -> cmd 실행, child proc가 wc 실행파일로 완전 대체 */
execvp(myargs[0], myargs);
}
else { // rc == child ID => parent proc
int wc = wait(NULL); // child 종료까지 block
}
return 0;
}
$ ./reTest // 기본 터미널 출력을 close 해서 여기 출력 안 됨
$ cat reTest.output // 새로 만든 파일에 출력되게끔 함
32 109 846 reTest.c // child proc가 새로 대체된 wc 프로그램의 결과 (execvp)
$
★ STDIN_FILENO: 1
STDOUT_FILENO: 2
STDERR_FILENO: 3
기본으로 정해진 건 위와 같고, 새로 file descriptor 생성할 때마다 사용하지 않는 것들 중 가장 최소 값으로 정해짐
즉 위 예제에서는 close로 1번 예약 값을 없애 버려서 빈 상태이기 때문에 새로 open 하면 그게 1번이 되는 것임
'🌙CS > 운영체제' 카테고리의 다른 글
[Linux] System Call 추가하기 (0) | 2024.09.23 |
---|---|
04. Limited Direct Execution (1) | 2024.09.18 |
02. Processes (0) | 2024.09.13 |
01. Operating System (OS) (0) | 2024.09.13 |
[Linux] 커널 설치, 컴파일 (2) | 2024.09.08 |