본문 바로가기

CS/유닉스프로그래밍

2-1. 유닉스 파일, 파일시스템

유닉스는 파일에 대해서 파일의 기능을 심플하게 함

 

파일

파일은 데이터를 포함하는 컨테이너라고 정의함

파일은 연속된 바이트의 시퀀스라고 정의함 

파일에는 어떤 포멧도 정해져있지 않음

(메인프레임의 경우 파일의 종류가 많았음 isam, random, vsam file, 등

파일의 종류에 따라 엑세스하는 API가 모두 다르고, 이를 OS가 다 정의해서 굉장히 무거웠음)

모든 바이트는 디스크 파일에서 바이트 하나하나 주소를 지정함, 다이렉트 랜덤 파일이 된다는 말임

파일에 관한 인터페이스를 다 uniform 하게 통일시킴

심지어는 외부의 디바이스 파일, 디스크 파일, 키보드, 유에스비, 마그네틱 테이프 등 모두 파일로 간주

모든 디바이스의 인터페이스를 다 통일함

 

파일 시스템

컴퓨터 파일과 데이터를 organzing 하고 hierarcichal하게 함

hierarchical하게 만들어서 체계적이고 파일을 찾기도 쉬움

데이터 스토리지 디바이스를 사용할 수 있음 (하드디스크, CD ROM, 마그네틱 테이프 등)

적어도 외형적으로는 통일된 파일 시스템이 들어갈 수 있게 만듦

 

파일 시스템을 엑세스하기 위한 시스템콜은 기본적으로 왼쪽에 있는 9개가 다임

9개 외에도 더 다양한 용도의 시스템콜이 있지만 기본적으로는 이 아홉개를 씀

유저 프로그램이 이 시스템콜을 사용해서 파일을 엑세스 함

시스템콜이 발생할 때마다 소프트웨어 인터럽트인 트랩이 발생함

snnchallenge.tistory.com/112?category=903783

 

7. 시스템콜

* 시스템콜 : os에 서비스를 요청할 수 있는 메커니즘 - 커널모드로 전환 : 하드웨어 인터럽트, 트랩(소프트웨어 인터럽트) - 응용프로그램에서 시스템콜 API 호출 - 시스템콜 API를 전형적으로 라이

snnchallenge.tistory.com

유저가 시스템콜로 파일을 엑세스하기 위한 시스템콜을 부르면

파일 시스템 매니지먼트가 있고, 하드웨어 컨트롤러로 하드웨어를 컨트롤함

디스크 등은 block device고, 키보드, 스크린, 프린터 등은 character device 임

character device는 단위가 1 byte라서 중간에 버퍼가 필요없음

block device는 단위가 1 block임. 1 block은 보통 4 Kbyte = 4096 byte임. 버퍼캐시가 있음

 

 

UNIX file access primitives

open은 파일을 열 때 사용

(read, write를 하기 위해서 파일을 open 해야 함, 새로 만들면서 open 하기도 함)

create는 없는 파일을 empty file로 새로 만들 때 사용

close는 open한 파일을 close할 때 사용

read는 open한 파일을 읽어들일 때 사용

write는 open한 파일에 데이터를 쓸 때 사용

lseek는 랜덤 엑세스가 되는 디바이스인 block device 디스크에서만 사용하는 시스템콜임

(파일은 바이트 단위로 addressible한데, 바이트마다 주소가 있음

해당 바이트만 콕 찝어내는, 위치를 이동시키는 데 사용)

unlink와 remove는 같은 시스템콜, 파일을 지우는 데 사용

fcntl은 파일에 관한 attribute를 변경시키거나 읽을 때 사용

 

이 시스템콜은 ANSI C. 국제표준이 아니라 미국 국립 표준 국에서 표준화한 거임

 

이 함수들은 unbuffered IO, 커널에 있는 그 버퍼가 아니라 라이브러리안에 있는 버퍼가 없다는 말임. 

유저 메모리에 있는 버퍼가 없다는 말임

 

파일디스크립터

파일을 open 하면, open할 때 넘어오는 포인터가 있음

파일을 지정하는 번호인데, 이를 파일디스크립터라고 함

 

파일디스크립터는 0,1,2,3...으로 음수가 아닌 정수로 주어짐

파일을 새로 만들어서 open하거나 기존에 있는 파일을 open 할 때, 파일디스크립터로 read write 함

유닉스에서 프로세스를 만들면 기본적으로 세 개의 파일디스크립터가 만들어짐

0은 표준 입력으로 대개가 키보드

1은 표준출력, 2는 표준에러인데 둘다 스크린임

둘다 스크린으로 같은데 output과 error를 구분해야할 때가 있어서 굳이 구분을 하는 거임

 

이 상태에서 어떤 파일을 오픈하면 그 파일디스크립터는 3번이 됨

사용되지 않은 정수 중 가장 낮은 숫자가 반환되는 거임

 

/* a rudimentary example program */

/* these header files are discussed below */
#include <fcntl.h>
#include <unistd.h>

main()
{
    int fd;
    ssize_t nread;
    char buf[1024];

    /* open file “data” for reading */
    fd = open(“data”, O_RDONLY); 

    /* read in the data */
    nread = read(fd, buf, 1024);

    /* close the file */
    close(fd);
}

 

fd = open("data", O_RDONLY)

첫번째 인자에 들어가 있는 건 relative pathname임

O_RDONLY는 읽기 전용으로 읽으라는 말임

fd는 3이 됨

 

nread = read(fd, buf, 1024)

첫번째 인자는 파일디스크립터이고, 

그 파일디스크립터가 가리키는 파일을 1024만큼 읽어서 두번째 인자인 buf에 넣으라는 말임

nread에는 read작업 후 실제로 읽은 바이트의 수가 들어감

만약 3번 파일에 1024 이상의 충분한 바이트 수가 있다면 nread는 1024가 됨

하지만 100 byte밖에 없으면 100만 읽어서 nread는 100이 될 거임

 

#include <fcntl.h>

<>라는 건 /usr/include/fcntl.h가 있다는 것을 나타내는 것임

/usr/include가 default directory임. 만약 absolute pathname으로 넣으려면 ""로 해주면 됨

fcntl.h는 open 시스템콜이 들어있음

 

#include <unistd.h>

ssize_t 타입은 사실 integer인데 복잡하게 ssize_t라는 이름을 붙임

#typedef ssize_t int 이런 명령어가 unistd.h 안에 sys/types.h에 저장되어 있음

이 ssize_t는 primitive system data type이라고 함 

유닉스에 버전이 너무 많다보니까 read의 return 타입을 short로 쓰기도 하고 long으로 쓰기도 했음

너무 제각각이라서 표준화를 하려다보니 이름을 하나로 통일한 거임

다른 버전에서는 #typedef ssize_t short로 써도 그 속성을 숨기는 거임