8 저수준 입/출력

이 장은 파일 기술자 상에서 저수준의 입/출력 명령을 행하는 함수들에 대해서 설명하고 있다. 이 함수들은 스트림과는 다른 것들을 위한 저수준의 제어 명령들을 수행하기 위한 함수로써, 7장 [I/O on Streams]안에 설명된 고 수준의 입/출력 함수들에 기본적으로 포함되어 있다.

스트림-수준 입/출력은 더 유연하고, 보통 더 편리하다; 그래서, 프로그래머들은 일반적으로 필요할 때만 기술자-수준 함수들을 사용한다. 이것에는 몇 가지 이유가 있다.


8. 1 파일 열고 닫기

이 절은 파일 기술자를 사용하여 파일들을 열고 닫는 원시적인 것들을 설명하고 있다. open 과 creat 함수들은 헤더파일 'fnctl. h'에 선언되어 있고, close는 'unistd. h'에 선언되어 있다.

함수 : int open (const char *filename, int flags[, mode_t mode])

open 함수는 filename으로 이름지어진 파일을 위한 새로운 파일 기술자를 만들고 반환한다. 처음에, 파일을 위한 파일 위치 지시자는 파일의 시작점이다. mode 인수는 오직 파일을 만들 때 사용되어지지만, 어떤 경우에도 인수로 공급하기 위한 탈이 없다. flags 인수는 파일을 어떤 방식으로 개방할 것인지를 제어한다. 이것은 비트 마스크(bit mask) 이다; 당신은 적당한 인수를 주어 OR연산을 사용해서 값을 만든다. ( C에서는 '|' 연산자를 사용해서 ) 파일 검색 모드를 정하기 위해서 flags인수는 이들 값 중에서 하나를 반드시 포함시켜야만 한다.
 

flags인수는 아래 값들과 조합을 통해서 포함할 수 있다.

O_APPEND

이것이 설정되면, 모든 쓰기 명령들은 지금 현재의 파일 위치에 개의치 않고, 파일의 끝에 데이터를 쓴다.

O_CREAT

파일이 이미 존재하지 않으면 새롭게 만들어질 것이다.

O_EXCL

만일 O_CREAT 와 O_EXCL 이 설정되면, 파일이 이미 존재할 때 open명령은 실패한다.

O_NOCTTY

만일 filename이 터미널 디바이스 이름이면, 프로세스를 위하여 제어하는 터미널이 만들어지지 않는다. 24장 [Job Control]를 참조하여, 제어하는 터미널이 되는 것이 무엇을 의미하는지에 대한 정보를 참조하라.

O_NONBLOCK

이것은 비 블록화 모드로 설정한다. 이 옵션은 보통 FIFO와( 10장 [Pipes and FIFOs]참조) 같은 특별한 파일들과 터미널과 같은 디바이스에 사용된다. 보통, 이들 파일들을 위하여 파일이 "준비"상태일 동안 블록을 개방한다. 만일 O_NONBLOCK이 설정되면, open은 즉시 반환한다. O_NONBLOCK 비트는 또한 읽기와 쓰기에 영향을 미친다: 만약 그곳에 읽기에 유용한 입력이 없거나, 출력이 쓰여질 수 없다면, 그러한 실패의 상황을 즉각적으로 그들에게 반환하는 것을 허용한다.

O_TRUNC

만일 파일이 존재하고 쓰기 모드로 개방이 되어 있다면, 0의 길이로 그것을 자른다. 이 옵션은 디렉토리나, FIFO들과 같은 특별한 파일들이 아니라 일반적인 파일들에게 유용하다.

이 심볼 상수들에 대한 더 많은 정보는 8.10절 [File Status Flags] 을 참조하라.

open으로 부터 보통 반환되는 값은 음이 아닌 정수 파일 기술자이다. 에러가 발생한 경우에는 대신에 -1의 값을 반환한다.

보통의 파일이름 문법에러에 더하여(6.2.3절 [File Name Errors] 참조), 다음은 이 함수에서 정의하고 있는 errno와 에러상황이다.

EACCES

파일은 존재하지만, flags 인수에 의한 요청으로 읽기/쓰기가 불가능하다.

EEXIST

O_CREAT 와 O_EXCL이 모두 설정되고, 그 파일은 이미 존재한다.

EINTR

open명령은 시그널 (signal) 에 의해 인터럽트 되어졌다. 21. 5절 [Interrupted Primitives] 참조

EISDIR

flags 인수는 쓰기 모드로 지정되어 있고, 파일은 디렉토리이다.

EMFILE

프로세스가 너무 많은 파일을 개방했다.

ENFILE

전제의 시스템이나, 아마도 디렉토리를 포함한 파일시스템이 어떤 순간에 더 이상 파일의 개방을 지원하지 않는다. (이 문제는 GNU시스템에서는 발생하지 않는다. )

ENOENT

같은 이름의 파일이 존재하지 않는데, O_CREAT 플래그도 선언되지 않았다.

ENOSPC

새로운 파일을 넣어야 하는 디렉토리나 파일 시스템이 확장되어질 수 없다. 왜냐하면 그곳에는 남아있는 공간이 없기 때문이다.

ENXIO

O_NONBLOCK 와 O_WRONLY가 둘다 설정되고 filename이름을 가진 파일이 FIFO이고, 어떤 프로세스도 읽기 위해서 파일을 개방하지 않았을 때.

EROFS

파일이 오직 읽기 모드인 파일 시스템 상에 존재하고 flags 인수로 O_WRONLY, O_RDWR, O_CREAT, O_TRUNC 의 어떤 것이라도 설정되면.

이 open 함수는 스트림을 만드는 fopen과 freopen함수들을 위해 원시적인 기초가 되어진다.

시대에 뒤떨어진 함수 : int creat (const char *filename, mode_t mode)

이 함수는 시대에 뒤떨어져 있다. creat(filename, mode) 이렇게 호출하는 것은 open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode) 이렇게 호출하는 것과 동등하다.

함수 : int close (int filedes)

close함수는 파일 기술자 filedes를 폐쇄한다. 파일 닫기는 다음의 결과를 갖는다. 파일 기술자가 해제되어진다. 파일에서 프로세스에 의해 소유된 어느 잠긴(lock) 레코드는 잠금해제 된다. pipe나 FIFO와 연관된 모든 파일 기술자가 닫혀질 때, unread 데이터는 버려진다. close로부터 반환되는 일반적인 값은 0이다. 실패의 경우에 -1이 반환된다.
 
다음의 errno는 이 함수에서 정의되어진 에러상황이다.

EBADF

filedes 인수가 유용한 파일 기술자가 아니다.

EINTR

close호출이 시그널에 의해 인터럽트 되었다. 21.5절 [Interrupted Primitives]를 참조. 이것은 EINTR을 어떻게 취급하는지에대한 적당한 예이다;

TEMP_FAILURE_RETRY (close (desc));

스트림을 닫기 위해서는, close로 파일 기술자를 닫으려 시도하는 대신에 fclose( 7. 4절 [Closing Stream] 참조)를 호출하라. 이것은 버퍼된 출력을 플러쉬하고, 닫혔음을 지적하기 위해 스트림 오브젝트를 갱신한다.


8. 2 기본 입력과 출력

이 절은 파일 기술자상의 기본 입력과 출력 명령을 수행하기 위한 함수들을 설명하고 있다: read, write, 그리고 lseek. 이들 함수들은 헤더파일 'unistd. h'에 선언되어 있다.

데이터 타입 : ssize__t

이 데이터 타입은 단일한 명령으로 읽혀지거나 쓰여질 수 있는 블록의 크기를 나타내기 위해 사용되어진다. 이것은 size_t 와 유사하지만 반드시 부호화된 타입이어야 한다.

함수 : ssize_t read (int filedes, void *buffer, size_t size)

read함수는 기술자 filedes의 파일로부터 size 바이트를 읽고, 그 결과를 버퍼에 저장한다. ( 이것은 문자 스트링이 필요하지 않고 그곳에는 부가된 널 종료문자가 없다. ) 반환 값은 실제로 읽은 바이트의 수이다. 이것은 size보다 적을수도 있다; 예를 들어, 만일 파일에 남겨진 바이트의 수가 적거나 즉시 유용한 바이트의 수가 적은 경우 등이 있다. 정확한 동작은 파일의 종류가 무엇인지에 따라 의존한다. size 바이트보다 덜 읽는 것은 에러가 아님을 기억하라. 0의 값은 파일의 끝을 지적한다. ( 만일 size 인수의 값이 0인 경우를 제외하고. . ) 이것은 에러로 간주하지 않는다.
 
만일 당신이 파일의 끝인 상태에서 read를 호출하면, 그것은 0을 반환하는 것 외에 아무 일도 하지 않는다. 만일 read가 적어도 한 문자를 반환한다면, 당신이 파일의 끝에 도달했는지를 알 수 있는 아무런 방법이 없다. 그러나 만일 당신이 끝에 도달해 있었다면 다음 read의 호출은 0을 반환해서 파일의 끝임을 지적해줄 것이다. 에러가 발생한 경우에, read는 -1을 반환한다.

다음의 errno는 이 함수에서 정의된 에러의 상황이다.

EAGAIN

일반적으로, 즉시 유용한 입력이 없을 때, read는 입력을 기다린다. 그러나 만일 그 파일에서 O_NONBLOCK가 설정되면( 8.10절 [File Status Flags] 참조), read는 아무런 데이터도 기다리지 않고 즉시 반환하고, 이 에러를 보고한다.
 
호환성 노트 : BSD Unix의 대부분의 버전은 이것을 위한 다른 에러코드를 사용한다: EWOULDBLOCK. GNU 라이브러리에서는, EWOULDBLOCK은 EAGAIN의 다른 이름이다. 그래서 당신이 어떤 이름을 사용해도 문제가 발생되지 않는다. 어떤 시스템들은, 특별한 문자 파일로부터 데이터의 큰 덩어리를 읽으려 할 때, 만일 커널(kernal)이 당신의 것을 담을 수 있는(to lock down the user's pages), 충분한 물리적 메모리를 얻을 수 없는 경우에 EAGAIN의 에러를 내고 실패했음을 지적한다. 디바이스가 사용자의 메모리 영역을 직접적으로 억세스 하는 것이 제한되어 있는 것은 그들은 커널내부의 분리된 버퍼를 사용하기 때문이다. 그것에 터미널들은 포함되지 않는다,

EBADF

filedes 인수에 주어진 것이 유용한 파일 기술자가 아니다.

EINTR

read가 입력을 기다리고 있는 동안 시그널에 의해 인터럽트 되어졌다. 21. 5절 [Interruped Primitives]를 참조.

EIO

많은 디바이스들, 그리고 디스크 파일들을 위하여, 이 에러는 하드웨어 에러를 지적한다. EIO는 또한 제어 중인 터미널로부터 배경 프로세스가 읽기를 시도하고, SIGTTIN의 신호가 아무런 동작도 하지 않고 보내짐에 의해 멈춘 프로세스의 일반적 동작에 대해 발생한다. 이것은 만약 신호가 블록되어지거나 무시되거나, 프로세스 그룹이 부모 프로세스를 잃어버렸다면 발생되어질 것이다. 24장 [Job Control]를 참조해서 job control에 대한 더 많은 정보를 얻고, 21장 [Signal Handling] 를 참조해서, 신호에 대한 정보를 참조하라.
 
---- 역자주 : blocked--> 블록된 이라고 이곳에서 해석하였다. 좀더 자세히 설명하자면 블록이란 보통 하나의 입/출력 단위로 표현되는 것이 일반적이지만 이곳에서 쓰인 것의 의미는 아마도(?) 유닉스처럼 다중 프로그래밍 시스템에서 하나의 프로세서가 자원을 획득하지 못하여 아무런 작업도 수행할 수 없는 상태에 처한 것. 이러한 상태를 블록된 상태라고 하는 것 같다. ( 크. . . 자신할 수 없어서 죄송. . 하지만 거의 맞을 듯~)

read 함수는 fgetc처럼 스트림으로부터 읽는 동작을 하는 모든 함수들에 기본적으로 포함되어 있다.


8. 3 기술자의 파일 위치 설정하기

당신이 fseek로 스트림의 파일 위치를 설정할 수 있는 것처럼, 당신은 lseek를 통해서 기술자의 파일 위치를 설정할 수 있다. 이것은 다음에 읽거나 쓸 명령을 위해 파일의 위치를 정한다. 파일의 위치와 그것이 어떤 의미를 갖는지에 대한 정보를 7. 15절 [File Position] 을 참조하라. 기술자로부터 현재 파일 위치의 값을 읽으려면, lseek(desc, 0, SEEK_CUR)을 사용하라.

함수 : off_t lseek (int filedes, off_t offset, int whence)

lseek 함수는 기술자 filedes 파일의 파일 위치를 변경하기 위해 사용된다. whence 인수는 fseek와 같은 방법으로 사용되어 offset을 어떻게 해석되어야 하는지를 정하고, 심볼 상수 SEEK_SET, SEEK_CUR, SEEK_END중에 하나가 될 수 있다.
 

SEEK_SET 이것은 파일의 시작점을 기준으로 문자들의 수를 셈한다.  

SEEK_CUR 현재의 파일 위치를 기준으로 문자들의 수를 셈한다. 여기서 문자들의 수(count)는 양이나 음의 값이 되어진다.  

SEEK_END 파일의 끝을 기준으로 문자들의 수를 셈한다. 음의 값은 현재의 파일안의 영역으로 파일의 위치를 정한다; 양의 값은 현재보다 앞의 영역으로 파일 위치를 정한다. 파일 위치를 앞의 영역으로 정하고, 실제로 데이터를 쓰면, 원래의 파일의 끝점에서 현재의 파일위치 사이의 빈 공간은 0으로 채워지고, 결국 파일은 확장되는 것이다.  

lseek를 통한 반환 값은 보통 파일의 시작점부터 바이트의 수를 계산한 파일의 위치이다. 당신은 현재의 파일 위치를 읽기 위해 SEEK_CUR와 함께 lseek를 사용할 수 있다. 또한 파일의 현재의 끝점을 넘어선 곳으로 파일의 위치를 설정할 수 있는데, 이것은 스스로 파일의 길이를 길게 만드는 것이 아니다; lseek는 결코 파일을 변화시키지 않는다. 그러나 그 위치에서 그후에 일어나는 출력은 파일의 크기를 확장할 것이다.

 
역자주 : 즉. . . 파일의 끝을 넘어선 곳으로 파일의 위치를 설정하고 다음에 그 위치에서 그 파일에 무언가를 쓴다면 원래의 파일의 끝점과 금방 파일에 쓴 데이터 사이의 빈 공간은 0으로 채워지고 파일은 확장된 결과에 이른다. . . 그말인 듯.

만일 파일의 위치를 변경할 수 없거나, 그 명령이 유용하지 못한 상황이라면, lseek는 -1의 값을 반환한다.

다음의 errno는 이 함수에서 정의한 에러의 상황이다.

lseek 함수는 파일 기술자 대신에 스트림에서 명령을 수행하는 fseek, ftell과 rewind함수를 위해 기본적으로 포함되어져 있다.

당신이 만일 여러 번 같은 파일을 개방하거나, dup을 통해서 기술자를 중복시킨다면 같은 파일에 다중의 기술자를 가질 수 있다. 분리된 open의 호출을 통해서 얻은 기술자는 독립적인 파일 위치를 가진다; 한 기술자에 lseek를 사용하는 것은 다른 것에 아무런 영향이 없다.

{
int d1, d2;
char buf[4];
d1 = open ("foo", O_RDONLY);
d2 = open ("foo", O_RDONLY);
lseek (d1, 1024, SEEK_SET);
read (d2, buf, 4);
}

이것은 'foo'파일의 처음 네 개의 문자들을 읽을 것이다. ( 실제의 프로그램에서는 필요한 에러 검색 코드가 여기서는 간결함을 이유로 생략되어져 있다. )

비교해서, dup로 만들어진 중복된 기술자는 보통의 파일 위치도 중복해서 사용한다. 중복된 파일 기술자중 하나가 읽거나 쓰는 동작이 포함된, 파일 위치를 변경하는 어느 동작을 하면, 그들 모두에게 영향이 미친다.

{
int d1, d2, d3;
char buf1[4], buf2[4];
d1 = open ("foo", O_RDONLY);
d2 = dup (d1);
d3 = dup (d2);
lseek (d3, 1024, SEEK_SET);
read (d1, buf1, 4);
read (d2, buf2, 4);
}

이것은 파일 'foo'의 1024번째 문자를 시작으로 해서 네 개의 문자를 읽고, 1028번째 문자를 시작으로 다시 네 개의 문자를 읽는다.

데이터 타입 : off__t

이것은 파일 위치를 나타내기 위한 수치적 데이터 타입니다. GNU시스템에서는 이것은 fpos_t나 long int와 동일하다.

다음 'SEEK_. . . '를 위한 세 개는 오래된 버전의 BSD 시스템들과의 호환을 목적으로 존재한다. 그들은 두 개의 다른 헤더파일에 정의되어 있다: 'fnctl. h'와 'sys'file. h'

L_SET : SEEK_SET의 다른 이름.

L_INCR : SEEK_CUR의 다른 이름.

L_XTND : SEEK_END의 다른 이름.


8. 4 기술자와 스트림

open을 통해 주어진 파일 기술자에, 당신은 fdopen함수를 가지고 그 기술자를 위한 스트림을 만들 수 있다. 당신은 fileno함수를 가지고 현존하는 스트림의 파일 기술자를 얻을 수 있다. 이 함수들은 헤더파일 'stdio. h'에 선언되어 있다.

함수 : FILE * fdopen (int filedes, const char *opentype)

fdopen함수는 파일 기술자 filedes를 위한 새로운 스트림을 반환한다. opentype 인수는 'b'옵션이 허용되지 않는 것을 제외하고는, fopen함수 와 같은 방법으로 사용된다( 7. 3절 [Opening Streams] 참조). 'b' 옵션이 허용되지 않는 것은 GNU 시스템이 텍스트와 바이너리 파일의 차이를 구분하지 않기 때문이다.
 
또한 "w"와 "w+"가 파일을 잘라내는 효과를 내지못한다; 그것은 그 옵션들이 파일을 개방할 때만 오직 영향을 미치는데, 이 경우에는 파일들이 이미 개방되었던 것이기 때문이다. 당신은 opentype인수를 개방한 파일 기술자의 실제 모드와 일치시켜야만 한다.
 
반환 값은 새로운 스트림이다. 만일 스트림이 만들어지지 못한다면, ( 예를 들어, 파일 기술자에 의해 정해진 파일의 모드가 opentype 인수에서 지정한 억세스를 허용하지 않을 때 ), 널 포인터를 반환한다. fdopen함수를 사용하는 예는 10.1절 [Creating a Pipe] 를 참조.

함수 : int fileno (FILE *stream)

이 함수는 스트림 stream과 연관된 파일 기술자를 반환한다. 만일 에러가 검출되거나( 예를 들어, 만일 그 스트림이 유용하지 않다면 ), 만일 스트림이 파일에 입/출력을 할 수 없다면, fileno는 -1을 반환한다. 표준 스트림 stdin, stdout 과 stderr에 속한 파일 기술자를 위한 심볼 상수가 'unistd. h'에 정의되어 있다. ; 7. 2절 [Standare Streams] 참조.

STDIN_FILENO

이 매크로는 표준 입력을 위한 파일 기술자로 0의 값을 가진다.

STDOUT_FILENO

이 매크로는 표준 출력을 위한 파일 기술자로 1의 값을 가진다.

STDERR_FILENO

이 매크로는 표준 에러출력을 위한 파일 기술자로 2의 값을 가진다.


8. 5 스트림과 기술자 혼용의 위험

당신은 동일한 파일과 연결된 다중의 파일 기술자와 스트림을 가질 수 있다( 짧게 스트림과 기술자를 "채널" 이라 부르자), 그러나 당신은 채널들 사이의 혼란을 피하도록 주의해야만 한다. 고려해야 하는 두 가지 경우가 있다: 단일한 파일 위치를 갖고 있는 연결된 채널과, 그들 자신의 파일 위치를 갖고 있는 독립적 채널

모든 억세스가 입력을 위한것임을 제외하고는, 어느 주어진 파일에서 실제의 데이터를 참조하기 위해서는 당신의 프로그램 안에 오직 하나의 채널을 사용하는 것이 좋다. 예를 들어 만일 당신이 파이프를 개방하여, ( 당신이 파일 기술자 수준에서 할 수 있는 어떤 것 ), 기술자를 가지고 입/출력을 하거나, 또는 fdopen으로 기술자로부터 스트림을 구성하고 스트림에 모든 입/출력을 한다면.

8. 5. 1 연결된 채널들

단 한 번 개방된 것으로부터 온 채널들은 동일한 파일 위치를 점유한다; 우리는 그들을 연결된 채널이라 부른다. 연결된 채널은, 당신이 fdopen을 사용하여 기술자로부터 스트림을 만들 때, fileno를 사용해서 스트림으로부터 기술자를 얻을 때, 그리고 dup나 dup2를 사용해서 기술자를 복제할 때 결과로 얻게된다. 터미널과 파이프처럼 랜덤 억세스(임의 접근)가 제공되지 않는 파일들에서 모든 채널들은 실제로 연결되어 있다. 임의 접근이 가능한 파일들에서는 연결된-형태의 출력 스트림들 모두가 서로 연결되어 있다.

만일 당신이 입/출력을 위해 한 스트림을 사용해 오면서, 그것과 연결된 다른 채널(스트림이나 기술자)을 사용해서 입/출력하기 원한다면, 당신은 첫째로 당신이 사용하고 있던 그 스트림을 정리해야만 한다. 8. 5. 3절 [Cleaning Streams] 참조.

프로세스가 종료하거나, 프로세스에 새로운 프로그램을 실행하면 그 프로세스안에 있던 모든 스트림들은 파괴된다. 만일 이 스트림과 연결된 기술자가 다른 프로세스안에 살아남아 있다면, 그들 파일의 위치는 정의되지 않은 상태로 되고 만다. 이것을 방지하기 위해서, 당신은 그들을 파괴하기 전에 그 스트림들을 정리해야만 한다.

8. 5. 2 독립적 채널들

분리되어 탐색 가능한 채널들( 스트림이나 기술자)을 개방할 때, 각 채널들은 자신의 파일 위치를 갖는다. 이들을 독립적 채널이라고 부른다.

시스템은 독립적으로 각 채널을 취급한다. 거의 대부분, 이것은 꽤 예측가능하고, 자연스럽다(특히 입력의 경우): 각 채널은 파일안의 자신의 위치에서 순차적으로 읽거나, 쓸 수 있다. 그렇지만, 만일 어떤 채널들이 스트림이라면, 당신은 이런 것들을 주의해야한다.

당신은 파일의 동일한 부분으로부터 읽거나 쓰는 어떤 것을 하기 전에 사용할 출력 스트림을 깨끗이 해야한다.

독립적인 채널을 사용해서 변경되었을 데이터를 읽기 전에는 입력 스트림을 깨끗이 해야한다. 그렇지 않으면 당신은 스트림의 버퍼에 존재하고 있는 쓸모 없는 데이터(obsolete data)를 읽게 될 것이다.

만일 당신이 파일의 끝에 있는 채널에 출력하려 한다면, 이 채널은 출력을 하기 전에 어느 곳인지에 있을 다른 독립적 채널들을 확실히 무시할 것이다. 만일 당신이 끝에 출력하기 원한다면, 당신은 첫째로, 파일의 끝으로 그들 파일 위치를 설정해야 한다. ( 이것은 연결된-형태의 기술자나 스트림에서는 필요치 않다; 그들은 항상 파일의 끝에서 출력한다. ) 파일의 끝 위치를 정확하게 만들려면, 당신은 만일 당신이 사용하고 있는 것이 스트림이라면 출력 채널을 깨끗이 해야한다. (이것은 심지어 다음에 연결된-형태의 채널을 사용하려 계획할 때도 필요하다. )

파일이 랜덤 억세스를 지원하지 않는다면 파일에서 분리된 파일 위치를 갖고있는 두 개의 채널을 갖는 것은 불가능하다. 그러므로, 파일처럼 읽거나 쓰기 위한 채널들은 항상 연결되어있고, 결코 독립적이지 않다. 연결된-형태의 채널들은 항상 연결되어 있다. 이와 같은 채널들을 위해 연결된 채널들을 위한 규칙을 따라야한다; 8.5.1 [Linked Channels] 참조.

8. 5. 3 채널 깨끗이 하기

GNU 시스템에서, 당신은 fclean을 사용해서 어느 스트림을 정리할 수 있다.

함수 : int fclean (FILE *stream)

버퍼를 비우기 위해 스트림 stream을 깨끗이 한다. 만일 스트림이 출력을 하고 있다면, 강제로 그것을 출력한다. 만일 스트림이 입력을 하고 있다면, 버퍼에 있는 데이터는 그것을 다시 읽도록 조정하는 시스템에 되돌려준다. 다른 시스템들에서는 대부분의 경우 스트림을 깨끗이 하기 위해서 fflush를 사용할 수 있다.
 
만일 당신이 이미 그 스트림이 깨끗하다란 것을 안다면 fclean이나 fflush를 건너뛸 수 있다. 스트림은 버퍼가 비어있을때는 깨끗하다. 예를 들어 비버퍼화된 스트림은 항상 깨끗하다. 그리고, 파일의 끝에서 입력 스트림은 깨끗하다. 라인 버퍼화된 스트림은 마지막 문자출력이 새줄 문자였을 때 깨끗하다.
 
스트림을 깨끗하게 하는 것이 대부분의 시스템에서 불가능한 한가지 경우가 있다. 이것은 스트림이 랜덤 억세스가 불가능한 파일로부터 입력을 하고 있을 때이다. 그와 같은 스트림은 이미 읽은 검색 데이터를 되돌려줄 아무런 방법이 없다. 입력 스트림이 랜덤 억세스 파일로부터 입력할 때, fflush로 스트림을 깨끗이 하지 않으면 파일 포인터가 예측 불가능한 영역에 남겨진고 만다. 당신은 입/출력을 하기 전에 먼저 파일 포인터를 설정하라. GNU 시스템에서는 이 문제들의 양쪽을 피하게 한다.
 
오직 출력만 가능한 스트림을 닫을 때도 fflush를 사용하는데, 이것은 출력 스트림을 깨끗이 하는데 유용한 방법이다. GNU 시스템에서는, 입력 스트림을 닫을 때 fclean을 사용한다.
 
당신은 터미널 모드를 설정하는 것과 같은 제어 명령을 위해 기술자를 사용하기 전에는 스트림을 깨끗이 할 필요가 없다; 이들 명령들은 파일 위치에 영향을 받지 않고, 그것에 영향을 미치지 않는다. 당신은 이 명령들은 위해서는 어느 기술자라도 사용할 수 있고, 모든 채널들은 동시에 영향을 받는다. 그렇지만, 스트림에 의해 새로운 터미널 모드로 설정되어질 스트림이 여전히 "출력"을 갖고 버퍼화된 상태라면 플러쉬 되어진다. "앞으로"의 출력을 확실히 하기 위해 터미널모드의 설정이 동시에 영향을 받도록 되어졌고, 그 모드를 설정하기 전에 터미널의 모든 출력 스트림들을 플러쉬 한다. 12. 4절[Terminal Modes] 참조.


8. 6 입력이나 출력을 위한 기다림

때때로 프로그램들은 입력이 도착할 때마다 다중 입력 채널들로부터 입력을 받아들일 필요가 있다. 예를 들어, 어떤 워크스테이션들은 보통의 비동기적 직렬 인터페이스를 경유하여 연결된, 디지타이징 태블릿 (역자주: 컴퓨터에 좌표 위치를 입력하는 장치), 함수 버튼 박스 (역자주: 버튼들이 모여있는 박스로, 버튼 하나가 한가지 함수를 수행하는 기능을 하는 것. . 일걸요?), 혹은 대화박스(역자주 : 대화상자)와 같은 디바이스들을 갖고 있을 것이다; 좋은 유저 인터페이스의 스타일은 어느 디바이스 상에서 들어온 입력에 대한 즉각적인 대응이 필요하다. 다른 예로써, 파이프나 소켓들을 경유한 여러 가지 다른 프로세스들에게 서버로서 동작하는 프로그램이 있다.

당신은 이러한 목적으로는 read를 사용할 수 없는데, 왜냐하면 프로그램은 어떤 특정한 파일 기술자 상에서 유용한 입력이 있을 때까지 블록 되어지기 때문이다. (역자주 : 여기서 블록의 개념은 입력을 얻지 못해서 아무런 작업도 수행할 수 없는 상태에 처한 것을 말함. ) 다른 채널들에서도 입력을 발생하지 않을 것이다. 당신은 비블럭화 모드로 설정하고 차례로 각 기술자들을 돌아볼 수 있지만, 이것은 매우 비능률적이다.

여기에 대한 훌륭한 해결책으로는 select함수를 사용하는 것이다. 이것은 설정된 기술자들이 입력이나 출력 준비상태가 될 때까지, 또는 설정된 시간이 끝날 때까지 등 이것 중 어떤 것이라도 먼저 될 때까지 프로그램을 블록 시킨다. 이 도구는 헤더파일 'sys/types. h'에 있다.

select 함수를 위해서 파일 기술자를 설정하는 것은 fd_set 오브젝트를 통해서 이루어진다. 다음은 이들 오브젝트들을 다루기 위한 데이터 타입과 매크로 들을 설명한다.

데이터타입 : fd__set

fd_set 데이터 타입은 select함수를 위해서 설정하는 파일 기술자를 나타낸다. 이것은 실제로 비트 배열이다.

매크로 : int FD__SETSIZE

이 매크로의 값은 fd_set 오브젝트가 파일 기술자에 대한 정보를 가질 수 있는 최대의 수이다. 고정된 최대 수를 가진 시스템들에서는, FD_SETSIZE는 적어도 그 개수이다. GNU 시스템을 포함한 다른 시스템에서는 개방하는 기술자의 수에 대한 절대적인 제한이 없지만, 이 매크로는 fd_set의 비트에 개방하는 기술자의 개수를 제어하기 위한 상수 값을 가지고 있다.

매크로 : void FD__ZERO (fd_set *set)

이 매크로는 파일 기술자에 대한 정보를 가지고 있는 set을 빈 공간이 되도록 초기화한다.

매크로 : void FD__SET (int filedes, fd_set *set)

이 매크로는 파일 기술자 정보를 갖고 있는 set에 기술자 filedes를 더한다.

매크로 : void FD__CLR (int filedes, fd_set *set)

이 매크로는 파일 기술자 정보를 갖고 있는 set에서 기술자 filedes를 제거한다.

매크로 : int FD__ISSET (int filedes, fo_set *set)

이 매크로는 만일 filedes가 파일 기술자에 대한 정보를 갖고 있는 set의 멤버라면 0이 아닌 값을 반환하고, 그렇지 않으면 0을 반환한다.

다음은 select 함수에 대한 설명이다.

함수 : int select (int nfds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout)

select함수는 정해진 파일 기술자들 중에서 어느 것이 어떤 동작을 하거나, 설정된 시간이 끝날 때까지 호출된 프로세스를 블록 시킨다.
 
read_fds 인수에 의해 정해진 파일 기술자들은 그들이 읽기 위한 준비가 되어있는지 체크되어진다. write_fds 인수로 정해진 파일 기술자들은 그들이 쓰기 위한 준비가 되었는지 체크되어진다. 그리고 except_fds 로 정해진 파일 기술자들은 예외적 상황을 위해 체크되어진다. 만일 당신이 위에 설명된 상황 중에 체크하지 않아도 되는 것에 대한 인수는 널 포인터로 주면 된다. "예외적 상황"은 이미 실행되어지고, 파일 기술자의 상황을 구성하지 않는 잘못된 시스템호출이 있을 때 즉시 보고되어지는 errors_errors를 의미하지 않는다. 오히려, 그들은 소켓에 긴급한 메시지가 나타난 그런 상황을 말한다. ( 11장 [Socket] 에서 긴급한 메시지들에 대한 정보를 참조 )

select 함수는 오직 첫 번째 nfds 파일 기술자를 체크하는데, 보통 이 인수의 값으로 FD_SETSIZE가 주어진다. timeout는 기다리기 위한 최대의 시간을 정한다. 만일 당신이 이 인수로 널 포인터를 준다면, 하나의 파일 기술자라도 준비될 때까지 무기한 블록 시킴을 의미한다. 그렇지 않다면, 당신은 struct timeval 형식 안에 시간의 값을 주어야한다; 17. 2. 2. 절 [High Resolution Calendar] 참조. 시간의 값으로 0을 정하면(모두 0의 값인 struct timeval) 이미 준비된 기술자를 찾거나, 만일 준비된 기술자가 없다면 기다리지 않음을 의미한다.

보통 select 함수로부터의 반환 값은 set안에 지정된 기술자 중에서 준비된 기술자의 총수이다. 인수 set들의 각각은 연관된 명령을 위해서 준비된 기술자에 대한 정보로 갱신된다. 만일 입력을 가진 특별한 기술자 desc를 보려면, select 함수가 반환한 후에 FD_ISSET (desc, read`fds)를 사용하라. 만일 시간 설정 값이 끝났다면 select는 0의 값을 반환한다. 어느 신호는 select가 기다리지 않고 즉시 반환해버리는 원인이 된다. 그래서 만일 당신의 프로그램이 신호들을 사용한다면, 당신은 select가 정해진 완전한 시간동안 기다림을 유지한다고 신뢰할 수 없다. 만일 당신이 어느 정해진 시간동안 확실히 기다림을 원한다면, 당신은 반드시 EINTR을 체크하고 현재의 시간에 기초한 새로이 설정된 시간(timeout)으로 select 을 다시 호출하라. 아래에 있는 예를 보라. 21.5절 [Interrupted Primitives] 참조. 만일 에러가 발생하면, select는 -1을 반환하고, 파일 기술자 셋(sets) 인수를 갱신하지 않는다.

다음 errno는 이 함수에서 정의한 에러 상황이다.

 
이식성 노트 : select 함수는 BSD Unix 용이다.

아래의 예는 파일 기술자로부터 읽기 위해 timeout시간을 설정하고 어떻게 select를 사용하는지에 대한 것을 보여주고 있다. input_timeout함수는 파일 기술자 상에 유용한 입력이 있을 때까지, 또는 정해진 timeout시간이 끝날 때까지 호출한 프로세스를 블록 시킨다.

 
#include <stdio. h>
#include <unistd. h>
#include <sys/types. h>
#include <sys/time. h>
 
int input_timeout (int filedes, unsigned int seconds)
{
fd_set set;
struct timeval timeout;
/* 파일 기술자 셋을 초기화 */ <--- 일단 파일 기술자 셋을 공백으로 초기화 한 다음 filedes기술자를 더했네요. */
FD_ZERO (&set);
FD_SET (filedes, &set);
 
/* timeout 데이터 구조를 초기화 */
timeout. tv_sec = seconds;
timeout. tv_usec = 0;
 
/* select는 정해진 시간이 끝나면 0을 반환하고, 입력이 있으면 1을 반환하고, 에러가 발생하면 -1을 반환한다. */
return TEMP_FAILURE_RETRY (select (FD_SETSIZE, &set, NULL, NULL,
&timeout));
--> 세 번째, 네 번째 인수가 널이므로 읽기 위한 기술자에서 정해진 시간동안에 입력이 있는지 체크하겠네요.
}
 
int main (void)
{
fprintf (stderr, "select returned %d. \n", input_timeout (STDIN_FILENO, 5));
return 0;
}

다중 소켓으로부터 다중 입력을 위해서는 select를 어떻게 사용하는지에 대한 예제는 11. 8. 7절 [Server Example] 참조.


8. 7 파일에서의 제어 명령들

이 절은 파일 기술자 상에서, 파일 기술자의 상황을 나타내고 있는 플래그를 세팅하거나, 레코드의 락(locks)을 다루는 것과 같은 다양한 명령들을 어떻게 수행할 수 있는지를 설명하고 있다. 이들 명령들은 모두 fcntl함수를 통해 이루어진다.

fcntl함수의 두 번째 인수는 수행하려는 명령으로 지정된 명령문이다. 그 함수와 매크로와 함께 사용되는 다양한 플래그들은 헤더파일 'fcntl. h' 에 선언되어 있다. ( 이들 플래그들의 대부분은 open함수에서 사용되어 지던 것이다; 8. 1절 [Opening and Closing Files] 참조)

함수 : int fcntl (int filedes, int command, . . . )

fcntl 함수는 파일 기술자 filedes 상에서 command에 의해 정해진 명령을 수행한다. 어떤 command들은 공급해야할 부가적 인수를 필요로 한다. 이곳에 각각의 명령들에 따른 부가적 인수들과 반환 값과 에러 상황 각각에 대한 상세한 설명이 있다.

간단히, 아래는 다양한 명령들에 대한 리스트이다.

F_DUPFD

파일 기술자를 복제하라(동일한 개방된 파일을 가리키는 다른 파일 기술자를 반환). 8. 8절 [Duplicating Descriptors] 참조.

F_GETFD

파일기술자와 연관된 플래그들을 얻어라. 8. 9절 [Descriptor Flags] 참조.

F_SETFD

파일 기술자와 연관된 플래그들을 설정하라. 8. 9절 [Descriptor Flags] 참조.

F_GETFL

개방한 파일과 연관된 플래그들을 얻어라. 8. 10절 [File Status Flags4] 참조.

F_SETFL

개방한 파일과 연관된 플래그들을 설정하라. 8. 10절 [File Status Flags] 참조.

F_GETLK

파일 록을 얻어라. 8.11절 [File Locks] 참조.

F_SETLK

파일 록을 설정하거나, 지워라. 8. 11절 [File Locks] 참조.

F_SETLKW

F_SETLK와 같지만, 완전하기까지 기다린다. 8. 11절 [File Locks] 참조.

F_GETOWN

SIGIO 신호들을 받기 위해서 프로세스나 프로세스 그룹 아이디를 얻어라. 8. 12절 [Interrupt Input] 참조.

F_SETOWN

SIGIO 신호들을 받기 위해서 프로세스나 프로세스 그룹 아이디를 설정하라. 8. 12절 [Interrupt Input] 참조.


8. 8 기술자 복제하기

당신은 파일 기술자를 복제하거나, 동일한 파일 기술자를 참조하기 위한 다른 파일기술자를 할당할 수 있다. 복제한 기술자는 하나의 파일 위치를 점유하고, 하나의 파일 상황 플래그들은 갖는다. ( 8. 10절 [File Status Flags] 참조. ), 그러나, 파일 기술자 플래그는 자신의 것을 소유한다. ( 8. 9절 [Descriptor Flags] 참조)

복제한 파일 기술자의 주된 사용처는 입력이나 출력의 리다이렉션(redirection)을 하기 위한 것이다. 그것은 특정한 파일 기술자에 해당하는 파일이나 파이프를 변화시킨다.

당신은 F_DUPFD 명령으로 fcntl 함수를 사용해서 이 동작을 수행할 수 있지만, 복제 기술자를 만들기 위해서는 dup와 dup2라는 편리한 함수가 있다. fcntl 함수와 플래그들은 'fcntl. h'에 선언되어 있고, dup와 dup2는 헤더파일 'unistd. h'에 있다.

함수 : int dup (int old)

이 함수는 이미 있는 기술자 old를 첫 번째 유용한 파일기술자 번호로 복제한다(첫 번째 번호는 현재 개방되지 않았다. ) 이것은 fcntl(old. F_DUPFD, 0)과 동일하다.

함수 : int dup2 (int old, int new)

이 함수는 기술자 번호 new로 old 기술자를 카피한다. 만일 old기술자가 유용하지 못한 기술자라면, dup2는 아무 것도 하지 않는다; new를 폐쇠하지 않는다. 그렇지 않다면, new가 old로 부터 복제되기 전에 이미 전에 존재하고 있던 기술자라면, new를 일단 먼저 폐쇄시켜야 한다. 만일 old와 new가 서로 다른 번호이고, old가 유용한 기술자라면, dup2는 다음과 동일하다:
 
close (new);
fcntl (old, F_DUPFD, new)
 
그렇지만, dup2는 이것을 자동적으로 한다. new가 폐쇄되어 있고, 아직 old가 복제되지 않았을 때, 호출된 dup2의 도중에는 아무런 걸림돌이 없다.

매크로 : int F__DUPFD

이 매크로는 첫 번째 인수로서 주어진 파일 기술자를 카피하기 위해 , fcntl에게 주는 명령어 인수로서 사용된다. 이 경우 호출 형식은 다음과 같다:

fcntl (old, F_DUPFD, next_filedes)

next_filedes 인수는 int 형이고, 반환될 다음에 유용한 기술자를 정하는데 사용되고, 그 기술자는 이 값보다 하나 크거나, 같은 것이다. 이 명령을 사용한 fcntl로 부터의 반환 값은 보통 new 파일 기술자의 값이다. -1의 반환 값은 에러가 발생했음을 나타낸다.
 
다음의 errono는 이 명령을 위하여 정의된 에러 상황이다.

EBADF

old 인수가 유용하지 않다.

EINVAL

next_filedes 인수가 유용하지 않다.

EMFILE

당신의 프로그램이 이미 최대한 사용하고 있기 때문에 더 이상 유용한 파일 기술자가 없다.

ENFILE

dup2가 파일의 개방으로 new를 만드는 것이 아니기 때문에 dup2에서는 발생할 수 있는 에러코드가 아니다; 복제한 기술자는 ENFILE이 지적한 제한을 미리 체크하지 않는다. EMFILE는 한 프로세스에서 사용중인 별개의 기술자 번호에 대한 제한을 참조하기 때문에 가능하다.

다음의 예는 리다이렉션을 하기 위해 dup2를 어떻게 사용하는지에 대한 것이다. 특별히, 표준 스트림(stdin 처럼)의 리다이렉션은 자식 프로세스안에서 새로운 프로그램을 수행하기 위해서 수행 함수들의 ( 23. 5절 [Executing a File] 참조 ) 수행 함수들중 하나를 호출하기 전에 쉘이나 쉘과 같은 프로그램에 의해 행해진다. 새로운 프로그램이 수행되어질 때, 그 프로그램의 메인 함수가 수행되기 전에 연관된 파일 기술자들을 가리키는 표준 스트림을 초기화한다. 그래서 파일로 표준 입력을 리다이렉트 하는 것은 쉘이 다음과 같은 어떤 것을 하는 것이다.

pid = fork ();
if (pid == 0)
{
char *filename;
char *program;
int file;
. . .
file = TEMP_FAILURE_RETRY (open (filename, O_RDONLY));
dup2 (file, STDIN_FILENO);
TEMP_FAILURE_RETRY (close (file));
execv (program, NULL);
}

24. 6. 3절 [Launching Jobs] 에 프로세스의 파이프라인에 어떻게 리다이렉션을 하는지에 대한 상세한 많은 예제가 있다.


8. 9 파일 기술자 플래그

파일 기술자 플래그는 파일 기술자의 잡다한 속성들이다. 이 플래그들은 특별한 파일 기술자와 연관되어 있기 때문에, 만일 당신이 한 번 개방한 파일로부터 복제해서 파일 기술자를 만들었다면, 각 기술자는 자신의 플래그 셋(set)을 갖는다.

현재 단지 하나의 파일 기술자 플래그가 있다: FD_CLOEXEC, 이것은 만일 당신이 exec. . . 함수(23. 5절 [Executing a File])들 중 어느 하나를 사용한다면 기술자가 닫히게 되는 결과를 가져온다.

이 절에서 설명하고 있는 것은 헤더파일 'fcntl. h'에 정의되어 있다.

매크로 : int F__GETFD

이 매크로는 fcntl의 command 인수로 사용되어, filedes 인수와 연관된 파일 기술자 플래그를 반환하도록 한다. 이 명령을 사용한 fcntl의 반환 값은 보통 음이 아닌 수로써, 각각의 플래그( 현재 그곳에서 사용하기 위한 오직 하나의 플래그를 제외하고)들을 비트별 OR 연산을 통해 해석 가능하도록 추출된 값이다. 에러가 발생한 경우에, fcntl은 -1을 반환한다.
다음 errno는 이 명령에서 정의한 에러의 상황이다.

EBADF

filedes인수가 유용하지 않다.

매크로 : int F__SETFD

이 매크로는 filedes 인수와 연관된 파일 기술자 플래그들을 설정함을 지시하기 위해 fcntl함수의 command인수로 사용되어진다. 이것은 새로운 플래그들을 정하기 위해서 세 번째 int 형 인수가 필요하므로, 다음과 같은 형식으로 호출한다:
 
fcntl (filedes, F_SETFD, new_flags)
이 명령과 함께 사용된 fcntl로 부터의 반환 값은 보통 정의되어있지 않지만, 에러가 발생한 경우에는 -1을 반환하여 에러임을 지적한다. 플래그들과 에러발생 상황은 F_GETFD 명령과 같다. 다음 매크로는 fcntl함수와 함께 파일 기술자 플래그로써 사용하기 위해 정의되었다. 이 값은 비트 마스크 값으로써 사용할 수 있는 정수 상수 값이다.

매크로 : int FD__CLOEXEC

이 플래그는 exec 함수가 불리워질 때 닫혀질 파일 기술자를 정한다; 23. 5절 [Executing a File] 를 참조하라. 파일 기술자가 할당되었을 때( open이나 dup를 통해서), 이 비트가 새로운 파일 기술자 상에서 클리어 되면 다른 새로운 프로그램에서 그 기술자를 사용할 수 있다.
 

만일 당신이 파일 기술자 플래그들을 갱신하기 원한다면, 당신은 F_GETFD를 통해서 현재의 플래그들은 얻고, 그 값을 갱신하라. 이곳에서 보여준 플래그들이 각각의 플래그 하나만 사용된다고 생각하지 말라; 당신의 프로그램은 지금으로부터 수년간 실행되어질 것이고 그러면 더 많은 플래그들이 존재하게 될 것이다. 예를 들어, 이곳에 있는 함수는 다른 플래그들을 변경하지 않고 FD_CLOEXEC 플래그만 클리어 하거나 설정하는 함수이다.

/* 만일 value가 0이 아닌 값이면 desc의 FD_CLOEXEC 플래그를 설정하고, value가 0이면 클리어 하라. 성공하면 0을, 실패하면 -1을 반환한다. */

int set_cloexec_flag (int desc, int value)
{
int oldflags = fcntl (desc, F_GETFD, 0);
/* 만일 플래그를 읽는 것이 실패하면, 에러임을 지적하라. */
if (oldflags < 0)
return oldflags; /* 우리가 원하는 값으로 플래그를 설정하라. */
if (value != 0)
oldflags |= FD_CLOEXEC;
else
oldflags &= ~FD_CLOEXEC; /* 기술자에 갱신된 플래그 워드를 저장하라 */
return fcntl (desc, F_SETFD, oldflags);
}


8. 10 파일 상황 플래그들

파일 상황 플래그들은 개방한 파일의 속성을 정하기 위해 사용되어진다. 8. 9절 [Descriptor Flags] 에서 논의된 파일 기술자 플래그들과 달리, 파일 상황 플래그들은 개방된 파일로부터 복제된 파일 기술자에 의해 공유되어진다.

파일 상황 플래그들은 open 함수의 flags인수를 통해 초기화되어진다. 플래그중 어떤 것은 개방할 때 의미가 있고 계속해서 기억되지 않는다; 나머지 대대 분은 그후에 변화되지 않아서, 당신은 파일 상황 플래그들을 시험함으로 인해서 그들의 값을 읽을 수 있다. 몇몇 파일 상황 플래그들은 fcntl 함수를 사용해서 변화시킬 수 있다. 그들에 O_APPEND 와 O_NONBLOCK 가 포함된다. 이 절에 있는 심볼들은 헤더파일 'fcntl. h'에 있다.

매크로 : int F__GETFL

이 매크로는 기술자 filedes로 개방한 파일의 파일 상황 플래그들은 읽기 위해 fcntl함수의 command 인수로써 사용된다. 이 명령과 함께 사용된 fcntl을 통한 반환 값은 보통 각각의 플래그들을 비트별 OR연산을 통해서 해석 가능한 값으로 추출된 음이 아닌 값이다. 그 플래그들은 open(8. 1절 [Opening and Closing Files] 참조) 함수의 flags 인수처럼 부호화 되었지만, 오직 파일 검색 모드와 O_APPEND 와 O_NONBLOCK 플래그들만 이곳에서 의미가 있다. 파일 억세스 모드들이 단일한-비트 값들이 아니기 때문에, 당신은 그들과 비교하기 위해서 O_ACCMODE을 사용하여 반환된 플래그들에서 다른 비트들을 마스크 시켜서 원하는 비트를 추출할 수 있다. 에러가 발생하면
 
fcntl은 -1을 반환한다. 다음의 errno는 이 명령에서 정의한 에러상황이다.

매크로 : EBADF

filedes 인수가 부적합하다.

매크로 : int F__SETFL

이 매크로는 filedes 인수와 연관된 개방파일의 파일 상황 플래그를 설정하기 위해서, fcntl 에서 command 인수로써 사용되어진다. 이 명령은 새로운 플래그를 설정하기 위해서 세 번째에 int 형 인수를 필요로 한다. 그래서 이것은 다음처럼 호출한다:
fcntl (filedes, F_SETFL, new_flags)
당신은 이 방법으로 파일 엑세스 모드를 변경할 수 있다; 읽거나 쓰기 위하여 개방되어진 파일 기술자든지 간에. 당신은 오직 O_APPEND와 O_NONBLOCK 플래그들을 변경할 수 있다. 이 명령을 사용한 fcntl로부터 반환된 값은 지정된 값이 아니지만, 에러가 발생한 경우에 -1을 반환한다. 에러의 발생 상황은 F_GETFL 명령과 같다.

다음 매크로들은 파일 상황 플래그 값들을 분석하고 구성하기 위해 정의되어졌다.

O_APPEND

파일 덧붙이기가 가능한 모드로 만들기 위한 비트이다. 만일 이 비트가 설정되면, 모든 쓰기(write) 명령들은 파일의 끝에서 쓰기가 이루어지고, 현재 파일의 위치가 무시된다.

O_NONBLOCK

파일을 비블록화 모드로 만들기 위한 비트이다. 만일 이 비트가 설정되면, 파일에서 읽기(read) 요청은 즉시 유용한 입력이 없다면 기다리지 않고 즉시 반환한다. 마찬가지로, 쓰기(write) 요청 또한 즉시 쓰기 가능한 출력이 없다면 즉시 반환하여 쓰기 요청은 실패한다.

O_NDELAY

이것은 O_NONBLOCK의 유사어로써, BSD와의 호환성을 위해 제공된다.

매크로 : int O__ACCMODE

이 매크로는 파일 검색모드를 나타내는 값을 알아내기 위해 파일 상황플래그와 비트별 AND를 시키는 마스크이다. 모드에는 O_RDONLY, O_WRONLY, 또 O_RDWR이 있다.

O_RDONLY

읽기 모드로 파일을 개방.

O_WRONLY

쓰기 모드로 파일을 개방.

O_RDWR

읽기 쓰기 모두 가능하게 파일을 개방.

만일 당신이 파일 상황 플래그를 갱신하기 원한다면, 당신은 F_GETFL을 사용해서 현재의 플래그들은 얻어서 그 값을 갱신하라. 이곳에서 보여준 플래그들이 오직 단독으로 존재한다고 가정하지 말아라; 당신의 프로그램은 지금부터 수년간 실행되어질 것이고 그러면 더 많은 플래그들은 존재할 것이다. 예를 들어, 이곳에 다른 플래그들을 변경함이 없이 오직 O_NONBLOCK 플래그만 설정하고, 클리어 하는 함수가 있다.

/* 만일 value 값이 0이 아닌 값이면 desc의 O_NONBLOCK 플래그를 설정하고, value의 값이 0이면 그 플래그를 클리어 하라. 성공하면 0을 반환하고, 에러가 발생하면 -1을 반환하라 */

int
set_nonblock_flag (int desc, int value)
{
int oldflags = fcntl (desc, F_GETFL, 0);
/* 만일 읽기 플래그들이 실패하면, 즉시 에러임을 지적하라. */
if (oldflags < 0)
return oldflags; /* 우리가 원하는 플래그를 설정하라 */
if (value != 0)
oldflags |= O_NONBLOCK;
else
oldflags &= ~O_NONBLOCK; /* 기술자에 갱신된 플래그 워드를 저장하라 */
return fcntl (desc, F_SETFL, oldflags);
}


8. 11 파일 록

위에서 설명한 것 외에 fcntl 에서 쓸 수 있는 명령은 레코드 락킹(locking)을 지원하기 위해 사용되어진다. 레코드 락킹은 다중 사용자 프로그램에서 동시에 파일의 같은 부분을 억세스 하는 것을 방지하기 위한 것이다.

배타적이거나 쓰기(write) 락(lock)은 프로세스가 파일의 지정한 부분에서 배타적인 동작으로 쓰기 위해서 주어진다. 쓰기 락이 있는 곳에서는 다른 프로세스들은 파일의 그 부분에 락을 걸 수가 없다.

분배하거나 읽기(read) 락(lock)은 파일의 지정된 부분을 읽고 있을 때 다른 프로세스로부터의 쓰기 락(write lock) 요청을 거부한다. 그렇지만 다른 프로세스가 읽기 락(read lock)을 요청하는 것은 가능하다.

read와 write 함수들은 실제로 그 위치에 락(lock)이 걸린 건지를 체크하지 않는다. 만일 당신이 다중 프로세스에 의해 점유된 파일을 위해 락킹(locking) 프로토콜(protocol)을 사용하기 원한다면, 당신의 어플리케이션은 적당한 지점에서 락(lock)을 요청하거나 클리어 하기 위해서 fcntl 함수를 호출해야만 한다.

락들은 프로세스들과 연관되어 있다. 한 프로세스는 주어진 파일의 각 바이트에 한 종류만의 락(lock) 설정을 할 수 있다. 파일기술자가 프로세스에 의해 닫혀질 때, 그 파일에서 프로세스가 갖고 있던 모든 락들은 해제된다, 심지어 현재 개방 상태인 다른 기술자를 사용해서 만들어진 락일지라도. . . 마찬가지로, 프로세스가 존재하고 있을 때 해제된 락들은, fork(23. 4절 [Creating a Process] 참조)를 사용해서 만들어진 자식 프로세스에게 물려줄 수 없다.

락을 만들 때, 락이 어떤 종류이고 현재 어디에 있는지를 알리기 위해 구조체 struct flock을 사용한다. 이 데이터 타입과 fcntl과 연관된 매크로는 헤더파일 'fcntl. h'에 선언되어 있다.

데이터타입 : struct flock

이 구조체는 파일락을 설명하기 위해 fcntl함수에서 사용되어진다. 이들 멤버는 다음과 같다.

short int l_type

락의 타입을 정한다. F_RDLCK, FWRLCK, 또는 F_UNLCK 중에 하나.

short int l_whence

이것은 fseek 나 lseek 함수의 인수인 whence 와 일치하고, 그것을 기점으로 offset을 정한다. SEEK_SET, SEEK_CUR, SEEK_END 중 하나.

off_t l_start

이것은 락이 적용되는 지역의 시작 offset을 지정하고, l_whence 멤버에 연관한 지점이 바이트로 주어진다.

off_t l_len

이것은 락이 설정된 지역의 길이를 지정한다. 0의 값은 특별히 취급된다; 그것은 파일의 끝에서 락의 범위를 확장하는걸 의미한다.

pid_t l_pid

이 필드는 락을 갖고 있는 프로세스의 프로세스 ID (23. 2절 [Process Creation Concepts] 참조)이다. F_GETLK 명령을 사용해서 fcntl 함수를 호출하면 이 필드가 채워지지만, 락을 만들 때는 무시된다.

매크로 : int F__GETLK

이 매크로는 락에 대한 정보를 얻고자 함을 알리기 위해, fcntl에게 command 인수로써 사용된다. 이 명령은 fcntl에게 주기 위해 struct flock * 타입의 세 번째 인수가 필요하다. 그래서 다음과 같은 형식으로 호출한다:

fcntl (filedes, F_GETLK, lockp)

만일 lockp 인수에 의해 묘사된 락이 설정된 블록의 영역에 이미 락이 존재한다면, 그 락에 대한 정보는 *lockp에 다시 쓰여 진다. 이미 존재하고 있는 락이 새로 만들고 있는 락과 호환성이 있다면 보고되지 않는다. 그래서, 만일 당신이 읽기와 쓰기 락들에 대해서 찾기를 원한다면 F_WRLK의 타입으로 락을 지정하거나, 만일 당신이 쓰기 락에 대한 것을 찾고 싶다면 F_RDLCK의 타입으로 락을 지정해야 한다.
 
lockp 인수에 의해 지정된 지역에는 한 개보다 더 많은 락들이 있겠지만, fcntl은 오직 그들중 하나에 대한 정보를 반환한다. lockp 구조체중 l_whence 멤버는 SEEK_SET으로 설정되고 l_start 와 l_len 필드들은 락이 걸린 지역과 동일하게 설정된다. 만일 아무 것도 락이 걸리지 않았다면, lockp 구조체에 유일하게 가해지는 변화는 F_UNLCK의 값으로 l_type이 갱신되는 것이다. 이 명령을 사용한 fcntl 으로부터의 반환 값은 보통 정해지지 않았고, 에러가 발생한 경우에는 -1을 반환한다.
 
다음 errno는 이 명령에서 정의한 에러상황이다.

EBADF filedes 인수가 유용하지 않다.

EINVAL lockp

인수가 유용한 락 정보를 가지고 있지 않거나, 파일이 락이 지원되지 않는 filedes와 연관이 있다.

매크로 : int F__SETLK

이 매크로는 락을 설정하거나, 클리어 할 것임을 지정하기 위해 fcntl 함수에서 command 인수로 사용되어진다. 이 명령은 fcntl 함수에 주기 위한 세 번째 인수로써 struct flock * 형의 인수를 필요로 한다. 그래서 다음과 같이 호출한다.

fcntl (filedes, F_SETLK, lockp)

만일 프로세스가 락을 설정할 지역에서 이미 어느 한 부분이 락을 가지고 있다면, 이미 있던 락은 새로운 락으로 대체된다. 당신은 F_UNLCK의 타입으로 락을 지정하여서 락을 제거할 수도 있다. 만일 락이 설정되지 않았다면, fcntl은 -1의 값을 즉시 반환한다. 이 함수는 락을 해제하기 위해 다른 프로세스들을 기다리는 블록을 하지 않는다. 만일 fcntl 이 성공하면, -1이 아닌 다른 값을 반환한다.
 
다음 errno는 이 함수에서 정의한 에러 상황이다.

EACCES

EAGAIN 파일에 존재하고 있는 락으로 블록 되어졌기 때문에 그 락을 설정할 수 없다. 어떤 시스템에서 이 경우에 EAGAIN을 사용하고, 다른 시스템에서는 EACCES를 사용한다; 당신의 프로그램은 F_SETLK 후 처럼 그들을 취급할 것이다.

EBADF

filedes 인수가 유용하지 않거나; 당신이 읽기 락을 요청했지만 filedes는 읽기 모드로 개방되지 않았거나, 또는 쓰기 락을 요청했는데, 그 filedes 는 쓰기 모드로 개방되지 않았거나; 이 둘 중에 하나이다.

EINVAL

lockp인수가 유용한 락 정보를 지정하지 않았거나, 또는 filedes와 연관된 파일이 락을 지원하지 않거나이다.

ENOLCK

시스템의 파일 락 자원들이 바닥났다; 이미 너무 많은 파일 락들이 그곳에 존재하고 있다. 잘-만들어진 파일시스템들은 결코 이 에러를 발생시키지 않는데, 그 이유는 그들이 락의 수에 대한 아무런 제한을 가지고 있지 않기 때문이다. 그렇지만, 다른 컴퓨터(machine)의 파일시스템을 네트웍으로부터 검색하는 것에서 발생할 수 있는 결과와 같이, 당신은 이처럼 이 에러가 발생할 가능성으로 인해서 여전히 락의 개수를 세어야만 한다.

매크로 : int F__SETLKW

이 매크로는 락을 설정하거나, 설정된 락을 지울 것임을 지정하기 위해서, fcntl 함수에 command 인수로서 사용되어진다. 이것은 F_SETLK 명령과 같지만, 그 요청이 받아들여 질 때까지 프로세스를 블록(또는 기다림)하게 한다. 이 명령은 F_SETLK 명령처럼 세 번째 인수로서 struct flock * 타입의 인수를 필요로 한다. fcntl의 반환 값과 에러들은 F_SETLK 명령을 위한 것과 동일하지만, 이것들에 더해진 에러 상황들이 밑에 설명되어 있다.

EINTR

이 함수는 기다리는 동안 신호에 의해 인터럽트 되어졌다. 21. 5절 [Interrrpted Primitives] 참조.

EDEADLK

교착상태(역자주: 다중 프로그래밍 시스템에서 발견될 수 있는 상황으로 프로세스들이 서로 작업을 진행하지 못하고 영원히 대기 상태로 빠지게 되는 현상을 교착 상태라 하는데, 보통 프로세스 사이에 할당된 자원의 충돌로 인하여 발생한다. )가 검출되어졌다. 이것은 만일 이미 자신이 제어하고 있는 락된 지역(locked region)을 갖고 있는 두 개의 프로세스가 다른 프로세스에 의해 락된 동일한 지역을 요청했을 때 발생할 수 있다.

다음 매크로는 flock 구조체의 1_type 멤버를 위한 값들로 사용되어지기 위해서 정의되어졌다. 이 값들은 정수 상수이다.

파일에 쓰기 위하여 동시에 여러 개의 프로그램 복제본을 갖는다면, 그 파일의 내용은 나중에 엉망진창이 되어질 것이다. 그러나 당신은 파일에 쓰기가 실제로 발생하기 전에 그 파일에 쓰기락을 설정해서 그와 같은 문제들의 발생을 예방할 수 있다.

만일 프로그램이 그 파일을 읽을 필요가 있고 그 파일의 내용이 확실하기를 원한다면, 그때 읽기 락을 사용할 수 있다. 읽기 락이 설정된 동안 어떤 다른 프로세스도 쓰기 위해서 그 부분에 락을 설정하려면 기다려야 한다.

파일 락은 파일을 억세스 하는 것을 제어하기 위한 자발적 프로토콜임을 기억하라. 그래서 락 프로토콜을 사용하지 않는 프로그램에 의해 그 파일을 억세스할 가능성은 여전히 있다.


8. 12 인터럽트로 조종된 입력

만일 당신이 파일 기술자상의 FASYNC 상황 플래그를 설정하면( 8. 10절[File Status Flags] 참조 ), SIGIO 신호는 파일 기술자 상에서 입력이나 출력이 가능하게 될 때마다 보내어진다. 그 신호를 받을 프로세스나 프로세스 그룹은 fcntl 함수에 F_SETOWN 명령을 사용해서 선택할 수 있다. 만일 그 파일 기술자가 소켓이라면, 이것은 소켓에 도착한 범위 밖의 데이터가 있을 때, 그때 배달된 SIGURG 신호를 받을 수령자를 선택한다; 11. 8. 8 [Out-of-Band Data] 참조.

만일 그 파일 기술자가 터미널 디바이스와 교신한다면, SIGIO 신호는 터미널의 전면(foreground) 프로세스 그룹에 보내어진다. 24장 [Job Control] 참조.

이 절안의 심볼들은 헤더파일 'fcntl. h'에 정의되어 있다.

매크로 : int F__GETOWN

이 매크로는 SIGIO 신호를 보낸 프로세스나 프로세스 그룹에 대한 정보를 얻음을 정하기 위해 fcntl 함수의 command 인수로서 사용되어진다. (터미널에서, 이것은 실제로는 전면(foreground) 프로세스 그룹 아이디(ID)를 tcgetpgrp을 사용해서 얻을 수 있다; 24. 7. 3절 [Terminal Access Functions] 참조. ) 반환 값은 프로세스 ID로 인터럽트 되어지고 만일 음의 값이면, 그 절대값이 프로세스 그룹 ID 이다.
 
다음의 errno는 이 명령에서 정의한 에러 상황이다.
EBADF : filedes 인수가 유용하지 않다.

매크로 : int F__SETOWN

이 매크로는 SIGIO 신호를 보내기 위한 프로세스나 프로세스 그룹 ID를 설정함을 정하기 위해 fcntl 함수의 command 인수로서 사용되어진다. 이 명령은 fcntl 함수의 세 번째 인수로 pid_t 타입의 인수를 필요로 한다, 그래서 다음과 같은 형식으로 호출된다:

fcntl (filedes, F_SETOWN, pid)

pid 인수는 프로세스 그룹 ID이다. 당신이 그 값을 음의 값으로 주더라도 그 절대값이 프로세스 그룹 ID가 되어진다. 이 명령을 사용한 fcntl함수로부터의 반환 값은 에러가 발생된 경우에 -1이고, 성공하면 어떤 다른값이 된다.
 
다음의 errno는 이 명령에서 정의한 에러상황이다.
EBADF : filedes 인수가 유용하지 않다.
ESRCH : pid에 해당하는 아무런 프로세스나 프로세스 그룹이 없다.


목차 이전 : 7. 스트림에서의 입출력 다음 : 9. 파일 시스템 인터페이스