일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스터디
- Unseen
- 으
- flask
- 프린세스메이커
- 스마일게이트
- Bootstrap4
- Express
- 마인크래프트뮤지컬
- 프메
- JWT
- 파이썬서버
- 정글사관학교
- 디자드
- 언리얼프로그래머
- 레베카
- Ajax
- Jinja2
- 언리얼
- 언리얼뮤지컬
- 데이터베이스
- R
- VUE
- EnhancedInput
- 카렌
- 알고풀자
- 게임개발
- node
- Enhanced Input System
- 미니프로젝트
- Today
- Total
Showing
[Pintos] Project 2 : syscall, 파일 디스크립터 fdt 본문
시스템 콜(System Call)
Argument Passing에서 pintos의 커맨드라인에서 인자를 파싱하고 패싱할 수 있도록 구현을 했다지만 우리의 project2 핀토스에서는 아직 시스템콜 핸들러와 각 시스템 콜에 대한 처리가 구현되어 있지 않기 때문에 시스템콜이 호출되지 않습니다! 따라서 응용프로그램을 사용자의 기대대로 쓰고, 읽고, 실행하는 등의 일련의 작업을 할 수 없습니다.
사용자모드, 커널모드, 인터럽트, 시스템 콜의 관계
운영체제에는 사용자 모드(User mode)와 커널 모드(Kernel mode)가 있는데 사용자 모드에서 자원에 대한 직접적인 접근을 할 수 없습니다. 반면에 커널모드는 컴퓨터의 모든 자원에 대한 접근 권한을 가집니다. 따라서 사용자 프로세스가 디스크 읽기와 같은 명령어를 실행하려면 시스템 콜(System call)을 부르게 됩니다. 이는 사용자 모드 프로그램이 커널 기능을 사용할 수 있도록 합니다. 시스템 콜은 커널 모드에서 실행되며, 실행이 끝나면 다시 사용자 모드로 복귀됩니다. 이에 대해서 좀 더 구체적으로 정리해보겠습니다.
사용자모드, 커널모드, 인터럽트, 시스템 콜은 운영체제와 컴퓨터 하드웨어가 서로 상호작용하는 방식을 이해하는 데 중요한 개념입니다. 이러한 개념을 이해하면 운영체제와 컴퓨터 시스템이 어떻게 동작하는지 알 수 있습니다.
운영체제는 하드웨어와 소프트웨어 자원을 관리하고, 프로세스 간의 자원 공유를 조정하며, 사용자와 컴퓨터 시스템 간의 인터페이스를 제공하는 등 중요한 역할을 합니다.
사용자모드와 커널모드는 컴퓨터 시스템에서 프로세스가 실행될 때의 권한 레벨을 의미합니다. 사용자모드에서는 제한된 하드웨어 리소스에만 접근할 수 있지만, 커널모드에서는 시스템 전체의 하드웨어 리소스에 접근할 수 있습니다. 사용자모드에서 실행되는 프로세스는 운영체제가 제공하는 인터페이스(시스템콜)를 통해 커널모드의 기능을 사용할 수 있습니다.
인터럽트는 하드웨어나 소프트웨어의 이벤트로 인해 발생하는 신호입니다. 예를 들어, 마우스 클릭, 키보드 입력, 하드웨어 오류 등이 인터럽트를 발생시킬 수 있습니다. 인터럽트가 발생하면 CPU는 현재 실행중인 작업을 중단하고 운영체제의 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)을 실행합니다. ISR은 인터럽트의 원인을 파악하고, 해당 인터럽트를 처리하기 위한 작업을 수행합니다. 이후, CPU는 이전에 실행되던 작업으로 돌아갑니다.
잠깐만! 지금은 인터럽트가 사용자모드에서 시스템콜이 발생할 때 나타나는 신호라서 커널모드로 전환된다는 측면이 강조하고 있지만, 이는 핀토스 유저프로그램 파트여서 그러하지 사실 인터럽트는 일반적으로는 커널모드에서 발생하며, 일부 하드웨어 이벤트에 대한 인터럽트는 사용자모드에서도 발생한다고 보는 것이 보다 정확할 것입니다. 사용자모드에서는 일부 하드웨어 리소스에만 접근할 수 있으므로, 일부 하드웨어 이벤트(예: 마우스 클릭, 키보드 입력 등)가 발생할 때에는 인터럽트가 발생합니다. 사용자모드에서 실행 중인 프로세스에서 인터럽트가 발생하면 CPU는 인터럽트 서비스 루틴으로 전환됩니다. 인터럽트 서비스 루틴은 인터럽트의 원인을 파악하고 필요한 처리를 수행한 후 CPU는 다시 이전에 실행 중이던 프로세스로 전환됩니다. 그리고 커널모드에서는 시스템 전체의 하드웨어 리소스에 접근할 수 있기 때문에 일부 인터럽트(예: 타이머 인터럽트, 네트워크 인터럽트 등)는 커널 모드에서만 운영체제의 특정 모듈에서 주기적으로 발생합니다. 따라서, 인터럽트는 사용자모드와 커널모드에서 모두 발생할 수 있지만, 사용자모드에서는 일부 하드웨어 이벤트에 대한 인터럽트만 발생하고, 커널모드에서는 시스템 전체의 하드웨어 리소스에 대한 인터럽트가 발생합니다. |
시스템 콜은 사용자모드에서 실행되는 프로세스가 운영체제 기능을 사용하기 위해 호출하는 함수입니다. 시스템 콜은 일반적인 함수 호출과는 달리, 커널모드에서 실행됩니다. 시스템 콜은 인터럽트를 사용하여 실행됩니다. 사용자모드에서 실행 중인 프로세스가 시스템 콜을 호출하면, 인터럽트가 발생하여 CPU는 커널모드로 전환됩니다. 이후, 운영체제는 시스템 콜을 처리하고, 결과를 반환합니다. 이후, CPU는 다시 사용자모드로 전환되어 프로세스가 실행을 계속합니다.
이들 개념을 활용하여 운영체제는 하드웨어와 소프트웨어 자원을 효율적으로 관리하고, 사용자와 컴퓨터 시스템 간의 인터페이스를 제공하여 운영체제의 기능을 사용하는 프로세스들이 원할하게 실행될 수 있도록 합니다.
시스템 콜의 종류(k-핀토스의 경우)
시스템 콜(System Call)은 운영체제(OS)에서 제공하는 서비스를 사용하기 위한 프로그래밍 인터페이스입니다. 이는 사용자가 직접 하드웨어를 제어하거나 직접 메모리에 접근하지 않는 대신, 운영체제를 통해 필요한 서비스를 요청할 수 있도록 해줍니다.
프로세스 관련 시스템 콜에는 halt(), wait(), fork(), exit(), exec()이 있습니다. 이들은 프로세스를 생성하고, 프로세스 간의 통신 및 종료 등을 수행합니다.
파일 관련 시스템 콜에는 open(), filesize(), close(), read(), write(), seek(), tell(), create(), remove()가 있습니다. 파일을 생성하고, 열고, 읽고, 쓰고, 크기를 확인하고, 위치를 이동하고, 삭제하는 등 파일에 대한 작업을 수행합니다. 이러한 작업을 할 때, 파일 디스크립터(file descriptor)를 사용합니다. 파일 디스크립터는 파일에 접근하기 위한 일종의 핸들로, 운영체제는 파일 디스크립터와 실제 파일을 매핑하여 관리합니다.
우리의 핀토스에서 syscall_handler를 보면 printf("system call!"); 만 외치고 퇴장해버립니다.
뿐만 아니라 syscall.c와 process.c를 중심으로 헤더파일 포함 6개 정도의 파일에서의 작업이 필요합니다.
본 포스팅에서는 syscall!을 구현하는데 있어 필요했던 개념 몇가지를 소개하고자 합니다.
파일 디스크립터
이중포인터
프로세스나 스레드는 파일을 다루기 위해 파일 디스크립터를 사용합니다. 파일 디스크립터는 파일을 식별하는 번호로, 파일을 열고 읽기/쓰기/닫기 등의 작업을 수행할 때 사용됩니다. 파일 디스크립터는 일반적으로 정수형으로 선언되며, 각 파일 디스크립터는 하나의 파일과 매핑됩니다.
그러나, 많은 파일을 다룰 경우 파일 디스크립터를 일일히 관리하는 것은 매우 어렵습니다. 이 때문에 파일 디스크립터를 관리하기 위한 자료구조인 파일 디스크립터 테이블( fd table )이 사용됩니다. 파일 디스크립터 테이블은 프로세스나 스레드마다 별도로 관리되며, 각 파일 디스크립터 테이블은 파일 디스크립터를 인덱스(index)로 사용하는 배열로 구성됩니다.
파일 디스크립터 테이블을 사용하면, 여러 개의 파일을 다루더라도 각 파일 디스크립터를 인덱스로 검색하여 파일에 대한 정보를 찾을 수 있습니다. 또한, 파일 디스크립터 테이블에서 파일 정보를 관리하므로, 파일 정보를 효율적으로 저장하고 검색할 수 있습니다.
따라서 파일 디스크립터 테이블은 운영체제에서 파일 관리를 수행하는 중요한 요소 중 하나이며, 파일 입출력 시스템 콜이나 파일 시스템 등과 함께 파일을 관리하는 데 사용됩니다.
파일 디스크립터는 정수형(int)으로, 파일 디스크립터 테이블은 매우 많은 파일을 처리할 때 메모리를 많이 사용하게 됩니다. 이를 해결하기 위해서는 각 파일 정보를 별도로 저장하는 것이 아니라, 파일 디스크립터가 가리키는 포인터의 주소만을 저장해야 합니다. 이를 위해 이중 포인터를 사용합니다.
참고로, 핀토스에서는 파일 디스크립터의 개수를 제한하기 위해 FDCOUNT_LIMIT를 사용합니다. 이 값은 파일 디스크립터의 비트 수와 파일 디스크립터 테이블을 위해 할당되는 페이지 수를 곱하여 계산됩니다. 이 값은 파일 디스크립터의 최대 개수를 제한하는 역할을 합니다.
fdt와 파일 관련 시스템 콜
open vs read vs write
'파일 디스크립터 테이블(fdt)'은 파일을 열고, 읽고, 쓰고, 닫는 등의 작업을 할 때, 어떤 파일을 다루는지 구분할 수 있도록 도와주는 테이블이기 때문에 각 파일을 열 때마다 자동으로 만들어지도록 설정해야 합니다. 각 파일을 열 때마다 하나씩 증가하므로 만약 철수가 노래 파일을 열었다면, 그 파일의 파일 디스크립터는 3이 되고, 이 파일 디스크립터를 이용해 노래 파일을 구분하게 됩니다.
따라서 open() 함수는 주어진 문자열 파일명(file)으로 파일을 열어서 파일 디스크립터를 반환합니다. 그리고 열린 파일에 대한 파일 포인터를 프로세스의 파일 디스크립터 테이블에 추가합니다. 만약 파일 디스크립터 테이블이 가득 찼다면, 이미 열린 파일들 중에서 하나를 닫습니다. 이를 위해 open() 함수는 파일을 열지 못했을 경우, 열려 있는 파일을 닫고 -1을 반환합니다.
(close는 반대로 cur->fd_table[fd] = NULL 처리를 신경쓰면 됩니다.)
read() 함수와 write() 함수의 차이점은 파일 디스크립터가 표준 입력/출력이냐 아니냐에 따라 다르게 동작한다는 것입니다.
파일 디스크립터를 파일디스크립터 테이블의 인덱스로 삼으면 파일 포인터(struct file *fileobj)로 변환되며,
struct file *fileobj = cur->fd_table[fd];
read() 함수는 주어진 파일 디스크립터(fd)로부터 데이터를 읽어서 버퍼(buffer)에 저장합니다. 파일 디스크립터가 표준 입력(0)인 경우에는 input_getc() 함수를 사용하여 키보드 입력을 읽어와서 버퍼에 저장합니다. 그렇지 않은 경우에는 파일을 읽어와서 버퍼에 저장합니다. 파일 읽기는 file_read() 함수를 사용하며, 이때 파일에서 읽은 데이터의 크기가 반환됩니다.
write() 함수는 주어진 파일 디스크립터(fd)로부터 버퍼(buffer)에 있는 데이터를 파일에 쓰게합니다. 파일 디스크립터가 표준 출력(1)인 경우에는 putbuf() 함수를 사용하여 데이터를 화면에 출력합니다. 그렇지 않은 경우에는 파일에 데이터를 씁니다. 파일 쓰기는 file_write() 함수를 사용하며, 이때 파일에 쓴 데이터의 크기가 반환됩니다.
기타 파일과 관련된 시스템 콜 중에는, 파일 크기를 알려주는 filesize(), 파일 내에서 읽고 쓸 위치를 지정하는 seek(), 그리고 파일에서 읽거나 쓸 다음 위치를 알려주는 tell()이 있습니다.
System Calls - read() |
System Calls - write() |
read() : fd를 통해 열린 파일의 데이터를 읽는 시스템 콜.
|
write() : fd를 통해 열린 파일의 데이터를 기록하는 시스템 콜.
|
filesys_lock
filesys_lock은 파일 시스템에 대한 접근을 동기화하는 데 사용되는 락(lock)으로, 파일 시스템에 접근하고 싶다면 이 락을 획득하고 파일 시스템 작업이 끝난 후 락을 해제해야 합니다. 이를 위해 lock_acquire() 함수와 lock_release() 함수를 사용하면 됩니다.
lock_acquire() 함수는 락을 획득하는 함수로, 파일 시스템 작업을 시작하기 전에 호출해야 합니다. 파일 시스템에 접근하는 코드에서 만약 락이 이미 다른 코드에 의해 획득되어 있다면, 현재 코드는 대기 상태로 들어갑니다.
lock_release() 함수는 락을 해제하는 함수로, 파일 시스템 작업이 끝난 후 호출해야 합니다. 파일 시스템에 접근하는 코드에서 이 함수를 호출하여 락을 해제합니다. 락을 해제함으로써 다른 코드가 락을 획득할 수 있게 됩니다.
파일 시스템은 여러 프로세스나 스레드가 동시에 접근할 수 있기 때문에, 파일 시스템 자원에 대한 접근을 동기화하여 충돌이나 데이터 불일치 문제를 방지하기 위해 filesys_lock을 사용합니다. 보다 안전하게 파일 시스템 작업을 처리할 수 있습니다.
프로세스 관련 시스템 콜
exec(), wait(), fork()
'컴퓨터 공학, 전산학 > 핀토스' 카테고리의 다른 글
[Pintos, 디버깅] Project 3 : VM, lazy_load_segment 추적 (0) | 2023.05.15 |
---|---|
[Pintos] 벌레를 잡기 위한 GDB (1) | 2023.05.11 |
[Pintos] Project 2 : Argument Passing, 인터럽트 프레임 (0) | 2023.05.07 |