더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

파일 정보 얻어오기

 

파일에 대한 정보를 얻어올때는 lstat시스템 콜을 사용하면 됩니다. 사실 stat, lstat, fstat등 여러가지 시스템 콜이 있는데요, 가장 간단하게 사용할 수 있는 시스템 콜이기 때문에 여기서는 lstat을 사용하겠습니다.

 

우선 lstat에 관한 시스템 콜 원형부터 보겠습니다.

int lstat(const char *filename, struct stat * buf);

lstat은 두개의 인자를 받게 되어있군요.

filename : 말 그대로 파일의 이름입니다. 하지만 경로를 명시하지 않을때는 현재 디렉토리를 기준으로 파일을 찾습니다. 절대경로나 상대경로를 줄 수 있다는 점은 알고 잇으세요.

buf : 파일의stat구조체를 저장합니다. stat??

무엇일까요. 구조체부터 보도록 하지요. vi 편집기를 사용한다면 lstat에서 3+K(shift)를 입력하면 시스템 콜을 볼 수 있습니다. 조금 더 밑으로 내려가다 보면 stat구조체를 만나볼 수 있게 되는데요. 아래와 같습니다.

struct stat

{

dev st_dev;    /* device */

ino_t st_ino;    /* inode */

mode_t st_mode;    /* protection */

nlink_t st_nlink;    /* number of hard links */

uid_t st_uid;    /* user ID of owner */

gid_t st_gid;    /* group ID of owner */

dev_t st_rdev;    /* device type (if inode device) */

off_t st_size;    /* total size, in bytes */

unsigned long st_blksize;    /* blocksize for filesystem I/O */

unsigned long st_blocks;    /* number of blocks allocated *.

time_t st_atime;    /* time of last access */

time_t st_mtime;    /* time of last modification */

time_t st_ctime;    /* time of last change */

}

음.. 파일에 대한 여러정보를 알 수 있군요. 

st_ino는 inode를 말하는 것이고 st_mode는 파일이 디렉토리인가, 단순 파일인가, 케릭터 디바이스인가를 알 수 있는 것이겠구요.

uid, gid도 알 수 있고 파일의 사이즈도 알 수 있습니다. 더군다나 파일이 언제 수정되고 접근되었는지에 대한 시간 정보도 알 수 있군요! 굿!!

 

그 중에서 ls명령어에 사용되는 st_mode는 어떤 파일의 타입인지 저장되어 있습니다. 어떤 파일 타입인지 알아보기 위해서 매크로 함수를 제공하는데요. 이것에 대해서 조금 더 알아보기 위해서 표로 준비해보았습니다.

 

모드  설명 
 S_ISLNK(m) 심볼릭 링크 파일인지 확인합니다. 
 S_ISREG(m) 단순 파일인지 확인합니다. 
 S_ISDIR(m)  디렉토리인지 확인합니다. 
 S_ISCHR(m)  문자 디바이스인지 확인합니다. 
 S_ISBLK(m)  블록 디바이스인지 확인합니다. 
 S_ISFIFO(m)  FIFO파일인지 확인합니다. 선입선출 구조의 파일인지 확인하는 것이죠. 
 S_ISSOCK(m)  네트워크 통신에 필요한 소켓파일인지 확인합니다. 

 

반환값 : 성공시 0이 반환되고 실패시 -1이 반환됩니다. 에러가 발생했다면 적절한 값으로 설정된다고 하는데 에러에 대해서는 구글형님께 물어봅시다.

 

구현

이제 대충 알아보았으니 코드를 직접 짜보도록하겠습니다. 우선 코드부터 보시죠. 

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <time.h> 
#define BUF_SIZE 1024     
void printType(const char *name,const struct stat *buf){
        if(S_ISDIR(buf->st_mode))   
                printf("%s is DIR \n",name);  
        else if(S_ISREG(buf->st_mode))  
                printf("%s is FILE\n", name);  
        else if(S_ISSOCK(buf->st_mode))    
                printf("%s is SOCKET\n",name);   
        else if(S_ISCHR(buf->st_mode))   
                printf("%s is CHARACTER DEVICE\n",name);    
        else if(S_ISFIFO(buf->st_mode))    
                printf("%s is FIFO\n",name);     
        else if(S_ISBLK(buf->st_mode))   
                printf("%s is BLOCK DEVICE\n",name);   
        else if(S_ISLNK(buf->st_mode))   
                printf("%s is LINK\n",name); 
}  

void printTime(struct stat *buf){       
        struct tm *mtime;      
        mtime=localtime(&buf->st_mtime);     
        printf("%04d-%02d-%02d %02d:%02d\n",
                        mtime->tm_year+1900, mtime->tm_mon+1,
                        mtime->tm_mday, mtime->tm_hour,  
                        mtime->tm_min); 
}   

void printFileSize(const char *name, struct stat *buf){      
        printf("%s size: %ld\n",name,buf->st_size); 
}  

int main(){      
        char filename_dir[128]="dir";     
        char filename_file[128]="aaa";   
        struct stat file;       
        struct stat dir;     

        lstat(filename_dir,&dir);     
        lstat(filename_file,&file);  

        printType(filename_dir,&dir);     
        printTime(&dir); 
        printFileSize(filename_dir,&dir);    
        printf("\n");

        printType(filename_file,&file);    
        printTime(&file);  
        printFileSize(filename_file, &file); 
}

 

 

printType의 함수는 파일이 어떤 종류의 파일인지 알아오는 함수입니다. 그러니까 이 파일이 정규파일인지, 디렉토리인지, 블록 디바이스 파일인지 등등 알아오는 함수지요. 매개변수는 stat구조체의 포인터를 매개변수로 받아야 알 수 있겠죠? 파일의 이름과 같아 받게 됩니다.
 
printTime은 파일이 언제 수정되었는지에 대해서 알아오는 함수입니다. 역시 stat구조체를 매개변수로 받습니다. 시간을 우리가 더 잘알아보기 위해서 localtime이라는 함수를 사용합니다. 그래서 tm구조체의 포인터를 반환하죠.
 
printFileSize는 파일의 크기를 알아오는 함수입니다. 역시 파일의 이름과 stat구조체를 매개변수로 받고 있습니다. 파일의 크기를 알아오는 변수는 st_size입니다.
 
이제 코드를 컴파일시키고 파일을 만들어 실험해봅시다.
# gcc lstat.c
# mkdir dir
# touch aaa

 

 
이제 파일의 크기를 조금 변경시켜보도록 하겠습니다. vi 편집기를 열어 aaa파일에 ABCDEF라는 문자열을 입력하고 저장합니다.
# vi aaa
ABCDEF
:wq
 
이제 컴파일된 파일을 실행시킵니다.
# ./a.out
dir is DIR
2018-11-19 00:06
dir size : 6

aaa is FILE
2018-11-19 00:46
aaa size : 7
 
그 후에 ls 명령어로 두 파일과 디렉토리를 보게되면 같은 정보를 확인할 수가 있습니다.
반응형
블로그 이미지

REAKWON

와나진짜

,

더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

파일입출력

 

리눅스 상에서 파일에 대한 입출력을 소개합니다. c언어에서의 라이브러리가 아닌 시스템 콜에는 open, read, write가 있는데요. 이것을 잘 포장하여 만든것이 fopen, fread, fwrite라는 함수랍니다. 이제 우리는 원시적인 시스템 콜을 가지고 파일에 대한 입출력을 하려고 합니다. 한가지 알아두셔야할것은 read, write는 버퍼링 없는 입출력 시스템 콜이라는 것입니다. 내부적으로 버퍼링을 하지 않기 때문에 read나 write시에 매번 커널 안의 시스템 호출이 실행이 됩니다. 반면 printf와 같은 라이브러리 함수는 버퍼링을 사용한 출력 함수입니다.

 

1. open

 

int open(const char *pathname, int flags, ... /* mode_t mode */ );

int openat(int fd, const char*pathname, int oflag, ... /* mode_t mode */ );

 

파일은 열 수 있는 두개의 시스템 콜이 있습니다. openopenat이 그것들입니다. 여기서는 open 함수만을 설명하도록 하겠습니다. 가장 오른쪽 인자 mode는 주석이 처리되어있고 주석 시작 앞에 ...을 볼 수 있는데요. ...은 가변인자를 의미하고 있을 수도 있고, 없을 수도 있다는 것을 의미합니다. 있다면 mode에 인수를 지정해야합니다.

pathname : 파일의 경로와 이름입니다. 절대경로의 파일명을 주어도 되고 상대경로의 파일명을 주어도 됩니다.

flags : 파일을 어떻게 열지를 결정합니다. 읽기 전용으로 열때는 O_RDONLY, 쓰기 전용으로 열때는 O_WRONLY, 읽기 쓰기로 열고 싶을때는 O_RDWR을 사용합니다. 이 밖에도 O_EXEC, O_SEARCH 등이 있습니다. 이 다섯개 중 하나는 반드시 지정돼야 있어야합니다. 아래의 표로 다시 정리해드리죠.

flag 설명
O_RDONLY 파일을 읽기 전용으로 엽니다.
O_WRONLY 파일을 쓰기 전용으로 엽니다.
O_RDWR 파일을 읽기, 쓰기 전용으로 엽니다.
O_EXEC 파일을 실행 전용으로 엽니다.
O_SEARCH 파일을 검색 전용으로 엽니다. 디렉토리만 해당합니다.

 

이 밖에도 자주쓰이는 flag들에 대해서는 아래를 참고하세요.

flag  설명 
O_CREAT   파일이 없으면 생성합니다. 이 옵션을 사용할때는 mode 인자를 추가로 지정해야합니다. 파일의 권한을 설정해야하기 때문이죠. 
O_EXCL  O_CREAT과 같이 사용되며 파일이 이미 존재하면 에러나 파일을 여는데에 실패합니다.
O_TRUNC  파일이 이미 존재한다면 이미 존재하는 파일의 내용을 무시하여 엽니다. 파일을 처음부터 쓴다는 flag입니다. 
 O_APPEND 파일에 내용을 추가할 수 있는 옵션입니다. 
 O_NONBLOCK
O_NDELAY
파일을 블록모드가 아닌 비블록모드로 엽니다. 기본적으로 파일은 블록모드입니다. 입력이 없을때는 입력할때까지 기다린다고 이해하시면 됩니다. 
O_DIRECTORY pathname이 디렉토리가 아니면 오류를 발생시킵니다.
O_CLOEXEC FD_CLOEXEC 파일 서술자 flag를 설정합니다. exec류 함수 호출시에 파일을 close한다는 flag입니다. 
O_NOFOLLOW pathname이 기호링크(symbolic link) 가리키면 오류를 발생시킵니다.

 

이 밖에도 여러 flag들이 쓰이는데, 여기서는 이정도만 설명하고 넘어가도록 하겠습니다. 그리고 여러개의 flag들을 사용하고 싶을때는 | (OR) 연산을 사용합니다. 예를 들어 읽기 쓰기 모드로 열고 싶은데 파일이 없으면 생성하는 flag는 O_RDWR| O_CREAT로 사용하면 됩니다.

 

mode : 파일을 생성할때의 권한을 주는 옵션입니다. O_CREAT과 같이 파일을 생성할때 사용합니다.

만약 0666을 주게 된다면 모두 읽기, 쓰기 권한을 가진 파일을 만들게 되는 것이죠. 현재 umask에 따라 생성 권한이 다르게 나타나겠지만, 정확히 확인하기 위해서는 umask(0)으로 umask를 해제한 이후에 O_CREAT을 통해 실제 생성한 파일의 권한을 확인할 수 있습니다. umask를 해제하면 file은 기본적으로 0666 권한으로 생성이 됩니다. 

또한 가독성을 높이기 위해 심볼릭 상수도 제공하고 있습니다. 심볼릭 상수를 사용할때 역시 | 연산으로 권한을 줄 수 있습니다.

심볼릭 상수는 <fcntl.h> 헤더파일에 존재하고 아래와 같습니다.

상수 설명
S_IRUSR 소유자 읽기 권한 설정
S_IWUSR 소유자 쓰기 권한 설정
S_IRGRP 소유자 그룹 읽기 권한 설정
S_IWGRP 소유자 그룹 쓰기 권한 설정
S_IROTH 일반 유저의 읽기 권한 설정
S_IWOTH 일반 유저의 쓰기 권한 설정

 

반환값 : 성공적으로 파일을 열게되면 파일 디스크립터를 반환합니다. 그렇지 않으면 음수를 반환합니다.

 

2. read

ssize_t read(int fd, void* buf, size_t len);

 

fd : 파일 디스크립터입니다. open은 정상적으로 파일을 열면 그 파일에 파일디스크립터를 반환하죠? 그 파일디스크립터를 써주면 됩니다. 하지만 표준 입력과 표준 출력, 표준 에러는 각각 순서대로 0, 1, 2가 되므로 표준 입력으로 읽어들일때는 0을 써주면 됩니다.

buf : 파일에서 읽어들일 버퍼를 말하고 있습니다. 어떤 자료형으로 읽어올지 모르므로 void*로 매개변수로 받습니다.

len : 얼마만큼 읽어올지를 결정합니다.

반환값 : 정상적으로 파일에 대한 내용을 읽어온다면 읽은 바이트수를 반홥합니다. 즉 len의 값을 반환하죠. 그렇지 않다면 0을 반환합니다.

 

3. write

파일에 내용을 기록합니다. 사용방법은 read와 거의 비슷합니다.

 

size_t write(int fd, const void *buf, size_t nbytes);

 

fd : 파일 디스크립터랍니다. read의 파일디스크립터를 받는 것처럼 사용합니다.

buf : 버퍼에 쓸 내용을 말합니다.

nbytes : 실제 버퍼에서 얼마만큼의 길이를 파일에 쓸것인지를 결정합니다.

반환값 : 올바로 write에 성공했다면 쓰여진 bytes수, 그렇지 않다면 -1을 반환합니다.

 

파일 쓰기, 읽기

이제 이것을 바탕으로 표준 입력으로 내용을 입력받아 파일에 내용을 기록할 겁니다. 그리고 난 후에는 쓴 파일에서 그 내용을 읽어 표준출력으로 출력할 겁니다.

여기에 그 코드가 있습니다.

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#define BUF_SIZE 1024  
int main(){ 
	int fd,n;         
	char buf[BUF_SIZE];       
	umask(0);       //0666의 권한을 확인하기 위해 umask해제
    //file.txt를 읽기,쓰기로 생성하고(기존에 있다면 내용무시), 0666 권한으로 엶
	fd=open("file.txt",O_CREAT|O_RDWR|O_TRUNC, 0666);     
	if(fd<0){                 
		printf("file open error\n");                 
		exit(1);         
	}         
	n=read(0,buf,BUF_SIZE);         
	n=write(fd,buf,n);         
	if(n<0){                 
		printf("file write error\n");                 
		exit(1);         
	}         
	
	lseek(fd,0,SEEK_SET);          
	n=read(fd,buf,BUF_SIZE);         
	if(n==0){                 
		printf("this file is  empty\n");                 
		exit(1);         
	}         
	n=write(1,buf,n);         
	if(n<0){                 
		printf("file write error\n");                 
		exit(1);         
	}         
	close(fd);  
}
 
fd=open("file.txt",O_CREAT|O_RDWR|O_TRUNC, 0666);

우선 파일을 열어야겠죠? open 시스템콜을 이용합니다. 파일명은 file.txt이고, 파일이 없으면 만들고 읽기쓰기 전용으로 엽니다. 그리고 파일의 내용이 있다면 그 내용을 잘라내고 씁니다. 

 

n=read(0,buf,BUF_SIZE); n=write(fd,buf,n);

파일을 여는데에 성공했다면 표준입력(키보드)로 데이터를 읽어옵니다.

 

그 이후 file.txt에 그 내용을 기록합니다.

 

lseek(fd,0,SEEK_SET);

기록을 했다면 파일 포인터는 파일의 맨끝을 가리키고 있겠네요. 그러니 lseek이란 함수를 통해서 파일 포인터의 위치를 가장 처음으로 위치시킵니다. 

lseek에 대해서 간단히 설명하자면 파일이 어디부터 읽고 쓰는지에 대한 위치를 변경하는 함수라고 기억하시면 됩니다. 

파일 디스크립터 fd를 갖고 처음(SEEK_SET), 현재 위치(SEEK_CUR), 끝 (SEEK_END)을 일단 정하고 난 후에 상대적인 값을 통해 파일 포인터의 위치를 결정합니다.

n = read(fd, buf, BUF_SIZE); 
if (n == 0) { 
	printf("this file is  empty\n");         
	exit(1); 
} 
n = write(1, buf, n);

이제 read를 통해서 file.txt에서 내용을 읽고 표준 출력으로 출력하는 프로그램입니다.

 

어때요? 이해 되셨나요?

 

이제 컴파일하고 실행시킵시다. 그리고 file.txt라는 파일에 기록이 됐는지 확인해봅시다.

 

[root@localhost ~] # gcc file.c

[root@localhost ~] # ./a.out

Hello, world!

Hello, world!

[root@localhost ~] # cat file.txt

Hello, world!

 

파일에도 기록이 잘 되어있고 출력도 잘 된것을 확인할 수 있습니다. 그리고 파일의 권한도 확인해볼까요?

# ls -al file.txt
-rw-rw-rw- 1 root root 12  4월  1 15:59 file.txt

저희가 생성했던 0666(-rw-rw-rw-) 권한으로 생성되었음을 확인할 수 있네요.

파일 다루는 방법 어렵지 않죠!?

반응형
블로그 이미지

REAKWON

와나진짜

,