뭐니 뭐니 해도 시스템 프로그래밍의 핵심은 파일. 리눅스 프로그래밍에 익숙한 관계로 리눅스(:12) 파일 프로그래밍과의 비교가 많을 것이다.
윈도에서 파일은 커널 Object로 관리된다. 파일을 포함한 모든 커널 Object는 Create로 시작하는 함수로 handle을 얻을 수 있다. 핸들은 커널 Object를 가리킨다. 파일외에 관리하는 커널 객체로는 아래와 같은 것들이 있다.
파이프 객체
이벤트 객체
쓰레드 객체
뮤텍스 객체
프로세스 객체
I/O 포트 객체
세마포어 객체
파일 생성과 읽고 쓰기
파일 만들기/열기
CreateFile(:4100)함수로 파일이나 입출력 장치를 열 수 있다. 입출력 장치는 파일, 파일 스트림, 디렉토리, 물리적인 디스크, 볼륨, 콘솔 버퍼, 테이프 드라이버, mailslot(:4100), pipe(:12)등이다. 16비트 버전 윈도에서는 OpenFile함수를 사용할 수 있는데, 지금은 거의 사용하지 않는다.
리눅스의 파일 열기 함수인 open(2)에 비해 많은 매개 변수를 필요로 한다.
lpfileName으로 열고자 하는 파일 이름을 지정한다.
dwDesiredAccess로 일반 접근 방법을 지정할 있다. GENERIC_READ와 GENERIC_WRITE를 사용한다. 전자는 읽기 전용, 후자는 쓰기전용으로 열기 위해서 사용한다. 읽기와 쓰기 모두를 위해서는 GENERIC_READ | GENERIC_WRITE를 사용하면 된다. 또한 상세 접근 방법을 지정할 수 있다. 상세 접근 방법은 MSDN문서를 참고한다.
dwShareMode는 열린 파일에 다른 프로세스나 쓰레드로 부터의 접근권한을 설정 하기 위해서 사용한다. 0을 사용한다면 다른 프로세스나 쓰레드는 읽기, 쓰기, 삭제를 할 수 없다. FILE_SHARED_DELETE, FILE_SHARED_READ, FILE_SHARED_WRITE를 사용할 수 있다.
lpSecurityAttribute는 보안속성을 지정하기 위해 사용한다. NULL을 지정하면 기본 보안속성이 사용되는데, 이 경우 파일은 자식 프로세스에서 상속할 수 없게 된다.
dwCreationDisposition은 생성 방법을 결정하기 위해서 사용한다. 새로이 생성하려면 CREATE_NEW와 CREATE_ALWAYS를 사용할 수 있다. CREATE_NEW는 파일이 존재하지 않을 경우 생성하고 CREATE_ALWAYS는 파일이 존재할경우 덮어쓴다는 차이점이 있다. 존재하는 파일을 열기 위해서는 OPEN_ALWAYS와 OPEN_EXISTING를 사용할 수 있다. OPEN_ALWAYS는 파일이 존재하든 그렇지 않든 참을 반환한다. 만약 파일이 존재하지 않는다면, 새로 파일을 만든다. OPEN_EXISTING는 존재하는 파일만 연다. 만약 파일이 존재하지 않는다면 거짓을 반환한다. TRUNCATE_EXISTING는 존재하는 파일의 크기를 0으로 해서 연다. 이 값을 사용하기 위해서는 반드시 GENERIC_WRITE가 설정되어 있어야 한다.
성공적으로 수행되면 파일 HANDLE을 반환한다. 실패하면 INVALID_HANDLE_VALUE를 반환한다.
파일 핸들 hFile로 부터 읽은 데이터를 lpBuffer에 저장한다. 읽을 데이터의 크기는 nNumberOfBytesToRead로 지정할 수 있다. 실제 읽은 데이터의 크기는 lpNumberOfBytesRead로 알아낼 수 있다. 마지막 매개 변수인 lpOverlapped는 파일이 FILE_FLAG_OVERLAPPED (비동기 모드)로 열렸을 때 사용한다. 자세한 내용은 비동기 입출력에서 다루도록 한다. 동기 모드로 열린 파일이라면 NULL을 지정한다.
파일 핸들 hFile에 lpBuffer의 데이터를 쓴다. 쓸 데이터의 크기는 nNumberOfBytesToWrite로 지정한다. 실제 쓴 데이터의 크기는 lpNumberOfBytesWritten으로 알아낼 수 있다. lpOverlapped는 비동기 모드에서 사용한다. 동시 모드로 열린 파일이라면 NULL을 지정한다.
닫기
더이상 CloseHandle(:4100) 사용하지 않는 파일은 CloseHandle로 닫아준다.
리눅스 함수의 차이
파일 생성
매개 변수가 많을 뿐더러, 매개 변수에 사용할 수 있는 값들도 많아서 리눅스의 open(:2)함수에 비교해서 복잡하다는 느낌을 받는다. 리눅스에서 파일의 접근은 공유가 아닌 유저별 파일 관리 권한의 형태로 달성하는데, 리눅스의 유저별 권한 부여 방식이 좀 더 명확해 보인다.
예컨데 매개 변수 dwShareMode는 파일이 열린 동안 다른 프로세스와 공유여부를 결정하기 위해서 사용하는데, 파일 잠금과 같은 효과를 가져올 수 있다. 이런 값을 굳이 번거롭게 파일 생성시 매개 변수로 받아올 필요가 있나라는 생각이 든다. fcntl(2)류의 함수로 분리시키는게 낫지 않을까 ? 작은게 아름답다라는 철학을 함수에도 구현하고 있는 리눅스 환경에 익숙해져있기 때문일지도 모르겠다.
파일 접근 방식은 윈도와 리눅스 동일하다.
윈도
리눅스
설명
GENERIC_READ
O_RDONLY
읽기 전용
GENERIC_WRITE
O_WRONLY
쓰기 전용
GENERIC_READ|GENERIC_WRITE
O_RDWR
읽기/쓰기 전용
기타 파일 생성 방법들이다.
윈도
리눅스
설명
CREATE_ALWAYS
O_CREAT
항상 파일을 연다.
CREATE_EXISTS
O_CREAT | O_EXCL
에러 값으로 파일의 존재여부를 확인할 수 있다. 주로 실수로 존재하는 파일을 덮어쓰지 않기 위한 목적으로 사용한다.
TRUNCATE_EXISTING
O_TRUNC
파일크기를 0으로 한다.
FILE_APPEND_DATA
O_APPEND
파일을 추가 모드로 연다. 파일의 위치는 마지막으로 설정 된다.
읽기 / 쓰기
리눅스의 read(2) write(2)함수가 반환 값으로 실제 읽고 쓴 데이터의 크기를 알아오는 것에 반해, 윈도의 함수는 매개 변수로 읽고 쓴 데이터의 크기를 가져 온다는 것 외에는 동일하다.
예제
파일에 내용을 추가하는 프로그램
a.txt의 내용을 읽어서 b.txt의 마지막에 추가하는 프로그램이다. a.txt는 읽기 모드로 열고, b.txt는 쓰기모드로 연다. 이때 FILE_APPEND_DATA를 매개 변수 값으로 줘서 파일 핸들의 위치를 마지막으로 만들어준다.
lpTargetFileName 이름을 가지는 원본 파일을 가리키는 lpSymlinkFileName이름의 심볼릭 링크 파일을 만든다. 원본 파일이 디렉토리일 경우 dwFlags의 값을 SYMBOLIC_LINK_FLAG_DIRECTORY로 하면 된다. 일반 파일이라면 0으로 한다.
windows vista, windows server 2008 이상에서 사용할 수 있다.
lpExistingFileName이름을 가지는 원본파일의 lpFileName 하드 링크를 만든다. lpSecurityAttributes는 사용하지 않는다. 반드시 NULL을 쓴다.
windows 2000 professional, windows 2000 server 이상에서 사용할 수 있다.
유닉스에서 링크는 매우 오래전 부터 사용되었는데, 윈도는 비교적 최근들어 지원되었다. 이유를 확인해 봐야 할 것 같다. 예제 프로그램은 xp에서 실행되지 않았다.
파일 카피
링크와 카피의 다른 점에 대해서 알아본다.
.. 그냥 ReadFile, WriteFile함수로 구현하는게 낫지 않을까란 생각이 든다.
디렉토리 관리
리눅스의 opendir(:3)에 해당하는 함수가 있으리라 생각된다. 알아보자.
기타
STDIN_FILENO에 대한 입출력 다중화
채팅 클라이언트 프로그램을 만든다고 가정해보자. 채팅은 표준입력(:12)과 소켓 입출력을 동시에 처리해야 한다. 리눅스(:12)는 소켓도 파일이니 select(:2)를 이용한 입출력:::다중화(:12)로 간단히 문제흘 해결할 수 있는데, 아 윈도는 그게 안된다.
윈도는 STDIN_FILENO를 비 봉쇄로 할 수 없다. 일단 파일과 소켓을 다르게 취급하기 때문에 select(:2)를 이용할 수도 없고, 비 봉쇄로 할 수도 없으니 결국 쓰레드(:12)로 처리하는 수 밖에 없을 것 같다. 다른 방법이 있을려나 ?
참고로 표준입력은 ReadFileEx에서도 블럭되며, 중첩 입출력 에서도 블럭된다고 한다. 쩝. 좀 더 테스트를 해봐야 겠는데, 우선은 쓰레드로 구현 하련다.
Contents
윈도 파일 프로그래밍
파일 생성과 읽고 쓰기
파일 만들기/열기
읽기 쓰기
닫기
리눅스 함수의 차이
예제
파일 제어
파일 찾기 및 파일 정보 확인
파일 링크
파일 카피
디렉토리 관리
기타
STDIN_FILENO에 대한 입출력 다중화
Recent Posts
Archive Posts
Tags