조건부 컴파일 (#ifdef, #endif)
#ifdef, #endif 는 C와 C++ 프로그래밍 언어에서 조건부 컴파일을 위해 사용되는 전처리 지시문이다. 이들은 특정 조건이 참일 때만 코드 블록을 컴파일에 포함하도록 하는 데 사용된다.
- #ifdef: "If Defined"의 약자로, 다음으로 오는 코드 블록이 컴파일에 포함되기 위해서는 1) 지정된 매크로가 정의되어 있거나, 2) 컴파일 시점에 해당 조건을 명령어에 포함해야 함을 나타낸다. 예를 들어, #ifdef USERPROG는 USERPROG이라는 매크로가 이미 정의된 경우에만 그 이후의 코드가 컴파일 과정에 포함되도록 한다.
- #endif: #ifdef로 시작된 조건부 컴파일 블록의 끝을 나타낸다. 이는 해당 조건이 끝났음을 컴파일러에게 알려준다.
initd, process_create_initd 등 -> d 는 무엇일까?
- d는 daemon 의 약자.
- daemon(데몬) 프로세스: 사용자 제어 없이, 백그라운드에서 여러 작업을 수행하는 프로세스를 의미. 보통 시스템 자체가 종료되기 전까지 쭉 실행상태이다.
read 함수에서 동시성 관리 문제
(syn-read, write, remove 테스트: 동시성 관리 필요)
lock은 결국 공유 자원을 보호하기 위함이니까, 가능한 함수 초반부에 lock_acquire을 해주고, 함수 마지막 부분에서 lock_release를 해주는게 안전하지 않을까라는 생각을 했다.
아래가 처음 버전 코드!
결과는??
read-bad-fd 테스트에서 FAIL이 발생한다. (이미 lock을 지니고 있다는 에러)
- 정상 범위에 해당하지 않는 fd(파일 디스크립터) 번호를 줌으로써 에러 처리를 적절하게 수행했는지 확인하는 테스트이다.
원인은 fd가 비정상적으로 들어왔을 때의 예외처리를 아래 if문 내부 process_get_file 함수에서 해주는데, 잘못된 fd가 입력되었다고 해도 if문에 진입하기 전에 이미 lock을 획득하게 되고, 이를 반환하지 않은 채 함수가 리턴되기 때문이다.
- fd가 음수이거나, 지정한 limit 값을 초과하게 되면 process_get_file 함수는 NULL을 반환하고, 이에 따라 오류(-1)를 반환하게 된다.
- 그렇게 되면, 획득한 filesys_lock을 해제해주는 과정없이 해당 시스템콜을 종료해버리고, 다음 파일 read를 시도하게 된다.
- 따라서 다음 차례의 read 시도에서, lock_release()를 호출할 때 해당 프로세스(= 스레드)는 이미 lock을 가지고 있다는 에러를 마주하게된다.
이를 해결하기 위해 두 가지 방법이 가능했다.
1. 일단 무조건 lock을 주고, 잘못된 fd 였다면 에러 처리를 해주기 전에 lock을 해제하고 함수 종료하도록 하기
2. fd가 2 이상(& 최대치 이하)인 경우에만 lock 획득/ 해제 메커니즘을 추가한다.
Q. 왜 표준 입력(fd == 0)일 때는 동시성 관리를 해주지 않아도 문제가 없을까?
표준 입력은 대부분의 경우 단일 사용자(현재 시스템을 사용하는 사람)에 의해 제어되는 경우가 많기 때문이다. 키보드 입력과 같은 표준 입력 장치는 한 번에 하나의 입력만을 처리하며, 이로 인한 동시성 문제가 발생할 가능성이 낮다. 즉, 이러한 환경에서는 동시성 문제가 일반적으로 발생하지 않는다.
[read 시스템 콜 역할]
1. 표준 입력 (fd == 0): 파일 디스크립터 0은 표준 입력(Standard Input)을 나타낸다. 즉 키보드 입력 등을 받는 함수이다. 이때, 함수는 input_getc()를 사용하여 사용자로부터 문자를 하나씩 읽고, 이를 buffer에 저장한다.
2. 표준 출력과 표준 에러 (fd == 1, 2): 파일 디스크립터 1과 2는 각각 표준 출력(Standard Output)과 표준 에러(Standard Error)를 나타낸다. 이들은 데이터를 출력하는 데 사용되므로, read 함수가 담당하는 영역이 아니다. 따라서 함수는 오류(-1)를 반환한다.
3. 그 외의 파일 디스크립터: fd 값이 2보다 크다면, 이는 일반 파일, 소켓 등 다른 종류의 I/O 자원을 가리키는 것으로 간주된다. 이 경우, process_get_file(fd)를 호출하여 해당 fd에 연결된 파일 객체를 검색하고, file_read() 함수를 사용하여 데이터를 읽는다.
이 과정에서 파일 시스템의 잠금(lock)을 통해 동시성 문제를 관리하며, 이는 여러 프로세스나 스레드가 동시에 같은 파일에 접근하는 것을 방지한다.