좁은 의미의 운영체제-메모리에 상주하는 커널부분
넓은 의미의 운영체제-커널 뿐만 아니라 주변 시스템 유틸리티까지 포함한 개념
이 사진이 대략적인 컴퓨터 시스템 구조이다.
간단하게 각각이 어떤기능을 하는지 살펴 보겠다.
IO장치에는 IO장치를 담당하는 작은 CPU가 달려있는데 이게 device controller이다.
또한 각 CPU마다 작업공간이 필요하고 메모리같은것이 있는데=>main CPU에서는 Memory에 해당하게되고, 이런 IO장치에서는 일종의 작은 메모리를 local buffer라고 한다.
CPU에서는 계속 기계어를 실행하고있다. 운영체제가 있는 위치에서 기계어를 실행 할 때가 있고, 사용자 프로그램이 있는 기계어를 실행 할때가 있다.
이미 운영체제가 있는 위치에서 실행하는 기계어는 미리 정의를 다 해놓은 안전한 기계어를 실행하므로 믿고 맏길 수 있다.
그러나 사용자 프로그램에는 무한루프를 도는 악성 코드가 있을 수 있다.
그런데 문제는 일단 CPU가 사용자 프로그램에 넘어가게되면 운영체제가 제어 할 수 있는 길이 없다.
왜냐하면, 운영체제는 cpu를 넘겨줄 수는 있는데 뺏어오지는 못하기 때문이다.
이를 해결하기 위해서 mode bit이라는게 있다.
그래서 mode bit이 1이라면 위험한 기계어는 실행하지 못하게 한다.
또한 만약 mode bit이 1인데 권한을 넘는 기계어를 수행하려 하면 cpu가 운영체제로 넘어가게 구현을 해두었다.
이러한 기능을 해주는것이 interrupt,exception이다. 인터럽트와 익셉션은 추후에 자세히 또 배울 것이다.
register에는 프로그램 카운터가 있는데 다음에 실행할 프로그램을 확인하는 책갈피 같은것이다.
timer는 운영체제로 다시 cpu를 뺏어오는 역할을 해주는데 일정시간간격으로 time가 interrupt를 발생시킨다. 그래서 무한루프를 돌다가도 interrupt가 발생하면 cpu가 계속 사용자 프로그램을 처리하는게아니라 운영체제로 넘어가는것이다.
그래서 애초에 사용자 프로그램에다가 cpu를 넘길 때 그냥 넘기는게 아니라, timer를 설정하고 넘겨준다.
결국 타이머는 cpu의 독점을 막기위해서 사용되는것이다.
cpu가 프로그램 A를 실행하고 있는데, 누가 인터럽트를 안걸면, 그냥 자기가 쓸 수 있는 시간동안 프로그램 A를 계속 수행하고 있을 것이다.
그런데 timer가 interrupt를 걸면 프로그램A는 cpu를 더 쓰고 싶어도 운영체제와 하드웨어 지원을 통해서 cpu의 독점권을 막는다.
그래서 항상 cpu를 프로그램에 넘길때 항상 timer를 설정해서 cpu를 넘긴다.
timer interrupt가 들어오면 다음 기계어를 실행하기 이전에 interrupt가 들어온것을 확인하고 cpu의 제어권이 OS에게 넘어간다.
방금 본 예시가 하드웨어 인터럽트라고 보면된다.
다른예시로, 자, 프로그램 A가 기계어를 실행하는 도중에, disk에서 파일을 읽어야된다고치자. 그러면
Cpu가 바로 disk로가서 file을 가져올 수 있는가? =>x
i.o는 매우 느린장치이고, cpu가 직접 접근할 수 없다.
그러면 어떻게 해야하냐면, cpu가 disk controller에 요청을 해야한다.
그러면 프로그램 A가 자동적으로 disk controller에게 요청할 수 있는가? => x
왜냐하면 cpu가 IO해달라는 기계어는 특권명령으로 묶여있다. 이말이 뭐냐면 사용자 프로그램이 cpu를 가지고 있으면서 i,o를 할수는 없다는말이다.
그러므로 운영체제한테 해달라고 부탁해야한다.
그러면 그림과 같이 프로그램 A가 OS로 cpu를 넘기기 위해서 programCounter를 OS로 넘길 수 있는가?=>x
가상메모리에 있는 프로그램A가 메모리를 가로질러서, 나 이file 가져다줘 이렇게 OS로 넘길 수 없다.
즉 프로그램이 직접 programCounter를 OS로 넘길 수 없기때문에, 무슨방법을 써야한다.
이전에 OS로 cpu가 넘어가려면 어떻게 해야하는지 딱 1가지 방법을 배웠다. 바로 interrupt이다.
그러므로 프로그램 A는 disk에 있는 file을 읽기위해서 systemcall(운영체제한테 file을 가져와달라고 부탁-사용자 프로그램이 운영체제의 서비스를 받기위해서 커널 함수를 호출하는것)을 호출하고, 그러면 이 프로그램이 자신의 기계어를 통해서 interruptline을 세팅한다.
그러면 cpu는 다음 기계어를 실행하기 전에 interrupt가 들어왔으니까 운영체제한테 제어권을 넘긴다.[이게 시스템콜 과정이다.]
그다음 disk가 file을 다읽으면 diskcontroller가 cpu에게 interrupt를 건다.
그럼 다른 프로그램이 cpu를 쓰더라도 인터럽트가 걸리면 cpu가 운영체제로 넘어가게 되고, 운영체제는 가만히 왜 인터럽트가 걸렸지? 보다가 아 file을 다 읽어서 이런거구나 하고
운영체제는 자신의 code를 이용해 메모리에 file 을 copy하게 되고 메모리에 이제 이 file이 올라와있으니까 프로그램 A가 사용할 수 있는것이다.
이처럼 프로그램이 직접 interrupt를 거는것을 소프트웨어 인터럽트,trap이라하고, Timer나,disk같은 하드웨어들이 interrupt를 거는 것을 하드웨어 인터럽트라고한다.
devicedriver: 이 devicedriver는 divecontroller가 수행하는것이 아니고, cpu가 devicecontroller에게 요청하는 방법이 들어있는것이다.(이거 파일 읽어줘~)
device conroller: device conroller도 결국 어떤 코드에 의해서 disk를 읽을텐데 이게 펌웨어이다. 이미 disk안에 내부에 있는 코드이고, 이 코드를 통해서 disk를 읽거나 쓰거나, 지우거나 하는 방법이 들어있는 코드이다.
결국 요약해보자면
운영체제로 cpu가 다시 넘어가는 3가지 경우는 인터럽트 라인을 세팅할때 넘어가게된다.
하드웨어 장치들이 인터럽트를 걸때
소프트 웨어가 인터럽트 라인을 거는거, system call => 자기가 그냥 io를 못해서 인터럽트를 거는거지 자기가 해야할 일이니까 interrupt라고 까지 보지않고 그냥 trap이라고 봄
timer
쨋든, 결국 각 인터럽트 라인마다, 자기가 해야 할 일이 다르다.
관련용어
인터럽트 백터 - 인터럽트종류별로 운영체제에서 실행해야할 코드가 있는것,
timer인터럽트가 걸리면 여기에 있는 코드를 실행시키고, 다른 인터럽트는 저기에 있는 코드를 실행시키는 이런식
인터럽트 처리 루틴 - 실제 주소에 있는 실행해야하는 인터럽트 코드를 실행시키는거, 인터럽트 핸들러라고도 한다.
동기식 입출력
사용자 프로그램이 io를 요청하고, io작업이 끝나면, disk컨트롤러가 인터럽트걸고, disk file을 읽어온걸 보고, file을 읽어올때까지 기다리는거, 왜냐하면 file을 읽어와야 뭘 하는데 뭘 읽어올지 모르니까 비동기로 못한다.
비동기식 입출력
io결과를 보지 않고, 그 이전에 작업할 수 있는 것들이 있다. io결과와 무관한 것들 혹은 io가 file을 읽어와라, 이런게 아니라 disk file에 써라 이러면, 프로그램에서는 disk에 꼭 써지던 안써지던 상관없이 다음일을 처리 할 수 있기 때문이다.
쓰기 작업은 비동기식으로 처리가 가능하다. 그런데 꼭 써졌는지 안써졌는지 확인하고 다음일을 해야겠으면 동기식도 가능하다.
두경우 모두 io가 끝나면 인터럽트로 알려준다.
DMA
만약 읽어오는 byte에 제한이 없다면, 1byte읽어오는 요청보내고, 읽어오고, 인터럽트 걸고 이걸 1byte마다 계속하면, cpu에 부담이 될것이다.
그래서 메모리에 직접 접근할 수 있는 장치를 하나 둔것이다. Direct Memory Access
아주 작은 data가 localbuffer에 들어있는데 이걸 계속 가져오는게 아니라, 한 512byte만큼 찼으면,
dma가 직접 buffer에서 가져와서 memory에 올려주고, 그다음에 인터럽트까지 걸어준다.
즉 block단위로 인터럽트를 발생시킨다는것이다.
그래서 결국 다시 정리하면, 이 device에서 buffer에 file이 올라가면, device controller가 memory에 직접 올리지 못한다. 왜냐하면 memory는 cpu의 작업 공간이기때문이다. 그래서 어느정도 용량이 차면, DMA가 직접 buffer access해서 memory에 올려주고 그다음 인터럽트까지 걸어준다.