본문 바로가기
2022-2/시스템 프로그래밍

4장 파일 입출력

by 철없는민물장어 2022. 10. 30.
728x90
4.1 시스템 호출

시스템 호출(system call)

커널에 서비스 요청을 위한 프로그래밍 인터페이스.

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

 

운영체제가 지원하는 라이브러리 함수라고 보면 이해하기 쉽다.

 

주요 시스템 호출

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

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

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

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

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


4.2 파일

파일 입출력

파일 열고->읽기/쓰기 등->파일닫기

 

파일 열기: open()

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

파일 열기에 성공하면 파일 디스크립터(열린 파일을 나타내는 번호)를, 실패하면 -1 리턴함.

 

path: 파일의 경로/이름

oflag: 파일을 어떤 형태로 열 것인지

  • 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
  • O_SYNC

파일 열기 예시

fd = open("account", O_RDONLY);

fd = open(argv[1],O_RDWR); //첫 번째 명령 줄 인수

fd = open(argv[1],O_RDWR|O_CREAT,0600); //해당 파일이 없으면 권한600짜리 파일 생성

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

fd = open("/sys/log",O_WRONLY|O_APPEND|O_CREAT, 0600);
//첨부용 쓰기전용으로 연다. 없으면 600권한으로 새로 생성

if((fd=open("tmpfile",O_WRONLY|O_CREAT|O_EXCL, 0666))==-1)
//파일이 없는 경우에만 권한 666으로 새로 생성하고 기존 파일이 있으면 오류.
if((fd=open(argv[1],O_RDWR))==-1)
	perror(argv[1]); //오류출력
printf("파일 %s 열기 성공\n", argv[1]);
close(fd);
exit(0);

 

파일 생성: creat()

int creat(const char *path, mode_t mode);

path 파일을 생성하고 쓰기 전용으로 연다.생성된 파일의 사용권한은 mode로 설정.

기존 파일이 있는 경우에는 그 내용을 삭제하고 쓴다.

open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) 와 동일하다

 

파일 닫기: close()

int close(int fd);

fd가 나타내는 파일을 닫는다. 성공하면 0, 실패하면 -1을 리턴함.

 

 

데이터 읽기: read()

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

fd가 나타내는 파일에서

nbytes만큼의 데이터를 읽어 buf에 저장한다.

 

파일 읽기에 성공하면 읽은 바이트 수를 반환, 파일 끝을 만나면 0, 파일 읽기에 실패하면 -1을 반환한다.

 

데이터 읽기 예시: 파일의 크기 계산하기

#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;
    long total =0;
    if((fd=open(argv[1],O_RDONLY))==-1)
    	perror(argv[1]); //파일 열기 실패한 경우 오류 출력
    
    while((nread = read(fd, buffer, BUFSIZE)) >0) //파일의 끝에 도달할 때까지 반복
    	total += nread //읽어온 데이터 크기를 total에 더함
    
    close(fd);
    printf("%s파일 크기: %ld 바이트\n", argv[1],total);
    exit(0);
}

 

데이터 쓰기: write()

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

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

 

데이터 쓰기 예시: 파일 복사하기

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

main(int argc, char *argv[])
{
	int fd1,fd2,n;
    char buf[BUFSIZ]; //사전 정의 상수 512
    if(argc !=3){
    	fprintf(stderr,"사용법: %s file1 file2\n",argv[0]);
        exit(1);
     }
    if((fd1 = open(argv[1],O_RDONLY))==-1){//1번 파일 열기 실패
    	perror(argv[1]);
        exit(2);}
    if((fd2 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644))==-1){//2번 파일 열기 실패
    	perror(argv[2]);
        exit(3);}
    
    while((n=read(fd1,buf,BUFSIZ))>0)//fd1의 파일의 끝에 도달할 때 까지 내용을 읽어 buf에 저장
    	write(fd2,buf,n); //buf에 저장된 내용을 fd2의 파일에 작성
   	exit(0);
}

 

파일 디스크립터 복제

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

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

fd2 = dup(fd1);

dup2(fd1, fd2);

위 두 코드는 사용상의 차이일 뿐 기능은 동일하다.

 

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

입출력 재지정에 유용하게 사용가능함.

 


4.3 임의 접근 파일

파일 위치 포인터

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

 

파일 위치 포인터 이동: lseek()

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

fd파일에서 whence에서부터 offset만큼 포인터를 이동

whence의 종류

  • SEEK_SET: 처음
  • SEEK_CUR: 현재
  • SEEK_END: 끝

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

 

파일 위치 포인터 이동 예시

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

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

lseek(fd, 0L, SEEK_END);//파일 끝으로 이동

//레코드 단위로 이동
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); //파일 끝에서 한 레코드 다음 위치로

offset은 long type이기 때문에 L로 표현

728x90

'2022-2 > 시스템 프로그래밍' 카테고리의 다른 글

8장 프로세스  (1) 2022.11.08
5. 파일 시스템  (0) 2022.11.02
gdb 디버거  (3) 2022.10.14
make 시스템  (1) 2022.10.11
3장 - C프로그래밍 환경  (0) 2022.10.07

댓글