4. 파일

김동웅·2021년 9월 10일
0

UNIX

목록 보기
5/14

핵심개념

• 시스템 호출은 커널에 서비스를 요청하기 위한 프로그래밍 인터페이스로 응용 프로그램은 시스템 호출을 통해서 커널에 서비스를 요청할 수 있다.

파일 디스크립터는 열린 파일을 나타낸다.

• open() 시스템 호출은 파일을 열고 열린 파일의 파일 디스크립터를 반환한다.

• read() 시스템 호출은 지정된 파일에서 원하는 만큼의 데이터를 읽고 write() 시스템 호출은 지정된 파일에 원하는 만큼의 데이터를 쓴다.

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

• lseek() 시스템 호출은 지정된 파일의 현재 파일 위치를 원하는 위치로 이동시킨다.


4.1 파일

유닉스에서의 파일이란

  • 연속된 바이트의 나열
  • 디스크 파일뿐만 아니라 외부 장치에 대한 인터페이스도 파일이다.
  • 특별한 다른 포맷을 정하지 않음

1. 파일 열기 : open()

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

파일 열기에 성공하면 파일 디스크립터를, 실패하면 -1을 리턴

1. path : 파일의 경로

2. oflag

  • O_RDONLY : 읽기모드. read()호출 사용가능

  • O_WRONLY : 쓰기모드. write()호출 사용가능

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

  • O_APPEND : 데이터를 쓰면 파일 끝에 첨부된다.
    ex) a.txt 파일 이미 존재.
    fd = open("a.txt",O_APPEND);
    텍스트를 write()하면 a.txt 내용 뒤에 해당 텍스트 추가

  • O_CREATE : 해당파일이 없는경우 생성하며 3.mode 는 생성할 파일의 사용권한을 나타낸다.

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

  • O_EXCL : O_CREATE와 함께 사용되며, 해당 파일이 이미 있으면 오류 (-1 출력)

  • O_NONBLOCK : 넌블로킹 모드로 입출력 하도록 한다.

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

파일 열기의 예시

fd = open("account",O_RDONLY);

fd = open(argv[1],O_RDWR);

fd = open(argv[1],O_RDWR | O_CREATE, 6000);

fd = open("tmpfile", O_WRONLY | O_CREATE | O_TRUNC , 0600);
// tmpfile이 있으면 지우고 다시만듦 (쓰기모드)

if ((fd = open("tmpfile", O_WRONLY|O_CREAT|O_EXCL, 0666))==-1)
// tmpfile이 있으면 -1 출력, 없다면 만든다.

2. 파일 생성 : creat()

  • creat() 시스템 호출
    - path가 나타내는 파일을 생성하고 쓰기 전용 으로 연다.
    • 생성된 파일의 사용권한은 mode로 정한다.

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

    • 다음 시스템 호출과 동일

      open(path, WRONLY | O_CREATE | O_TRUNC, mode);

3. 파일 닫기 : close()

  • close 시스템 호출은 fd가 나타내는 파일을 닫는다.
#include <unistd.h>
int close(int fd);
// fd가 나타내는 파일을 닫는다.
닫는데 성공하면 0, 실패하면 -1리턴

4. 데이터 읽기 : read()

  • read() 시스템 호출
    - fd가 나타내는 파일에서
    • nbytes 만큼의 데이터를 읽고
    • 읽은 데이터는 buf에 저장한다.
#include <unistd.h>
ssize_t read ( int fd, void *buf, size_t nbytes );
파일 읽기에 성공하면 읽은 바이트 수, 파일 끝을 만나면 0, 
실패하면 -1을 리턴

5. 데이터 쓰기 : write()

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

#파일에 쓰기를 성공하면 실제 쓰여진 바이트 수를 리턴하고, 
실패하면 -1을 리턴
• buf에 있는 nbytes 만큼의 데이터를 fd가 나타내는 파일에 쓴다

ex ) 첫번째 파일의 내용을 두번째 파일에 복사하는 코드


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

int main(int argc, char *argv[])
{
	int fd1, fd2, n;

	char buf[5000];
    
    // 입력받은 파일이 두개가 아닌경우 
	if(argc!=3)
	{
		fprintf(stderr," : %s file1 file2\n",argv[0]);
		exit(1);
	}
   // 첫번째 입력받은 파일이 없는 경우
   if ((fd1 = open(argv[1], O_RDONLY)) ==    -1)
   {
      perror(argv[1]);
      exit(2);
   }
   
   // 두번째 입력받은 파일이 없는 경우
   if ((fd2 =open(argv[2], O_WRONLY |O_CREAT|O_TRUNC, 0644)) == -1)
   {
      perror(argv[2]);
      exit(3);
   }
   
   fd1의 내용을 buf에 저장해서 fd2에 쓴다.
   while ((n = read(fd1, buf, BUFSIZ)) > 0)
      write(fd2, buf, n);
   exit(0);
}

6. 파일 디스크립터 복제

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


4.2 임의 접근 파일

1. 파일 위치 포인터(File position pointer)

  • 파일 위치 포인터는 파일 내에 읽거나 쓸 위치인 현재
    파일 위치(current file position)를 가리킨다.

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

  1. lseek() 시스템 호출
    임의의 위치로 파일 위치 포인터를 이동시킬 수 있다.

ex1) 파일 위치 이동

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

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

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

ex2) 레코드 단위로 이동

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

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

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

ex3) 파일 끝 이후로 이동

lseek(fd,sizeof(record), SEEK_END);

ex4) 레코드 저장 예시


write(fd, &record1, sizeof(record));
write(fd, &record2, sizeof(record));
lseek(fd,sizeof(record), SEEK_END); // 파일 포인터 이동시킴
write(fd, &record3, sizeof(record));


레코드 만들기

- student.h

#define MAX 24
#define START_ID 1401001

struct student{
	char name[MAX];
	int id;
	int score;
};

- dbcreate.c

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

int main(int argc, char *argv[])
{
	int fd;
	struct student record;
	
        // 입력받은 인자가 없는 경우
        // argv[0]에는 프로그램 Name, 그다음부터는 인자 리스트.
	if(argc <2 )
	{
		fprintf(stderr, "use : %s file\n", argv[0]);
		exit(1);
	}
	
    	// argv[1] 에 입력받은 인자(파일)를 쓰기모드로 열건데,
        // 만약 해당 파일이 없으면 만들고,
        // 해당파일이 있으면 -1 리턴
	if( (fd = open(argv[1], O_WRONLY| O_CREAT | O_EXCL, 0640)) == -1)
	{
		perror(argv[1]);
		exit(2);
	}
	//  NUM NAME SCORE 출력 '%-'는 공백
	printf("%-9s %-8s %-4s\n","NUM","NAME","SCORE");

	// 학번 이름 점수를 3개입력받음 
 	while( scanf("%d %s %d" , &record.id, record.name, &record.score) == 3)
	{
    	// 파일 위치포인터를 (입력한 학번 - 1401001(시작학번) 위치로 이동
		lseek(fd, (record.id - START_ID) * sizeof(record), SEEK_SET);
        
        // 해당 위치포인터에 기록 
		write(fd, &record, sizeof(record));
	}
	// 파일닫고
	close(fd);

	// 종료
	exit(0);
}



레코드 검색

  • dbquery.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "student.h"

int main(int argc, char* argv[])
{
	int fd, id;
	struct student record;

	char c;

	if(argc  < 2)
	{
		fprintf(stderr, "use : %s file\n", argv[0]);
		exit(1);
	}
	
    // 인자를 읽기모드로 열수 없다면 -1 리턴
	if( (fd= open(argv[1],O_RDONLY)) == -1)
	{
    	// 에러메세지 출력
		perror(argv[1]);
		exit(2);
	}

	do
	{
		printf("\n Enter the number what std : ");
		
                // 찾고자 하는 id입력 
		if( scanf("%d", &id)==1 )
		{
        		// 파일포인터를 해당하는 레코드로 이동
			lseek(fd, (id-START_ID)*sizeof(record), SEEK_SET);
			//  해당학생의 정보를 읽어서
			if(( read(fd, &record, sizeof(record)) > 0) && record.id !=0)
			{	
           		 // 프린트한다
			printf("NAME : %s , NUM : %d \t, SCORE : %d \n",record.name, record.id, record.score);

			}
			// 해당학생의 정보를 찾을 수 없으면 메세지 출력
			else
			{
				printf(" No record for %d\n",id);
			}
		}
        	
		else
		{
			printf("input error!\n");
		}

		printf("Want to continue? (Y/N)");
		scanf(" %c", &c);
	}while(c=='Y');

	close(fd);
	exit(0);
}

레코드 수정

  • 레코드 수정 과정
    (1) 파일로부터 해당 레코드를 읽어서
    (2) 이 레코드를 수정한 후에
    (3) 수정된 레코드를 다시 파일 내의 원래 위치에 써야 한다.

(1) read

(2) update

(3) write

- dbupdate.c

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

int main(int argc, char *argv[])
{
	int fd, id;
	char c;
	struct student record;

	if(argc < 2)
	{
		fprintf(stderr, " use %s file\n",argv[0]);
		exit(1);
	}

	if( (fd= open(argv[1],O_RDWR)) == -1)
	{
		perror(argv[1]);
		exit(2);
	}
	
	do
	{
		printf("Enter the student's student number to modify : ");

		if(scanf("%d",&id) == 1)
		{	
        
        		// 파일 시작에서 해당 레코드의 위치로 이동 
			lseek(fd, (long) (id- START_ID) * sizeof(record), SEEK_SET);
			
            
            		// (1)번 과정 -  해당 레코드를  읽음
                    // -> 읽고난뒤에 파일포인터는 해당 레코드의 끝에 위치함 
			if( (read(fd,&record, sizeof(record)) > 0 ) && (record.id!=0))
			{
				printf("NUM : %8d\t NAME : %4s\t, SCORE: %4d\n",
						record.id,record.name,record.score);

				printf("NEW SCORE : ");
                
               			 // (2)번 과정 - 해당 레코드의 점수 수정
				scanf("%d" ,&record.score);
				
            	   		 // 해당레코드의 시작부분으로 파일 포인터 이동 
				lseek(fd, (long) -sizeof(record), SEEK_CUR);\
                		// (3)번 과정 - 기록 
				write(fd, &record, sizeof(record));
			}
			else
			{
				printf("No %d record\n",id);
			
			}
		}
		else
		{
			printf("Input error ! \n");
		}

			printf("want to continue ? : (y/n) : ");
			scanf(" %c",&c);



	}while(c=='Y');


	close(fd);
	exit(0);
}



0개의 댓글

관련 채용 정보