560 likes | 871 Views
11 장 파일. 11.1 시스템 호출. 유닉스 / 리눅스 커널 (kernel). 하드웨어를 운영 관리하여 다음과 같은 서비스를 제공 프로세스 관리 (Process management) 파일 관리 (File management) 메모리 관리 (Memory management) 통신 관리 (Communication management) 주변장치 관리 (Device management). 시스템 호출 (system call). 시스템 호출은 유닉스 / 리눅스 커널에 서비스를 요청하기 위한 프로그래밍 인터페이스
E N D
유닉스/리눅스 커널(kernel) • 하드웨어를 운영 관리하여 다음과 같은 서비스를 제공 • 프로세스 관리(Process management) • 파일 관리(File management) • 메모리 관리(Memory management) • 통신 관리(Communication management) • 주변장치 관리(Device management)
시스템 호출(system call) • 시스템 호출은 유닉스/리눅스 커널에 서비스를 요청하기 위한 프로그래밍 인터페이스 • 응용 프로그램은 시스템 호출을 통해서 유닉스/리눅스 커널에 서비스를 요청한다 • /usr/include/asm/unistd.h 참조
유닉스/리눅스에서 파일 • 연속된 바이트의 나열 • 특별한 다른 포맷을 정하지 않음 • 디스크 파일뿐만 아니라 외부 장치에 대한 인터페이스
파일 열기: open() • 파일을 사용하기 위해서는 먼저 open() 시스템 호출을 이용하여 파일을 열어야 한다 • 파일 디스크립터는 열린 파일을 나타내는 번호이다. • oflag, mode : 교재 331,332 쪽 참조
파일 열기: 예 • 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)
fopen.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.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); }
파일 생성: creat() • creat() 시스템 호출 • path가 나타내는 파일을 생성하고 쓰기 전용으로 연다 • 생성된 파일의 사용권한은 mode로 정한다 • 기존 파일이 있는 경우에는 그 내용을 삭제하고 연다 • 다음 시스템 호출과 동일 open(path, WRONLY | O_CREAT | O_TRUNC, mode);
파일 닫기: close() • close() 시스템 호출은 fd가 나타내는 파일을 닫는다
데이터 읽기: read() • read() 시스템 호출 • fd가 나타내는 파일에서 • nbytes 만큼의 데이터를 읽고 • 읽은 데이터는 buf에 저장한다
fsize.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> //#include <unistd.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; close(fd); printf ("%s 파일 크기 : %ld 바이트 \n", argv[1], total); exit(0); }
데이터 쓰기: write() • write() 시스템 호출 • buf에 있는 nbytes 만큼의 데이터를 fd가 나타내는 파일에 쓴다
fcp.c #include <stdio.h> #include <stdlib.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); } 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); } O_APPEND
(참고) $ man $ man read $ whatis read $ man –f read (Unix) $ man –l read read (2) - 파일 기술자(file descriptor)를 통해서 읽어... read (1) - bash built-in 명령어들, bash(1)를 참고하라. read (3p) - read from a file read (1p) - read a line from standard input read (n) - Read from a channel $ man –s 2 read 또는 $ man2 read $ man –M /usr/share/man 2 read $ man man 1절 명령어 2절 시스템 호출 3절 라이브러리 함수 4절 특수화일 5절 파일 형식 6절 게임 7절 기타 정보 8절 유지보수 명령어 9절 커널 루틴 1 Executable programs or shell commands 2 System calls (functions provided by the kernel) 3 Library calls (functions within program libraries) 4 Special files (usually found in /dev) 5 File formats and conventions eg /etc/passwd 6 Games 7 Miscellaneous (including macro packages and conven-tions 8 System administration commands (usually only for root) 9 Kernel routines [Non standard]
(참고) set-user-id set-group-id set-sticky bit • Symbolic File Access Modes $ chmod a=rw myfile $ chmod g+x myfile $ chmod g-rwx myfile $ chmod g=u myfile • Octal File Access Modes $ chmod 644 myfile $ chmod 751 myfile $ chmod 000 myfile • 실행파일에 대하여 • 4000 set user ID : $ chmod 4751 myfile • $ chmod u+s myfile • 2000 set group ID : $ chmod 2751 myfile • $ chmod g+s myfile • 1000 set sticky bit : $ chmod 1751 myfile • $ chmod o+t myfile $ find / -perm -4755 -ls > result $ find / -perm -4700 -ls > result • 디렉토리에 대하여 • 1000 set sticky bit : $ chmod 1777 share • 그 디렉토리 안의 파일제거 또는 이름 변경은 파일 소유자, 디렉토리 소유자, 수퍼유저만 가능 • 다른 사용자는 그 안에 자기파일 생성만 • umask (user mask) : file 생성시 허가권 제한 - rws r-x --x - 100 111 101 001 - rwx r-s --x - 010 111 101 001 - rwx r-x --t - 001 111 101 001
(참고) IA-32(Intel Architecture, 32-bit) int 표현 Q:IA-32(Intel Architecture, 32-bit) int는 signedor unsigned? • 리틀 엔디안(Little-endian) 사용 • 고정 소수점 (Fixed-Point) 표현 • 첫 번째 비트는 부호 비트 양수(+)는 0, 음수(-)는 1로 표시 • 나머지는 정수부가 저장되고, 소수점은 맨 오른쪽에 있는 것으로 가정 • 고정 소수점 음수 표현 방법 • 부호와 절대치(signed-magnitude) 양수 표현에 대하여 부호 비트만 1로 변경 • 1의 보수(1’s complement) 양수 표현에 대하여 1의 보수 계산 • 2의 보수(2’s complement) 양수 표현에 대하여 2의 보수 계산 • (예) shortscore = 99; • 99 = 6*16 + 3 = 0x63 • 0000 0000 0110 0011 • (예) shortscore = -99; • -99 = -6*16 + 3 = -0x63 • (부호 정수) 1000 0000 0110 0011 = (2의 보수) 1111 1111 1001 1101
(참고) 비트단위 정수 출력 bitwise.c #include <stdio.h> #include <limits.h> void bit_print(int); main(int argc, char *argv[]) { int i=atoi(argv[1]), mask=1; bit_print(i); printf("\n정수: %d\n", i); } void bit_print(int a) { int i; int n=sizeof(int) * CHAR_BIT; // limits.h 55행 int mask = 1 << (n-1); for (i=1; i<=n; ++i) { putchar(((a & mask) == 0) ? '0' : '1'); a <<= 1; if(i % CHAR_BIT == 0 && i < n) putchar(' '); } } 매개변수 없을 경우 debug 하세요!
파일 위치 포인터(file position pointer) • 파일 위치 포인터는 파일 내에 읽거나 쓸 위치인 현재 파일 위치(current file position)를 가리킨다.
파일 위치 포인터 이동: lseek() • lseek() 시스템 호출 • 임의의 위치로 파일 위치 포인터를 이동시킬 수 있다.
파일 위치 포인터이동: 예 • 파일 위치 이동 • 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); 파일끝에서 한 레코드 다음 위치로
레코드 저장 예 student.h #define MAX 20 struct student { int id; char name[MAX]; int score; }; • write(fd, &record1, sizeof(record)); • write(fd, &record2, sizeof(record)); • lseek(fd, sizeof(record), SEEK_END); • write(fd, &record3, sizeof(record));
dbcreate.c if ((fd = open(argv[1], O_WRONLY | O_CREAT | O_EXCL, 0640)) == -1) { perror(argv[1]); exit(2); } printf("%7s %6s %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); } #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include "student.h" #define START_ID 1201001 /* 학생 정보를 입력받아 데이터베이스 파일에 저장한다 */ int main(int argc, char *argv[]) { int fd; struct student record; if (argc < 2) { fprintf(stderr, "사용법 : %s file\n", argv[0]); exit(1); }
dbquery.c do { printf("검색할 학생의 학번 입력: "); 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); } #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include "student.h" #define START_ID 1201001 /* 학번을 입력받아 해당 학생의 레코드를 파일에서 읽어 출력한다 */ 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_RDONLY)) == -1) { perror(argv[1]); exit(2); }
레코드 수정과정 (1) 파일로부터 해당 레코드를 읽어서 (2) 이 레코드를 수정한 후에 (3) 수정된 레코드를 다시 파일 내의 원래 위치에 써야 한다
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); } #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include "student.h" #define START_ID 1201001 /* 학번을 입력받아 해당 학생 레코드를 수정한다 */ 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); }
파일 상태(file status) • 파일 상태 • 파일에 대한 모든 정보 • 블록수, 파일 타입, 접근 권한, 링크수, 파일 소유자의 사용자 ID, • 그룹 ID, 파일 크기, 최종 수정 시간 등 • 예 $ ls -ls hello 2 -rw-r--r-- 1 mysung other 617 11월 26일 15:59 hello.c 블록수 ^ 사용권한 링크수 사용자ID 그룹ID 파일크기 최종 수정 시간 파일이름 | 파일 타입
상태 정보: stat() • 파일 하나당 하나의 i-노드가 있으며 • i-노드 내에 파일에 대한 모든 상태 정보가 저장되어 있다 Q: stat()과 lstat()의 차이점은? $ manstat
stat 구조체 • /usr/include/sys/stat.h 107행 /usr/include/bits/stat.h 39행 참조 struct stat { dev_t st_dev; /* 장치 */ ino_t st_ino; /* inode 번호 */ mode_t st_mode; /* 파일타입과 사용권한 */ nlink_t st_nlink; /* 하드 링크 수 */ uid_t st_uid; /* 소유자의 사용자 ID */ gid_t st_gid; /* 소유자의 그룹 ID */ dev_t st_rdev; /* 장치이면 장치 번호 */ off_t st_size; /* 파일의 바이트 크기 */ unsigned long st_blksize; /* 파일시스템 I/O 블록크기 */ unsigned long st_blocks; /* 파일에 할당 된 블록 수 */ time_t st_atime; /* 최종 접근 시간 */ time_t st_mtime; /* 최종 수정 시간 */ time_t st_ctime; /* 최종 상태 변경 시간 */ };
파일 타입 검사 함수 • 파일 타입을 검사하기 위한 매크로 함수 • /usr/include/sys/stat.h 111행 /usr/include/bit/stat.h 145행 참조
ftype.c $ gcc –o ftype ftype.c $ ftype you $ ftype you* $ ftype * $ ftype /dev/* | more Q: stat()과 lstat()의 차이점은? st_ino와 t_size 출력해 보세요! #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> /* 파일 타입을 검사한다. */ int main(int argc, char *argv[]) { int i; struct stat buf; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { perror("lstat()"); continue; } if (S_ISREG(buf.st_mode)) printf("%s\n", "일반 파일"); if (S_ISDIR(buf.st_mode)) printf("%s\n", "디렉터리"); if (S_ISCHR(buf.st_mode)) printf("%s\n", "문자 장치 파일"); if (S_ISBLK(buf.st_mode)) printf("%s\n", "블록 장치 파일"); if (S_ISFIFO(buf.st_mode)) printf("%s\n", "FIFO 파일"); if (S_ISLNK(buf.st_mode)) printf("%s\n", "심볼릭 링크"); if (S_ISSOCK(buf.st_mode)) printf("%s\n", "소켓"); } exit(0); }
파일 상태 정보 변경 • chmod() 시스템 호출 • 파일의 상태 정보를 변경
fchmod.c #include <stdio.h> #include <stdlib.h> /* 파일 사용권한을 변경한다. */ main(int argc, char *argv[]) { long int strtol( ); int newmode; newmode = (int) strtol(argv[1],(char **) NULL, 8); if (chmod(argv[2], newmode) == -1) { perror(argv[2]); exit(1); } exit(0); } # include <sys/types.h> /usr/include/stdlib.h 320행 typedef __mode_t mode_t; /usr/include/sys/types.h 71행 # define __STD_TYPE typedef /usr/include/bits/types.h 127행 __STD_TYPE __MODE_T_TYPE __mode_t; /usr/include/bits/types.h 139행 #include <bits/typesizes.h> /usr/include/bits/types.h 131행 #define __MODE_T_TYPE __U32_TYPE /usr/include/bits/typesizes.h 35행 #define __U32_TYPE unsigned int /usr/include/bits/types.h 102행
디렉터리 구현 • 디렉터리 내에는 무엇이 저장되어 있을까? • 디렉터리 엔트리 #include <dirent.h> struct dirent { ino_t d_ino; // i-노드 번호 char d_name[NAME_MAX + 1]; // 파일 이름 }
디렉터리 리스트 • opendir() • 디렉터리 열기 함수 • DIR 포인터(열린 디렉터리를 가리키는 포인터) 리턴 • readdir() • 디렉터리 읽기 함수 typedef struct __dirstream DIR; /usr/include/dirent.h 128행 #include <bits/types.h> /usr/include/dirent.h 30행
list1.c #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> /* 디렉터리 내의 파일 이름들을 리스트한다 */ int main(int argc, char **argv) { DIR *dp; char *dir; struct dirent *d; struct stat st; char path[BUFSIZ+1]; if (argc == 1) dir = "."; // 현재 디렉터리를 대상으로 else dir = argv[1]; if ((dp = opendir(dir)) == NULL) // 디렉터리 열기 perror(dir); while ((d = readdir(dp)) != NULL) // 각 디렉터리 엔트리에 대해 printf("%s \n", d->d_name); // 파일 이름 프린트 closedir(dp); exit(0); }
파일 이름/크기 프린트 • 디렉터리 내에 있는 파일 이름과 그 파일의 크기(블록의 수)를 프린트하도록 확장 while ((d = readdir(dp))!= NULL) { //디렉터리 내의 각 파일 sprintf(path, "%s/%s", dir, d->d_name); // 파일경로명 만들기 if (lstat(path, &st) < 0) // 파일 상태 정보 가져오기 perror(path); printf("%5d %s", st->st_blocks, d->name); // 블록 수, 파일 이름 출력 putchar('\n'); }
디렉터리 리스트: 예 • list2.c • ls –l 명령어처럼 파일의 모든 상태 정보를 프린트 • 프로그램 구성 • main() 메인 프로그램 • printStat() 파일 상태 정보 프린트 • type() 파일 타입 리턴 • perm() 파일 접근권한 리턴 Q: 한 BLOCK 의크기는? Linux: $ getconfPAGESIZE 또는 PAGE_SIZE (4096) Solaris: $ pagesize –a (8192)
list2.c if ((dp = opendir(dir)) == NULL) /* 디렉터리 열기 */ perror(dir); while ((d = readdir(dp)) != NULL) { /* 디렉터리의 각 파일에 대해 */ sprintf(path, "%s/%s", dir, d->d_name); /* 파일경로명 만들기 */ if (lstat(path, &st) < 0) /* 파일 상태 정보 가져오기 */ perror(path); printStat(path, d->d_name, &st); /* 상태 정보 출력 */ putchar('\n'); } closedir(dp); exit(0); } #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <pwd.h> #include <grp.h> #include <stdio.h> #include <stdlib.h> char type(mode_t); char *perm(mode_t); void printStat(char*, char*, struct stat*); /* 디렉터리 내용을 자세히 리스트한다 */ int main(int argc, char **argv) { DIR *dp; char *dir; struct stat st; struct dirent *d; char path[BUFSIZ+1]; if (argc == 1) dir = "."; else dir = argv[1];
list2.c 교재 코드 Debug Q: 블록의크기는? char* perm(mode_t mode) { int i; static char perms[10] = "---------"; for (i=0; i < 3; i++) { if (mode & (S_IREAD >> i*3)) perms[i*3] = 'r'; if (mode & (S_IWRITE >> i*3)) perms[i*3+1] = 'w'; if (mode & (S_IEXEC >> i*3)) perms[i*3+2] = 'x'; } if (mode & (S_ISUID)) perms[2] = 's'; if (mode & (S_ISGID)) perms[5] = 's'; if (mode & (S_ISVTX)) perms[8] = 't'; return(perms); } /* 파일 상태 정보를 출력 */ void printStat(char *pathname, char *file, struct stat *st) { printf("%5d ", st->st_blocks); printf("%c%s ", type(st->st_mode), perm(st->st_mode)); printf("%3d ", st->st_nlink); printf("%s %s ", getpwuid(st->st_uid)->pw_name, getgrgid(st->st_gid)->gr_name); printf("%9d ", st->st_size); printf("%.12s ", ctime(&st->st_mtime)+4); printf("%s", file); } /* 파일 타입을 리턴 */ char type(mode_t mode) { if (S_ISREG(mode)) return('-'); if (S_ISDIR(mode)) return('d'); if (S_ISCHR(mode)) return('c'); if (S_ISBLK(mode)) return('b'); if (S_ISLNK(mode)) return('l'); if (S_ISFIFO(mode)) return('p'); if (S_ISSOCK(mode)) return('s'); } /usr/include/bit/stat.h 161행 참조 #define __S_ISUID 04000 /* Set user ID on execution. */ #define __S_ISGID 02000 /* Set group ID on execution. */ #define __S_ISVTX 01000 /* Save swapped text after use (sticky). #define __S_IREAD 0400 /* Read by owner. */ #define __S_IWRITE 0200 /* Write by owner. */ #define __S_IEXEC 0100 /* Execute by owner. */
디렉터리 만들기 • mkdir() 시스템 호출 • path가 나타내는 새로운 디렉터리를 만든다 • "." 와 ".." 파일은 자동적으로 만들어진다
디렉터리 삭제 • rmdir() 시스템 호출 • path가 나타내는 디렉터리가 비어 있으면 삭제한다
링크 • 링크는 기존 파일에 대한 또 다른 이름으로 • 하드 링크와 심볼릭(소프트) 링크가 있다
링크의 구현 • link() 시스템 호출 • 기존 파일 existing에 대한 새로운 이름 new 즉 링크를 만든다