프로세스는 한마디로 실행중인 프로그램임!
근데 프로세스의 개념을 이해할 때 굉장히 중요한 게 있는데, 그게 프로세스의 context, 즉 문맥임!
사전을 보면, 한 단어가 여러가지의 의미로 정의되어 있음. 그래서 그 단어가 정확이 어떤 의미로 쓰이고 있는지 알려면 문맥을 봐야함. 전체적인 문장구조 속에서 이사람이 이 단어를 왜 썼는지, 그걸 이해해야함. 프로세스의 문맥도 마찬가지! 프로그램이 처음에 태어나서, 쭉 실행이 되다가 언젠가는 종료가 될텐데 문맥이라는 것은 그 중간 어느시점을 탁 짤라놓고 봤을 때 도대체 이 프로그램이 무엇을 어떻게 실행했는지, 그리고 현재 시점이 어떤 상태에 있는지를 정확하게 나타내기 위해서 사용되는 개념.
프로세스는 실행이 되면 code, data, stack으로 구성된 독자적인 주소공간을 형성하는데, 이 프로세스가 CPU를 잡게되면 PC(Program Counter)가 이 프로세스의 코드 어느부분을 가리키고 있고, 그러면 매 순간 instruction, 기계어를 하나씩 읽어서 CPU 안으로 불러들임. 그래서 레지스터 안에 값을 넣고, 산술 논리 연산 장치(ALU)에서 연산을 하고 그다음에 그 결과를 레지스터에 저장하거나 바깥의 메모리(프로세스A의 주소공간 등)에 저장하거나 하는 식으로 진해하고 있음. 이런식으로 계속 진행을 하다가 어느 시점에 과연 이 프로세스는 지금 어디까지 와있는가, 그걸 규명하는데 필요한 요소가 바로 프로세스의 문맥임.
그래서 현재시점에 프로세스의 문맥을 나타내기 위해서는 이 프로그램 카운터가 어디를 가리키고 있느냐, 즉 코드의 어디까지 실행했느냐, 그것 또한 알아야되고 또 프로세스A의 메모리(가상 메모리)에 지금 어떤 내용을 담고 있는가, 뭐 예를 들면 스택에서 지금 무슨 내용을 어디까지 쌓아놓고 있는가, 그리고 data면 변수의 값 이런걸 바꾸고 그랬을테니까 지금 이 변수의 값은 얼마이며 그다음에 이 프로그램이 실행되면서 또 이 레지스터에다가 값을 넣었을테니까 레지스터에다가 지금 어떤 값을 넣어놨고, 어떤 instruction까지 실행을 했는 가, 그런 모든 요소들을 다 알아야지만 지금 프로세스의 현재 상태를 나타낼 수 있음. 이런 식으로 프로세스의 현재 상태를 나타내는데 필요한 모든 요소를 프로세스의 문맥이라고 부름.
사람으로 치면 몇살이고, 키는 얼마고, IQ는 얼마고, 몸무게는 얼마고, 지식은 얼마나 있고, 밥은 얼마나 먹고.. 과거에 무언가를 했기 때문에 지금의 상태에 이르렀지만 과거의 것은 무시하고 현재의 상태를 통해서 지금의 문맥을 나타낼 수 있을 것임. 프로세스의 문맥도 마찬가지. 과거의 프로그램들이 실행이 되면서 현재의 시점까지 왔을 텐데, 현재 시점에 정확한 상태를 규명하기 위해서 필요한 요소들을 문맥이라고 부름.
문맥은 크게 3가지 정도로 규명할 수 있음. 첫번째는 CPU와 관련된 하드웨어 문맥! 프로세스라는 것은 CPU를 잡고 매순간 instruction을 실행함. 그래서 현재 시점에 이 프로세스가 그 instruction을 어디까지 실행했는 가를 알기 위해서는 레지스터에 어떤 값을 넣고 있었고, 프로그램 카운터가 어디를 가르키고있었고, 이런 요소들이 필요함. 그게 CPU와 관련된 하드웨어 문맥, 주로 레지스터가 현재 어떤 값을 가지고 있었는 가를 나타내게 됨.
두번째는 메모리와 관련된 프로세스의 주소공간임. 현재 시점에 코드, 데이터, 스택에 어떤 내용이 들어있는가, 그걸 알아야지만 프로세스의 현재 상태를 정확하게 나타낼 수 있음.
세번째는 운영체제의 역할중 하나가 프로세스 관리인데, 그래서 프로세스가 하나 생길때마다 운영체제는 그 프로세스를 관리하기 위해서 프로세스가 하나 실행될 때 마다 자신의 데이터영역에 PCB를 하나씩 두어서 이친구한테 CPU를 얼마나 줘야될지, 이 친구한테 메모리를 얼마나 줘야할지, 이친구가 혹시 나쁜 짓을 하고 있지는 않은지 이런 것을 관리하는 그런 역할을 하고 있음. 그래서 프로세스의 현재 상태를 알려면 CPU 상태나 메모리 상태도 알아야지만, 운영체제가 이친구에 대해서 어떤 값을 가지고 있는지도 알아야됨. 그래서 커널이 가지고 있는 프로세스의 PCB와 커널스택도 알아야됨.
커널 스택은 각 프로세스가 자기자신의 코드를 실행중일때는, 만약 함수 호출이 이루어진다면 본인의 스택에다가 함수호출한다음에 리턴하고, 관련 정보를 쌓아놓게 됨. 그런데 프로세스가 실행이 되다가 본인이 할 수 없는 일을 운영체제에게 대신 요청, 즉 시스템 콜을 하게되면 프로그램카운터가 프로세스A의 주소공간을 가리키는게 아니라 커널 주소공간의 어딘가를 가리키면서 커널의 코드를 실행하게 됨. 커널도 함수들로 이루어져있어서 커널에서 함수호출이 이루어지게되면 커널에서 스택에다가 또 관련된 정보들을 쌓아놓게 됨. 그런데 커널이라는 것은 여러 프로세스들이 공유하는 코드라고 할 수 있음. 즉 어떤 프로세스든 간에 운영체제한테 요청을 할 때, 서비스를 해 달라고 부탁을 할 때 커널의 코드를 실행하게 되고 그러면 누구의 부탁을 받고 실행하는지 매번 다르게 됨. 그래서 커널에서 함수호출이 이루어져서 스택에다가 정보를 쌓을때는 프로세스 별로, 어떤 프로세스가 커널을 호출했는지에 따라서 스택을 별도로 두고 있음. 그렇게해야지만 정보가 꼬인다던지 하는 문제를 막을 수 있기 때문에 커널스택을 별도로 두고 있음. 그래서 프로세스의 현재 상태를 규명하기 위해서는 유저스택 말고도 커널스택도, 본인의 커널스택에 어떤 정보를 쌓고 있는지도 필요함.
그래서 이런 일련의 정보를 통해 프로세스가 현재 어떤 상태를 가지고 있는지 규명할 수 있게 됨. 만약 프로세스가 혼자 실행이 된다면 이런걸 알필요는 없을텐데, 현대의 컴퓨터 시스템에서는 타임쉐어링, 멀티테스킹 → 즉 프로세스들이 번갈아가면서 실행이 되기 때문에 하나의 프로세스가 CPU를 잡고 실행을 하다가 또 CPU를 놓고 다른 프로세스한테 CPU가 넘어가도록 되어있음. 그럼 현재 이 프로세스가 CPU의 레지스터에 어떤 값을 넣고 실행을 하고 있었다고 할 때, 이걸 백업해놓지 않으면, 즉 프로세스의 현재 문맥을 알고있지 않으면 다음번 CPU를 잡았을 때 앞부분부터 다시 실행해야된다거나 그렇게 됨. 그래서 항상 프로세스의 문맥이라는걸 파악하고 있다가 문맥을 통해 얘가 어느시점까지 실행이 됐었는지 알아야 이어서 실행할 수 있게 됨.
컴퓨터안에 CPU는 하나밖에 없음(운영체제 수업에서 CPU가 여러개인 경우는 논하지 않음).
CPU가 하나밖에 없기 때문에 CPU를 잡고있는 프로세스는 매 순간 하나! 바로 그 프로세스의 상태를 Running이라고 함.
그리고 CPU가 하나밖에 없기 때문에, 다른 프로세스들이 너도나도 CPU를 쓰겠다고 줄서있지만 기다려야함. 그렇게 기다리는 프로세스의 상태를 Ready라고 부름. 그런데 보통 Ready라는 건 다른 조건은 모두 만족하고, 즉 당장 이 프로그램이 CPU에서 실행될려면, CPU는 디스크 접근은 못하니까 당장 필요한 부분은 물리적인 메모리에 올라와있어야지만 CPU를 얻었을 때 instruction을 실행할 수 있음. 그래서 Ready상태라는건 다른 모든 준비는 다 끝나있고, CPU만 내가 얻으면 instruction을 실행할 수 있는 상태. 그래서 보통은 Ready상태에 있는 프로세스들이 번갈아가면서 CPU를 잡았다 놓았다해서 time sharing을 구현하고 있음.