Unix 의 역사 Bell Lab의 Ken Tompson이 PDP-7 위에서 개발 (1969년) Dennis Ritchie가 참가하여 대부분을 C언어로 작성 Sixth Edition 개발 (1976년) 다른 기관이 사용할 수 있도록 배포한 최초의 버전 이후에 UNIX는



Similar documents
chap7.key

슬라이드 1

Microsoft PowerPoint APUE(File InO).ppt

Microsoft PowerPoint APUE(File InO)

제1장 Unix란 무엇인가?

Microsoft PowerPoint APUE(File InO).pptx

Microsoft PowerPoint - chap2

10.

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

Microsoft PowerPoint - chap4 [호환 모드]

제1장 Unix란 무엇인가?

歯9장.PDF

제12장 파일 입출력

<4D F736F F F696E74202D FC6C4C0CFB0FA20B5F0B7BAC5E4B8AE205BC8A3C8AF20B8F0B5E55D>

Microsoft PowerPoint APUE(Intro).ppt

제1장 Unix란 무엇인가?


로봇SW교육원 강의자료

Microsoft PowerPoint - chap12 [호환 모드]

6주차.key

로봇SW교육원 강의자료

2009년 상반기 사업계획

학번 : 이름 : 1. 다음파일트리구조를가진유닉스시스템이있다고가정하자. / /bin/ /home/ /home/taesoo/ /usr/ /usr/lib/ /usr/local/lib /media 모든폴더에파일이하나도없다고가정했을때사용자가터미널에서다음 ls 명령입력시화면출력

금오공대 컴퓨터공학전공 강의자료

Microsoft PowerPoint - Lecture 4-1 Linux File Environment.ppt [호환 모드]

제1장 Unix란 무엇인가?

PowerPoint 프레젠테이션

Microsoft PowerPoint - 제11강 파일 처리

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

K&R2 Reference Manual 번역본

프로그램을 학교 등지에서 조금이라도 배운 사람들을 위한 프로그래밍 노트 입니다. 저 역시 그 사람들 중 하나 입니다. 중고등학교 시절 학교 도서관, 새로 생긴 시립 도서관 등을 다니며 책을 보 고 정리하며 어느정도 독학으르 공부하긴 했지만, 자주 안하다 보면 금방 잊어

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

untitled

학번 : 이름 : 1. 다음파일트리구조를가진유닉스시스템이있다. / /bin/ /home/ /home/taesoo/ /home/taesoo/downloads /usr/ /usr/lib/ /usr/local/lib /media 모든폴더에파일이하나도없다고가정했을때사용자 (t

PowerPoint 프레젠테이션

Adobe Flash 취약점 분석 (CVE )

BMP 파일 처리

고급 프로그래밍 설계

Microsoft PowerPoint - ch09_파이프 [호환 모드]

휠세미나3 ver0.4

2009년 상반기 사업계획

PowerPoint 프레젠테이션

3. 다음그림은프로세스의 file table 과 v-node 테이블의연결관계예제이다. 위그림을참고하여두개의서로다른프로세스가같은파일을 open 명령을사용하여열었을때의연결관계를도시하시오. 4. 메모리영역은 low-address 부터 high-adress 까지순서대로나열했을

À©µµ³×Æ®¿÷ÇÁ·Î±×·¡¹Ö4Àå_ÃÖÁ¾

C Programming

API 매뉴얼

13주-14주proc.PDF

Chapter #01 Subject

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

제7장 C 표준 파일 입출력

Microsoft PowerPoint - 09_(C_Programming)_(Korean)_File_Processing

Microsoft Word - Network Programming_NewVersion_01_.docx

금오공대 컴퓨터공학전공 강의자료

PowerPoint 프레젠테이션

The Pocket Guide to TCP/IP Sockets: C Version

PowerPoint 프레젠테이션

제7장 C 표준 파일 입출력

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

PowerPoint 프레젠테이션

강의10

슬라이드 1

컴파일러

Microsoft PowerPoint - chap10-함수의활용.pptx

SRC PLUS 제어기 MANUAL

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>

PowerPoint 프레젠테이션

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

<C0CCBCBCBFB52DC1A4B4EBBFF82DBCAEBBE7B3EDB9AE2D D382E687770>

Microsoft PowerPoint - a10.ppt [호환 모드]

The Pocket Guide to TCP/IP Sockets: C Version

1217 WebTrafMon II

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

ActFax 4.31 Local Privilege Escalation Exploit

Microsoft PowerPoint - [2009] 02.pptx

, ( ),, ( ), 3, int kor[5]; int eng[5]; int Microsoft Windows 4 (ANSI C2 ) int kor[5] 20 # define #define SIZE 20 int a[10]; char c[10]; float

/chroot/lib/ /chroot/etc/

Transcription:

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 제1장 소개 2004. 08. 22 1

Unix 의 역사 Bell Lab의 Ken Tompson이 PDP-7 위에서 개발 (1969년) Dennis Ritchie가 참가하여 대부분을 C언어로 작성 Sixth Edition 개발 (1976년) 다른 기관이 사용할 수 있도록 배포한 최초의 버전 이후에 UNIX는 크게 분류해서 연구용, AT&T 계열, BSD 계열로 분 화해서 발전 Seventh Edition (1979년): Bourne shell 개발 Eighth Edition (1985년): STREAMS 개발 2

AT&T UNIX System V, Release 3 개발 (1987년) : STREAMS 도입 System V, Release 4 개발 (1989년) : C/Korn shell, Job control, Symbolic link BSD UNIX BSD 4 개발 (1980년) : Job control, Reliable signal BSD 4.4 개발 (1993년) 기타 Mach Linux 3

Unix 표준화 동향 ANSI C (1989년) American National Standards Institute C 언어의 문법, 라이브러리와 헤더 파일의 표준을 제정 다양한 운영 체제 환경에서 C 프로그램의 호환성을 높이기 위함 POSIX (1988년) Portable Operating System Interface for Computer Environments 운영 체제가 제공해 주어야 하는 서비스를 정의 (POSIX Compliant) 1003.1 : 운영 체제의 인터페이스 표준 (POSIX.1 이라고도 함) 1003.2, 1003.7 등 XPG3 (1989년) X/Open Portability Guide 4

시스템 호출과 라이브러리 함수 System Calls well defined entry points directly into the kernel documented in section 2 of the UNIX man pages look like C functions which can be called from a user's program just need to include the appropriate header Library Functions the library function is often more elaborate than the system call, and usually invokes the system call 5

application code user process C library functions system calls kernel kernel hardware (harddisk ) 6

Error Handling #include <errno.h> extern int errno; UNIX 시스템 호출에서 에러가 발생하면 에러 발생을 의미하는 값이 리턴되고 (-1 or NULL) 전역 변수 errno 에 자세한 에러 상수 값이 설정 된다 errno 에 설정될 수 있는 에러 상수 값은 <errno.h> 에 정의되어 있다 7

#include <string.h> char *strerror(int errno); 파라미터 errno 값에 해당하는 에러 메시지를 리턴 #include <stdio.h> void perror(const char *msg); 표준 에러에 다음과 같은 순서로 메시지를 출력한다 msg 출력 문자열 ": " 출력 전역 변수 errno 값에 해당하는 에러 메시지 출력 8

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 2장. 파일 입출력 (File I/O) 2004. 08. 22 1

Contents I/O를 위한 kernel 자료구조 open() - 열기 creat() - 파일 생성 close() - 닫기 read() - 읽기 write() - 쓰기 lseek() - 이동 I/O Efficiency I/O 시스템 호출과 커널 자료구조 dup(), dup2() fcntl() Atomic operations 2

1. I/O를 위한 kernel 자료구조 process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2: fd 3:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size v-node information i-node information current file size 3

Process table entry 프로세스 테이블 (process table) 커널(kernel) 자료구조 프로세스 목록 프로세스 à 프로세스 테이블 항목 프로세스 테이블 항목 (Process table entry) 파일 디스크립터(file descriptor)의 배열을 포함 4

File descriptor 읽거나 쓰기 위해서 파일을 열면 (open 시스템 호출) 파일 디스크립터(file descriptor) 배열의 항목이 하나 할당되고 그 파일 디스크립터가 리턴된다 (배열의 인덱스가 리턴됨) 이후 이 파일에 대한 읽기/쓰기 시스템 호출(system call)에서는 이 파일 디스크립터를 파라미터로 전달해야 한다 파일 디스크립터 배열의 크기가 유한하므로 프로세스가 동시에 열 수 있는 파일의 수도 유한하다 파일 테이블 항목(file table entry)에 대한 포인터를 파일 디스크립 터는 가지고 있다. 5

File table entry 파일 테이블 (file table) 커널 자료구조 열려진 모든 파일 목록 열려진 파일 à 파일 테이블의 항목 파일 테이블 항목 (file table entry) 파일 상태 플래그 (read, write, append, sync, nonblocking, ) 파일의 현재 위치 (current file offset) v-node에 대한 포인터 6

v-node v-node의 내부 파일의 유형 함수들에 대한 포인터 배열 이 파일을 다루는데 사용되는 함수들 i-node 하드 디스크에 저장되어 있는 자료구조 파일에 대한 정보를 가지고 있음 소유자, 크기 파일이 위치한 장치 파일 내용 디스크 블럭에 대한 포인터 7

2. open() - 열기 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open (const char *pathname, int oflag, [ mode_t mode ]); 파일을 연다 파일이 없으면 경우에 따라 새로 만들어 질 수도 있다 리턴 값 : 파일 디스크립터(file descriptor), 실패하면 -1 pathname : 파일의 이름 mode : 파일의 access permission 값. 생략가능. 새로운 파일을 만드는 경우에만 사용됨 creat() 함수 설명 참조 8

open() 의 파라미터 두 번째 파라미터 oflag는 다음 상수들의 OR 이다 예) int fd; fd = open("afile", O_RDWR O_CREAT, S_IRUSR S_IWUSR ); 반드시 하나 지정해주어야 할 값 O_RDONLY : 읽기 모드, write 함수를 사용 할 수 없음 O_WRONLY : 쓰기 모드, read 함수를 사용 할 수 없음 O_RDWR : 읽고 쓰기 모드, read write 사용 가능 9

open() 의 파라미터 선택적 지정 가능 O_APPEND : 모든 write 된 데이터는 파일의 끝에 추가된다 O_CREAT : 파일이 없을 경우 파일을 생성한다 이때에만 세번째 인자 mode 가 필요하다 O_EXCL : 그 이름의 파일이 이미 있고 O_CREATE 를 지정한 경우 에러를 발생시킨다 O_TRUNC : 파일이 있는 경우에 기존 내용을 지우고 파일 크기를 0으로 만든다 O_NONBLOCK : nonblocking 모드로 입출력을 함 O_SYNC : 각 write 함수 호출은 디스크에 물리적으로 쓰여진 후 리턴한다 10

/* open.c */ #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include "error.h" int main() { int filedes; char fname[] = "afile"; if ((filedes = open (fname, O_RDWR)) == -1) error(fname); } printf("%s is opened.\n", fname); close(filedes); return 0; 11

3. creat () - 파일 생성 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int creat ( const char *pathname, mode_t mode ); 새로운 파일을 생성한다 리턴 값 : 파일 디스크립터, 실패하면 -1 pathname : 생성하고자 하는 파일의 이름 mode : 파일의 access permission 값 12

creat () 의 파라미터 다음의 두 함수 호출은 기능이 동일하다 fd = creat ( pathname, mode ); fd = open ( pathname, O_WRONLY O_CREAT O_TRUNC, mode); 두 번째 인자 mode는 다음 상수들의 OR 이다 S_IRUSR : user-read, 소유자의 읽기 허용 S_IWUSR : user-write, 소유자의 쓰기 허용 S_IXUSR : user-execute, 소유자의 실행 허용 S_IRGRP : group-read, 그룹 멤버의 읽기 허용 S_IWGRP : group-write, 그룹 멤버의 쓰기 허용 S_IXGRP : group-execute,그룹 멤버의 실행 허용 S_IROTH : other-read, 아무나 읽기 허용 S_IWOTH : other-write, 아무나 쓰기 허용 S_IXOTH : other-execute,아무나 실행 허용 13

4. close () - 닫기 #include <unistd.h> int close ( int filedes ); 작업이 끝난 후 파일을 닫는다. 리턴 값 : 성공하면 0, 실패하면 -1 filedes : 닫고자 하는 파일의 파일 디스크립터 파일을 닫지 않더라도 프로세스가 종료하면 모든 열려진 화일들은 자 동적으로 닫힌다 14

5. read () - 읽기 #include <unistd.h> ssize_t read ( int filedes, void *buf, size_t nbytes ); 파일에서 데이터를 읽는다 리턴 값: 성공하면 읽은 바이트 수 파일의 끝을 만나면 0 실패하면 -1 buff : 읽은 데이터를 저장할 메모리 nbytes : 메모리의 크기 15

read () - 읽기 읽을 데이터가 충분하면 한 번에 nbytes 만큼 읽는다. 읽을 데이터가 nbytes 보다 적은 경우 더 적게 읽기도 한다 파일의 끝에서 네트웍 입출력에서 size_t : unsigned integer ssize_t : signed integer 16

/* count.c -- 파일의 문자 수를 센다 */ #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include "error.h" #define BUFSIZE 512 int main() { char buffer[bufsize]; int filedes; ssize_t nread; long total = 0; if ((filedes = open("afile", O_RDONLY)) == -1) error("afile"); /* 파일의 끝에 도달할 때까지 반복 */ while( (nread = read(filedes, buffer, BUFSIZE)) > 0) total += nread; } close(filedes); printf ("total chars in afile: %ld\n", total); return 0; 17

6. write() - 쓰기 #include <unistd.h> ssize_t write (int filedes, const void *buf, size_t nbytes); 파일에 데이터를 쓴다 리턴 값 성공하면, 파일에 쓰여진 데이터의 바이트 수 실패하면, -1 buff : 쓸 데이터를 저장하고 있는 메모리 공간 nbytes : 쓸 데이터의 바이트의 수 18

/* copyfile.c -- 파일을 복사한다 */ #include <unistd.h> #include <fcntl.h> #define BUFSIZE 512 #define PERM 0644 int copyfile (const char *srcfile, const char *destfile) { int infile, outfile; ssize_t nread; char buffer[bufsize]; } if ((infile = open(srcfile, O_RDONLY)) == -1) return -1; if ((outfile = open(destfile, O_WRONLY O_CREAT O_TRUNC, PERM)) == -1) { close(infile); return -2; } while ((nread = read(infile, buffer, BUFSIZE)) > 0) { if (write(outfile, buffer, nread) < nread) { close (infile); close (outfile); return -3; } } close (infile); close (outfile); if (nread == -1) return -4; /* 마지막 읽기에서 에러 발생 */ return 0; 19

7. lseek() - 이동 #include <sys/types.h> #include <unistd.h> off_t lseek (int filedes, off_t offset, int whence ); 파일의 현재 위치(current file offset)를 이동 리턴 값: 성공하면 현재 위치를 리턴, 실패하면 -1 whence : 위치 기준점 SEEK_SET : 파일의 시작점을 기준으로 이동 SEEK_CUR : 현재 위치를 기준으로 이동 SEEK_END : 파일의 끝을 기준으로 이동 offset : 기준점에서의 상대적인 거리 (byte 단위) SEEK_CUR, SEEK_END 와 같이 쓰일 때는 음수도 가능 20

lseek() 이동 파일의 현재 위치를 임의의 위치로 이동한다 파일의 현재 위치(current file offset) 파일에 대한 읽기/쓰기는 파일의 현재 위치에서 실행된다 파일을 처음 열면 현재 위치는 0 즉 파일의 시작이다 읽기/쓰기 후 파일의 현재 위치는 읽기/쓰기 한 byte 수 만큼 저 절로 뒤로 이동된다 lseek()를 사용하여 임의의 위치로 파일의 현재 위치를 이동할 수 있다 21

/* lseek.c */ #include <unistd.h> int main() { if (lseek(stdin_fileno, 0, SEEK_CUR) == -1) printf("cannot seek\n"); else printf("seek OK\n"); return 0; } 22

#include <unistd.h> /* lseek1.c */ #include <fcntl.h> #include "error.h" char char buf1[] = "abcdefghij"; buf2[] = "ABCDEFGHIJ"; int main() { int fd; } if ( (fd = creat("file.hole", S_IRUSR S_IWUSR S_IRGRP S_IROTH)) < 0) error("file.hole"); if (write(fd, buf1, 10)!= 10) /* offset now = 10 */ error("buf1"); if (lseek(fd, 40, SEEK_SET) == -1) /* offset now = 40 */ error("lseek"); if (write(fd, buf2, 10)!= 10) /* offset now = 50 */ error("buf2"); return 0; 23

lseek1.c 의 출력 파일 file.hole의 내용 0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j 10 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 20 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 30 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 40 A B C D E F G H I J 24

#include <sys/types.h> /* lseek2.c */ #include <unistd.h> #include <fcntl.h> #include "error.h" int main() { int fd; off_t fsize; char buf[11]; } if((fd=open("file.hole", O_RDONLY)) < 0) error("file.hole"); fsize = lseek(fd, 0, SEEK_END); printf("size: %lu\n", fsize); lseek(fd, 40, SEEK_SET); read(fd, buf, 10); buf[10] = 0; puts(buf); return 0; 25

8. I/O Efficiency 디스크는 메모리에 비해서 상대적으로 매우 느린 장치 성능을 위해서는 디스크 I/O의 횟수를 줄이는 것이 바람직 작은 양을 여러번 I/O 하는 것 보다 많은 양을 I/O 하여 횟수를 줄이는 것이 좋다 write1.c 에서 BUFFSIZE 값을 변경하며 수행 시간을 측정 8192 까지는 BUFFSIZE 가 클수록 성능이 향상 8192 보다 큰 경우는 성능에 변화가 없다 26

Berkeley fast filesystem 의 I/O 단위는 8192 BUFFSIZE 가 8192 보다 큰 경우 내부 I/O 의 단위는 8192 이므로 성능의 향상이 없다 BUFFSIZE 가 8192의 배수가 아닌 경우는 오히려 8192 인 경우보다 I/O 횟수가 많아져 성능이 하락할 수 있다 27

#include <sys/types.h> /* write1.c */ #include <sys/stat.h> #include <fcntl.h> #include "error.h" #define BUFFSIZE 512 #define FILESIZE (100 * 1024 * 1024) #define COUNT FILESIZE / BUFFSIZE int main() { int i, fd; char buf[buffsize]; } memset(buf, '.', BUFFSIZE); if ((fd = creat("file.write", S_IRUSR S_IWUSR)) < 0) error("file.write"); for (i=0; i < COUNT; ++i) write(fd, buf, BUFFSIZE); close(fd); return 0; 28

BUFFSIZE User CPU (seconds) System CPU (seconds) Clock time (seconds) #loops 1 140.9 1,378.1 1,527.8 104,857,600 2 73.3 681.1 764.5 52,428,800 4 31.6 343.9 379.6 26,214,400 8 16.6 172.3 191.5 13,107,200 16 8.5 86.7 98.1 6,553,600 32 3.9 45.1 51.6 3,276,800 64 2.0 23.5 28.1 1,638,400 128 1.1 12.8 16.3 819,200 256 0.5 7.4 10.4 409,600 512 0.3 4.9 7.9 204,800 1,024 0.1 2.9 6.0 102,400 2,048 0.0 2.0 4.7 51,200 4,096 0.0 1.6 4.2 25,600 8,192 0.0 1.1 3.6 12,800 16,384 0.0 1.1 3.6 6,400 32,768 0.0 1.1 3.6 3,200 65,536 0.0 1.1 3.6 1,600 131,072 0.0 1.1 3.6 800 29

9. I/O 시스템 호출과 커널 자료구조 process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2: fd 3:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size v-node information i-node information current file size 30

read / write 시스템 호출 file table entry 의 current file offset 값이 읽은/쓰여진 바이 트 수 만큼 증가 write 후 file table entry의 current file offset 이 i-node의 current file size 보다 크면 current file size 값이 증가 파일이 O_APPEND 플래그로 오픈되었으면 file table entry의 file status flags 에 해당 플래그가 세팅됨 모든 write 시스템 호출은 먼저 current file offset을 i-node 의 current file size 값 으로 설정한 후 수행된다 따라서 모든 write 는 파일의 끝에 덧붙여진다 31

lseek 시스템 호출은 file table entry 의 current file offset 값을 변경 disk I/O 는 없음 lseek에서 SEEK_END 를 사용하여 파일의 끝으로 이동하면 file table entry 의 current file offset 값은 i-node 의 current file size 값으로 설정됨 32

10. dup(), dup2() #include <unistd.h> int dup (int filedes); int dup2 (int filedes, int filedes2); 사용 중인 파일 디스크립터의 복사본을 만듦 dup() 는 새 파일 디스크립터 번호가 할당됨 dup2() 는 filedes2 를 사용 filedes2 가 이미 사용 중이면 먼저 close 한다 리턴 값: 성공하면 복사된 새 파일 디스크립터, 실패하면 -1 dup() 함수는 할당 가능한 가장 작은 번호를 리턴한다. dup2() 함수는 filedes2 를 리턴한다. 33

파일 공유 1 process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2: fd 3:.... file table fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size newfd = dup(1); or newfd = dup2(1,3); 호출 후 리턴 값은 3 file table 의 current file offset 와 fd status flags 는 공유됨 file descriptors의 fd flags 는 공유안됨 34

#include <fcntl.h> /* dup.c */ #include <unistd.h> #include "error.h" int main() { int fd1, fd2, n; char buf[11]; } if ((fd1 = open("afile", O_RDONLY)) < 0) error("afile"); if ((fd2 = dup(fd1)) < 0) error("dup"); n = read(fd1, buf, 10); buf[n] = 0; puts(buf); n = read(fd2, buf, 10); buf[n] = 0; puts(buf); close(fd1); close(fd2); return 0; 35

파일 공유 2 process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2: fd 3:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size 같은 파일에 대하여 open() 을 두번 호출한 경우 current file offset 은 공유되지 않는다 36

#include <fcntl.h> /* open2.c */ #include <unistd.h> int main() { int fd1, fd2, n; char buf[11]; fd1 = open("afile", O_RDONLY); fd2 = open("afile", O_RDONLY); if (fd1 < 0 fd2 < 0) error("afile"); } n = read(fd1, buf, 10); buf[n] = 0; puts(buf); n = read(fd2, buf, 10); buf[n] = 0; puts(buf); close(fd1); close(fd2); return 0; 37

파일 공유 3 process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2:.... file descriptors fd 0: fd flags ptr fd 1: fd 2:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size 두 프로세스(process)에서 같은 파일을 연 경우 38

dup(), dup2() 의 용도 표준 입출력의 redirection 표준 입출력 대상을 파일로 바꿈 표준 입출력의 파일 디스크립터 #include <unistd.h> #define STDIN_FILENO 0 /* 표준 입력 */ #define STDOUT_FILENO 1 /* 표준 출력 */ #define STDERR_FILENO 2 /* 표준 에러 */ 39

#include <unistd.h> /* dup1.c */ #include <fcntl.h> #include <stdio.h> int main() { int fd; } if((fd = creat("afile", S_IRUSR S_IWUSR)) == -1) error("afile"); printf("this is displayed on the screen.\n"); dup2(fd, STDOUT_FILENO); printf("this is written into the redirected file.\n"); return 0; 40

#include <unistd.h> /* dup2.c */ #include <fcntl.h> #include <stdio.h> #include "error.h" int main() { int fd; } if((fd = creat("afile", S_IRUSR S_IWUSR)) == -1) error("afile"); printf("this is displayed on the screen.\n"); close(stdout_fileno); dup(fd); printf("this is written into the redirected file.\n"); return 0; 41

11. fcntl() #include <sys/types.h> #include <fcntl.h> #include <unistd.h> int fcntl (int filedes, int cmd, /* int arg */ ); 열려져 있는 파일의 속성을 알아내거나 속성을 변경한다. 리턴 값: cmd 값에 따라 다름, 실패하면 -1 42

fcntl() 의 두번째 파라미터 F_DUPFD 파일 디스크립터를 복사 dup() dup2() 와 유사함 세 번째 인수 보다 크거나 같은 값 중, 가장 작은 미사용의 값을 리턴 F_GETFD : file descriptor의 file descriptor flags 를 리턴 F_SETFD : file descriptor flags 를 설정 F_GETFL : file table 의 file status flags 를 리턴 F_SETFL : file status flags 를 설정 43

file table 의 file status flags File status flag O_RDONLY O_WRONLY O_RDWR O_APPEND O_NONBLOCK O_SYNC O_ASYNC Description open for reading only open for writing only open for reading and writing append on each write nonblocking mode wait for writes to complete asynchoronouse I/O (4.3 BSD only) F_SETFL 는 O_APPEND O_NONBLOCK O_SYNC O_ASYNC 만을 설정할 수 있음 44

F_GETOWN asynchronous I/O ownership 을 리턴 SIGIO, SIGURG 시그널을 받는 프로세스 ID를 리턴 F_SETOWN asynchronous I/O ownership 을 설정 SIGIO, SIGURG 시그널을 받는 프로세스 ID를 설정 45

#include <sys/types.h> /* fileflags.c */ #include <fcntl.h> #include "error.h" int main() { int accmode, val; int fd; fd = open("afile", O_WRONLY O_APPEND); if (fd == -1) error("afile"); if ((val = fcntl(fd, F_GETFL, 0)) < 0) error("fcntl"); accmode = val & O_ACCMODE; if (accmode == O_RDONLY) printf("read only"); else if (accmode == O_WRONLY) printf("write only"); else if (accmode == O_RDWR) printf("read write"); else printf("unknown access mode"); } if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking"); if (val & O_SYNC) printf(", synchronous writes"); putchar('\n'); return 0; 46

Asynchronous Write UNIX 에서 write 는 asynchronous write 이다 데이터가 디스크에 쓰여지길 기다리지 않고 쓰여질 데이터를 큐(queue)에 등록하고 바로 리턴한다 큐에 등록된 데이터는 나중에 디스크에 쓰여진다 synchronous write write 호출은 데이터가 실제로 디스크에 쓰여진 후 리턴한다 asynchronous write에 비해서 clock time 이 증가한다 file table 의 file status flags 에서 O_SYNC 플래그를 설정 47

#include <sys/types.h> /* write2.c */ #include <sys/stat.h> #include <fcntl.h> #include "error.h" #define BUFFSIZE 8192 #define FILESIZE (100 * 1024 * 1024) #define COUNT FILESIZE / BUFFSIZE void set_fl(int fd, int flags) /* flags are file status flags to turn on */ { int val; } if ( (val = fcntl(fd, F_GETFL, 0)) < 0) error("fcntl F_GETFL error"); val = flags; /* turn on flags */ if (fcntl(fd, F_SETFL, val) < 0) error("fcntl F_SETFL error"); 48

int main() { int i, fd; char buf[buffsize]; } memset(buf, '.', BUFFSIZE); if ((fd = creat("file.write", S_IRUSR S_IWUSR)) < 0) error("creat erroror"); set_fl(fd, O_SYNC); /* turn on the synchronous-write flag */ for (i=0; i < COUNT; ++i) write(fd, buf, BUFFSIZE); close(fd); return 0; 49

BUFFSIZE User CPU (seconds) System CPU (seconds) Clock time (seconds) #loops asynchrohous 8,192 0.0 1.1 3.6 12,800 synchrohous 8,192 0.0 3.4 180.3 12,800 위 결과에서 볼 수 있듯이 synchronous write 가 훨씬 더 느림 데이터베이스 시스템처럼 실제로 데이터가 디스크에 기록되었는지 여 부가 중요한 경우에 사용됨 50

12. Atomic operations 복수개의 스텝으로 구성된 operation으로 모든 스텝이 성공적으로 수행되거나, 전부 실패하거나 둘 중의 하나 부분적으로 성공하고 부분적으로 실패하는 경우는 없음 스텝이 실행되는 중간에 다른 operation 의 개입으로 영향을 받지 않 는다 51

Atomic operation 의 예 1 A) fd = open(, O_WRONLY O_APPEND); write(fd, buff, 100); B) fd = open(, O_WRONLY); lseek(fd, 0L, SEEK_END); write(fd, buff, 100); A)와 B)의 기능은 같다 step1) 먼저 current file offset 이 파일의 끝으로 이동한 후 step2) buff를 쓴다 52

A) 의 경우 step1) 과 step2) 는 write 시스템 호출 내부에서 실행 step1) 과 step2) 는 atomic operation B) 의 경우 step1) 은 lseek 시스템 호출 step2) 는 write 시스템 호출 step1) 과 step2) 는 atomic operation 이 아님 53

두 개 이상의 프로세스가 공유 파일에 대해서 write 하는 경우 10절의 파일 공유 3 에 해당 예) 여러 프로세스가 동일한 log 파일에 메시지를 추가하는 경우 A) 의 경우 Process 1 Process 2 write - step1 - step2 write - step1 - step2 54

B) 의 경우 Process 1 Process 2 lseek - step1 lseek step2 write step2 write step2 Process 1 과 Process 2 의 lseek step1 는 동일한 current file offset 을 설정함 Process 2 의 write step2 는 Process 1 의 write 를 덮어 쓰게 됨 55

Atomic operation 의 예 2 A) fd = open(pathname, O_WRONLY O_CREAT, mode); B) if ((fd = open(pathname, O_WRONLY)) < 0) { if (errno == ENOENT) fd = creat(pathname, mode); } A) 와 B) 의 기능은 동일 A) 는 atomic operation 56

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 3장. 파일시스템 (Filesystem) 2004. 08. 22 1

Contents 1. stat(), fstat(), lstat() 2. File Type 3. File Access Permissions 4. access() 5. umask() 6. chmod(), fchmod() 7. Sticky bit 8. chown(), fchown(), lchown() 9. truncate(), ftruncate() 10. Filesystem 11. link() unlink() remove() rename() 12. Symbolic Links 13. File Times 14. Directory 15. sync(), fsync() 2

1. stat(), fstat(), lstat() #include <sys/types.h> #include <sys/stat.h> int stat (const char *pathname, struct stat *buf ); int fstat (int filedes, struct stat *buf ); int lstat (const char *pathname, struct stat *buf ); 주어진 파일에 대한 정보를 stat 구조체에 얻어 온다 리턴 값 : 성공하면 0, 실패하면 -1 buf : stat 구조체에 대한 포인터로 lstat()는 심볼릭 링크(symbolic link)가 가르키는 파일이 아니고 심볼릭 링크 자체에 대한 정보를 얻는다 3

stat 구조체 <sys/stat.h> 에 정의 struct stat { mode_t st_mode; /* file type & mode (permissions) */ ino_t st_ino; /* i-node number (serial number) */ dev_t st_dev; /* device number (filesystem) */ dev_t st_rdev; /* device number for special files */ nlink_t st_nlink; /* number of links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ off_t st_size; /* size in bytes, for regular files */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last file status change */ long st_blksize;/* best I/O block size */ long st_blocks; /* number of 512-byte blocks allocated */ }; 4

st_atime : 마지막으로 파일의 데이터를 읽은 시각 st_mtime : 마지막으로 파일의 데이터를 수정한 시각 st_ctime : 파일의 데이터가 아니고 이름이나 권한 같은 상태를 변경한 시각 st_blksize : 가장 효율적인 I/O 블럭 크기 (2장 8절 I/O 성능 참고, 8192 bytes) st_blocks : 파일이 차지하고 있는 공간의 크기를 512 byte 블럭의 수로 5

2. File Type 보통 파일 (Regular file) 데이터를 포함하고 있는 텍스트 또는 이진 화일 디렉토리 파일 (Directory file) 파일의 이름들과 파일 정보에 대한 포인터들을 포함 문자 특수 파일 (Character special file) 시스템에 장착된 어떤 장치를 가리키는 파일 문자 단위로 데이터를 전송하는 장치 블록 특수 파일 (Block special file) 시스템에 장착된 어떤 장치를 가리키는 파일 블럭 단위로 데이터를 전송하는 장치 6

FIFO 프로세스 간 통신에 사용되는 파일 named pipe 라고도 불림 소켓 (socket) 네트웍을 통한 프로세스 간 통신에 사용되는 파일 심볼릭 링크 (Symbolic link) 다른 파일을 가리키는 포인터 역할을 하는 파일 7

파일 타입 검사 파일 타입을 검사하는 매크로 함수 #include <sys/stat.h> S_ISREG() : 정규 파일 S_ISDIR() : 디렉토리 파일 S_ISCHR() : 문자 특수 파일 S_ISBLK() : 블록 특수 파일 S_ISFIFO() : pipe 또는 FIFO S_ISLNK() : 심볼릭 링크 S_ISSOCK() : 소켓 해당 종류의 파일이면 1, 아니면 0 을 리턴 stat 구조체의 st_mode 값을 검사함 8

파일 타입 상수 #include <sys/stat.h> S_IFREG : 정규 파일 S_IFDIR : 디렉토리 파일 S_IFCHR : 문자 특수 파일 S_IFBLK : 블록 특수 파일 S_IFFIFO : pipe 또는 FIFO S_IFLNK : 심볼릭 링크 S_IFSOCK : 소켓 S_ISxxx() 매크로 함수는 S_IFxxx 상수 값의 설정 여부를 판단 9

#include <sys/types.h> /* stat.c */ #include <sys/stat.h> int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; 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)) ptr = "regular"; else if (S_ISDIR(buf.st_mode)) ptr = "directory"; else if (S_ISCHR(buf.st_mode)) ptr = "character special"; else if (S_ISBLK(buf.st_mode)) ptr = "block special"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo"; else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; else ptr = "** unknown mode **"; printf("%s\n", ptr); } exit(0); 10

[실행 결과] % a.out /etc /dev/ttya /bin a.out /etc: directory /dev/ttya: symbolic link /bin: symbolic link a.out: regular symbolic link 에 대한 정보도 얻기 위해서 stat() 대신 lstat() 를 사용 11

3. File Access Permissions file access permission bits (stat 구조체의 st_mode 의 값) #include <sys/stat.h> st_mode mask S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH Meaning user-read user-write user-execute group-read group-write group-execute other-read other-write other-execute 12

UNIX 명령어 chmod file access permission 설정 stat 구조체의 st_mode UNIX 명령어 chown file 소유 user id 설정 stat 구조체의 st_uid UNIX 명령어 chgrp file 소유 group id 설정 stat 구조체의 st_gid 13

read 권한이 있어야 O_RDONLY O_RDWR 을 사용하여 파일을 열 수 있다 write 권한이 있어야 O_WRONLY O_RDWR O_TRUNC 을 사용하여 파일을 열 수 있다 디렉토리에 write 권한과 execute 권한이 있어야 그 디렉토리에 파일을 생성할 수 있고 그 디렉토리의 파일을 삭제할 수 있다 삭제할 때 그 파일에 대한 read write 권한은 없어도 됨 14

파일이 포함된 모든 상위 디렉토리에 대해 execute 권한이 있어야 그 파일을 열 수 있다 디렉토리에 대한 read 권한이 있어야 디렉토리 안에 들어 있는 파일 이름 목록을 읽을 수 있다 디렉토리에 대한 write 권한이 있어야 디렉토리에 파일을 생성 삭제 할 수 있다 디렉토리에 대한 execute 권한이 있어야 그 디렉토리나 그 하위 디렉 토리에 있는 파일을 열 수 있다 15

Effective User ID real user ID 와 real group ID 실제 사용자 ID 와 그 사용자가 속한 그룹 ID 로그인한 사용자 ID effective user ID 와 effective group ID 프로세스의 속성 file access permission 을 검사할 때 사용된다 대부분의 경우 real user ID 와 real group ID 가 사용되고 다음 슬라이드와 같은 경우만 예외 16

S_ISUID 와 S_ISGID stat 구조체의 st_mode 의 비트 S_ISUID : set-user-id S_ISGID : set-group-id st_mode 의 S_ISUID 비트가 설정된 실행 파일을 실행한 경우 그 실행 파일이 실행된 프로세스의 effective user ID 는 real user ID 가 아니고 st_uid : 그 실행 파일의 소유자 user ID st_mode 의 S_ISGID 비트가 설정된 실행 파일을 실행한 경우 그 실행 파일이 실행된 프로세스의 effective group ID 는 st_gid : 그 실행 파일의 소유자 group ID 17

S_ISUID, S_ISGID 가 설정된 실행 파일을 실행하는 경우는 real user ID, real group ID 의 권한이 아니고 그 파일 소유 user ID, group ID 의 권한으로 실행됨 예) $ ls -al /bin/passwd -r-sr-sr-x 1 root sys 23500 Jul 30 2003 /bin/passwd* 18

4. access() #include <unistd.h> int access (const char *pathname, int mode ); real user ID와 real group ID로 파일의 permission 체크 리턴 값: 성공하면 0, 실패하면 -1 mode 값 mode R_OK W_OK X_OK F_OK Description test for read permission test for write permission test for execute permission test for existence of tile 19

#include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include "error.h" int main(int argc, char *argv[]) { if (argc!= 2) halt("usage: a.out <pathname>"); if (access(argv[1], R_OK) < 0) error("r_ok"); else printf("read access OK\n"); if (open(argv[1], O_RDONLY) < 0) error("o_rdonly"); else printf("open for reading OK\n"); } return 0; 20

5. umask() #include <sys/types.h> #include <sys/stat.h> mode_t umask (mode_t cmask ); 파일 생성 시 적용할 마스크 값 설정 리턴 값: 기존의 마스크 값 cmask: bitwise OR S_IRUSR, S_IWUSR, S_IXUSR S_IRGRP, S_IWGRP, S_IXGRP S_IROTH, S_IWOTH, S_IXOTH umask() 호출 후 파일을 생성할 때는 cmask 에 설정된 bit 들이 꺼진 다 UNIX 명령어 : umask 참조 21

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include "error.h" int main(void) { umask(0); if (creat("foo", S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOTH) < 0) error("foo"); } umask(s_irgrp S_IWGRP S_IROTH S_IWOTH); if (creat("bar", S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOTH) < 0) error("bar"); return 0; 22

[실행 결과] % a.out % ls -al foo bar -rw------- 1 lsj 없음 0 Jul 21 11:54 bar -rw-rw-rw- 1 lsj 없음 0 Jul 21 11:54 foo % umask 22 23

6. chmod(), fchmod() #include <sys/stat.h> #include <sys/types.h> int chmod (const char *pathname, mode_t mode ); int fchmod (int filedes, mode_t mode ); 파일에 대해 access permission 을 변경한다 stat 구조체의 st_mode 변경 리턴 값: 성공하면 0, 실패하면 -1 mode : bitwise OR S_ISUID, S_ISGID, S_ISVTX S_IRUSR, S_IWUSR, S_IXUSR S_IRGRP, S_IWGRP, S_IXGRP S_IROTH, S_IWOTH, S_IXOTH 24

#include <sys/types.h> #include <sys/stat.h> #include "error.h" int main() { struct stat statbuf; /* turn on set-group-id and turn off group-execute */ if (stat("foo", &statbuf) < 0) error("stat(foo)"); if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) S_ISGID) < 0) error("chmod(foo)"); /* set absolute mode to "rw-r--r--" */ if (chmod("bar", S_IRUSR S_IWUSR S_IRGRP S_IROTH) < 0) error("chmod(bar)"); } return 0; 25

[실행 결과] $ ls -l foo bar -rw------- 1 lsj 없음 0 Jul 21 12:04 bar -rw-rw-rw- 1 lsj 없음 0 Jul 21 12:04 foo $ a.out $ ls -l foo bar -rw-r--r-- 1 lsj 없음 0 Jul 21 12:04 bar -rw-rwlrw- 1 lsj 없음 0 Jul 21 12:04 foo 26

foo 파일의 set-group-id bit 를 켜고, group-execute bit 를 끄다 à mandatory record locking 활성화 à ls 의 출력에서 group-execute permisson 문자가 'l' ls 출력에서 foo 와 bar 의 날짜가 변경되지 않는다 chmod 는 stat 구조체의 st_ctime 만을 변경 ls 는 stat 구조체의 st_mtime 을 출력 27

chmod() 의 mode 상수 값 #include <sys/stat.h> mode S_ISUID S_ISGID S_ISVTX S_IRWXU S_IRUSR S_IWUSR S_IXUSR S_IRWXG S_IRGRP S_IWGRP S_IXGRP S_IRWXO S_IROTH S_IWOTH S_IXOTH Description set-user-id on execution set-group-id on execution saved-text (sticky bit) read, write, and execute by user (owner) read by user (owner) write by user (owner) execute by user (owner) read, write, and execute by group read by group write by group execute by group read, write, and execute by other (world) read by other (world) write by other (world) execute by other (world) 28

7. Sticky bit (S_ISVTX) S_ISVTX 비트의 기원 sticky bit 가 설정된 실행 파일은 프로그램의 종료 후 swap 영영 에 저장하여 프로그램의 재실행 시 빠르게 메모리에 로드할 수 있도록 함 자주 사용되는 실행 파일의 경우에 설정 초기의 느린 디스크 입출력 속도 때문에 사용되었으나 현재는 가상 메모리 기법과 빠른 파일 시스템으로 인하여 이런용도로 사용되지 않는다 29

현재 시스템에서 Sticky bit 는 특수한 디렉토리를 설정하는데 사용 파일을 지우거나 이름을 바꾸려고 할 때 보통의 디렉토리는 사용자가 디렉토리에 대한 쓰기 권한만 있으면 가능 Sticky bit 가 설정된 디렉토리는 파일의 소유자이거나, 디렉토리의 소유자이거나, 슈퍼 유저인 경 우에만 그 디렉토리의 파일을 지우거나 이름을 바꿀 수 있다. Sticky bit 는 슈퍼 유저만 설정할 수 있다 30

Sticky bit 디렉토리의 예: /tmp 임시 파일 디렉토리 모든 사용자에게 쓰기 권한이 있어서 파일을 만들 수 있다 그러나 자신의 소유 파일만 삭제하거나 이름을 바꿀 수 있다 $ ls ld /tmp drwxrwxrwt 7 root sys 5628 Jul 21 12:50 /tmp/ à ls 의 출력에서 other-execute permisson 문자가 't' 31

8. chown(), fchown(), lchown() #include <sys/types.h> #include <unistd.h> int chown (const char *pathname, uid_t owner, gid_t group ); int fchown (int filedes, uid_t owner, gid_t group ); int lchown (const char *pathname, uid_t owner, gid_t group ); 파일의 user ID와 group ID를 변경한다. stat 구조체의 st_uid, st_gid 변경 리턴 : 성공하면 0, 실패하면 -1 lchown()은 심볼릭 링크(symbolic link)가 가르키는 파일이 아니고 심볼릭 링크 자체를 변경한다 Berkeley 기반 시스템에서는 super-user만 변환 가능 System V 계열 시스템은 일반 사용자도 변경 가능 (_POSIX_CHOWN_RESTRICTED 값에 의해 결정) 32

9. truncate(), ftruncate() #include <sys/types.h> #include <unistd.h> int truncate (const char *pathname, off_t length ); int ftruncate (int filedes, off_t length ); 파일의 크기를 length 로 줄인다 리턴 : 성공하면 0, 실패하면 -1 33

10. Filesystem disk drive partition partition partition i-list directory blocks and data blocks boot block(s) super block i-node i-node... i-node 34

i-node 하드 디스크에 저장되어 있는 자료구조 파일에 대한 정보를 가지고 있음 file type access permission bits file size pointers to data blocks for the file stat 구조체의 필드는 i-node 에서 읽어온다 35

directory blocks and data blocks i-list data block data block directory block data block directory block i-list i-node i-node... i-node directory block i-node number filename directory block i-node number filename 36

directory block 은 파일 목록을 저장 파일 이름과 그 파일의 i-node number hard link directory block 의 파일 이름과 i-node 와의 연결 i-node number 로 연결 UNIX 명령어 ln 참고 i-node number filesystem 의 i-list 에서 i-node 의 일련번호 따라서 hard link 는 같은 filesystem 내부에서만 가능 37

symbolic link 파일의 내용(data block의 내용)은 링크 대상 파일의 pathname 예) lrwxrwxrwx 1 root 7 Sep 25 07:14 lib -> usr/lib 파일 이름은 lib 파일 내용은 usr/lib 파일 크기 7 bytes UNIX 명령어 ln s 참고 i-node 의 link count stat 구조체의 st_nlink 이 i-node 를 가르키는 directory entry 수 앞 슬라이드 그림에서 두 directory entry 는 동일한 i-node 를 가르키므로 link count 는 2 38

파일의 삭제 directory block 에서 파일을 삭제하면 i-node 의 link count 가 하나 감소 link count 가 0 에 도달하면 그 파일의 i-node 와 data block 이 삭제됨 파일의 이동 UNIX 명령어 mv 파일의 data block 이나 i-node 의 변화는 없이 directory entry 만 변경됨 39

directory blocks and data blocks i-list data block data block directory block directory block i-node 0 i-node 1267 i-node 2549 directory block 2549. 1267.. directory block 1267 i-node... 2549 testdir 40

testdir 디렉토리의 i-node number는 2549 testdir의 부모 디렉토리 i-node number는 1267 i-node 2549의 link count는 최소 2 i-node 1267의 link count는 최소 3 41

11. link() #include <unistd.h> int link (const char *existingpath, const char *newpath ); 이미 존재하는 파일(existingpath)에 대해서 새로운 디렉토리 항목 (newpath)을 만든다 리턴 값: 성공하면 0, 실패하면 -1 42

hard link 가 만들어짐 같은 i-node를 가리키는 directory entry 가 하나 더 만들어짐 그 i-node의 link count 가 하나 증가 hard link는 하나의 파일시스템 내부에서만 가능 43

unlink() #include <unistd.h> int unlink (const char *pathname); 파일의 link count를 감소 시킴 리턴 값: 성공하면 0, 실패하면 -1 unlink()는 directry entry를 지우며, 파일의 link count를 1 감소 시킨다 link count 가 0이 되면, 파일을 디스크에서 제거한다. i-node와 data block 삭제 44

다른 프로세스가 사용중인 파일이 삭제되는 것을 방지하기 위해 link count가 0에 도달했더라도 그 파일을 open 한 프로세스의 수가 0 이상이면 삭제하지 않는다 파일이 close 될 때 kernel은 그 파일을 open 한 프로세스 수가 0 인 지 확인하고, 0 일 경우 link count가 0 인지 확인하고 삭제한다 45

#include <sys/types.h> /* unlink.c */ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include "error.h" int main() { int fd, len; char buf[20]; fd = open("tempfile", O_RDWR O_CREAT O_TRUNC, 0666); if (fd == -1) error("open1"); unlink("tempfile"); len = write(fd, "How are you?", 12); if (len!= 12) error("write"); lseek(fd, 0L, SEEK_SET); len = read(fd, buf, sizeof(buf)); if (len < 0) error("read"); buf[len] = '\0'; printf("%s\n", buf); close(fd); } fd = open("tempfile", O_RDWR); if (fd == -1) error("open2"); close(fd); return 0; 46

remove() #include <unistd.h> int remove (const char *pathname); 파일의 link count를 감소 시킴 리턴 값: 성공하면 0, 실패하면 -1 pathname이 파일이면 unlink()와 같은 일을 하고, 디렉토리이면 rmdir()과 같은 일을 한다 47

rename() #include <unistd.h> int rename (const char *oldname, const char *newname ); 파일이나 디렉토리의 이름을 바꾼다. 리턴 값: 성공하면 0, 실패하면 -1 newname이 이미 존재하는 경우 oldname이 파일을 가리키고 newname이 파일로서 존재할 때, 기존 을 파일을 덮어 쓴다. oldname이 디렉토리를 가리키고 newname이 비어 있는 디렉토리일 때, 기존의 디렉토리를 덮어 쓴다. 48

12. Symbolic Links 심볼릭 링크(symbolic link)는 파일에 대한 간접적인 포인터 실제 파일에 대한 경로를 저장하고 있다 링크의 한계점을 극복 링크는 단일 파일시스템에서만 허용된다 디렉토리에 대한 링크는 수퍼 유저만 만들 수 있다 49

파일을 다루는 함수를 사용할 때 심볼릭 링크를 자체를 다루는지 아 니면 링크된 대상 파일을 다루는지 심볼릭 링크 자체를 다루는 함수 lchown, lstat, readlink, remove, rename, unlink 등 링크된 대상 파일을 다루는 함수 access, chdir, chmod, chown, creat, exec, link, mkdir, mkfifo, open, opendir, pathconf, stat, truncate 등 50

symlink() #include <unistd.h> int symlink (const char *actualpath, const char *sympath ); 심볼릭 링크를 만든다 심볼릭 링크 파일의 내용은 actualpath 심볼릭 링크 파일의 크기는 actualpath 문자열의 길이 리턴 값: 성공하면 0, 실패하면 -1 actualpath : 실제 파일 sympath : 만들 심볼릭 링크의 이름 51

readlink() #include <unistd.h> int readlink (const char *pathname, char *buf, int bufsize ); 심볼릭 링크 파일의 내용을 읽는 함수 symlink() 에서 저장된 actualpath 가 buf에 채워진다 null 문자는 채워지지 않는다 리턴 값: 성공하면 읽은 문자열의 길이, 실패하면 -1 심볼릭 링크를 open 으로 열면 링크된 대상 파일이 열린다 52

13. File Times stat 구조체의 st_atime 파일의 데이터가 마지막으로 읽혔던 시간 read() 호출 stat 구조체의 st_mtime 파일의 데이터가 마지막으로 변경된 시간 write () 호출 stat 구조체의 st_ctime 파일의 stat 구조의 내용이 마지막으로 변경된 시간 chmod(), chown() 호출 UNIX 명령어 ls 의 l u c 옵션 참고 53

utime() #include <sys/types.h> #include <utime.h> int utime (const char *pathname, const struct utimbuf *times ); 파일의 최종 접근 시간과 최종 변경 시간을 조정한다. times가 NULL 이면, 현재시간으로 설정된다. 리턴 값: 성공하면 0, 실패하면 -1 UNIX 명령어 touch 참고 54

struct utimbuf { time_t actime; /* access time */ time_t modtime; /* modification time */ } 각 필드는 1970-1-1 00:00 부터 현재까지의 경과 시간을 초로 환산한 값 55

#include <sys/types.h> /* utime.c */ #include <sys/stat.h> #include <fcntl.h> #include <utime.h> #include "error.h" int main(int argc, char *argv[]) { int i; struct stat statbuf; struct utimbuf timebuf; } for (i = 1; i < argc; i++) { if (stat(argv[i], &statbuf) < 0) /* fetch current times */ error(argv[i]); if (open(argv[i], O_RDWR O_TRUNC) < 0) /* truncate */ error(argv[i]); timebuf.actime = statbuf.st_atime; timebuf.modtime = statbuf.st_mtime; if (utime(argv[i], &timebuf) < 0) /* reset times */ error(argv[i]); } return 0; 56

앞 슬라이드의 예제 (utime.c) 1) 파일의 내용을 truncate 하기 전에 파일의 utime과 atime을 읽음 2) 파일의 내용을 truncate 하면 utime과 atime이 현재 시각으로 변 경됨 3) 저장해둔 utime과 atime으로 복구 4) 파일의 크기는 0으로 변화했지만 utime과 atime은 변화 없음 [실행 결과] % ls -lu afile -rw-r--r-- 1 lsj user 14 Jul 1 09:10 afile % a.out afile % ls -lu afile -rw-r--r-- 1 lsj user 0 Jul 1 09:10 afile 57

14. Directory 디렉토리 파일 일종의 파일이므로 open, read, close 함수 등을 사용할 수 있다 디렉토리 파일 사용에 편리한 새로운 함수들도 제공된다. 디렉토리 파일의 내용은 구조체 dirent의 배열 file name : 파일 이름, 하위 디렉토리 이름, ".", ".." i-node number #include <dirent.h> struct dirent { ino_t d_ino; /* i-node number */ char d_name[name_max + 1]; /* filename */ } 58

DIR 구조체 오픈된 디렉토리를 다루는데 사용되는 구조체 표준 I/O 라이브러리의 FILE 구조체와 비슷한 역할 59

opendir(), readdir(): 디렉토리 접근 #include <sys/types.h> #include <dirent.h> DIR *opendir (const char *pathname); struct dirent *readdir(dir *dp); opendir()로 디렉토리 파일을 열고 readdir()로 디렉토리 파일의 내용을 읽는다 리턴 값: 성공하면 구조체 주소, 실패하면 NULL readdir()로 읽을 때마다 디렉토리 파일의 current file offset은 읽 은 구조체 dirent의 크기 만큼 증가한다. 60

디렉토리의 항목 읽기 위해서는 해당 디렉토리에 대한 읽기 권한이 있어야 한다 그러나 쓰기권한이 있어도 write 함수로 직접 쓸 수는 없다. mkdir rmdir 를 사용해야함 사용자는 디렉토리에 대해서 쓰기 작업을 직접 수행할 수는 없다. 61

rewinddir(), closedir() #include <sys/types.h> #include <dirent.h> void rewinddir (DIR *dp); int closedir (DIR *dp); rewinddir() 디렉토리 파일의 current file offset을 처음으로 옮긴다 closedir() 디렉토리 파일을 닫는다. 리턴 값: 성공하면 0, 실패하며 -1 62

mkdir() #include <sys/types.h> #include <sys/stat.h> int mkdir (const char *pathname, mode_t mode ); 새로운 디렉토리를 만든다. 리턴 값: 성공하면 0, 실패하면 -1 성공하면 "." 와 ".." 파일은 자동적으로 만들어진다. "."은 이 디렉토리 파일의 i-node를, ".."은 부모 디렉토리 파일의 i-node를 가르킨다 63

rmdir() #include <unistd.h> int rmdir (const char *pathname ); 비어 있는 디렉토리를 삭제한다. 디렉토리 파일의 link count를 감소 시킴 link count가 0이 되면 삭제됨 리턴 값: 성공하면 0, 실패하면 -1 64

#include <sys/types.h> /* listfiles.c */ #include <sys/stat.h> #include <dirent.h> #include <stdio.h> #include "error.h" /* typeoffile - return the letter indicating the file type. */ char typeoffile(mode_t mode) { switch (mode & S_IFMT) { case S_IFREG: return('-'); case S_IFDIR: return('d'); case S_IFCHR: return('c'); case S_IFBLK: return('b'); case S_IFLNK: return('l'); case S_IFIFO: return('p'); case S_IFSOCK: return('s'); } return('?'); } 65

/* permoffile - return the file permissions in an "ls"-like string. */ char* permoffile(mode_t mode) { int i; char *p; static char perms[10]; } p = perms; strcpy(perms, "---------"); /* The permission bits are three sets of three * bits: user read/write/exec, group read/write/exec, * other read/write/exec. We deal with each set * of three bits in one pass through the loop. */ for (i=0; i < 3; i++) { if (mode & (S_IREAD >> i*3)) *p = 'r'; p++; if (mode & (S_IWRITE >> i*3)) *p = 'w'; p++; if (mode & (S_IEXEC >> i*3)) *p = 'x'; p++; } /* Put special codes in for set-user-id, set-group-id, * and the sticky bit. (This part is incomplete; "ls" * uses some other letters as well for cases such as * set-user-id bit without execute bit, and so forth.) */ if ((mode & S_ISUID)!= 0) perms[2] = 's'; if ((mode & S_ISGID)!= 0) perms[5] = 's'; if ((mode & S_ISVTX)!= 0) perms[8] = 't'; return(perms); 66

/* outputstatinfo - print out the contents of the stat structure. */ void outputstatinfo(char *pathname, char *filename, struct stat *st) { int n; char slink[bufsiz+1]; } /* Print the number of file system blocks, permission bits, * number of links, user-id, and group-id. */ printf("%5d ", st->st_blocks); printf("%c%s ", typeoffile(st->st_mode), permoffile(st->st_mode)); printf("%3d ", st->st_nlink); printf("%5d/%-5d ", st->st_uid, st->st_gid); /* If the file is not a device, print its size; otherwise * print its major and minor device numbers. */ if (((st->st_mode & S_IFMT)!= S_IFCHR) && ((st->st_mode & S_IFMT)!= S_IFBLK)) printf("%9d ", st->st_size); else printf("%4d,%4d ", major(st->st_rdev), minor(st->st_rdev)); /* Print the access time. The ctime() function is * described in Chapter 7, "Time of Day Operations." */ printf("%.12s ", ctime(&st->st_mtime) + 4); /* Print the file name. If it's a symblic link, also print * what it points to. */ printf("%s", filename); if ((st->st_mode & S_IFMT) == S_IFLNK) { if ((n = readlink(pathname, slink, sizeof(slink))) < 0) printf(" ->???"); else printf(" -> %.*s", n, slink); } 67

int main(int argc, char **argv) { DIR *dp; char *dirname; struct stat st; struct dirent *d; char filename[bufsiz+1]; /* For each directory on the command line... */ while (--argc) { dirname = *++argv; /* Open the directory */ if ((dp = opendir(dirname)) == NULL) error(dirname); printf("%s:\n", dirname); /* For each file in the directory... */ while ((d = readdir(dp))!= NULL) { /* Create the full file name. */ sprintf(filename, "%s/%s", dirname, d->d_name); /* Find out about it. */ if (lstat(filename, &st) < 0) error(filename); /* Print out the information. */ outputstatinfo(filename, d->d_name, &st); putchar('\n'); } putchar('\n'); closedir(dp); } return 0; } 68

chdir(), fchdir() #include <unistd.h> int chdir (const char *pathname); int fchdir (int filedes); 현재 작업 디렉토리를 변경한다 반환 값: 성공하면 0, 실패하면 -1 현재 작업 디렉토리는 프로세스의 속성 69

getcwd() #include <unistd.h> char *getcwd (char *buf, size_t size ); 현재 작업 디렉토리 경로명을 얻는다 리턴 값: 성공하면 buf의 주소, 실패하면 NULL 70

#include <unistd.h> #include <stdio.h> #include "error.h" #define PATH_MAX 1024 int main(void) { char path[path_max+1]; if(chdir("/tmp")<0) error("error chdir"); else { if(getcwd(path, PATH_MAX) == NULL) error("error getcwd"); else printf("current working directory changed to %s \n", path); } } 71

15. sync(), fsync() #include <unistd.h> void sync (); int fsync(int filedes); 버퍼에 있는 내용을 디스크에 쓰도록 한다. sync()는 시스템 데몬 프로세스에 의해서 30초마다 호출된다. I/O 작업이 끝나기 전에 리턴한다 fsync()는 지정된 파일에 대해서만 I/O 작업을 수행하도록 한다. I/O 작업이 끝난 후 리턴한다 참고) O_SYNC 플래그 72

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 4장. 표준 입출력 라이브러리 (Standard I/O Library) 2004. 08. 22 1

Contents 1. Standard I/O Library 2. Buffing 3. Opening a Stream: fopen() 4. Reading / Writing a Stream 5. Standard I/O Efficiency 6. Binary I/O: fread() 7. Positioning a Stream 8. Formatted I/O 9. Temporary Files 2

1. Standard I/O Library Written by Dennis Ritchie in 1975 Implemented on many OS ANSI C Standard Library Buffer allocation 최적의 크기 단위로 I/O를 수행 디스크 I/O 횟수 최소화 Streams 문자의 흐름으로 파일 입출력을 다룬다 3

System calls vs. C Library functions application code user process C library functions C library buffers system calls system buffers (buffer caches) kernel hardware (harddisk ) 4

FILE 구조체 3장의 파일 관련 시스템 호출 함수 à 파일 디스크립터 (file descriptor) 표준 입출력 함수 à FILE 구조체에 대한 포인터 하나의 스트림을 다루기 위한 정보를 포함하는 구조체 파일 디스크립터 (File descriptor) 버퍼에 대한 포인터, 버퍼 크기 에러 플래그 등 #include <stdio.h> 5

FILE 구조체 FILE * (FILE 구조체에 대한 포인터) 스트림을 열면 (fopen 함수) 리턴됨 표준 I/O 함수들의 파라미터로 전달해야함 프로그래머는 FILE 구조체의 내부를 알 필요 없음 6

FILE 구조체 typedef struct { int _cnt; unsigned char *_ptr; unsigned char *_base; unsigned char _flag; unsigned char _file; } FILE ; _cnt : 버퍼에 남아 있는 문자의 수 _ptr : 버퍼 내에 다음에 쓸(읽을) 위치를 가리키는 포인터 _base : 버퍼 시작 주소 _flag : 스트림의 현재 상태 _IOFBF, _IOLBF, _IONBUF _IOEOF, _IOERR _IOREAD, _IOWRT _file : 파일 디스크립터 7

표준 입력/출력/에러 스트림 (stream) stdin, stdout, stderr FILE* #include <stdio.h> 파일 디스크립터 (file descriptorint) STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO int #include <unistd.h> 8

2. Buffing C library buffer 사용 목적 디스크 I/O 수행의 최소화 read (), write () 함수 호출의 최소화 최적의 크기 단위로 I/O 수행 시스템 성능 향상 C library buffer 방식 fully buffered line buffered unbuffered 9

C library buffer 방식 Fully Buffered 버퍼가 꽉 찼을 때 실제 I/O 수행 디스크 파일 입출력 Line Buffered 줄 바꿈 문자(newline)에서 실제 I/O 수행 터미널 입출력 (stdin, stdout) Unbuffered 버퍼를 사용하지 않는다. 표준 에러 (stderr) 10

setbuf() setvbuf() #include <stdio.h> void setbuf (FILE *fp, char *buf ); int setvbuf (FILE *fp, char *buf, int mode, size_t size ); 버퍼의 관리 방법을 변경한다 호출 시기 스트림이 오픈된 후, 입출력 연산 수행 전에 호출되어야 함 11

setbuf() void setbuf (FILE *fp, char *buf ); 버퍼 사용을 on/off 할 수 있다. buf 가 NULL 이면 unbuffered buf 가 BUFSIZ 크기의 공간을 가리키면 fully/line buffered 터미날 장치면 line buffered 그렇지 않으면 fully buffered 12

setvbuf() int setvbuf (FILE *fp, char *buf, int mode, size_t size ); 버퍼 사용 방법을 변경 리턴 값: 성공하면 0, 실패하면 nonzero mode _IOFBF : fully buffered _IOLBF : line buffered _IONBF : unbuffered 13

setvbuf() mode == _IONBF buf 와 size 는 무시됨 mode == _IOLBF or _IOFBF buf 가 NULL이 아니면 buf 에서 size 만큼의 공간 사용 NULL이면 라이브러리가 알아서 적당한 크기 할당 사용 stat 구조체의 st_blksize 크기 할당 (disk files) st_blksize 값을 알 수 없으면 BUFSIZ 크기 할당 (pipes) 14

setvbuf() Function mode buf Buffer & length Type of buffering setbuf nonnull user buf of length BUFSIZ fully or line buffered NULL no buffer unbuffered setvbuf _IOFBF nonnull user buf of length size fully buffered NULL System buffer of appropriate length _IOLBF nonnull user buf of length size line buffered NULL System buffer of appropriate length _IONBF ignored no buffer unbuffered 일반적으로 표준 C 라이브러리가 버퍼의 크기를 결정하고 할당하도록 하는 것이 좋다 15

#include <stdio.h> /* buffer.c */ main() { char buf[bufsiz]; } printf("hello, "); sleep(1); printf("unix!"); sleep(1); printf("\n"); sleep(1); setbuf(stdout,null); printf("how "); sleep(1); printf("are "); sleep(1); printf("you?"); sleep(1); printf("\n"); sleep(1); 16

fflush() #include <stdio.h> int fflush (FILE *fp); fp 스트림의 출력 버퍼에 남아있는 내용을 write() 시스템 호출을 통 하여 커널에 전달한다 리턴 값 : 성공하면 0, 실패하면 EOF (-1) fp 가 NULL이면, 모든 출력 스트림의 출력 버퍼에 남아있는 내용을 커널에 전달한다 17

3. Opening a Stream: fopen() #include <stdio.h> FILE *fopen (const char *pathname, const char *type); 파일을 스트림으로 연다 리턴 값: 성공하면 FILE *, 실패하면 NULL fopen () 함수는, FILE 구조체의 각 필드들을 초기화 한 후에 open() 함수를 호출한다. 18

fopen() - type type 파라미터 "r", "rb" "w", "wb" "a", "ab" "r+", "r+b", "rb+" "w+", "w+b", "wb+" "a+", "a+b", "ab+" Description open for reading O_RDONLY truncate to 0 length or create for writing O_WRONLY O_CREAT O_TRUNC append; open for writing at end of file, or create for writing O_WRONLY O_APPEND O_CREAT open for reading and writing O_RDWR truncate to 0 length or create for reading and writing O_RDWR O_CREAT O_TRUNC open or create for reading and writing at end of file O_RDWR O_APPEND O_CREAT 19

fopen() - type Restriction r w a r+ w+ a+ file must already exist o o previous contents of file discarded o o stream can be read o o o o stream can be wirtten o o o o o stream can be wirtten only an end o o 20

fopen() - "b" "b" 를 지정해주면 binary option, 지정해 주지 않으면 text option FTP 의 binary 명령과 같은 효과 UNIX에서는 à 아무 효과 없음 Windows 운영체제에서 binary option à 아무 효과 없음 Windows 운영체제에서 text option 일 경우 '\n' 을 출력하면 '\r', '\n' 으로 자동 변환됨 '\r', '\n' 을 읽으면 '\n' 으로 자동 변환됨 21

제한사항 출력 함수 다음에 입력함수를 호출하기 위해서는, 사이에 fflush(), fseek(), fsetpos(), rewind() 등의 함수를 반드시 호출해야 한다. 입력 함수 다음에 출력함수를 호출하기 위해서는, 사이에 fseek(), fsetpos(), rewind() 등의 함수를 호출하거나 입력 연산이 파일의 끝 에 도달했어야 한다. 파일이 새로 생성될 경우에, 새로 생성하는 파일에 대한 permission S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOTH POSIX.1 표준 22

freopen() #include <stdio.h> FILE *freopen (const char *pathname, const char *type, FILE *fp ); fp 가 가리키는 스트림으로 파일을 연다 fp 가 이미 열려진 스트림이면 새로 열기 전에 먼저 닫는다 stdin, stdout, stderr 스트림으로 파일을 열 때 사용한다 리턴 값 : 성공하면 FILE* fp, 실패하면 NULL type : fopen()에서와 같음 23

#include <fcntl.h> /* freopen.c */ #include <stdio.h> #include "error.h" main() { FILE* f; } printf("this is displayed on the screen.\n"); if((f = freopen("afile", "w", stdout)) == NULL) error("freopen"); printf("this is written into the redirected file.\n"); 24

fdopen() #include <stdio.h> FILE *fdopen (int filedes, const char *type); 이미 열려진 파일 디스트립터 (filedes)에 대해, 표준 입출력의 스트 림을 생성하여 리턴한다 파일 디스트립터 -> FILE * pipe, cummunication channel 등을 열 때 사용 25

fclose() #include <stdio.h> int fclose ( FILE *fp ); 스트림을 닫는다 리턴 값: 성공하면 0, 실패하면 EOF (-1) 출력 버퍼에 있는 모든 자료는 파일에 저장되고, 입력 버퍼에 있는 모든 자료는 버려진다. 프로세스가 정상적으로 종료한 경우에는 모든 열려진 스트림이 저절 로 닫힌다 26

4. Reading / Writing a Stream 3 types of Unformatted I/O: Character-at-a-time I/O: getc(), fgetc(), getchar() Line-at-a-time I/O: fgets(), fputs() Direct I/O: fread(), fwrite() read/write some number of objects Formatted I/O: scanf(), fscanf(), printf(), fprintf() 27

getc() fgetc() getchar() #include <stdio.h> int getc (FILE *fp ); int fgetc (FILE *fp ); int getchar (void ); 스트림에서 한 문자를 읽어온다 Return: next char if OK, EOF on EOF or error 28

getc() fgetc() getchar() getc()는 매크로 함수 인자로 side effect 없는 수식을 사용하여야 한다 함수의 주소를 얻을 수 없다 fgetc()는 함수 함수의 주소를 사용할 수 있다 수행 시간은 getc()가 빠르다 getchar()는 getc(stdin)과 같다 29

에러검사: ferror() feof() #include <stdio.h> int ferror (FILE *fp); int feof (FILE *fp); void clearerr (FILE *fp); 스트림 입력 함수가 EOF 를 리턴했을 때, feor() : 파일의 끝에 도달했는지 확인 ferror() : 에러가 발생했는지 확인 clearerr()는 EOF 상태나 에러 상태를 지운다 30

ungetc() #include <stdio.h> int ungetc (int c, FILE *fp ); 문자를 스트림에 다시 되돌림 리턴 값: 성공하면 c, 실패하면 EOF c 는 원래 읽었던 문자일 필요는 없음 단어나 토큰(token) 단위로 읽을 때 사용 다음 문자가 무엇인지 미리 읽고 난 후 되돌릴 때 31

putc() fputc() putchar() #include <stdio.h> int putc (int c, FILE *fp ); int fputc (int c, FILE *fp ); int putchar (int c ); 스트림에 한 문자를 쓴다 Return: c if OK, EOF on error 32

putc() fputc() putchar() putc()는 매크로 함수 인자로 side effect 없는 수식을 사용하여야 한다 함수의 주소를 얻을 수 없다 fputc()는 함수 함수의 주소를 사용할 수 있다 수행 시간은 putc()가 빠르다 putchar()는 putc(stdout)과 같다 33

fgets() #include <stdio.h> char *fgets (char *buf, int n, FILE *fp ); '\n' 문자까지 한줄을 입력받는다 '\n' 문자도 읽혀지고 '\0' 이 채워진다 n - 1 개 이상의 문자열을 읽을 수는 없다 Return buf if OK, NULL on end of file or error 34

gets() #include <stdio.h> char *gets (char *buf ); 표준 입력으로부터 한 줄을 읽어 들인다 '\n' 문자를 buf 에 저장하지 않는다. deprecated function buffer overflow 에러가 발생할 수 있으므로 바람직하지 않다 Return buf if OK, NULL on end of file or error 35

fputs() puts() #include <stdio.h> int fputs (const char *str, FILE *fp ); int puts (const char *str); 문자열을 스트림에 출력한다 puts() 는 stdout에 출력하고, '\n'을 덧붙인다 Return nonnegative value if OK, EOF on error 36

5. Standard I/O Efficiency How does the three types of unformatted I/O compare in efficiency? How does unformatted I/O compare with direct read() and write()? Are macros more efficient than functions? Why? What about Program Text size? 37

#include <stdio.h> /* fputc.c */ #include "error.h" #define FILESIZE (100 * 1024 * 1024) main() { FILE* f; int i, fd; if ((f = fopen("file.write", "w")) == NULL) error("file.write"); for (i=0; i < FILESIZE; ++i) fputc('.', f); } fclose(f); 38

Function User CPU (seconds) System CPU (seconds) Clock time (seconds) #loops write() (write2.c) 140.9 1,378.1 1,527.8 104,857,600 User CPU (seconds) System CPU (seconds) Clock time (seconds) #loops Bytes of program text fputc() (fputc.c) putc() (putc.c) 16.1 1.4 21.1 104,857,600 980 7.8 1.1 11.1 104,857,600 1056 39

6. Binary I/O: fread() #include <stdio.h> size_t fread(void *ptr, size_t size, size_t nobj, FILE *fp ); 스트림에서 이진 데이터를 읽는다 size 크기의 객체를 nobj 개 ptr 에 읽어들인다 리턴 값: 읽은 객체의 수 리턴 값이 nobj 값보다 작으면 파일의 끝이거나 에러 ferror(), feof() 40

fwrite() #include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp ); 스트림에 이진 데이터를 출력한다 size 크기의 객체를 nobj 개 출력한다 리턴 값: 출력된 객체의 수 41

Binary Data Exchange May be incompatible among different machine architectures because: Offset of a member in a structure can differ between compilers and systems Binary formats of int and float may differ between different machines (little endian, big endian) So use a higher level protocol instead! 42

7. Positioning a Stream #include <stdio.h> long ftell (FILE *fp ); int fseek (FILE *fp, long offset, int whence ); void rewind (FILE *fp); ftell() - 파일의 현재 위치를 리턴, -1 on error fseek() 파일의 현재 위치를 변경 (lseek() 참고) whence : SEEK_SET, SEEK_CUR, SEEK_END Returns: 0 if OK, nonzeron on error rewind() 파일의 현재 위치를 파일 선두로 변경 fseek (fp, 0L, SEEK_SET) 43

fgetpos() fsetpos() #include <stdio.h> int fgetpos (FILE *fp, fpos_t *pos ); int fsetpos (FILE *fp, const fpos_t *pos ); 새 ANSI C 표준, fpos_t 형 도입 fgetpos() 파일의 현재 위치를 pos 에 얻는다 fsetpos() 파일의 현재 위치를 pos 로 설정 Return 0 if OK, nonzero on error 44

8. Formatted I/O Formatted Output printf() fprintf() sprintf() vprintf() vfprintf() vsprintf() Formatted Input scanf() fscanf() sscanf() 45

printf() fprintf() sprintf() #include <stdio.h> int printf (const char *format, ); int fprintf (FILE *fp, const char *format, ); int sprintf (char *buf, const char *format, ); printf() - stdout 에 출력 fprintf() - fp 스트림에 출력 Both return: number of characters output if OK, negative value if output error sprintf() 문자열 buf 에 출력 ('\0' 이 추가됨) Returns: number of characters stored in array 46

vprintf() vfprintf() vsprintf() #include <stdarg.h> #include <stdio.h> int vprintf (const char *format, va_list arg ); int vfprintf (FILE *fp, const char *format, va_list arg ); int vsprintf (char *buf,const char *format, va_list arg ); 가변 인자 리스트(variable length argument lists)를 va_list args 로 전달 47

scanf() fscanf() sscanf() #include <stdio.h> int scanf (const char *format, ); int fscanf (FILE *fp, const char *format, ); int sscanf (const char *buf,const char *format, ); scanf() 표준 입력에서 입력 fscanf() 스트림 fp 에서 입력 sscanf() 문자열 buf 에서 입력 Return: number of input items assigned, EOF if input error or end of file before any conversion 48

9. Temporary Files #include <stdio.h> char *tmpnam(char *ptr); FILE *tmpfile (void ); char *tempnam(const char *directory,const char *prefix ); tmpnam() : 임시파일이름 생성 L_tmpnam : 파일 이름의 최대 길이 상수 (stdio.h> if ptr!= NULL: ptr 에 이름 저장하고 ptr 리턴 if ptr == NULL: 라이브러리 내부 static 변수에 이름 저장하고 주소 리턴 tmpfile() : "wb+" 로 임시파일을 생성하고 스트림 포인터 리턴 tempnam() : 만들어질 임시 파일 이름의 경로와 접두어를 지정 49

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 5장. 고급 입출력 (Advanced I/O) 2004. 08. 22 1

Contents 1. Nonblocking I/O 2. Record Locking 3. I/O Multiplexing 4. Asynchronous I/O 5. Memory Mapped I/O 2

1. Nonblocking I/O Blocking I/O I/O 작업이 완료되기를 기다리며 영원히 리턴안할 수 있다 (block forever) 예) getchar() 은 엔터키를 입력할 때까지 영원히 기다린다 Nonblocking I/O I/O 작업이 당장 완료될 수 없으면 곧장 에러를 리턴한다 예) 이미 입력된 키가 있으면 그 값을 리턴하고, 아니면 기다리지 않고 즉시 에러를 리턴한다 주의: Asynchronous I/O 와 혼동하지 말라 3

Slow system calls which can block forever Reads from pipes, terminals, network devices, if data isn't present Writes to above if they don't handle reading promptly Opens of files block until some condition occurs Open of a terminal device attached to a modem Open of a FIFO for writing only when there is no other process for reading the FIFO Reads and writes of files that have mandatory record locking enabled Certain ioctl operations Some of IPC functions 4

Nonblocking I/O 설정 방법 open() 으로 파일을 열 때 O_NONBLOCK 플래그 open("a.out",o_rdonly O_NONBLOCK); 열려진 파일에 대하여 fcntl() 함수로 O_NONBLOCK 플래그 설정 int flag; flag = fcntl(fd, F_GETFL, 0); flag = O_NONBLOCK; fcntl(fd, F_SETFL, flag); O_NONBLOCK 플래그 설정 취소 int flag; flag = fcntl(fd, F_GETFL, 0); flag &= ~O_NONBLOCK; fcntl(fd, F_SETFL, flag); 5

#include <fcntl.h> #include "ourhdr.h" void set_fl(int fd, int flags) { int val; } if ( (val = fcntl(fd, F_GETFL, 0)) < 0) err_sys("fcntl F_GETFL error"); val = flags; /* turn on flags */ if (fcntl(fd, F_SETFL, val) < 0) err_sys("fcntl F_SETFL error"); void clr_fl(int fd, int flags) { int val; } if ( (val = fcntl(fd, F_GETFL, 0)) < 0) err_sys("fcntl F_GETFL error"); val &= ~flags; /* turn flags off */ if (fcntl(fd, F_SETFL, val) < 0) err_sys("fcntl F_SETFL error"); 6

#include <sys/types.h> /* nonblockw.c */ #include <errno.h> #include <fcntl.h> #include "ourhdr.h" char buf[100000]; int main(void) { int ntowrite, nwrite; char *ptr; } ntowrite = read(stdin_fileno, buf, sizeof(buf)); fprintf(stderr, "read %d bytes\n", ntowrite); set_fl(stdout_fileno, O_NONBLOCK); /* set nonblocking */ for (ptr = buf; ntowrite > 0; ) { errno = 0; nwrite = write(stdout_fileno, ptr, ntowrite); fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno); if (nwrite > 0) { ptr += nwrite; ntowrite -= nwrite; } } clr_fl(stdout_fileno, O_NONBLOCK); /* clear nonblocking */ exit(0); 7

2. Record Locking 하나의 파일에 두 프로세스가 동시에 쓴다면 어떤 내용은 덮어 쓰여 질 수 있다 만약 쓰여지는 내용이 파일의 뒤에 덧붙여지기만 할 것이라면 open() 에서 O_APPEND 모드로 열면 된다 한 프로세스가 파일의 영역을 읽거나 수정할 때 다른 프로세스의 접 근을 제한하기 위해서는 그 영역에 lock 을 걸어야 한다 8

Advisory vs. Mandatory Locking Advisory Locking lock 을 걸 수 있고 검사할 수 있지만 강제되지는 않는다 즉 locking 규칙을 무시하고 읽고 쓰기 가능 따라서 모든 프로세스들이 자발적으로 규칙을 준수해야함 Mandatory Locking 커널이 locking 규칙을 강제하므로 무시하고 읽고 쓰기 불가능 커널이 모든 입출력 함수의 호출을 감시해야 하므로 시스템 부하 증가 설정 방법: set-group-id 비트를 켜고 group-execute 비트를 끔 -rw-r-sr-- 1 lsj 5 Jul 15 12:11 lockfile 9

Compatibility between different lock types Request for read lock Request for write lock Region currently has No locks OK OK One or more read locks One write lock OK denied denied denied Types of Locks F_RDLCK : Shared Read Lock F_WRLCK : Exclusive Write Lock 10

fcntl() #include <sys/types.h> #include <unistd.h> #include <fcntl.h> int fcntl(int filedes, int cmd, struct flock *flockptr); returns : depends on cmd if OK, -1 on error cmd : F_GETLK, F_SETLK, F_SETLKW 11

fcntl() F_GETLK flockptr 에 설정한 lock 을 걸 수 있는지, 즉 이미 lock 이 걸려 있는지 검사 걸 수 없다면 이미 걸려있는 lock 정보 flockptr 가 채워짐 걸 수 있다면 flockptr 의 l_type 만 F_UNLCK 으로 변경됨 F_SETLK and l_type == F_RDLCK or F_WRLCK flockptr 에 설정한 lock 을 건다 실패하면 즉시 -1을 리턴한다 (errno = EACCESS or EAGAIN) F_SETKL and l_type == F_UNLCK flockptr 에 설정한 lock 을 해제한다 12

fcntl() F_SETLKW F_SETLK 의 blocking version 프로세스는 lock 이 가능할 때까지 sleep 상태가 된다 This sleep is interrupted if a signal is caught 13

fcntl() struct flock { short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ off_t l_start; /* 로킹 영역의 시작 지점 */ short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_len; /* 로킹 영역의 길이 (0이면 파일 끝까지) */ pid_t l_pid; /* 프로세스의 PID */ }; When setting or releasing a lock on a file, the system combines or splits adjacent areas as required. 14

Requesting and Releasing A Lock #include <sys/types.h> #include <fcntl.h> int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ lock.l_start = offset; /* byte offset, relative to l_whence */ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ lock.l_len = len; /* #bytes (0 means to EOF) */ } return( fcntl(fd, cmd, &lock) ); 15

Requesting and Releasing A Lock #define read_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len) #define readw_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len) #define write_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len) #define writew_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len) #define un_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len) 16

Tesing for A Lock #include <sys/types.h> #include <fcntl.h> #include "ourhdr.h" pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_type = type; /* F_RDLCK or F_WRLCK */ lock.l_start = offset; /* byte offset, relative to l_whence */ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ lock.l_len = len; /* #bytes (0 means to EOF) */ if (fcntl(fd, F_GETLK, &lock) < 0) err_sys("fcntl error"); } if (lock.l_type == F_UNLCK) return(0); /* false, region is not locked by another proc */ return(lock.l_pid); /* true, return pid of lock owner */ 17

Implied Inheritance and Release of Locks 프로세스가 종료하면 그 프로세스가 설정한 모든 lock 은 해제된다 file descriptor 가 닫히면 이와 관련된 모든 lock 은 해제된다. 다음과 같은 경우들에도 모든 lock 은 해제된다 fd1 = open("a", ); read_lock(fd1, ); fd2 = dup(fd1); close(fd2) fd1 = open("a", ); read_lock(fd1, ); fd2 = open("a", ); close(fd2) 18

Implied Inheritance and Release of Locks fork() 에 의한 자식 프로세스에게 lock 은 상속되지 않는다 exec() 의 경우에는 상속된다 19

Kernel Implementation fd1 = open("a", ); write_lock(fd1, 0, ); /* parent write locks byte 0 */ If (fork() > 0) { /* parent */ fd2 = dup(fd1); fd3 = open("a", ); pause(); close(fd3); /* is the write lock released? */ } else { /* child */ read_lock(fd1, ); /* child read locks byte 1 */ pause(); } 위 실행 결과는 다음 슬라이드와 같음 lock 은 file descriptor 가 아니고 file 에 연관되어 있다 따라서 fd1, fd2, fd3 중 어느 것이라도 close 하면 lock 은 해제 된다 20

Kernel Implementation process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size linked list of locks file descriptors fd 0: fd flags ptr fd 1: fd 2:.... struct flock link flags, etc starting offset length process ID struct flock link flags, etc starting offset length process ID 21

Mandatory Locking with read and write Blocking descriptor, tries to Non Blocking descriptor, tries to read write read write Read lock exists on region OK blocks OK EAGAIN Write lock exists on region blocks blocks EAGAIN EAGAIN open() with O_TRUNC or O_CREAT fails with outstanding mandatory locks (either read locks or wirte locks) 22

3. I/O Multiplexing What if we have to read from two descriptor? Blocking I/O in a loop? 한 디스크립터에서 읽기 위해 블록되어있는 동안 다른 디스크립터 에서 데이터를 읽을 수 없다 다른 기법이 필요 23

Read from two descriptor Nonblocking I/O using polling 첫번째 디스크립터에서 읽는다 데이터가 있으면 읽어서 처리하고 없으면 리턴하여 두번째 디스크팁터에서 읽는다 데이터가 있으면 읽어서 처리하고 없으면 리턴하여 일정 시간 동안 대기하고 (sleep) 다시 첫번째 디스크립터에서 읽는 것부터 반복한다 문제점 일종의 polling 불필요한 반복으로 CPU 시간의 낭비 24

Read from two descriptor Asynchronous I/O 어느 디스크립터에서 읽을 데이터가 있을 때 커널이 이를 signal 로 통보해 주도록 한다 불필요한 polling 을 피할 수 있다 문제점 표준화된 방법이 없음 읽어야할 디스크립터가 무엇인지 알 수 없음 25

Read from two descriptor I/O multiplexing 디스크립터들을 파라미터로 I/O multiplexing 함수를 호출한다 디스크립터들 중 하나에서 읽을 수 있을 때까지 대기한다 CPU 시간을 낭비하지 않는다 리턴할 때는 어느 디스크립터에서 읽어야할지 알 수 있다 26

select() The arguments we pass to select tell the kernel Which descriptors we're interested in What conditions we're interested in for each descriptor (read, write, exception condition). How long we want to wait. On the return from select, the kernel tells us The total count of the number of descriptors that are ready. Which descriptors are ready for each of the three conditions (read, write, or exception condition). 27

select() #include <sys/types.h> #include <sys/time.h> #include <unistd.h> int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr); Returns : count of ready descriptors, 0 on timeout, -1 on error maxfdp1 : max fd plus 1 readfds,writefds, exceptfds : pointers to descriptor sets tvptr : a pointer to a timeval 28

select() struct timeval { long tv_sec; /* second */ long tv_usec; /* microseconds */ } the last argument struct timeval *tvptr tvptr == NULL tvptr->tv_sec == 0 && tvptr->tv_usec == 0 tvptr->tv_sec!= 0 tvptr->tv_usec!= 0 29

select() tvptr == NULL Wait forever Returns if one of the descriptors is ready or a signal is caught (returns 1 with errno = EINTR) tvptr->tv_sec == 0 && tvptr->tv_usec == 0 Don't wait at all. All descriptors are tested and return is made immediately. tvptr->tv_sec!= 0 tvptr->tv_usec!= 0 Wait for the specified number of seconds and microseconds. Returns if one of descriptor is ready Returns if the timeout expires 30

select() The middle three arguments fd_set *readfds fd_set *writefds fd_set *exceptfds fd_set: 파일 디스크립터의 set Specify which descriptors we're interested in and for which conditions(readable, writable, or an exception condition) Any of the middle three arguments can be null pointers If all three pointers are NULL, then select can be used a higher precision timer than sleep 31

select() readfds writefds exceptfds fd0 fd1 fd2 0 0 0 one bit per possible descriptor 0 0 0 fd_set data type 0 0 0 FD_ZERO(fd_set *fdset); // clear all bits in fdset FD_SET(int fd, fd_set *fdset); // turn on bit for fd in fdset FD_CLR(int fd, fd_set *fdset); // turn off bit for fd in fdset FD_ISSET(int fd, fd_set *fdset); // test bit for fd in fdset 32

select() The first argument, maxfdp1 max fd plus 1 the highest descriptor set, add 1 최대값: FD_SETSIZE in <sys/types.h> 33

select() - three possible return values -1 : error occurred A signal is caught before any of descriptor is ready 0 : no descriptors are ready The time limit expires before any of the descriptors are ready the number of descriptors that are ready Only the ready descriptors are set in the three descriptors returned 34

select() what 'ready' means A descriptor in the read set (readfds) is considered ready if a read from that descriptor won't block A descriptor in the write set (writefds) is considered ready if a write from that descriptor won't block A descriptor in the exception set is considered ready if there is an exception condition pending on that descriptors the arrival of out-of-band data on a network connnection certain conditions occurring on a pseudo terminal that has been placed into packet mode 35

select() Whether a descriptor is blocking or not doesn't affect where select blocks or not The end of file is read condition, not exception condition 36

select() 사용예 fd_set readset, writeset; FD_ZERO(&readset); FD_ZERO(&writeset); FD_SET(0, &readset); FD_SET(3, &readset); FD_SET(1, &writeset); FD_SET(2, &writeset); if ((n = select(4, &readset, &writeset, NULL, NULL)) > 0) { if (FD_ISSET(0, &readset)) if (FD_ISSET(3, &readset)) if (FD_ISSET(1, &writeset)) if (FD_ISSET(2, &writeset)) } 37

4. Asynchronous I/O 입출력 가능시 시그널 발생 BSD 계열: SIGIO,SIGURG, SysV 계열: SIGPOLL 입출력 시그널은 하나뿐이므로 두 개 이상의 디스크립터를 다중화할 때는 앞서 설명한 다중화 모델 필요 38

readv() writev() #include <sys/types.h> #include <sys/uio.h> int readv(int filedes, const struct iovec iov[], int iovcnt); int writev(int filedes, const struct iovec iov[], int iovcnt); Both return: number of bytes read or written, -1 on error Read into and write from multiple noncontiguous buffers in a single function call called scatter read and gather write fd : file descriptor iov[] : buffers 39

struct iovec struct iovec { void *iov_base; /* 버퍼 시작 주소 */ size_t iov_len; /* 버퍼 크기 */ }; iov[0].iov_base iov[0].iov_len iov[1].iov_base iov[1].iov_len iov[iovcnt-1].iov_base iov[iovcnt-1].iov_len len0 len1 lenn buffer0 len0 buffer1 len1 bufferi leni 40

5. Memory Mapped I/O 디스크의 파일에 메모리 주소를 부여한다 주소에서 읽는 것은 파일의 데이터를 읽는다 주소에 쓰면 파일에 저장된다 메모리인 것처럼 포인터와 배열을 사용하여 파일의 데이터를 다룰 수 있게된다 성능 향상을 기대할 수 있다 파일 I/O 에서처럼 시스템 버퍼와 라이브러리 버퍼 사이에 데이터 가 복사될 필요 없다 알고리즘 단순화 마치 메모리처럼 배열과 포인터 사용 가능 41

mmap() #include <sys/types.h> #include <sys/mman.h> caddr_t mmap(caddr_t addr, size_t len, int prot, int flag, int filedes, off_t off); Returns : starting address of mapped region if OK, -1 on error 디스크 파일에 메모리 주소를 부여한다 addr : 부여될 메모리 시작 주소 addr 가 NULL 이먄 시스템이 적당한 시작 주소를 선택한다 virtual memory page 의 배수이어야 한다 len : 메모리 크기 42

mmap() prot : specify the protection of the mapped region. PROT_READ : the region can be read. PROT_WRITE : the region can be written. PROT_EXEC : the region can be executed. PROT_NONE : the region cannot be accessed (not in 4.3+BSD). The protection specified for a region has to match the open mode of the file. 43

mmap() flag : various attributes of the mapped region MAP_FIXED the return value must equal addr MAP_FIXED 가 지정되지 않으면, and addr is nonzero, then the kernel uses addr as a hint. MAP_SHARED 부여된 주소에 쓰면 파일에 저장된다 MAP_SHARED 나 MAP_PRIVATE 중의 하나가 반드시 지정되어야 한다 MAP_PRIVATE 부여된 주소에 쓰면 파일의 복사본이 만들어지고 이후부터는 복사본을 읽고 쓰게 된다 44

Example of a memory mapped file high address stack len memory mapped portion of file start addr heap uninitialized data (bss) initialized data low address text file : memory mapped portion of file off len 45

#include <sys/types.h> /* mcopy.c */ #include <sys/stat.h> #include <sys/mman.h> /* mmap() */ #include <fcntl.h> #include "ourhdr.h" int main(int argc, char *argv[]) { int fdin, fdout; char *src, *dst; struct stat statbuf; } if (argc!= 3) err_quit("usage: a.out <fromfile> <tofile>"); if ( (fdin = open(argv[1], O_RDONLY)) < 0) err_sys("can't open %s for reading", argv[1]); if ( (fdout = open(argv[2], O_RDWR O_CREAT O_TRUNC, FILE_MODE)) < 0) err_sys("can't creat %s for writing", argv[1]); if (fstat(fdin, &statbuf) < 0) /* need size of input file */ err_sys("fstat error"); if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) /* set size of output file */ err_sys("lseek error"); if (write(fdout, "", 1)!= 1) err_sys("write error"); if ( (src = mmap(0, statbuf.st_size, PROT_READ, MAP_FILE MAP_SHARED, fdin, 0)) == (caddr_t) -1) err_sys("mmap error for input"); if ( (dst = mmap(0, statbuf.st_size, PROT_READ PROT_WRITE, MAP_FILE MAP_SHARED, fdout, 0)) == (caddr_t) -1) err_sys("mmap error for output"); memcpy(dst, src, statbuf.st_size); /* does the file copy */ exit(0); 46

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 6장. 프로세스 (Process) 2004. 08. 22 1

Contents 1. Process Start 2. Process Termination 3. Command-Line Arguments 4. Environment Variables 5. Memory Layout of a C program 6. Memory Allocation 7. setjmp() longjmp() 2

1. Process Start Kernel exec system call user process C start-up routine call return int main(int argc, char * argv[]); 3

main() int main(int argc, char *argv[]); arc : the number of command-line arguments argv[] : an array of pointers to the arguments C start-up routine Started by the kernel (by the exec system call) Take the command-line arguments and the environment from the kernel Call main exit( main( argc, argv) ); 4

2. Process Termination Normal termination: return from main() calling exit() : calling _exit() Abnormal termination calling abort() terminated by a signal 5

exit() #include <stdlib.h> void exit(int status); 프로세스를 정상적으로 종료한다 cleanup processing 을 수행한다 모든 열려진 스트림을 닫고 출력 버퍼의 내용을 디스크에 쓴다 status the exit status of a process 이 값은 UNIX shell 에 의해서 사용됨 6

_exit() #include <unistd.h> void _exit(int status); 프로세스를 정상적으로 종료한다 커널로 즉시 리턴한다 7

atexit() #include <stdlib.h> void atexit(void (*func)(void)); returns: 0 if OK, nonzero on error exit handler 를 등록한다 프로세스당 32개까지 func an exit handler a function pointer exit() 는 exit handler 들을 등록된 역순으로 호출한다 8

C Program Start and Termination _exec user function exit handler _exec call return main function exit (does not return) exit function exit handler user process call return C start-up routine _exit standard I/O cleanup exec kernel 9

Example of exit handlers #include "ourhdr.h" /* doatexit.c */ static void my_exit1(void), my_exit2(void); int main(void) { if (atexit(my_exit2)!= 0) err_sys("can't register my_exit2"); if (atexit(my_exit1)!= 0) err_sys("can't register my_exit1"); if (atexit(my_exit1)!= 0) err_sys("can't register my_exit1"); printf("main is done\n"); return 0; } static void my_exit1(void) { printf("first exit handler\n"); } static void my_exit2(void) { printf("second exit handler\n"); } 10

3. Command-Line Arguments exec() can pass command-line arguments to a new program Part of normal operation of Unix shells argv[argc] is NULL (ANSI, POSIX.1) 11

Echo all command-line arguments #include "ourhdr.h" /* echoarg.c */ int main(int argc, char *argv[]) { int i; } for (i = 0; i < argc; i++) /* echo all command-line args */ printf("argv[%d]: %s\n", i, argv[i]); exit(0); #include "ourhdr.h" /* echoarg1.c */ int main(int argc, char *argv[]) { int i = 0; char **p = argv; } while (*p) printf("argv[%d]: %s\n", i++, *p++); exit(0); 12

4. Environment Variables 환경 변수(environment variable)는 부모 프로세스에서 자식 프로세 스로 전달된다.login 또는.cshrc 파일에서 환경 변수를 설정한다. 환경변수: 이름=값 $ env USER=lsj LOGNAME=lsj HOME=/user1/lsj PATH=/bin:/usr/bin:/usr/local/bin:/usr/ccs/bin:/usr/ucb:/usr/openwin/bin:/etc:. MAIL=/var/mail/lsj SHELL=/bin/csh 13

Environment List 전역 변수 environ을 이용하여 환경 변수에 접근한다. extern char ** environ; 각 항목은 "환경 변수 이름=값" 의 형식 각 문자열은 '\0'로 끝난다 환경 변수 리스트의 마지막은 NULL 포인터 argv 와 같은 구조 14

Environment List environ: environment pointer environment list environment strings "USER=lsj" "LOGNAME=lsj" "HOME=/user1/lsj" "PATH=/bin:/usr/local " "MAIL =/var/mail/lsj"... NULL "SHELL=/bin/csh" 15

getenv() #include <stdlib.h> char *getenv(const char *name); Returns : pointer to value associated with name, NULL if not found 환경 변수 리스트에서 이름이 name 인 것을 찾아 값에 대한 포인터를 리턴한다 실패하면 NULL 포인터를 리턴 16

putenv() #include <stdlib.h> int putenv(const char *str); Returns: 0 if OK, nonzero on error 환경 변수를 추가한다 str은 "name=value" 형식의 문자열 성공적으로 실행된 경우 0을 리턴 같은 이름의 환경 변수가 이미 있다면 새 값으로 변경된다 17

setenv() unsetenv() #include <stdlib.h> int setenv(const char *name, const char *value, int rewrite); Returns: 0 if OK, nonzero on error void unsetenv(const char *name); setenv 는 환경 변수 name = value 을 등록한다 name 의 환경변수가 이미 있을 경우 rewrite!= 0 이면 새 값으로 변경되고 rewrite == 0 이면 값이 변경되지 않는다 unsetenv 는 환경 변수 name 을 제거한다 18

5. Memory Latout of a C program high address stack command-line arguments and environment variables low address heap uninitialized data (bss) initialized data text initialized to zero by exec read from program file by exec 19

Memory Layout of a C program Text segment: Machine instructions (read-only, sharable) Initialized data segment: e.g. int maxcount = 99; (initialized) Uninitialized data segment: (bss: block started by symbol) e.g. long sum[1000]; Stack: automatic variables, temporary variables, return address, caller's environment (registers) Heap: dynamic memory allocation 20

Shared Libraries Static linking library 사용된 라이브러리 루틴들이(예: printf) 실행파일에 추가된다 실행파일 크기 증가 Shared library 실행파일에 라이브러리 루틴들을 포함하지 않는다. 공용의 메모리에 라이브러리 루틴을 로드하고 이를 공유한다 프로그램 크기 감소 처음 실행될때 오버헤드 발생 라이브러리가 버전업되어도 실행파일을 다시 컴파일 않해도됨 21

6. Memory Allocation #include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nobj, size_t size); void *realloc(void *ptr, size_t newsize); returns : nonnull pointer if OK, NULL on error void free(void *ptr); dynamic allocation of memory from heap provide suitable alignment ex) doubles must start at the addresses that are multiples of 8 library manages memory pool 22

malloc(): allocates specified number of bytes, initial value of memory is indeterminate calloc(): allocates specified number of objects of specified size, initialized to all 0 bits realloc(): changes size of previously allocated memory, initial value of new area is indeterminate 23

7. setjmp(), longjmp() #include <setjmp.h> int setjmp(jmp_buf env); returns : 0 if called directly, nonzero if returning from a call to longjmp void longjmp(jmp_buf env,int val); Jump across function call (nonlocal goto) Useful for dealing with error conditions in deeply nested function calls An error occurs, We want to print an error, ignore rest of input, and return to main() Large number of levels à handle return at each level for each error Direct nonlocal goto: setjmp, longjmp 24

setjmp store information for return to setjmp point at env returns 0 if called directly returns nonzero(val) if returning from a call to longjmp longjmp use env to jump to setjmp point and val as a return value. Several longjmp can use the same setjmp location with different val. normally env is a global variable since it can be referenced from another function. 25

Automatic, Register, Volatile Variables Problems with automatic variables and register variables when using setjmp and longjmp After longjmp,the values of these variables are implementation dependent. A variable stored in a register can be rolled back (returned to the original value saved by setjmp) A volatile variable can not be rolled back. Volatile variables : forces compiler to suppress optimizations. Global and static variables are left alone. 26

#include <setjmp.h> /* testjmp.c */ #include "ourhdr.h" static void f1(int, int, int); static void f2(void); static jmp_buf jmpbuffer; int main(void) { int count; register int val; volatile int sum; } count = 2; val = 3; sum = 4; if (setjmp(jmpbuffer)!= 0) { printf("after longjmp: count = %d, val = %d, sum = %d\n", count, val, sum); exit(0); } count = 97; val = 98; sum = 99; /* changed after setjmp, before longjmp */ f1(count, val, sum); /* never returns */ static void f1(int i, int j, int k) { printf("in f1(): count = %d, val = %d, sum = %d\n", i, j, k); f2(); } static void f2(void) { longjmp(jmpbuffer, 1); } 27

Results: testjmp.c $ cc testjmp.c $ a.out in f1(): count = 97, val = 98, sum = 99 after longjmp: count = 97, val = 98, sum = 99 $ cc O testjmp.c $ a.out in f1(): count = 97, val = 98, sum = 99 after longjmp: count=2, val=3, sum=99 28

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 7장. 프로세스 통제 (Process Control) 2004. 08. 22 1

Contents 1. Process Identifiers 2. fork() 3. vfork() 4. exit() 5. wait() waitpid() 6. Race Conditions 7. exec 8. Changing User IDs and Group IDs 9. system() 10. Process Times 2

1. Process Identifiers Every process has a unique process ID, a nonnegative integer the tmpnam() uses the process ID to generate unique names Process ID 0 : swapper (scheduler process) system process part of the kernel no program on disk corresponds to this process 3

Process Identifiers Process ID 1: init process invoked by the kernel at the end of the bootstrap procedure /etc/init or /sbin/init reads the system-dependent initialization files (/etc/rc*) brings the system to a certain state (multi-user) a normal user process but runs with super-user privileges PID 1 is immortal Process ID 2: pagedaemon supports the paging of the virtual memory system kernel process 4

Process Identifiers #include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void); uid_t getuid(void); uid_t geteuid(void); gid_t getgid(void); gid_t getegid(void); // returns process ID // returns parent process ID // returns real user ID // returns effective user ID // returns real group ID // returns effective group ID none of these functions has an error return 5

2. fork() #include <sys/types.h> #include <unistd.h> pid_t fork(void); Returns: 0 in child, process ID of child in parent, -1 on error fork() is the ONLY way to create a process in Unix kernel child process is the new process created by fork() fork() is called once, but returns twice! returns 0 in child process returns the child process ID in parent process 6

fork() the child process can call getppid() to obtain the parent process ID Parent and child continue executing instructions following the fork() call Child gets a copy of parent s data space, heap, and stack Often, read-only text segment is shared Often, fork() is followed by exec() 7

#include <sys/types.h> /* fork1.c */ #include "ourhdr.h" int glob = 6; /* external variable in initialized data */ char buf[] = "a write to stdout\n"; int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; if (write(stdout_fileno, buf, sizeof(buf)-1)!= sizeof(buf)-1) err_sys("write error"); printf("before fork\n"); /* we don't flush stdout */ } if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { /* child */ glob++; /* modify variables */ var++; } else sleep(2); /* parent */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0); 8

fork1.c: results $ a.out a write to stdout before fork pid = 430, glob = 7, var = 89 pid = 429, glob = 6, var = 88 $ a.out > temp.out $ cat temp.out a write to stdout before fork pid = 432, glob = 7, var = 89 before fork pid = 431, glob = 6, var = 88 "before fork" printed twice. the standard I/O buffer is copied to the child process and is not yet flushed because it is fully buffered (redirected) in the second execution example 9

File sharing after fork() Parent and child share the same file descriptors As if dup() had been called on all open file descriptors Parent and child share the same file offset, o/w overwrite Intermixed output from parent and child 10

Handle descriptors after fork() What happens if both parent and child write to the same file opened before fork()? Parent waits for child to complete File offsets updated by child Parent and child go their own way Each closes the descriptors that it doesn t need Often the case with network servers 11

File Sharing after fork() parent process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2:.... child process table entry file descriptors fd 0: fd flags ptr fd 1: fd 2:.... file table fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr fd status flags current file offset v-node ptr v-node table v-node information i-node information current file size v-node information i-node information current file size v-node information i-node information current file size 12

Properties inherited to the child real user and group ID, effective user and group ID supplementary group IDs process group ID, session ID, set-user-id and set-group ID flags current working directory root directory file mode creation mask signal mask and dispositions the close-on-exec flag for any open file descriptors environment attached shared memory segments resource limits 13

Properties not inherited to the child the return value from fork() the process IDs are different file locks pending alarms are cleared for the child the set of pending signals for the child is set to the empty set 14

3. vfork() Creates a new process only to exec a new program No copy of parent's address space for child (not needed!) Before exec, child runs in "address space of parent" Efficient in paged virtual memory Child runs first Parent waits until child exec or exit Then the parent resume The deadlock possibility if the child wait for something from the parent 15

#include <sys/types.h> /* vfork1.c */ #include "ourhdr.h" int glob = 6; /* external variable in initialized data */ int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; printf("before vfork\n"); /* we don't flush stdio */ if ( (pid = vfork()) < 0) err_sys("vfork error"); else if (pid == 0) { /* child */ glob++; /* modify parent's variables */ var++; _exit(0); /* child terminates */ } } /* parent */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0); 16

vfork1.c: Results increments by child appear in parent address space $ a.out before vfork pid = 607, glob = 7, var = 89 Instead of _exit() à exit(), results in: $ a.out before vfork ß stdio closed by child 17

4. exit() Process Termination A process can terminate in 5 ways: Normal Termination return from main() exit() _exit() Abnormal Termination calling abort() (generates SIGABRT signal) process receives signals 18

exit() Process Termination Regardless how a process terminates, the same code in the kernel is eventually executed it closes all the open descriptors for the process, release memory, and so on. 19

exit() Termination status Exit status: argument of exit(), _exit() the return value from main() Termination status: Normal termination: Exit status à Termination status Abnormal termination: kernel indicates reason à Termination status 20

exit() Termination status Parent can obtain the termination status of the child process by wait() or waitpid() What if parent terminates before child? init becomes the parent of the child process. 21

exit() Child Termination Suppose child terminates first If child disappeared, parent would not be able to check child s termination status Zombie: minimal info of dead child process (pid, termination status, CPU time) kept by the kernel for the parent to call wait() or waitpid() init s inherited child do not become zombies init call wait() to fetch status 22

5. wait() waitpid() Child terminates Kernel sends SIGCHLD signal to parent an asynchronous event Default action for SIGCHLD signal: ignore it Signal handlers can be defined by users call wait() to fetch the termination status of child 23

wait() waitpid() #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); Both returns: process ID if OK, 0, or -1 on error A process that calls wait or waitpid can block (if all of its children are still running), or return immediately with the termination status of a child, or return immediately with an error (if it doesn t have any child processes) statloc a pointer to an integer to store the termination status 24

Macros to examine the termination status Macro WIFEXITED(status) Description True if child is terminated normally WEXITSATUS(status) : get exit status (low-order 8 bits) WIFSIGNALED(status) WIFSTOPPED(status) True if child is terminated abnormally (by receipt of a signal) WTERMSIG(status) : fetch the signal number that caused the termination. WCOREDUMP(status) : true if a core file was generated True if child is currently stopped WSTOPSIG(status) : fetch the signal number that caused the stop 25

#include <sys/types.h> /* prexit.c */ #include <sys/wait.h> #include "ourhdr.h" void pr_exit(int status) { if (WIFEXITED(status)) printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status)? " (core file generated)" : ""); #else ""); #endif else if (WIFSTOPPED(status)) printf("child stopped, signal number = %d\n", WSTOPSIG(status)); } 26

#include <sys/types.h> /* wait1.c */ #include <sys/wait.h> #include "ourhdr.h" int main(void) { pid_t pid; int status; if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) exit(7); /* child */ if (wait(&status)!= pid) err_sys("wait error"); /* wait for child */ pr_exit(status); /* and print its status */ if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) /* child */ abort(); /* generates SIGABRT */ if (wait(&status)!= pid) err_sys("wait error"); /* wait for child */ pr_exit(status); /* and print its status */ if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) /* child */ status /= 0; /* divide by 0 generates SIGFPE */ } if (wait(&status)!= pid) err_sys("wait error"); /* wait for child */ pr_exit(status); /* and print its status */ exit(0); 27

wait1.c: Results $ a.out normal termination, exit status = 7 abnormal termination, signal number = 6 (core file generated) abnormal termination, signal number = 8 (core file generated) signal number = 6 ß SIGABRT signal number = 8 ß SIGFPE 28

waitpid() waitpid() waits for one particular process provides a nonblocking version of wait supports job control (with the WUNTRACED option) the pid argument for waitpid -1 : waits for any child process > 0 : waits for the child process whose process ID equals pid. 0 : waits for any child whose process GID equals that of the calling process. < -1 : waits for any child whose process GID equals the absolute value of pid. 29

waitpid() the options constants for waitpid WNOHANG : does not block if a child specified by pid is not immediately available, return 0 WUNTRACED: job control support returns the status of any stopped child specified by pid if its status has not reported yet WIFSTOPPED macro needs this option 30

Avoiding zombie processes A process forks a child It does not wait for the child to complete It does not want child to become zombie How to do this? Answer: fork twice! fork2.c 31

#include <sys/types.h> /* fork2.c */ #include <sys/wait.h> #include "ourhdr.h" int main(void) { pid_t pid; if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { /* first child */ if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid > 0) exit(0); /* parent from second fork == first child */ /* We're the second child; our parent becomes init as soon as our real parent calls exit() in the statement above. Here's where we'd continue executing, knowing that when we're done, init will reap our status. */ sleep(2); printf("second child, parent pid = %d\n", getppid()); exit(0); } if (waitpid(pid, NULL, 0)!= pid) /* wait for first child */ err_sys("waitpid error"); } /* We're the parent (the original process); we continue executing, knowing that we're not the parent of the second child. */ exit(0); 32

wait3() wait4() #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); Both returns: process ID if OK, 0, or -1 on error return the resource information - refer to getrusage() 33

wait arguments support Function pid options rusage POSIX.1 SVR4 4.3+BSD wait o o o waitpid o o o o o wait3 o o o o wait4 o o o o 34

6. Race Conditions Multiple processes share some data Outcome depends on the order of their execution (i.e. RACE) After fork(), we cannot predict if the parent or the child runs first! The order of execution depends on: system load kernel s scheduling algorithm 35

Race Conditions Race condition problems are hard to detect because they work "most of the time"! For parent to wait for child call wait, waitpid, wait3, wait4 For child to wait for parent while (getppid()!= 1) sleep(1); polling! wastes CPU time use signals or other IPC methods 36

Race Conditions After fork parent and child both need to do something on its own e.g. parent: write a record in a log file e.g. child: creates a log file Parent and child need to: TELL each other when its initial set of operations are done, and WAIT for each other to complete 37

TELL and WAIT child parent #include ourhdr.h TELL_WAIT(); /* setup for TELL_XXX and WAIT_XXX */ if ( (pid = fork()) < 0 ) err_sys( fork error ); else if (pid==0) { /* child */ /* child does whatever is necessary */ TELL_PARENT(getppid()); /* tell parent we're done */ WAIT_PARENT(); /* & wait for parent */ /* and child continues on its way */ exit(0); } /* parent does whatever is necessary */ TELL_CHILD(pid); /*tell child we're done */ WAIT_CHILD(); /* wait for child */ /* and parent continues on its way */ exit(0); 38

Race Condition #include <sys/types.h> /* tellwait1.c */ #include "ourhdr.h" static void charatatime(char *); int main(void) { pid_t pid; } if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) charatatime("output from child\n"); else charatatime("output from parent\n"); exit(0); static void charatatime(char *str) { char *ptr; int c; } setbuf(stdout, NULL); /* set unbuffered */ for (ptr = str; c = *ptr++; ) putc(c, stdout); 39

tellwait1.c: Results $ a.out output from child output from parent $ a.out oouuttppuutt ffrroomm pcahrielndt $ a.out ooutput from parent utput from child 40

Avoid Race Condition #include <sys/types.h> /* tellwait2.c */ #include "ourhdr.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); } if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { WAIT_PARENT(); /* parent goes first */ charatatime("output from child\n"); } else { charatatime("output from parent\n"); TELL_CHILD(pid); } exit(0); 41

7. exec - Program Execution When a process calls one of the exec functions that process is completely replaced by the new program (text, data, heap, and stack segments) and the new program starts at its main function #include <unistd.h> /* exec_echo.c */ int main() { execl("/bin/echo", "echo", "execute /bin/echo", (char*) 0); printf("can this message be printed? ERROR!\n"); } $ a.out execute /bin/echo 42

exec functions #include <unistd.h> int execl(const char *pathname, const char *arg0, /* (char*) 0 */); int execv(const char *pathname, const char *argv[]); int execle(const char *pathname, const char *arg0, /* (char*) 0, char *const envp[] */); int execve(const char *pathname, const char *argv[],char *const envp[]); int execlp(const char *filename, const char *arg0, /* (char*) 0 */); int execvp(const char *filename, const char *argv[]); All six return: -1 on error, no return on success 43

exec functions postfix (p, l, v, e) p: filename + PATH environment variable l : takes a list of arguments the last argument should be a null pointer v: takes argv[] vector e: takes envp[] array without 'e', the environment variables of the calling process are copied 44

Properties inherited to the new program process ID, parent process ID, real user ID, real group ID, supplementary group IDs, process group ID, session ID Controlling terminal time left until alarm clock current working directory root directory file mode creation mask file locks process signal mask pending signals resource limits tms_utime, tms_stime, tms_cutime, tms_ustime values 45

Changes on exec FD_CLOEXEC flag for files set à close file descriptors on exec unset (default) à open across exec FD_CLOEXEC flag for directories closed across exec (POSIX.1) UIDs and GIDs real à same across exec effective à may change (sticky bit) 46

#include <sys/types.h> /* exec1.c */ #include <sys/wait.h> #include "ourhdr.h" char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL }; int main(void) { pid_t pid; } if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { /* specify pathname, specify environment */ if (execle("/home/stevens/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *) 0, env_init) < 0) err_sys("execle error"); } if (waitpid(pid, NULL, 0) < 0) err_sys("wait error"); if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { /* specify filename, inherit environment */ if (execlp("echoall", "echoall", "only 1 arg", (char *) 0) < 0) err_sys("execlp error"); } exit(0); 47

#include "ourhdr.h" /* echoall.c */ int main(int argc, char *argv[]) { int i; char **ptr; extern char **environ; for (i = 0; i < argc; i++) /* echo all command-line args */ printf("argv[%d]: %s\n", i, argv[i]); for (ptr = environ; *ptr!= 0; ptr++) /* and all env strings */ printf("%s\n", *ptr); } exit(0); 48

exec1.c: Results $a.out argv[0]: echoall argv[1]: myarg1 argv[2]: MY ARG2 USER=unknown PATH=/tmp argv[0]: echoall $ argv[1]: only 1 arg ß $ prompt, the parent process finished USER=stevens HOME=/home/stevens... EDITOR=/usr/ucb/vi 49

8. Changing User IDs and Group IDs #include <sys/types.h> #include <unistd.h> int setuid(uid_t uid); int setgid(gid_t gid); Both return: 0 if OK, -1 on error setuid() sets the real/effective user ID If the process has superuser privileges, it sets the real user ID, effective user ID, saved set-user-id to uid else if uid equals either the real user ID or the saved set-user-id, it sets effective user ID (Others do not change.) to uid else errno is set to EPERM, returns -1. setgid() sets the real/effective group ID 50

Changing User IDs and Group IDs Only a superuser process can change the real user ID login(1) calls setuid to set all three user IDs The effective user ID is set from the file s user ID by the exec functions, only if the set-user-id bit is set for the program file. The saved set-user-id is copied from the effective user ID by exec after the effective user ID is stored from the file s user ID (if set-user-id on) 51

Different ways to change the three User IDs ID exec setuid(uid) set-user-id bit off set-user-id bit on superuser unprivileged user real user ID unchanged unchanged set to uid unchanged effective user ID unchanged set from user ID of program file set to uid set to uid saved set-user ID copied from effective user ID copied from effective user ID set to uid unchanged 52

What is "saved set-uid"? Example: Berkeley tip program Program to remote connection through modem exclusive access through lock files 1. tip is owned by uucp, set-uid bit is SET, on exec: Real UID = our own UID Effective UID = uucp Saved set-uid = uucp 2. tip can accesses lock files owned by uucp allowed because e-uid == uucp 53

What is "saved set-uid"? 3. tip executes setuid(getuid()) Real UID = our own UID (unchanged!) Effective UID = our own UID Saved set-uid = uucp (unchanged!) tip runs with our own UID as effective UID, can access only our own normally accessed files. No additional permissions. 54

What is "saved set-uid"? 4. tip executes setuid(uucpuid) uucpuid was saved by calling geteuid() when tip started This call is allowed because the saved set-uid == uucpuid! Real UID = our own UID (unchanged!) Effective UID = uucp Saved set-uid = uucp (unchanged!) 5. tip can now release lock files because its efffective UID == uucp! 55

setreuid() setregid() #include <sys/types.h> #include <unistd.h> int setreuid(uid_t ruid, uid_t euid); int setregid(gid_t rgid, gid_t egid ); Both return: 0 if OK, -1 on error An unprivileged user can always swap between the real user ID and effective user ID For swapping real ID ßà effective ID 56

seteuid() setegid() #include <sys/types.h> #include <unistd.h> int seteuid(uid_t uid); int setegid(gid_t gid); Both return: 0 if OK, -1 on error An unprivileged user can set its effective user ID to either its real user ID or its saved set-user-id For a privileged user only the effective user ID is set to uid Only effective UID or GID is changed 57

Summary of set ID functions superuser setreuid(ruid, euid) superuser setuid(uid) superuser seteuid(uid) ruid euid uid uid uid uid real user ID unpriviliged setreuid effective user ID unpriviliged setreuid saved set-user-id exec of set-user-id unpriviliged setuid or seteuid unpriviliged setuid or seteuid 58

9. system() #include <stdlib.h> int system(const char *cmdstring); Causes the string to be given to sh(1) as input as if the string had been typed as a command at a terminal ex) system("date > file"); System is implemented by calling fork, exec, and waitpid Return values: -1 with errno: fork or waitpid fails 127 : exec fails Termination status of shell: all 3 succeed 59

#include <sys/types.h> /* system.c */ #include <sys/wait.h> #include <errno.h> #include <unistd.h> int system(const char *cmdstring) /* version without signal handling */ { pid_t pid; int status; if (cmdstring == NULL) return(1); /* always a command processor with Unix */ if ( (pid = fork()) < 0) { status = -1; /* probably out of processes */ } else if (pid == 0) { /* child */ execl("/bin/sh", "sh", "-c", cmdstring, (char *) 0); _exit(127); /* execl error */ } } else { /* parent */ while (waitpid(pid, &status, 0) < 0) if (errno!= EINTR) { status = -1; /* error other than EINTR from waitpid() */ break; } } return(status); 60

10. Process Times #include <sys/times.h> clock_t times(struct tms *buf); Returns: elapsed wall clock time in clock ticks if OK, -1 on error struct tms ( clock_t tms_utime; /* user cpu time */ clock_t tms_stime; /* system cpu time */ clock_t tms_cutime; /* child user cpu time */ clock_t tms_cstime; /* child system cpu time */ } 61

Wall clock time: the amount of time the process takes to run and depends on the system loads. The wall clock time returned is measured from some arbitrary point in the past. Use its relative value. User CPU time: attributed to user instructions System CPU time: attributed to the kernel, when it executes on behalf of the process 62

#include <sys/times.h> /* times1.c */ #include "ourhdr.h" static void pr_times(clock_t, struct tms *, struct tms *); static void do_cmd(char *); int main(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) do_cmd(argv[i]); /* once for each command-line arg */ exit(0); } static void do_cmd(char *cmd) /* execute and time the "cmd" */ { struct tms tmsstart, tmsend; clock_t start, end; int status; fprintf(stderr, "\ncommand: %s\n", cmd); if ( (start = times(&tmsstart)) == -1) /* starting values */ err_sys("times error"); if ( (status = system(cmd)) < 0) /* execute command */ err_sys("system() error"); if ( (end = times(&tmsend)) == -1) /* ending values */ err_sys("times error"); pr_times(end-start, &tmsstart, &tmsend); pr_exit(status); } 63

static void pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend) { static long clktck = 0; if (clktck == 0) /* fetch clock ticks per second first time */ if ( (clktck = sysconf(_sc_clk_tck)) < 0) err_sys("sysconf error"); fprintf(stderr, " real: %7.2f\n", real / (double) clktck); fprintf(stderr, " user: %7.2f\n", (tmsend->tms_utime - tmsstart->tms_utime) / (double) clktck); fprintf(stderr, " sys: %7.2f\n", (tmsend->tms_stime - tmsstart->tms_stime) / (double) clktck); fprintf(stderr, " child user: %7.2f\n", (tmsend->tms_cutime - tmsstart->tms_cutime) / (double) clktck); fprintf(stderr, " child sys: %7.2f\n", (tmsend->tms_cstime - tmsstart->tms_cstime) / (double) clktck); } 64

Results: times1.c $ a.out "sleep 5" "date" command: sleep 5 real: 5.25 user: 0.00 sys: 0.00 child user: 0.02 child sys: 0.13 normal termination, exit status = 0 command: date Sun Aug 18 09:25:38 MST 1991 real: 0.27 user: 0.00 sys: 0.00 child user: 0.05 child sys: 0.10 normal termination, exit status = 0 65

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 8장. 프로세스 관계 (Process Relationship) 2004. 08. 22 1

Contents 1. Terminal Logins 2. Network Logins 3. Process Groups 4. Sessions 5. Controlling Terminal 6. Job Control 7. Orphaned Process 2

1. Terminal Logins: programs & files /etc/ttys: 1 line per terminal device getty: opens terminal device for reading and writing gettytab: data file for getty login: login program getpass: read password crypt: encrypts plain text (password) 3

To allow terminal logins process ID 1 init fork init exec getty /reads/etc/ttys; forks once per terminal; create empty environment; each child execs getty 4

State after login process ID 1 init fork init exec getty exec login /reads/etc/ttys; forks once per terminal; create empty environment; each child execs getty open terminal device (file descriptors 0, 1, 2); reads user name; initial environment set 5

All set for terminal login process ID 1 init login shell fd 0, 1, 2 terminal device driver through getty and login user at a termina l RS-232 connection 6

2. Network Logins inetd - Internet superserver waits for TCP/IP connection requests when request arrives, does a fork and exec of the appropriate program TELNET a remote login application that uses the TCP protocol. telnetd TELNET server opens pseudo-terminal device splits into two processes using fork The parent handles the communication across the network connection The child does an exec of the login program. 7

TELNET server TCP connection request from TELNET client process ID 1 init inetd fork inetd exec telnet d fork /exec of /bin/sh which executes shell script /etc/rc when system comes up multiuser when connection request arrives from TELNET client 8

All set for network login process ID 1 init login shell through inetd, telnetd, and login fd 0, 1, 2 pseudo-terminal device driver user at a termina l network connection through telnetd server and telnet client 9

3. Process Groups #include <sys/types.h> #include <unistd.h> pid_t getpgrp(void); Returns: process GID of calling process Process GID = PID à process group leader 10

Process Groups A process can create a process group and become leader A process group leader can create processes in the group A process group exists till the last process terminates OR joins another process group (leader may terminate first) 11

Process Groups #include <sys/types.h> #include <unistd.h> int setpgid(pid_t pid, pid_t pgid); Returns: 0 if OK, -1 on error Join existing group or create new group Sets process GID of a process with PID pid to pgid. 12

Process Groups pid == pgid à leader pid == 0 à pgid := PID of caller pgid == 0 à pgid := pid Process can set its own PGID or PGID of its children Both parent and child use setpgid to set their own process GIDs 13

4. Sessions login shell proc1 proc2 proc3 proc4 process group process group session proc5 process group Session = Collection of one or more process groups proc1 proc2 & proc3 proc4 proc5 14

Sessions #include <sys/types.h> #include <unistd.h> pid_t setsid(void); Returns: PGID if OK, -1 on error A process (not a process group leader) creates a new session: Process becomes session leader (new session contains this process only) Process become process group leader of a new process group (PGID=PID) Process has no controlling terminal. 15

Sessions Process group leader calling setsid() returns ERROR Solution to ensure not leader: fork() a child (new pid, parent gpid, not a process group leader) parent exits child continues as parent 16

5. Controlling Terminal Session has a controlling terminal Session leader is controlling process Process groups: 1 foreground, 1 or more background Interrupt key à quit key to all foreground processes Modem disconnect à hangup signal to controlling process 17

Controlling Terminal session login shell proc1 proc2 proc3 proc4 background process group session leader = controlling process background process group proc5 foreground process group modem disconnect (hangup signal) terminal input and terminal-generated signals controlling terminal 18

Controlling Terminal To talk to controlling terminal open /dev/tty (fails if no controlling terminal) Example crypt < salaries lpr stdin of crypt = salaries Qs: crypt needs password, how to input it? Ans: input password using controlling terminal by opening /dev/tty 19

tcgetpgrp() tcsetpgrp() #include <sys/types.h> #include <unistd.h> pid_t tcgetpgrp(int filedes); Returns: PGID of foreground process group if OK, -1 on error int tcsetpgrp(int filedes, pid_t pgrpid); Returns: 0 if OK, -1 on error tcgetpgrp returns PGID of foreground process group associated with the terminal open on filedes tcsetpgrp set the foreground process group ID to pgrpid only the process that has a controlling terminal can call this Normally called by job-control shells. 20

6. Job Control A feature added by Berkeley in 1980 To start multiple jobs (groups of processes) from a single terminal and To control: which jobs can access terminal which jobs are to run in background. 21

Job Control 3 forms of support required by job control: A shell to support job control Terminal driver must support job control Must support job-control signals _POSIX_JOB_CONTROL defines whether a system support job control Examples: vi main.c ß 1 foreground process pr *.c lpr & make all & ß 2 background processes 22

Job Control Shells assign a job identifier to a background job $ make all > Make.out & [1] 1475 $ pr *.c lpr & [2] 1490 process IDs of 1st process $ [2] + Done pr *.c lpr & [1] + Done make all > Make.out & job number 23

Job Control Terminal driver looks out for 3 special characters: Interrupt character (DELETE or CTRL-C) generates SIGINT Quit character (CTRL-BACKSLASH) generates SIGQUIT Suspend character (CTRL-Z) generates SIGTSTP SIGTTIN When background job tries to read from the terminal, the terminal driver detects this and sends it to the background job. SIGTTOU When background job tries to write to the terminal 24

Job Control $ cat temp.foo & [1] 1719 $ hello, world [1] + Done cat temp.foo & $ stty tostop $ cat temp.foo & [1] 1721 $ [1] + Stopped(tty output) cat temp.foo & $ fg %1 cat temp.foo hello, world 25

Job Control fg command The shell places the job into the foreground process group (tcsetpgrp) And sends SIGCONT to the process group stty command stty tostop disable ability of background jobs to output to the controlling terminal 26

Job Control 27

Job Control init or inetd getty or telnetd login exec login shell change in status of children setpgid setpgid exec, after setsid, then establishing controlling terminal change in status of children background process group(s) tcsetpgrp to set process group for controlling terminal write to terminal may generate SIGTTOU terminal input/output foreground process group Read from terminal generate SGITTIN terminal driver user at a terminal terminal-generated signals (SIGINT, SIGQUIT, SIGSTP) delivered to process group 28

7. Orphaned Process Groups What happens if a child is stopped (using job control) when parent terminates? How will the child ever be continued? Does the child know it has been orphaned? POSIX.1 definition of Orphaned Process Group: Parent of every process in a group is either itself a process in the group or is not a process in the group s session Not orphaned as long as a process has a parent in the same session 29

Orphaned Process Groups process group 442 login shell (PID 442) session fork/exec parent (PID 512) fork child (PID 513) process group 512 30

Orphaned Process Groups A stopped process in an orphaned process group is sent: the hang-up signal (SIGHUP), and the continue signal (SIGCONT) SIGHUP default action: terminate When parent terminates, child becomes a background process read from ctrl terminal à error is returned (errno:= EIO==5) 31

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 9장. 시그널 (Signal) 2004. 08. 22 1

Contents 1. Signal Concepts 2. signal() 3. Interrupted System Calls 4. Reentrant Functions 5.Reliable Signal Terminology and Semantics 6. kill() raise() 7. alarm() pause() 8. Signal Sets 9. sigprocmask() 10. sigpending() 11. sigaction() 12. sigsetjmp() siglongjmp() 13. sigsuspend() 2

Contents 14. abort() 15. sleep() 16. Job-Control Signals 3

1. Signal Concepts Signals are software interrupts Handles asynchronous events user typing CTRL-C to stop a program next program in pipeline terminated prematurely Every signal has a name begin with 'SIG' SIGABRT: abort signal from abort() SIGALRM: alarm signal from alarm() SVR4, 4.3+BSD: 31 different signals 4

Signal Concepts Signals are all defined by positive integer constants (the signal number) in the header <signal.h> Version 7 had 15 different signals Unreliable signal model - get lost and hard to turn off. SVR4 and 4.3+BSD both have 31 different signals Reliable signals added. 5

Signal Generation Terminal-generated signals DELETE or CTRL-C à SIGINT Hardware exceptions generate signals divide by 0 à SIGFPE invalid memory reference à SIGSEGV kill(2) function sends any signal to a process or process group need to be owner or superuser kill(1) command sends signal to a processes Software conditions: SIGALRM: alarm clock expires SIGPIPE: broken pipe SIGURG: out-of-band network data 6

Dispositions of signals Disposition or action: Process has to tell the kernel if and when this signal occurs, do the following. Ignore the signal: all signals can be ignored, except SIGKILL and SIGSTOP Catch the signal: Call a function of ours when a signal occurs. Let the default action apply most are to terminate process 7

Unix Signals SIGART: generated by calling the abort function. SIGALRM: generated when a timer set with the alarm expires. SIGCHLD : Whenever a process terminates or stops, the signal is sent to the parent. SIGCONT : This signal(job-control) sent to a stopped process when it is continued. SIGFPE : signals an arithmetic exception, such as divide-by-0, floating point overflow, and so on SIGHUP : disconnect detected, session leader terminates, daemon processes reread configuration files 8

Unix Signals SIGILL : indicates that the process has executed an illegal hardware instruction. SIGINT : generated by the terminal driver when we type the interrupt key and sent to all processes in the foreground process group SIGIO : indicates an asynchronous I/O event SIGKILL : can t be caught or ignored. a sure way to kill any process. SIGPIPE : If we write to a pipeline but the reader has terminated, SIGPIPE is generated SIGPWR : related to power failure. (read the book for the detail) SIGQUIT : generated by the terminal driver when we type terminal quit key and sent to all processes in the foreground process group 9

Unix Signals SIGSEGV : indicates that the process has made an invalid memory reference SIGSTOP : This signal(job-control) stops a process and can t be caught or ignored SIGSYS : signals an invalid system call SIGTERM : the termination signal sent by the kill(1) command by default. SIGTSTP : This is the interactive stop signal generated by the terminal driver when we type the terminal suspend key and sent to all processes in the foreground process group. SIGTTIN : generated by the terminal driver when a process in a background process group tries to read from its controlling terminal SIGTTOU : generated by the terminal driver when a process in a background process group tries to write to its controlling terminal 10

Unix Signals SIGURG : notifies the process that an urgent condition has occurred. Optionally generated when out-of-band data is received on a network connection. SIGUSR1, SIGUSR2 : user-defined signals, for use in application programs SIGWINCH : generated to the foreground process group when a process changes the window size from its previous value, with the ioctl set-window-size command SIGXCPU : generated if the process exceeds its soft CPU time limit SIGXFSZ : generated if the process exceeds its soft file size limit 11

2. signal() #include <signal.h> void ( *signal ( int signo, void (*func) (int))) (int) Returns: previous disposition of signal if OK, SIG_ERR on error signo : the name of the signal func: user-defined function (signal handler) called when the signal occurs. SIG_IGN - ignore the signal SIG_DFL - take its default action The signal handler is passed a single integer argument (the signal number) and returns nothing. signal() returns the pointer to the previous signal handler 12

#include <signal.h> /* sigusr.c */ #include "ourhdr.h" static void sig_usr(int signo) /* argument is signal number */ { if (signo == SIGUSR1) } printf("received SIGUSR1\n"); else if (signo == SIGUSR2) printf("received SIGUSR2\n"); else err_dump("received signal %d\n", signo); return; int main(void) { if (signal(sigusr1, sig_usr) == SIG_ERR) err_sys("can't catch SIGUSR1"); if (signal(sigusr2, sig_usr) == SIG_ERR) err_sys("can't catch SIGUSR2"); } for ( ; ; ) pause(); 13

sigusr.c: Results $ a.out & n[1] 4720 $ kill USR1 4720 received SIGUSR1 $ kill USR2 4720 received SIGUSR2 $ kill 4720 [1] + Terminated a.out & 14

Program Startup When a process is forked, the child inherits the parent s signal dispositions. When a program is execed the disposition of any signals that are being caught à default action the status of all other signals (ignored or default) à left alone 15

An interactive shell sets the disposition of the interrupt and quit signals in the background process to be ignored Many interactive programs catches the signals only when not in the background by doing the following: int sig_int(), sig_quit(); if (signal(sigint, SIG_IGN)!= SIG_IGN) signal(sigint, sig_int); if (signal(sigquit, SIG_IGN)!= SIG_IGN) signal(sigquit, sig_quit); 16

Signal disposition on fork On fork: Child inherits parent s signal dispositions Old signal handler has meaning in child 17

3. Interrupted System Calls A process is blocked in a slow system call The process receives a signal The system call is interrupted and returns an error (errno = EINTR) May be something happened that should wake up the blocked system call 18

Interrupted System Calls Slow system calls reads from/writes to files that can block the caller forever (pipes, terminal, network) open files that block until some condition occurs (opening terminal devices that waits until a modem answers the phone) pause() and wait() certain ioctl() operations and some IPC functions 19

Interrupted System Calls Need to restart an interrupted system call Again: if ((n = read(fd, buff, BUFFSIZE)) < 0) { if (errno == EINTR) goto Again; /* just an interrupted system call */ /* handle other errors */ } 20

Interrupted System Calls Automatic restarting of certain interrupted system calls (4.2BSD) ioctl, read, readv, write, and writev are interrupted by a signal only if they are operating on a slow device wait, waitpid are always interrupted when a signal is caught 4.3BSD allow to disable this feature on a per-signal basis Without the automatic restart feature, we need to test every read/write for the interrupted error return and reissue the read or write. 21

4. Reentrant Functions Process is executing Signal arrives and is caught Signal handler starts executing Signal handler returns Process resumes execution Any problem? 22

Reentrant Functions What if process was in the middle of malloc(), signal handler starts executing and also calls malloc()? malloc() maintains a linked list of all allocated areas process may have been in the middle of updating the linked list! 23

Reentrant Functions POSIX.1 specifies the functions that are guaranteed to be reentrant Reentrant Functions Functions that can be called by two or more processes (tasks, signal handlers), with arbitrary preemption (interrupt), and still give the same predictable output results Reentrancy conditions Use static variables in an atomic way, Do not call malloc or free, Does not belong to standard I/O library 24

Reentrant Functions Signal handlers should only call reentrant functions! Problems still exist: errno variable: updated by reentrant functions such as read(), wait(), etc. signal handler should save errno, and restore it on exit 25

#include <pwd.h> /* reenter.c */ #include <signal.h> #include "ourhdr.h" static void my_alarm(int signo) { struct passwd *rootptr; } printf("in signal handler\n"); if ( (rootptr = getpwnam("root")) == NULL) /* nonreenterant function call */ err_sys("getpwnam(root) error"); alarm(1); return; int main(void) { struct passwd *ptr; signal(sigalrm, my_alarm); alarm(1); /* generates SIGALRM after 1 sec */ } for ( ; ; ) { if ( (ptr = getpwnam("stevens")) == NULL) err_sys("getpwnam error"); if (strcmp(ptr->pw_name, "stevens")!= 0) printf("return value corrupted!, pw_name = %s\n", ptr->pw_name); } 26

reenter.c: Results Results are random Usually terminated by SIGSEGV Internal pointers corrupted when signal handler also called getpwnam() after main called it Sometimes terminated correctly, but return value corrupted, invalid file descriptor error (EBADF) returned 27

5.Reliable Signal Terminology and Semantics Signal is generated when event that causes the signal occurs hardware exception (divide by 0) software condition (alarm timer expiring) terminal-generated signal call to kill function Kernel sets a flag in the process table indicating that the signal is generated Signal is delivered to a process if action for signal is taken 28

Reliable Signal Terminology and Semantics During the time between the generation of signal and its delivery, the signal is said to be pending. Each process has a signal mask that defines the set of signals currently blocked from delivery to that process 29

Reliable Signal Terminology and Semantics A blocked signal remains pending when: default signal action, or catch signal using user-defined handler A signal stops being pending when: unblocks the signal, or changes the action to ignore the signal What to do with blocked signals is determined when it is delivered and not when it is generated (to change action) 30

6. kill() raise() #include <sys/types.h> #include <singnal.h> int kill(pid_t pid, int signo); int raise(int signo); Both return: 0 if OK, 1 on error kill - sends a signal to a process or a group of process raise - function allows a process to send a signal to itself 31

kill() pid > 0 : signal to the process whose process ID is pid pid == 0 : signal to the processes whose process group ID equals that of sender pid < 0 : signal to the processes whose process group ID equals abs. of pid pid == -1 : POSIX.1 leaves this condition unspecified (used as a broadcast signal in SVR4, 4.3+BSD) 32

kill() - Permission to send signals The superuser can send a signal to any process The real or effective user ID of the sender has to equal the real or effective user ID of the receiver If _POSIX_SAVED_IDS is defined, the saved set-user-id of the receiver is checked instead of its effective user ID. SIGCONT can be sent to any member process of the same session 33

kill() - Sending NULL signal NULL signal: signo == 0 Sent with kill for error checking e.g. to check if a process exists no signal is sent If the process doesn t exist, kill returns 1 and errno is set to ESRCH 34

7. alarm() #include <unistd.h> unsigned int alarm (unsigned int seconds); Returns: 0 or number of seconds until previously set alarm alarm() sets a timer to expire at a specified time in future when timer expires, SIGALRM signal is generated, default action of the signal is to terminate the process. seconds is the number of clock seconds in the future when the signal should be generate scheduling delays may occur 35

alarm() only one alarm clock per process previously registered alarm clock is replaced by the new value if alarm(0), a previous unexpired alarm is cancelled 36

pause() #include <unistd.h> int pause (void); Returns: -1 with errno set to EINTR suspends the calling process until a signal is caught. returns only if a signal handler is executed and that handler returns. 37

using alarm and pause to implement sleep #include <signal.h> /* sleep1.c */ #include <unistd.h> static void sig_alrm(int signo) { return; /* nothing to do, just return to wake up the pause */ } unsigned int sleep1(unsigned int nsecs) { if (signal(sigalrm, sig_alrm) == SIG_ERR) return nsecs; alarm(nsecs); /* start the timer */ pause(); /* next caught signal wakes us up */ return alarm(0); /* turn off timer, return unslept time */ } 38

sleep1.c: Implementation Problems If the caller of sleep1() already has an alarm set, the alarm is erased by the first call to alarm. Modify the disposition for SIGALRM Save the disposition and restore it when we're done Race condition: between alarm & pause the signal handler may be called before pause the alarm goes off before the pause() the called is suspended forever at pause() 39

Another implementation of sleep #include <setjmp.h> /* sleep2.c */ #include <signal.h> #include <unistd.h> static jmp_buf env_alrm; static void sig_alrm(int signo) { longjmp(env_alrm, 1); } unsigned int sleep2(unsigned int nsecs) { if (signal(sigalrm, sig_alrm) == SIG_ERR) return nsecs; if (setjmp(env_alrm) == 0) { alarm(nsecs); /* start the timer */ pause(); /* next caught signal wakes us up */ } return alarm(0); /* turn off timer, return unslept time */ } 40

sleep2.c: Implementation Problems The previous race condition was avoided Another problem if SIGALRM interrupts some other signal handler and the longjmp() aborts the other signal handler (see the next example) 41

Calling sleep2 #include <signal.h> /* tsleep2.c */ #include "ourhdr.h" unsigned int sleep2(unsigned int); static void sig_int(int signo) { int i; volatile int j; } printf("\nsig_int starting\n"); for (i = 0; i < 2000000; i++) j += i * i; printf("sig_int finished\n"); return; int main(void) { unsigned int unslept; } if (signal(sigint, sig_int) == SIG_ERR) err_sys("signal(sigint) error"); unslept = sleep2(5); printf("sleep2 returned: %u\n", unslept); exit(0); 42

tsleep2.c: Results $ a.out sleep2 starts running ^? Type our interrupt char sig_int starting SIGALRM generated while in sig_int() sleep2 returned: 0 longjmp aborted sig_int 43

alarm: Timeout on blocking operations A read operation on a "slow" device can block for a long time An upper time limit can be imposed using the alarm function and SIGALRM 44

Timeout #include <signal.h> /* read1.c */ #include "ourhdr.h" static void sig_alrm(int signo) { return; /* nothing to do, just return to interrupt the read */ } int main(void) { int n; char line[maxline]; if (signal(sigalrm, sig_alrm) == SIG_ERR) err_sys("signal(sigalrm) error"); alarm(10); if ( (n = read(stdin_fileno, line, MAXLINE)) < 0) err_sys("read error"); alarm(0); } write(stdout_fileno, line, n); exit(0); 45

read1.c A common use for alarm : timeout function Race condition: alarm may go off before read() If the read system call is automatically restarted, the read is not interrupted when the SIGALRM signal handler returns timeout does not work. 46

Another Timeout #include <setjmp.h> /* read2.c */ #include <signal.h> #include "ourhdr.h" static jmp_buf env_alrm; static void sig_alrm(int signo) { longjmp(env_alrm, 1); } int main(void) { int n; char line[maxline]; if (signal(sigalrm, sig_alrm) == SIG_ERR) err_sys("signal(sigalrm) error"); if (setjmp(env_alrm)!= 0) err_quit("read timeout"); alarm(10); if ( (n = read(stdin_fileno, line, MAXLINE)) < 0) err_sys("read error"); alarm(0); } write(stdout_fileno, line, n); exit(0); 47

read2.c No problems with automatic restart But still has the race condition and the problem with other signal handler interactions 48

8. Signal Sets datatype to represent multiple signals (sigset_t) the five functions to manipulate signal sets #include <signal.h> int sigemptyset (sigset_t *set); int sigfillset (sigset_t *set); int sigaddset (sigset_t *set, int signo); int sigdelset (sigset_t *set, int signo); all four return: 0 if OK, -1 on error int sigismember ( const sigset_t *set, int signo); Returns:1 if true, 0 if false 49

Functions to manipulate signal sets sigemptyset initialize set so that all signals are excluded sigfillset initialize set so that all signals are included sigaddset add a single signal to an existing set sigdelset remove a single signal from a set sigismember test if signal is a member of a signal set 50

9. sigprocmask() #include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oset); Returns: 0 if OK, -1 on error Examine or change signals to be blocked 1. if oset!= NULL, the current signal mask is returned through oset 2. if set!= NULL, how SIG_BLOCK : block the signal in set SIG_UNBLOCK : unblock the signals in set SIG_SETMASK : the new signal mask = set 51

#include <errno.h> /* prmask.c */ #include <signal.h> #include "ourhdr.h" void pr_mask(const char *str) { sigset_t sigset; int errno_save; errno_save = errno; /* we can be called by signal handlers */ if (sigprocmask(0, NULL, &sigset) < 0) err_sys("sigprocmask error"); } printf("%s", str); if (sigismember(&sigset, SIGINT)) printf("sigint "); if (sigismember(&sigset, SIGQUIT)) printf("sigquit "); if (sigismember(&sigset, SIGUSR1)) printf("sigusr1 "); if (sigismember(&sigset, SIGALRM)) printf("sigalrm "); /* remaining signals can go here */ printf("\n"); errno = errno_save; 52

10. sigpending() #include <signal.h> int sigpending (sigset_t *set); Returns: 0 if OK, -1 on error returns the set of signals that are blocked and pending returned through the set argument 53

#include <signal.h> /* critical.c */ #include "ourhdr.h" static void sig_quit(int signo) { printf("caught SIGQUIT\n"); if (signal(sigquit, SIG_DFL) == SIG_ERR) err_sys("can't reset SIGQUIT"); return; } int main(void) { sigset_t newmask, oldmask, pendmask; } if (signal(sigquit, sig_quit) == SIG_ERR) err_sys("can't catch SIGQUIT"); sigemptyset(&newmask); sigaddset(&newmask, SIGQUIT); /* block SIGQUIT and save current signal mask */ if (sigprocmask(sig_block, &newmask, &oldmask) < 0) err_sys("sig_block error"); sleep(5); /* SIGQUIT here will remain pending */ if (sigpending(&pendmask) < 0) err_sys("sigpending error"); if (sigismember(&pendmask, SIGQUIT)) printf("\nsigquit pending\n"); /* reset signal mask which unblocks SIGQUIT */ if (sigprocmask(sig_setmask, &oldmask, NULL) < 0) err_sys("sig_setmask error"); printf("sigquit unblocked\n"); sleep(5); /* SIGQUIT here will terminate with core file */ exit(0); 54

critical.c : Results $ a.out ^\ SIGQUIT pending caught SIGQUIT SIGQUIT unblocked ^\Quit(coredump) 55

11. sigaction() #include <signal.h> int sigaction (int signo, const struct sigaction *act, struct sigaction *oact); Returns:0 if OK, -1 on error examines or modify the action associated with a particular signal supersedes the signal function from earlier UNIX signo : signal number If act!= NULL, we are modifying the action If oact!= NULL, the system returns the previous action for the signal 56

sigaction() struct sigaction { void (*sa_handler)(); /* address of signal handler */ sigset_t sa_mask; /* additional signals to block */ int sa_flags; /* signal options */ }; sa_handler : address of signal handler, SIG_IGN, or SIG_DFL sa_mask : additional signals to block when the signal handler is called sa_flags : option flags for the handling each signal 57

Option flags (sa_flags) SA_NOCLDSTOP : If signo is SIGCHLD, do not generate this signal when a child process stops (job control) SA_RESTART : System calls interrupted by this signal are automatically restarted SA_ONSTACK : If an alternate stack has been declared with sigaltstack(2), this signal is delivered to the process on the alternate stack SA_NOCLDWAIT : If signo is SIGCHID, this option causes the system not to create zombie processes when children terminate 58

Option flags (sa_flags) SA_NODEFER : When this signal is caught, the signal is not automatically blocked by the system while the signal-catching function executes (unreliable!) SA_RESETHAND : The disposition for this signal is reset to SIG_DFL on entry to the signal-catching function. Note that this type of operation corresponds to the earlier unreliable signals SA_SIGINFO : This option provides additional information to a signal handler 59

Implementation of signal using sigaction #include <signal.h> /* signal.c */ #include "ourhdr.h" Sigfunc * signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; /* SunOS */ #endif } else { #ifdef SA_RESTART act.sa_flags = SA_RESTART; /* SVR4, 44BSD */ #endif } if (sigaction(signo, &act, &oact) < 0) return(sig_err); return(oact.sa_handler); } 60

12. sigsetjmp() siglongjmp() #include <set jmp.h> int sigsetjmp (sigjmp_buf env, int savemask) ; Returns: 0 if called directly, nonzero if returning from a call to siglongjmp int siglongjmp (sigjmp_buf env, int val) ; When a signal is caught, the signal is added to the signal mask What happens to the signal mask for the process if we longjmp out of the signal handler? sigsetjmp and siglongjmp saves and restores the signal mask if savemask!= 0, sigsetjmp saves the current signal mask in env use these functions for nonlocal branching from a signal handler 61

#include <signal.h> /* mask.c */ #include <setjmp.h> #include <time.h> #include "ourhdr.h" static void sig_usr1(int), sig_alrm(int); static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump; int main(void) { if (signal(sigusr1, sig_usr1) == SIG_ERR) err_sys("signal(sigusr1) error"); if (signal(sigalrm, sig_alrm) == SIG_ERR) err_sys("signal(sigalrm) error"); pr_mask("starting main: "); /* {Prog prmask} */ } if (sigsetjmp(jmpbuf, 1)) { pr_mask("ending main: "); exit(0); } canjump = 1; /* now sigsetjmp() is OK */ for ( ; ; ) pause(); 62

static void sig_usr1(int signo) { time_t starttime; if (canjump == 0) return; /* unexpected signal, ignore */ pr_mask("starting sig_usr1: "); alarm(3); /* SIGALRM in 3 seconds */ } starttime = time(null); for ( ; ; ) /* busy wait for 5 seconds */ if (time(null) > starttime + 5) break; pr_mask("finishing sig_usr1: "); canjump = 0; siglongjmp(jmpbuf, 1); /* jump back to main, don't return */ static void sig_alrm(int signo) { pr_mask("in sig_alrm: "); return; } 63

mask.c: Results $ a.out & start process in background starting main: [1] 531 the job-control shell prints its process ID $ kill USR1 531 send the process SIGUSR1 starting sig_usr1: SIGUSR1 $ in sig_alrm: SIGUSR1 SIGALRM finishing sig_usr1: SIGUSR1 ending main: [1] + Done a.out & 64

13. sigsuspend() #include <signal.h> int sigsuspend (const sigset_t *sigmask) ; Returns: -1 with errno set to EINTR Reset the signal mask and put the process to sleep in a single atomic operation Signal mask of the process is set to sigmask suspended until a signal is caught or until a signal occurs that terminates the process If a signal is caught and if the signal handler returns, then sigsuspend returns and the signal mask of the process is set to its value before the call to sigsuspend 65

sigsuspend() sigset_t newmask, oldmask; sigemptyset(&newmask); sigaddset(&newmask, SIGINT); /* block SIGINT and save current signal mask */ if (sigprocmask(sig_block, &newmask, &oldmask) < 0) err_sys("sig_block error"); /* critical region of code */ /* reset signal mask which unblocks SIGINT */ if (sigprocmask(sig_setmask, &oldmask, NULL) < 0) err_sys("sig_setmask error"); pause(); /* wait for signal to occur */ /* and continue processing... */ Protect critical regions of code from interrupt signals Problem if the signal occurs between the unblocking and the pause. any occurrence of the signal in this window of time is lost we need a way to both Reset the signal mask and put the process to sleep in a single atomic operation 66

#include <signal.h> /* suspend1.c */ #include "ourhdr.h" static void sig_int(int signo) { pr_mask("\nin sig_int: "); return; } int main(void) { sigset_t newmask, oldmask, zeromask; } if (signal(sigint, sig_int) == SIG_ERR) err_sys("signal(sigint) error"); sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGINT); /* block SIGINT and save current signal mask */ if (sigprocmask(sig_block, &newmask, &oldmask) < 0) err_sys("sig_block error"); /* critical region of code */ pr_mask("in critical region: "); /* allow all signals and pause */ if (sigsuspend(&zeromask)!= -1) err_sys("sigsuspend error"); pr_mask("after return from sigsuspend: "); /* reset signal mask which unblocks SIGINT */ if (sigprocmask(sig_setmask, &oldmask, NULL) < 0) err_sys("sig_setmask error"); /* and continue processing... */ exit(0); 67

14. abort() #include <stdlib.h> void abort(void); This function never returns Causes abnormal program termination This function sends the SIGABRT signal to the process SIGABRT signal handler to perform any cleanup that it wants to do, before the process terminated POSIX.1 states that if the process does not terminate itself from this signal handler, when signal handler returns, abort terminates the process. 68

15. sleep() #include <signal.h> unsigned int sleep(unsigned int seconds) ; Returns: 0 or number of unslept seconds This function causes the calling process to be suspended until either The amount of wall clock time specified by second has elapsed (returns 0) A signal is caught by the process and the signal handler returns (returns the number of unslept seconds) The actual return may be at a time later than requested, because of other system activity 69

16. Job-Control Signals SIGCHLD : Child process has stopped or terminated SIGCONT : Continue process, if stopped SIGSTOP : Stop signal (can t be caught or ignored) SIGTSTP : Interactive stop signal SIGTTIN : Read from controlling terminal by a background process group SIGTTOUT: Write to controlling terminal by member of a background process group A program that manages the terminal needs to handle job-control signals When type Control-Z (suspend Character), SIGTSTP sent to all processes in the foreground process group. 70

#include <signal.h> /* sigtstp.c */ #include "ourhdr.h" #define BUFFSIZE 1024 static void sig_tstp(int); int main(void) { int n; char buf[buffsize]; /* only catch SIGTSTP if we're running with a job-control shell */ if (signal(sigtstp, SIG_IGN) == SIG_DFL) signal(sigtstp, sig_tstp); } while ( (n = read(stdin_fileno, buf, BUFFSIZE)) > 0) if (write(stdout_fileno, buf, n)!= n) err_sys("write error"); if (n < 0) err_sys("read error"); exit(0); 71

static void sig_tstp(int signo) /* signal handler for SIGTSTP */ { sigset_t mask; /*... move cursor to lower left corner, reset tty mode... */ /* unblock SIGTSTP, since it's blocked while we're handling it */ sigemptyset(&mask); sigaddset(&mask, SIGTSTP); sigprocmask(sig_unblock, &mask, NULL); signal(sigtstp, SIG_DFL); /* reset disposition to default */ kill(getpid(), SIGTSTP); /* and send the signal to ourself */ /* we won't return from the kill until we're continued */ signal(sigtstp, sig_tstp); /* reestablish signal handler */ } /*... reset tty mode, redraw screen... */ return; 72

고급 시스템 프로그래밍 (소프트웨어 개발 트랙) 10장. 프로세스간 통신 (Interprocess Communication) 2004. 08. 22 1

Contents 1. Pipes 2. FIFOs 3. System V IPC 4. message queues 5. semaphores 6. shared memory 2

1. Pipes In UNIX, pipes are the oldest form of IPC Limitations of Pipes: Half duplex (data flows in one direction) Can only be used between processes that have a common ancestor (Usually used between the parent and child processes) Processes cannot pass pipes and must inherit them from their parent If a process creates a pipe, all its children will inherit it 3

Pipes In a UNIX shell, the pipe symbol is: (the vertical bar) In a shell, UNIX pipes look like: $ ls -alg more where the standard output of the program at the left (i.e., the producer) becomes the standard input of the program at the right (i.e., the consumer). We can have longer pipes: $ pic paper.ms tbl eqn ditroff -ms 4

Pipes #include <unistd.h> int pipe(int filedes[2]) Returns: 0 if OK, -1 on error two file descriptors are returned through the filedes argument filedes[0] : can be used to read from the pipe, and filedes[1] : can be used to write to the pipe Anything that is written on filedes[1] may be read by filedes[0] This is of no use in a single process. However, between processes, it gives a method of communication The pipe() system call gives parent-child processes a way to communicate with each other 5

Pipes user process parent fork child fd[0] fd[1] fd[0] fd[1] fd[0] fd[1] pipe kernel pipe kernel 6

Pipes parent à child: parent closes fd[0] child closes fd[1] parent ß child: parent closes fd[1] child closes fd[0] parent child parent child fd[1] fd[0] fd[0] fd[1] pipe kernel pipe kernel 7

Pipes Read from a pipe with write end closed: returns 0 to indicate EOF Write to a pipe with read end closed: SIGPIPE generated, write() returns error (errno == EPIPE) A write of PIPE_BUF (kernel s pipe buffer size) bytes or less will not be interleaved with the writes from other processes atomic write 8

#include "ourhdr.h" /* pipe1.c */ int main(void) { int n, fd[2]; pid_t pid; char line[maxline]; if (pipe(fd) < 0) err_sys("pipe error"); if ( (pid = fork()) < 0) err_sys("fork error"); else if (pid > 0) { /* parent */ close(fd[0]); write(fd[1], "hello world\n", 12); } else { /* child */ close(fd[1]); n = read(fd[0], line, MAXLINE); write(stdout_fileno, line, n); } } exit(0); 9

popen() pclose() #include <stdio.h> FILE *popen(const char *cmdstring, const char *type); Returns: file pointer if OK, NULL on error int pclose(file *fp); Returns: termination status of cmdstring, or 1 on error handle all the dirty work the creation of a pipe, the fork of a child, closing the unused ends of the pipe, execing a shell to execute the command, and waiting for the command to terminate popen does a fork and exec to execute the cmdstring and returns a file pointer. 10

popen() pclose() fp = popen(cmdstring, "r"); parent cmdstring(child) fp stdout fp = popen(cmdstring, "w"); parent cmdstring(child) fp stdin 11

#include <sys/wait.h> /* popen1.c */ #include "ourhdr.h" int main(void) { char line[maxline]; FILE *fpin; if ( (fpin = popen("myuclc", "r")) == NULL) err_sys("popen error"); } for ( ; ; ) { fputs("prompt> ", stdout); fflush(stdout); if (fgets(line, MAXLINE, fpin) == NULL) /* read from pipe */ break; if (fputs(line, stdout) == EOF) err_sys("fputs error to pipe"); } if (pclose(fpin) == -1) err_sys("pclose error"); putchar('\n'); exit(0); 12

2. FIFOs Pipes can be used only between related processes FIFOs are "named pipes" that can be used between unrelated processes A type of file stat.st_mode == FIFO Test with S_ISFIFO macro 13

FIFOs #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); Returns: 0 if OK, -1 on error Creating FIFOs is similar to creating a file pathname : filename mode: permissons, same as for open() function Using a FIFO is similar to using a file we can open, close, read, write, unlink, etc., to the FIFO 14

FIFOs if FIFO opened without O_NONBLOCK flag an open for read-only blocks until some other process opens the FIFO for writing an open for write-only blocks until some other process opens the FIFO for reading if O_NONBLOCK is specified (nonblocking) an open for read-only returns immediately if no process has the FIFO open for writing an open for write-only returns an error (errno=enxio) if no process has the FIFO open for reading 15

FIFOs Like a pipe, if we write to a FIFO that no process has open for reading, the signal SIGPIPE is generated When the last writer for a FIFO closes the FIFO, an end of file is generated for the reader of the FIFO. PIPE_BUF specifies the maximum amount of data that can be written atomically to a FIFO writes from multiple process are not interleaved 16

Uses of FIFOs Used by shell commands to pass data from one shell pipeline to another, without creating intermediate files Used in client-server application to pass data between clients and server 17

Using FIFOs to Duplicate Output Streams tee(1) copies its standard input to both its standard output and to the file named on its command line $ mkfifo fifo1 $ prog3 < fifo1 & $ prog1 < infile tee fifo1 prog2 fifo1 prog3 infile prog1 tee prog2 18

Client-Server Communication Using a FIFO Server creates a "well-known" FIFO to communicate with clients To avoid interleaving of client data, client must write at most PIPE_BUF bytes at a time Problem: Server can't reply clients using a single "well-known" FIFO 19

Client-Server Communication Using a FIFO client write request... well-known FIFO read request server client write request 20

Client-Server Communication Using a FIFO Create a FIFO for each client such that server can reply using the client-specific FIFO e.g.: /tmp/serv1.xxxx, where XXXXX is client s process ID Impossible for server to know if a client has crashed FIFOs left in system Server must catch SIGPIPE (FIFO with 1 writer, no reader) it's possible for a client to send a request and terminate before reading the response, leaving the client-specific FIFO with one writer (the server) and no reader 21

Client-Server Communication Using a FIFO client read replies write request client-specific FIFO write replies... well-known FIFO read request server client write request read replies client-specific FIFO write replies 22

3. System V IPC Message Queues Semaphores Shared Memory Each IPC structure has a nonnegative integer identifier (large!) When creating an IPC structure, a key must be specified (type: key_t) 23

Identifiers and Keys Server creates a new IPC structure using key = IPC_PRIVATE Server stores returned identifier in some file for client to obtain Disadvantage: file I/O! 24

Identifiers and Keys Define a key in a common header Client and server agree to use that key Server creates a new IPC structure using that key Problem: key exists? (msgget, semget, shmget returns error) Solution: delete existing key, create a new one again! 25

Identifiers and Keys Client and server agree on a pathname a project ID (char between 0 ~ 255) ftok() converts the 2 values into a key Client and server use that key (cf. 2) Disadvantage: ftok à a function call! 26

Permission Structure struct ipc_perm { uid_t uid; /* owner's effective user id */ gid_t gid; /* owner's effective group id */ uid_t cuid; /* creator's effective user id */ gid_t cgid; /* creators' effective group id */ mode_t mode; /* access modes */ ulong seq; /* slot usage sequence number */ key_t key; /* key */ }; 27

System V IPC permissions Permission Message queue Semaphore Shared memory user-read MSG_R SEM_R SHM_R user-write (alter) MSG_W SEM_W SHM_W group-read MSG_R >> 3 SEM_R >> 3 SHM_R >> 3 group-write (alter) MSG_W >> 3 SEM_W >> 3 SHM_W >> 3 other-read MSG_R >> 6 SEM_R >> 6 SHM_R >> 6 other-write (alter) MSG_W >> 6 SEM_W >> 6 SHM_W >> 6 ipc_perm.mode 28

Comparison of features IPC type connectionless? reliable? flow control? records? message queue no yes yes yes yes streams no yes yes yes yes Unix stream socket no yes yes no no Unix datagram socket yes yes no yes no FIFOs no yes yes no no message types or priorities? 29

4. Message Queues Linked list of messages Stored in kernel Identified by message queue identifier msgget: create new or open existing queue nmsgsnd: add new msg to a queue msgrcv: receive msg from a queue Fetching order: based on type 30

Message Queues Each queue has a structure struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; /* ptr to first msg on queue */ struct msg *msg_last; /* ptr to last msg on queue */ ulong msg_cbytes; /* current # bytes on queue */ ulong msg_qnum; /* # msgs on queue */ ulong msg_qbytes; /* max # bytes on queue */ pid_t msg_lspid; /* pid of last msgsnd() */ pid_t msg_lrpid; /* pid of last msgrcv() */ time_t msg_stime; /* last-msgsnd() time */ time_t msg_rtime; /* last-msgrcv() time */ time_t msg_ctime; /* last-change time */ }; 31

System limits on queues Name Description Typical Value MASMAX The size in bytes of the largest message we can send. 2048 MSGMNB MSGMNI The maximum size in bytes of a particular queue (i.e., the sum of all the message on the queue) The maximum number of messages queues, systemwide 4096 MSGTQL The maximum number of messages, systemwide 40 50 32

msgget() #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int flag); Returns: msg queue ID if OK, -1 on error create new or open existing queue flag : ipc_perm.mode 33

msgctl() #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf); Returns: 0 if OK, -1 on error performs various operations on a queue 34

msgctl() cmd IPC_STAT : Fetch the msqid_ds structure for this queue, storing it in buf IPC_SET : Set the following four fields from buf: msg_perm.uid, msg_perm.gid, msg_perm.mode, msg_qbytes IPC_RMID : Remove the message queue and any data still on the queue 35

msgsnd() #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); Returns: 0 if OK, -1 on error place a message at the end of the queue ptr points to a message nbytes : length of message data 36

msgsnd() struct mymesg { long mtype; /* positive message type */ char mtext[512]; /* message data, of length nbytes */ }; A flag value of IPC_NOWAIT is similar to the nonblocking I/O flag for file I/O 37

msgrcv() #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); Returns: data size in message if OK, -1 on error retrieve a message from a queue type == 0 : the first message on the queue is returned type > 0 : the first message on the queue whose message type equals type is returned type < 0 : the first message on the queue whose message type is the lowest value less than or equal to the absolute value of type is returned 38

Comparision between Queues vs. Stream Pipes test program create IPC channel, call fork, then send 20 MB data from parent to child using 10,000 calls to msgsnd with a message length of 2,000 bytes Operation SPARC, SunOS 4.1.1 80386, SVR4 User System Clock User System Clock message queue 0.8 10.7 11.6 0.7 19.6 20.1 stream pipe 0.3 10.6 11.0 0.5 21.4 21.9 39

5. Semaphores A counter to provide access to shared data object for multiple processes To object a shared resource: 1. Test semaphore controlling resource 2. If value > 0, value--, grant use 3. If value == 0, sleep until value > 0 4. Release resource, value ++ Step 1, 2 must be an atomic operation 40

System V Semaphores A semaphore is defined as a set of one or more semaphore values Creation (semget) is independent of initialization (semctl) All IPCs exist even if no process is using them. Need worry about process terminating without releasing semaphore. 41

Semaphore structure struct semid_ds { struct ipc_perm sem_perm; struct sem *sem_base; /*ptr to first semaphore in set */ ushort sem_nsems; /* # of semaphors in set */ time_t sem_otime; /* last-semop() time */ time_t sem_ctime; /* last-change time */ }; struct sem { ushort semval; /* semaphore value, always >= 0 */ pid_t sempid; /* pid for last operation */ ushort semncnt; /* # processes awaiting semval > currval */ ushort semzcnt; /* # processes awaiting semval = 0 */ }; 42

semget() #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int flag); Returns: semaphore ID if OK, -1 on error obtain a semaphore ID nsems : sem_nsens flag : ipc_perm.mode 43

setctl() #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, union semun arg); union semun { int val; /* for SETVAL */ struct semid_ds *buf; /* for IPC_START and IPC_SET */ ushort *array; /* for GETALL and SETALL */ }; 44

setctl() - cmd IPC_STAT: fetch the semid_ds structure into arg.buf IPC_SET: set the following three fields from arg.buf: sem_perm.uid, sem_perm.gid, sem_perm.mode IPC_RMID: remove the semaphore set from the system GETVAL: return the value of semval for the member semnum SETVAL: set the value of semval for the member semnum GETPID: return the value of sempid for the member semnum GETNCNT: return the value of semncnt GETZCNT: return the value of semzcnt GETALL: fetch all the semaphore values in the set into arg.array SETALL: set all the semaphore values in the set from arg.array 45

Comparison between semaphore vs. record locking Operation SPARC, SunOS 4.1.1 80386, SVR4 User System Clock User System Clock semaphore with undo 0.9 13.9 15.0 0.5 13.1 13.7 advisory record locking 1.1 15.2 16.5 2.1 20.6 22.9 46

6. Shared Memory Fastest form of IPC: no need of data copying between client & server Must synchronize access to a shared memory segment Semaphores are used 47

Shared Memory Segment Structure struct shmid_ds { struct ipc_perm shm_perm; struct anon_map *shm_amp; /* pointer in kernel */ int shm_segsz; /* size of segment in bytes */ ushort shm_lkcnt; /* # of times segment is being locked */ pid_t shm_lpid; /* pid of last shmop() */ pid_t shm_cpid; /* pid of creator */ ulong shm_nattch; /* # of current attaches */ ulong shm_cnattch; /* used only for shminfo() */ time_t shm_atime; /* last-attach time */ time_t shm_dtime; /* last-detach time */ time_t shm_ctime; /* last-change time */ }; 48

shmget() #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, int size, int flag); Returns: shared memory ID if OK, -1 on error obtain a shared memory identifier size : is the size of the shared memory segment flag : ipc_perm.mode 49

shmctl() #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf); Returns: 0 if OK, -1 on error various shared memory operations 50

shmctl() - cmd IPC_STAT: fetch the shmid_ds structure into buf IPC_SET: set the following three fields from buf: shm_perm.uid, shm_perm.gid, shm_perm.mode IPC_RMID: remove the shared memory segment set from the system SHM_LOCK: Lock the shared memory segment in memory SHM_UNLOCK: Unlock the shared memory segment 51

shmat() #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> void *shmat (int shmid, void *addr, int flag); Returns: pointer to shared memory segment if OK, -1 on error attached a shared memory to an address flag SHM_RDONLY : the segment is read-only 52

shmat() addr==0: at the first address selected by the kernel recommended addr!=0 && SHM_RND is not specified: at the address given by addr addr!=0 && SHM_RND is specified: at the address given by (addr (addr modulus SHMLBA)) 53

shmdt() #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> void shmdt (void *addr); Returns: 0 if OK, -1 on error detach a shared memory segment 54

#include <sys/types.h> /* tshm.c */ #include <sys/ipc.h> #include <sys/shm.h> #include "ourhdr.h" #define ARRAY_SIZE 40000 #define MALLOC_SIZE 100000 #define SHM_SIZE 100000 #define SHM_MODE (SHM_R SHM_W) /* user read/write */ char array[array_size]; /* uninitialized data = bss */ int main(void) { int shmid; char *ptr, *shmptr; printf("array[] from %x to %x\n", &array[0], &array[array_size]); printf("stack around %x\n", &shmid); if ( (ptr = malloc(malloc_size)) == NULL) err_sys("malloc error"); printf("malloced from %x to %x\n", ptr, ptr+malloc_size); if ( (shmid = shmget(ipc_private, SHM_SIZE, SHM_MODE)) < 0) err_sys("shmget error"); if ( (shmptr = shmat(shmid, 0, 0)) == (void *) -1) err_sys("shmat error"); printf("shared memory attached from %x to %x\n", shmptr, shmptr+shm_size); } if (shmctl(shmid, IPC_RMID, 0) < 0) err_sys("shmctl error"); exit(0); 55

Results: $ a.out array[] from 18f48 to 22b88 stack around f7fffb2c malloced from 24c28 to 3d2c8 shared memory attached from f77d0000 to f77e86a0 56

Memory Layout high address stack command-line arguments and environment variables 0xf7fffb2c shared memory 0xf77e86a0 0xf77d0000 shared memory of 100,000 bytes heap uninitialized data (bss) initialized data 0x0003d2c8 0x00024c28 0x0003d2c8 0x00024c28 malloc of 100,000 bytes array[] of 40,000 bytes low address text 57