본 포스트팅은 인하대학교 컴퓨터공학과 시스템 프로그래밍 수업자료에 기반하고 있습니다. 개인적인 학습을 위한 정리여서 설명이 다소 미흡할 수 있으니, 간단히 참고하실 분들만 포스팅을 참고해주세요 😎
이전에 배웠던 Jump, call, return 등을 사용했을 떄 프로그램의 state (수행 흐름) 을 바꿀 수 있었다.
그런데 이들만 가지고는 프로그램의 state 를 바꿀 수 없다. 어떤게 더 필요하나면, 프로그래머가 앞선 명령어들처럼 일부러 의도를 가지지 state 대해 처리하는 것
그 이외의 것들이다.
=> Exceptional Control Flow : 프로그램 Handling 을 자동화해주는것
유저의 코드(user code)를 실행하고 있었는데 하드웨어에 에러를 발생시키는 event 가 발생했다면,
Exception 이 발생했다고 인식하고 user code 실행을 멈추고 kernel code 로 넘어간다. 이게 Context Switching 이다.
운영체제가 이 에러가 뭔지를 확인하고 해당 에러에 대해 적절히 핸들링(Exception Handling) 해준다.
Handling 이 끝났다면 다시 user code 로 되돌아가서 코드를 이어서 계속 실행(resume) 한다.
발생한 에러에 대해 바로 처리하지 않아도 되는 에러들
ex) time interrupt, I/O interrupt 와 같은 에러들은 당연히 핸들링 해줘야하지만, 에러 발생 즉시 바로 처리하지는 않고 천천히 처리한다.
만일 어떤 수를 0으로 나누는 상황이 발생하고나서 별도로 에러 처리를 안하고 그 결과값을 이후의 명령어들에 활용한다면 모든 명령어들이 다 수행이 망가질 것이다. 따라서 에러 발생 즉시 바로 처리를 따로 해주고 와야한다.
디폴트 에러 처리방식은 Synchornous 이다.
- Synchornous 란? : 에러 발생시 기존에 하던것을 멈추고 핸들링(에러 처리)을 다 하고나서 재개하는 것. (위에서 봤던 그림 처리방식)
=> 외부에서 발생한 에러 관련 event 로 인해 발생
앞서 언급했던 Fault 에러를 더 살펴보자.
Fault란 위처럼 잘못된 인덱싱 같은 이상한 행위를 했을 떄 자동으로 발생하는 에러라고 했었다.
프로그램은 소스코드와 데이터를 합쳐놓은 마치 패키치 형태를 가지고 있는데, 이를 실행시키면 생기는 인스턴스가 process 이다.
각 프로세스(process) 는 각자 본인만의 메모리(스택)을 가지고있다.
프로세스는 아래의 2가지 개념을 보유하고 있어야한다.
메모리안에는 프로그램이 여러개 있을 수 있다. 실제로 core 가 하나만 있다고하면, 여러 프로그램중에서 어떤 특정 프로그램의 process 를 실행하고 있을 것이다.
그런데 한 프로그램의 Saved register 라는 곳에 현재 상태를 저장하고 옆에있는 다른 프로그램으로 넘어가서 그 프로그램을 실행시킬 수 있다.
=> 이 과정이 굉장히 빠르게 왔다갔다하면 사람이 볼떄는 여러 프로그램이 마치 동시에 수행되는 것 처럼 보인다.
그런데 실제로는 core 가 하나가 아니고 여러개이다.
그래서 위에서 본 옛날 multiprocessing 처럼 계속 왔다갔다 하긴하는데, 여러 프로그램을 core 가 동시에 수행하고 있는 것이다. (아까는 딱 하나의 core 만 수행하고 있었음)
그런데 이런 프로세스가 여럿이면, 여러 프로세스가 공유하는 데이터가 생길수도 있고, 2개 이상의 프로세스가 동시에(Courrently 하게) 수행될 수도 있다.
=> Concurrent 프로그래밍이란 Courrently 한 프로그램을 구현하는 것이다. 같은 프로그램을 구현을해도, Courrently 한 프로그램을 잘 최적화시켜 구현한 경우 자원을 최소한으로 사용하며 훨씬 빠르게 수행할 수 있다.
프로세스 A가 먼저 시작되고, 나중에 B가 실행되는데 A와 함께 같은 데이터를 공유하며 실행되고 있다.
지금까지는 프로세스간에 순차적으로(Sequential) 실행되지 않고있다.
그런데 프로세스 B와 C는 순차적으로 실행되고있다. B가 끝나고 C가 실행되었다. 그런데 A와 B는, A를 하다가 stop 하고 B를 수행했다. 그러고 다시 돌아와서 B를 수행하는 것이다.
=> A와 B는 동시에 돌아갈 수 있다. A와 B는 concurrency 가 있다고 표현하며, B와 C는 concurrency 가 없다고 표현한다.
- 즉, Concurrency 란 두 프로세스가 시작하는 시간과 끝나는 시간을 기준으로 시간대가 겹쳐있다면 (동시에 프로세스가 실행되고 있다면) Concurrent 하다고 표현한다.
- 반대로 프로세스가 동시에 수행되지 않고 순차적으로 수행되는 것을 Sequential 하다고 표현한다.
=> 이떄 어떤 core 를 사용하던 상관없음. 그냥 프로세스가 동시에 돌아가는지 아닌지 여부만으로 Concurrent, Sequential 을 구분짓는 것이다.
user code 의 프로세스가 수행되다가 커널로 갔다가 바로 다시 돌아옴
=> Running (실행되고 있거나), Stopped(잠시 스탑되서 기다리고 있거나), Terminated(끝났거나) 이다.
=> 나는 parent process 이고, fork 된 새로운 프로세느는 child process 이다.
=> 원래 있던 프로세스(parent)와 새로운 프로세스(child) 는 구분하는 방법은 리턴되는 PID 값을 보고 판단한다. (리턴되는 PID 값이 0이면 child process, 그 외의 값은 process process)
child process 로의 분기가 발생했다면 프로그램이 끝날떄 exit 함수가 각자 따로 호출되면 안되고, 동시에 호출되서 같이 종료되어야 한다. parent 가 child 의 수행이 다 끝날때까지 기다려주고 exit 가 호출되어야함
wait(), waitpid() 함수를 써서 parent가 끝나면 child가 끝나도록 해야함
Exception 과 비슷한 방식으로 소프트웨어 하나를 구현한 것.
어떤 중요한 특정 event 가 발생했을 때 운영체제가 user 프로그램들한테 알려주는 방법이다.
(이러한 중요한 이벤트가 발생했어! 라고 signal(시그널) 로 알려주는 것)
특정 이벤트마다 유일한(unique) 번호(ID) 가 정해져있어서 구분할 수 있다.
커널은 Signal 을 2가지 상황일때 보낸다.
1) 시스템 event 를 감지해서 user process들 한테 알려주고 싶을때
2) system call 을 사용해서 운영체제가 생성한 API 를 통해 한 user process 에서 다른 user process 로 보내고 싶을 때