User process tree
아래 사진은 리눅스에서 부팅과 동시에 초기화되는 시스템 상태이다.
먼저 init이라는 프로세스가 생성된다.
그 후, fork와 exec를 통해 이를 복사하고 변경해 getty라는 프로세스를 만들어 유저에게 콘솔을 제공한다.
이 getty라는 프로세스는 유저의 커넥션을 기다리고 있고, 유저가 연결되면 getty는 다시 exec를 통해 login이라는 프로세스로 변경된다.
login은 유저의 ID, PW를 입력받아 로그인할 수 있게 하는 프로세스이다.
이렇게 로그인을 하면 login은 다시 exec를 통해 shell이라는 프로세스로 변경되고,
shell 프로세스에서 명령어를 입력하면 fork와 exec를 통해 명령어를 수행하는 새로운 프로세스를 만들어 명령을 수행한다.
* shell에서 ./a.out &로 명령어를 입력하면 백그라운드로 동작하고, ./a.out > output으로 입력하면 stdout이 output으로 변경되어 output 파일에 출력이 저장된다.
Multitask Scheduling
하나의 CPU에서 여러개의 Process를 동시에 처리하는 방식.
Context Switching (or Process Switching)
일정 시간이 지나면 프로세스를 변경해 작업하는 방식.
위 사진은 P0, P1 프로세스가 동시에 실행중일 때 OS 스케줄러가 이를 관리하는 방법을 나타낸 사진이다.
먼저 P0이 실행중이고 time interrupt가 발생하면(일정 시간이 지나면) OS 스케줄러는 P0를 idle 상태로 바꾸고 이 상태를 P0의 PCB의 stack 영역에 저장한다. (필기는 P0의 PCB를 스케줄러의 Stack 영역에 저장한다고 했음)
그리고 P1을 동작상태로 만들고 P1의 PCB에서 정보를 가져와 CPU의 각종 레지스터에 적재한다.
race condition
위와 같은 context switching에서는 특정 경우에 문제가 발생한다.
예를 들어 아래와 같은 copy 프로그램이 존재한다고 해보자.
int main() {
int fd1, fd2, n; char buf;
fd1 = open (“myfile”, O_RDONLY); // open in read mode
fd2 = creat (“copyfile”, 0777); // create an empty file
fork();
// parent & child shares open files and their file pointers(r/w offset)
// if a parent get a char form the file then the child get the next char // because they share the file pointer
while ((n=read(fd1, &buf, 1)) != 0) write(fd2, &buf, 1);
close(fd1); close(fd2);
}
이 copy 프로그램은 fork()를 통해 2개의 프로세스로 동작한다.
고로 context switching이 발생하는데, 만약 n=read(fd1, &buf, 1)이 동작 후 write(fd2, &buf, 1)가 수행되지 않고 context switching이 일어나면 fd1의 offset은 이미 하나 증가한 후 context switching이 일어나는 것이므로 문자 하나를 건너뛰고 복사를 진행한다.
그리고 다시 원래의 프로세스로 context switching이 일어나면 그제서야 해당 문자를 출력하게 되는 것이다.
이 문제는 동일한 파일 디스크립터를 공유하기 때문에 발생하는 문제로, 2개의 프로세스가 경쟁하는 양상을 보인다고 해 race condition이라고 한다.
또한 이러한 문제를 일으키는 부분이 while문인데 이를 critical section이라고 한다.
이를 해결하기 위해서는 여러가지 방법이 있는데, 여기서는 read와 write를 나눠서 수행하지 않고 한번에 수행하도록 하는 mutual exclusion을 사용할 수 있다.
Inter-Process Communication (IPC)
race condition을 피하기 위해 두 프로세스간 정보를 주고 받기 위한 통신으로 이를 구현하는 여러가지 방식이 존재한다.
- sharing files
- message queue
- semaphores - mutual exclusion
- signals
- network sockets
- pipe - 프로세스간 circular queue
Pipe
프로세스간 IPC를 위한 tool로 r/w pointer를 사용하는 FIFO file이다.
각 프로세스들은 오로지 write와 read 중 하나의 역할만 수행할 수 있다.
이때 write를 수행하는 프로세스를 Producer, read를 수행하는 프로세스를 Consumer라고 한다.
Consumer는 데이터가 작성될 때까지 blocking으로 기다리고, Producer에서 데이터를 쓰면 Consumer에서는 blocking을 해제하고 데이터를 읽는다.
아래 코드는 자식 프로세스를 만들어 Producer로 동작시키고 부모 프로세스는 Consumer로 동작하는 코드이다.
int main(void) {
int fd[2]; // two descripters for pipe
int input, output; // copy input and output
int n; char buf[10];
pipe(fd); // fd[0] for reading, fd[1] for writing
if (fork() == 0) { // child process executes
input = open ("input.dat", 0); // open file “input.dat” for reading close(fd[0]); // close read pipe channel
while ( (n = read (input, buf, 10)) != 0) // until EOF
write (fd[1], buf, n); // to pipe
close(input);
close(fd[1]);
exit(0);
} else { // parent process executes
close (fd[1]); // close unnecessary wrting channel
output = creat("output.dat", 0666);
while ( (n = read(fd[0], buf, 10)) != 0) // until pipe closed
write(output, buf, n);
close (output); close(fd[0]);
wait();
}
}
'대학 강의 > 시스템 프로그래밍' 카테고리의 다른 글
[시스템 프로그래밍] 5 Threads (0) | 2023.04.13 |
---|---|
[시스템 프로그래밍] 4 Process Control (0) | 2023.04.03 |
[시스템 프로그래밍] 3-1 Concurrent Processes (0) | 2023.03.26 |
[시스템 프로그래밍] 1-3 Library Buffering (0) | 2023.03.26 |
[시스템 프로그래밍] 1-2 Standard I/O Library (0) | 2023.03.26 |