학교 과제로 저수준 파일 입출력 함수를 이용해서 파일을 복사하는 간단한 C 프로그램을 만들었다.
정확한 문제 내용은,
newcp src.c dst.c
위 명령어를 실행하면 src.c의 내용을 dst.c에 복사하게 만들어야 한다.
코드
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int fd1, fd2;
char fileName1[50], fileName2[50];
char buf[10];
strcpy(fileName1, argv[1]);
strcpy(fileName2, argv[2]);
fd1 = open(fileName1, O_RDONLY);
fd2 = open(fileName2, O_CREAT | O_WRONLY | O_TRUNC, 0644);
int byte;
while((byte = read(fd1, buf, 4)) > 0) {
write(fd2, buf, byte);
}
close(fd1);
close(fd2);
return 0;
}
저수준 파일 입출력 = 시스템콜
저수준 파일 입출력은 시스템의 커널에서 기본적으로 제공하는 시스템콜 함수이다. 고수준과 뭐가 다르냐면, 바이트 단위로 파일을 다뤄서 속도가 빠르고 어떤 유형의 파일이든 읽을 수 있다. 저수준 파일 입출력에 해당하는 함수들은 open, read, write 등이 있다. 고수준과 다르게 어두에 f가 붙어있지 않다.
고수준 파일 입출력은 저수준 파일 입출력 기반으로 만들어졌다. 바이트가 아니라 버퍼 단위로 한 번에 읽고 쓴다. fopen, fread 등이 있다.
파일 디스크립터
위 코드를 보면 변수 fd1, fd2의 타입이 int형이다. 예전에 C언어로 파일 입출력 실습을 했을 땐 파일 디스크립터 변수의 타입이 FILE형이어서 의문이 들었는데 사실 고수준 파일 입출력 함수에서 쓰이는 FILE 구조체의 내부를 보면 int형 변수인 fd가 있다. 파일 디스크립터 외에 파일을 다루는데 필요한 여러 정보가 있는 것이다.
여기서 쓰이는 파일 디스크립터 변수는 왜 int형일까? 이것은 파일 디스크립터에게 부여되는 번호이다. 프로세스가 생성되면 아까 내가 만든 fd1, fd2 외에 자동으로 만들어지는 파일 디스크립터가 3개가 있다. 0은 표준 입력(STDIN), 1은 표준 출력(STDOUT), 2는 표준 에러이다. fd1과 fd2는 그 다음 번호인 3부터 부여되는 것이다.
실제로 3부터 부여되는지 테스트하기위해 출력해봤는데, fd1과 fd2가 각각 3, 4로 출력되었다.
open()에 있는 의문의 상수들
fd1 = open(fileName1, O_RDONLY);
fd2 = open(fileName2, O_CREAT | O_WRONLY | O_TRUNC, 0644);
open() 함수는 두 번째 인자로 파일을 여는 방식에 대한 상수를 받는다. 이 상수들은 정해져있다.
- O_RDONLY : 이름에서 알 수 있듯이 읽기 전용이다.
- O_CREAT : 이름에서 알 수 있듯이 해당하는 파일이 존재하지 않으면 새로 만든다. (그런데 왜 CREATE가 아니라 CREAT인지는 모르겠다. 딱히 무슨 의미가 있는 것 같지는 않은데...)
- O_WRONLY : 이름에서 알 수 있듯이 쓰기 전용이다.
- O_TRUNC : 기존의 파일 내용을 삭제한다.
두 번째 줄의 open() 함수의 세 번째 인자에 있는 0644는 접근 권한을 설정하는 상수이다. O_CREAT로 파일을 생성하는 경우에는 접근 권한도 설정해야 한다.
"-rw- r-- r--" 같은 문자들을 본 적이 있을 것이다.
- | r | w | - | r | - | - | r | - | - |
파일 유형 | 소유자 권한 | 그룹 권한 | 일반 사용자 권한 |
- 파일 유형: 일반 파일이면 -, 디렉토리면 d, 소켓 파일이면 s 등.
- 위 알파벳 코드를 숫자 코드로 바꾸면 0644가 된다. r은 4, w은 2, x는 1이다. rw는 4 + 2 = 6, r--는 4 이기 때문에 0644가 된다.
read() 함수와 write() 함수
while((byte = read(fd1, buf, 4)) > 0) {
write(fd2, buf, byte);
}
read()
- fd1이 참조하는 파일 오프셋에서 4바이트만큼 읽어서 buf에 저장한다.
- 0보다 큰 수를 리턴한다.
- 읽을 데이터가 없으면 0을 리턴한다. EOF이다. 그래서 while문 조건을, 0이 리턴되면 멈추도록 설정하면 된다.
- byte = read(fd1, buf, 4) 이 표현식을 괄호로 감싸지 않으면 이상한 내용이 복사가 된다. 이유는 연산자 우선순위와 관련이 있는 것 같은데... 검색해보니 할당 연산자 '='가 비교 연산자보다 우선순위가 낮다. 그런데 그래도 납득이 안 간다. 왜지? 비교하고 byte에 할당하면 되는 거 아닌가.
write()
- fd2가 참조하는 파일 오프셋에 byte(위 read()에서 읽어들인 만큼의 바이트 수. 4라고 보면 됨)만큼의 buf에 저장된 데이터를 작성한다.
참고: CREAT() 함수
fd2 = open(fileName2, O_CREAT | O_WRONLY | O_TRUNC, 0644);
위 코드에서 쓰인 O_CREAT | O_WRONLY | O_TRUNC 조합은 워낙에 자주 쓰여서 아예 함수로 나왔는데 그것이 creat() 함수이다. 단, 쓰기 전용으로만 열기 때문에 creat()으로 파일을 만들고 나서 open()으로 다시 열어줘야 한다.
'리눅스' 카테고리의 다른 글
[리눅스] chmod 함수 직접 구현하기 (0) | 2022.06.14 |
---|---|
GCC 컴파일 에러 undefined reference to '함수' & 컴파일 과정 (0) | 2022.04.28 |