Linux

[리눅스] 시스템 프로그래밍 - 파일 입출력

안도일 2022. 11. 23. 16:52

 

시스템 호출

 

시스템 호출은 커널에 서비스 요청을 위한 프로그래밍 인터페이스로, 

응용 프로그램은 시스템 호출을 통해서 커널에 서비스를 요청한다.

 

 

 

주요 시스템 호출

 

파일 : open(), close(), read(), write(), dup(), lseek() 등

 

프로세스 : fork(), exec(), exit(), wait(), getpid(), getppid() 등

 

메모리 : malloc(), calloc(), free() 등

 

시그널 : signal(), alarm(), kill(), sleep() 등

 

프로세스 간 통신 : pipe(), socket() 등

 


 

파일

 

유닉스에서 파일이란 연속된 바이트의 나열을 뜻한다.

유닉스와 리눅스는 그 이외의 파일별 특별한 다른 포맷을 정의하지 않음

디스크 파일 뿐만 아니라, 외부 장치에 대한 인터페이스(장치 파일) 역시 파일로 구현됨

 

 

open()

 

파일을 사용하기 위해서는 먼저 open() 시스템 호출을 이용하여 파일을 열어야 한다.

 

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *path, int oflag, [mode_t mode]);

open() 함수는 path가 나타내는 파일을 연다.

성공 시, 열린 파일을 나타내는 번호인 파일 디스크립터를 반환, 실패 시 -1 반환

즉 open() 함수는 path name을 file descriptor로 변환하는 역할

 

 

두 번째 매개 변수 oflag : 파일을 어떤 형태로 open 할 것인가?

 

 

O_RDONLY : 읽기 모드, read() 호출은 사용가능

 

O_WRONLY :  쓰기 모드, write() 호출은 사용 가능

 

O_RDWR : 읽기/쓰기 모드, read(), write() 호출 사용 가능

 

O_APPEND : 데이터를 쓰면 파일 끝에 첨부된다.

 

O_CREAT : 해당 파일이 없는 경우에 생성하며 세 번째 매개 변수 mode는 O_CREAT의 경우 생성할 파일의 사용 권한을 나타내게 된다. (이 경우에만 세번째 매개변수 사용)

 

O_TRUNC : 파일이 이미 있는 경우 내용을 지운다.

 

O_EXCL : O_CREAT와 함께 사용되며 해당 파일이 이미 있으면 오류 출력

 

O_NONBLOCK : non-blocking 모드로 입출력 (버퍼 사용) 하도록 한다.

 

O_SYNC : write() 시스템 호출을 하면 디스크에 물리적으로 쓴 후 반환된다.

 

 

fd = open(argv[1], O_RDWR|O_REAT, 0600);
//읽기 쓰기 모드로 열고, 해당 파일이 없으면 새로 생성, 파일의 권한은 0600

fd = open("tmpfile", O_WRONLY|O_CREAT|O_TRUNC, 0600);
//쓰기 전용으로 열고, 없으면 새로 생성하고, 기존 내용이 있으면 지운다. 없으면 0600 권한을 생성

if((fd = open("tmpfile", O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) 
//파일이 없는 경우에만 생성하고, 기존의 파일이 있으면 오류, 없으면 666권한으로 생성

 

 

 

creat()

 

path가 나타내는 파일을 생성하고 쓰기 전용으로 연다. 

생성된 파일의 사용권한은 mode로 지정, 기존 파일이 있는 경우에는 그 내용을 삭제하고 연다.

 

creat() 함수는 다음의 open() 시스템 호출과 완전히 동일하므로 부차적, 이차적 함수이다.

 

open(path, WRONLY | O_CREAT | O_TRUNC, mode)

creat(const char *path, mode_t mode);

 

 

close()

 

close() 시스템 호출은 fd가 나타내는 파일을 닫는 수행을 함.

성공하면 0, 실패하면 -1을 return

 

 

read()

 

fd가 나타내는 파일에서 nbytes 만큼의 데이터를 읽고 읽은 데이터는 buf에 저장한다.

파일 읽기에 성공하면 읽은 바이트 수, 파일 끝을 만나면 0, 실패하면 -1을 return

 

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFSIZE 512

/* 파일 크기를 계산 한다 */
int main(int argc, char *argv[]) 
{
	char buffer[BUFSIZE];
	int fd;
	ssize_t nread; //signed integer type
	long total = 0;

	if ((fd = open(argv[1], O_RDONLY)) == -1) 
		perror(argv[1]); //오류 메시지를 stderr로 출력하는 함수

	while( (nread = read(fd, buffer, BUFSIZE)) > 0) // nread  실제 읽은 크기
		total += nread; //512씩 total에 더함	
      
	close(fd);
	printf ("%s 파일 크기 : %ld 바이트 \n", argv[1], total);
	exit(0);
}

 

 

write()

 

buf에 있는 nbytes 만큼의 데이터를 fd가 나타내는 파일에 쓴다

 

 

 

dup() / dup2()

 

dup(), dup2() 호출은 기존의 파일 디스크립터를 복제한다.

 

oldfd와 복제된 새로운 디스크립터는 하나의 파일을 공유한다.

 

입출력 재지정에 유용하게 사용

 

#include <unistd.h>

int dup(int oldfd);
// oldfd에 대한 복제본인 새로운 파일 디스크립터를 생성하여 그 번호를 반환한다. 실패하면 -1 반환

int dup2(int oldfd, int newfd);
// oldfd을 newfd에 복제하고, 복제된 새로운 파일 디스크립터 번호를 반환한다. 실패하면 -1 반환

 

 


 

임의 접근 파일

 

파일 위치 포인터는 파일 내에 읽거나 쓸 위치인 현재 파일 위치를 가리킨다

순서대로 출력가능하며, 임의의 위치로의 접근이 불가능

 

lseek()

 

임의의 위치로 파일 위치 포인터를 이동시킬 수 있다.

이동에 성공하면 현재 위치를 return 하고 실패하면 -1을 return 한다.

 

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

 

whence

SEEK_SET : 처음

SEEK_CUR : 현재

SEEK_END : 끝

 

 

lseek(fd, 0L, SEEK_SET);
// 파일 시작으로 이동

lseek(fd, 100L, SEEK_SET);
//파일 시작에서 100바이트 위치로 이동

lseek(fd, n*sizeof(record), SEEK_SET);
//n+1 번째 레코드 시작 위치로 이동

lseek(fd, sizeof(record), SEEK_CUR);
//다음 레코드 시작 위치로 이동

lseek(fd, -sizeof(record), SEEK_CUR);
//전 레코드 시작 위치로 이동

lseek(fd, sizeof(record), SEEK_END)
//파일 끝에서 한 레코드 다음 위치로 이동