본문으로 이동
주 메뉴
주 메뉴
사이드바로 이동
숨기기
둘러보기
대문
최근 바뀜
임의 문서로
~ldmsys
검색
검색
외관
로그인
개인 도구
로그인
현재 모든 문서는 작성 중입니다. 보시는 데 불편함이 있더라도 양해 부탁드립니다.
운영체제 기본/파일과 입출력 문서 원본 보기
문서
토론
한국어
읽기
원본 보기
역사 보기
도구
도구
사이드바로 이동
숨기기
작업
읽기
원본 보기
역사 보기
일반
여기를 가리키는 문서
가리키는 글의 최근 바뀜
문서 정보
외관
사이드바로 이동
숨기기
←
운영체제 기본/파일과 입출력
문서 편집 작업을 수행할 권한이 없습니다. 다음 이유를 확인해주세요:
요청한 작업은 다음 권한을 가진 사용자에게 제한됩니다:
사용자
.
문서의 원본을 보거나 복사할 수 있습니다.
[[운영체제 기본|목차]] · 이전: [[운영체제 기본/가상 메모리|가상 메모리]] C에서 파일을 다룰 때에는 파일 이름, 파일 디스크립터, 열린 파일의 상태를 구분해 두면 좋다. 셸 리다이렉션이나 파일을 여러 실행 흐름에서 함께 쓰는 코드를 읽을 때 이 차이가 드러난다. == 파일 디스크립터와 열린 상태 == <code>open()</code>이 성공하면 음이 아닌 정수인 파일 디스크립터(fd)를 돌려준다. 이 값은 프로세스의 fd 테이블에서 열린 대상을 찾는 데 사용한다. 그 아래에는 커널이 관리하는 '''open file description'''이 있다. 여기에는 현재 파일 오프셋과 파일 상태 플래그 등이 들어 있다. fd와 열린 상태를 구별해야 하는 이유는 여러 fd가 같은 상태를 가리킬 수 있기 때문이다. <code>dup()</code>으로 fd를 복제하면 두 fd는 같은 open file description을 참조한다. 일반 파일에서 한쪽으로 읽어 오프셋이 이동하면 다른 쪽에도 그 위치가 적용된다. 반면 같은 경로를 <code>open()</code>으로 두 번 열면 각각 새로운 open file description이 만들어져 오프셋이 별도로 유지된다. [https://man7.org/linux/man-pages/man2/open.2.html open(2)] · [https://man7.org/linux/man-pages/man2/dup.2.html dup(2)] 파일 상태 플래그를 공유한다는 말과 fd 자체의 플래그까지 공유한다는 말도 구별해야 한다. 예를 들어 close-on-exec 같은 파일 디스크립터 플래그는 fd별로 관리된다. == 이름이 사라지는 것과 파일이 사라지는 것은 다르다 == 경로는 디렉터리를 따라 대상을 찾는 방법이고, 열린 파일 디스크립터는 이미 찾은 대상에 접근하는 참조다. 일반적인 Unix 파일 시스템에서 이름은 inode로 표현되는 파일 객체에 연결된다. 같은 객체에 여러 이름이 붙을 수도 있다. 이름을 지우는 일과 객체를 더는 아무도 사용하지 않는 일은 구분해야 한다. 로그 이름을 삭제해도 프로세스가 기존 파일을 열어 두었다면 계속 쓸 수 있다. 경로 검색으로 이름을 찾지 못해도 열린 참조가 남기 때문이다. 따라서 파일이 디렉터리에서 사라졌다고 저장 공간까지 즉시 회수되었다고 결론 내릴 수 없다. 같은 이름으로 새 파일을 만들어도 기존 디스크립터가 새 파일로 옮겨 가지는 않는다. 설정 파일을 교체할 때도 이 구분이 중요하다. 같은 파일 시스템에 새 파일을 완성한 뒤 기존 이름으로 바꾸면, 새로 그 경로를 여는 쪽에는 새 대상을 제시하면서 이미 옛 파일을 연 쪽의 참조는 유지한다. 다만 이름 교체의 원자성은 여러 파일을 함께 바꾸는 트랜잭션이나 전원 장애 뒤에도 변경이 남는 내구성을 뜻하지 않는다. 경로가 가리키는 대상, fd가 참조하는 객체, 데이터의 저장 상태는 각각 이름 공간, 객체 수명, 영속성의 문제다. 참고: [https://man7.org/linux/man-pages/man2/open.2.html open(2)], [https://man7.org/linux/man-pages/man2/unlink.2.html unlink(2)], [https://man7.org/linux/man-pages/man2/rename.2.html rename(2)]. == 표준 입출력도 fd다 == 보통 fd 0, 1, 2는 각각 표준 입력, 표준 출력, 표준 오류에 쓰인다. Bash 터미널에서 다음 명령으로 현재 셸의 연결 대상을 볼 수 있다. <pre> ls -l /proc/$$/fd </pre> 터미널에서 실행했다면 세 번호가 같은 터미널 장치를 가리킬 수 있다. 그러나 프로그램의 표준 출력이 반드시 화면인 것은 아니다. 셸이 리다이렉션이나 파이프를 구성하면 그 대상은 파일이나 파이프일 수 있다. 프로그램은 표준 출력에 쓰는 인터페이스를 그대로 사용하고 셸이 연결을 바꾼다. <code>FILE *</code>와 fd도 같은 것이 아니다. <code>fopen()</code> 등으로 얻는 C 표준 I/O 스트림은 사용자 공간의 버퍼링을 포함하며, <code>open()</code>으로 얻는 정수 fd와 구분해야 한다. == 읽기와 쓰기는 일부만 완료될 수 있다 == <code>read(fd, buf, count)</code>의 성공 반환값은 실제 읽은 바이트 수다. 요청한 <code>count</code>보다 작을 수 있으므로 고정 길이 데이터를 읽는 코드는 반환값을 누적하며 필요한 만큼 처리해야 한다. 오류와 EOF도 구분한다. <code>write()</code>도 전달한 데이터를 한 번에 모두 기록했다고 가정할 수 없다. 반환값과 오류를 확인해야 한다. 특히 파이프나 TCP 같은 스트림 소켓을 다룰 때 “한 번 보낸 크기”와 “상대가 한 번 읽는 크기”가 같다고 가정하면 프로토콜 처리가 깨질 수 있다. [https://man7.org/linux/man-pages/man2/read.2.html read(2)] · [https://man7.org/linux/man-pages/man2/write.2.html write(2)] == 저장 완료는 어느 시점인가 == 일반적인 버퍼드 파일 I/O에서는 <code>write()</code>가 성공해도 데이터가 페이지 캐시에만 반영되어 있을 수 있다. 이후 커널이 저장장치에 기록한다. 따라서 전원 차단이나 시스템 장애에도 데이터가 남아야 하는 프로그램은 쓰기의 반환값만으로 작업을 끝내면 안 된다. <code>fsync()</code>는 파일의 데이터와 필요한 메타데이터를 저장장치에 동기화하는 인터페이스다. 이 호출의 실패도 처리해야 한다. 새 파일을 만들거나 이름을 바꾼 경우에는 파일 내용 외에 디렉터리 항목의 영속성도 고려해야 하며, 파일 하나에 대한 <code>fsync()</code>만으로 그 부분까지 보장되지는 않는다. 출력 스트림에 대한 C 표준 I/O의 <code>fflush()</code>는 사용자 공간의 버퍼에 쌓인 데이터를 하위 쓰기 함수로 전달한다. 이것만으로 저장장치까지 동기화되었다고 볼 수 없다. '''사용자 공간 버퍼 → 커널의 캐시 → 저장장치'''를 구분하면 두 함수의 역할이 왜 다른지 이해할 수 있다. == 관련 각론 == * [[운영체제 기본/Linux의 파일 읽기]]: fd 테이블, struct file, file_operations의 연결. == 참고 문헌 == * [https://man7.org/linux/man-pages/man2/open.2.html open(2)] · [https://man7.org/linux/man-pages/man2/dup.2.html dup(2)] * [https://man7.org/linux/man-pages/man2/read.2.html read(2)] · [https://man7.org/linux/man-pages/man2/write.2.html write(2)] * [https://man7.org/linux/man-pages/man3/fflush.3.html fflush(3)] · [https://man7.org/linux/man-pages/man2/fsync.2.html fsync(2)] * [https://man7.org/linux/man-pages/man5/proc_pid_fd.5.html proc_pid_fd(5)]
운영체제 기본/파일과 입출력
문서로 돌아갑니다.
검색
검색
운영체제 기본/파일과 입출력 문서 원본 보기
새 주제