4 장파일 컴퓨터과학과박환수 1
2 4.1 시스템호출
컴퓨터시스템구조 유닉스커널 (kernel) 하드웨어를운영관리하여다음과같은서비스를제공 파일관리 (File management) 프로세스관리 (Process management) 메모리관리 (Memory management) 통신관리 (Communication management) 주변장치관리 (Device management) 3
시스템호출 시스템호출은커널에서비스요청을위한프로그래밍인터페이스 응용프로그램은시스템호출을통해서커널에서비스를요청한다. 4
시스템호출과정
시스템호출요약 주요자원파일프로세스메모리 * 시그널 시스템호출 open(), close(), read(), write(), dup(), lseek() 등 fork(), exec(), exit(), wait(), getpid(), getppid() 등 malloc(), calloc(), free() 등 signal(), alarm(), kill(), sleep() 등 프로세스간통신 pipe(), socket() 등 6
7 4.2 파일
유닉스에서파일 연속된바이트의나열 특별한다른포맷을정하지않음 디스크파일뿐만아니라외부장치에대한인터페이스 8
파일열기 : open() 파일을사용하기위해서는먼저 open() 시스템호출을이용하여파일을열어야한다. #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open (const char *path, int oflag, [ mode_t mode ]); 파일열기에성공하면파일디스크립터를, 실패하면 -1을리턴 파일디스크립터는열린파일을나타내는번호이다. 9
파일열기 : open() oflag O_RDONLY 읽기모드, read() 호출은사용가능 O_WRONLY 쓰기모드, write() 호출은사용가능 O_RDWR 읽기 / 쓰기모드, read(), write() 호출사용가능 O_APPEND 데이터를쓰면파일끝에첨부된다. O_CREAT 해당파일이없는경우에생성하며 mode는생성할파일의사용권한을나타낸다. 10
파일열기 : open() oflag O_TRUNC 파일이이미있는경우내용을지운다. O_EXCL O_CREAT와함께사용되며해당파일이이미있으면오류 O_NONBLOCK 넌블로킹모드로입출력하도록한다. O_SYNC write() 시스템호출을하면디스크에물리적으로쓴후반환된다 11
파일열기 : 예 fd = open("account",o_rdonly); fd = open(argv[1], O_RDWR); fd = open(argv[1], O_RDWR O_CREAT, 0600); fd = open("tmpfile", O_WRONLY O_CREAT O_TRUNC, 0600); fd = open("/sys/log", O_WRONLY O_APPEND O_CREAT, 0600); if ((fd = open("tmpfile", O_WRONLY O_CREAT O_EXCL, 0666))==-1) 12
fopen.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd; if ((fd = open(argv[1], O_RDWR)) == -1) perror(argv[1]); printf(" 파일 %s 열기성공 \n", argv[1]); close(fd); exit(0); } 13
파일생성 : creat() creat() 시스템호출 path가나타내는파일을생성하고쓰기전용으로연다. 생성된파일의사용권한은 mode로정한다. 기존파일이있는경우에는그내용을삭제하고연다. 다음시스템호출과동일 open(path, WRONLY O_CREAT O_TRUNC, mode); #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int creat (const char *path, mode_t mode ); 파일생성에성공하면파일디스크립터를, 실패하면 -1을리턴 14
파일닫기 : close() close() 시스템호출은 fd 가나타내는파일을닫는다. #include <unistd.h> int close( int fd ); fd가나타내는파일을닫는다. 성공하면 0, 실패하면 -1을리턴한다. 15
데이터읽기 : read() read() 시스템호출 fd가나타내는파일에서 nbytes 만큼의데이터를읽고 읽은데이터는 buf에저장한다. #include <unistd.h> ssize_t read ( int fd, void *buf, size_t nbytes ); 파일읽기에성공하면읽은바이트수, 파일끝을만나면 0, 실패하면 -1을리턴 16
fsize.c #include <stdio.h> #include <unistd.h> #include <fcntl.h> #define BUFSIZE 512 #include <sys/types.h> #include <stdlib.h> /* 파일크기를계산한다 */ 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]); 17
fsize.c } /* 파일의끝에도달할때까지반복해서읽으면서파일크기계산 */ while( (nread = read(fd, buffer, BUFSIZE)) > 0) total += nread; close(fd); printf ("%s 파일크기 : %ld 바이트 \n", argv[1], total); exit(0); 18
데이터쓰기 : write() write() 시스템호출 buf 에있는 nbytes 만큼의데이터를 fd 가나타내는파일에쓴다 #include <unistd.h> ssize_t write (int fd, void *buf, size_t nbytes); 파일에쓰기를성공하면실제쓰여진바이트수를리턴하고, 실패하면 -1을리턴 19
copy.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> /* 파일복사프로그램 */ main(int argc, char *argv[]) { int fd1, fd2, n; char buf[bufsiz]; if (argc!= 3) { fprintf(stderr," 사용법 : %s file1 file2\n", argv[0]); exit(1); } 20 } 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); while ((n = read(fd1, buf, BUFSIZ)) > 0) write(fd2, buf, n); // 읽은내용을쓴다. exit(0);
파일디스크립터복제 dup()/dup2() 호출은기존의파일디스크립터를복제한다. #include <unistd.h> int dup(int oldfd); oldfd에대한복제본인새로운파일디스크립터를생성하여반환하다. 실패하면 1을반환한다. int dup2(int oldfd, int newfd); oldfd을 newfd에복제하고복제된새로운파일디스크립터를반환한다. 실패하면 1을반환한다. oldfd 와복제된새로운디스크립터는하나의파일을공유한다. 21
파일디스크립터복제 oldfd 열린파일 newfd
dup.c 1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <stdlib.h> 4 #include <stdio.h> 6 int main() 7 { 8 int fd, fd2; 9 10 if((fd = creat("myfile", 0600)) == -1) 11 perror("myfile"); 12 13 write(fd, "Hello! Linux", 12); 14 fd2 = dup(fd); 15 write(fd2, "Bye! Linux", 10); 16 exit(0); 17 } 23 $ dup $ cat myfile Hello! LinuxBye! Linux
24 4.3 임의접근파일
파일위치포인터 (file position pointer) 파일위치포인터는파일내에읽거나쓸위치인현재파일위치 (current file position) 를가리킨다. 25
파일위치포인터이동 : lseek() lseek() 시스템호출 임의의위치로파일위치포인터를이동시킬수있다. #include <unistd.h> off_t lseek (int fd, off_t offset, int whence ); 이동에성공하면현재위치를리턴하고실패하면 -1 을리턴한다. 26
파일위치포인터이동 : 예 파일위치이동 lseek(fd, 0L, SEEK_SET); 파일시작으로이동 (rewind) lseek(fd, 100L, SEEK_SET); 파일시작에서 100바이트위치로 lseek(fd, 0L, SEEK_END); 파일끝으로이동 (append) 레코드단위로이동 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); 파일끝에서한레코드다음위치로 27
레코드저장예 write(fd, &record1, sizeof(record)); write(fd, &record2, sizeof(record)); lseek(fd, sizeof(record), SEEK_END); write(fd, &record3, sizeof(record)); 레코드 #1 레코드 #2 레코드 #3 28
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; if (argc < 2) { fprintf(stderr, " 사용법 : %s file\n", argv[0]); exit(1); } 29
dbcreate.c } if ((fd = open(argv[1], O_WRONLY O_CREAT O_EXCL, 0640)) == -1) { perror(argv[1]); exit(2); } printf("%-9s %-8s %-4s\n", " 학번 ", " 이름 ", " 점수 "); while (scanf("%d %s %d", &record.id, record.name, &record.score) == 3) { lseek(fd, (record.id - START_ID) * sizeof(record), SEEK_SET); write(fd, (char *) &record, sizeof(record) ); } close(fd); exit(0); 30
student.h #define MAX 24 #define START_ID 1601001 struct student { char name[max]; int id; int score; }; 31
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; if (argc < 2) { fprintf(stderr, " 사용법 : %s file\n", argv[0]); exit(1); } if ((fd = open(argv[1], O_RDONLY)) == -1) { perror(argv[1]); exit(2); 32 }
dbquery.c } 33 do { printf("\n검색할학생의학번입력 :"); if (scanf("%d", &id) == 1) { lseek(fd, (id-start_id)*sizeof(record), SEEK_SET); if ((read(fd, (char *) &record, sizeof(record)) > 0) && (record.id!= 0)) printf(" 이름 :%s\t 학번 :%d\t 점수 :%d\n", record.name, record.id, record.score); else printf(" 레코드 %d 없음 \n", id); } else printf( 입력오류 ); printf(" 계속하겠습니까?(Y/N)"); scanf(" %c", &c); } while (c=='y'); close(fd); exit(0);
레코드수정과정 (1) 파일로부터해당레코드를읽어서 (2) 이레코드를수정한후에 (3) 수정된레코드를다시파일내의원래위치에써야한다. 34
레코드수정 35
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, " 사용법 : %s file\n", argv[0]); exit(1); } if ((fd = open(argv[1], O_RDWR)) == -1) { perror(argv[1]); exit(2); 36 }
dbupdate.c } do { printf(" 수정할학생의학번입력 : "); if (scanf("%d", &id) == 1) { lseek(fd, (long) (id-start_id)*sizeof(record), SEEK_SET); if ((read(fd, (char *) &record, sizeof(record)) > 0) && (record.id!= 0)) { printf(" 학번 :%8d\t 이름 :%4s\t 점수 :%4d\n", record.id, record.name, record.score); printf(" 새로운점수 : "); scanf("%d", &record.score); lseek(fd, (long) -sizeof(record), SEEK_CUR); write(fd, (char *) &record, sizeof(record)); } else printf(" 레코드 %d 없음 \n", id); } else printf(" 입력오류 \n"); printf(" 계속하겠습니까?(Y/N)"); scanf(" %c",&c); } while (c == 'Y'); close(fd); exit(0); 37
핵심개념 시스템호출은커널에서비스를요청하기위한프로그래밍인터페이스로응용프로그램은시스템호출을통해서커널에서비스를요청할수있다. 파일디스크립터는열린파일을나타낸다. open() 시스템호출을파일을열고열린파일의파일디스크립터를반환한다. read() 시스템호출은지정된파일에서원하는만큼의데이터를읽고 write() 시스템호출은지정된파일에원하는만큼의데이터를쓴다. 파일위치포인터는파일내에읽거나쓸위치인현재파일위치를가리킨다. lseek() 시스템호출은지정된파일의현재파일위치를원하는위치로이동시킨다. 38