Robust Reading and Writing with the RIO Package
RIO (Robust I/O) 패키지는 short count를 자동적으로 처리한다.
Rio 패키지는 네트워크 프로그램과 같은 short count 대상이 되는 프로그램에서 편리하고 견고하며 효율적인 I/O를 제공한다. Rio는 두 가지 다른 기능을 제공한다.
Unbuffered input and ouput finctions.
이러한 함수는 응용 프로그램 수준의 버퍼링 없이 메모리와 파일 간에 데이터를 직접 전송할 수 있다.
네트워크로부터 binary 데이터를 읽고 쓰는 데에 특히 유용하다.
- rio_readn
- rio_writen
Buffered input functions.
이러한 함수를 사용하면 printf와 같은 표준 I/O 함수에 제공되는 것과 유사하게 내용이 응용 프로그램 수준의 버퍼에 캐시 된 파일에서 텍스트와 binary 데이터를 효율적으로 읽을 수 있다.
- rio_readlineb
- rio_readnb
텍스트 또는 binary를 읽었을 때 사용자 레벨의 버퍼에 저장해놓고 카피해주는 함수.
RIO Unbuffered Input and Output Functions.
#include "csapp.h"
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
Return: num. bytes transferred if OK, 0 on EOF (rio_readn only), -1 on error
- rio_readn
fd의 현재 파일 위치에서 usrbuf로 최대 n바이트를 전송한다.
EOF가 발생한 경우에만 short count를 반환할 수 있다. - rid_writen
usrbuf에서 fd로 n바이트를 전송한다.
rio_readn과 rio_writen에 대한 호출은 같은 파일 디스크립터로 임의로 interleaved 될 수 있다.
Implemenation of rio_readn
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)
{
if (errno == EINTR) /* Interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* Return >= 0 */
}
Buffred RIO Input Function
텍스트 파일의 줄 수를 세는 프로그램을 작성하려 한다.
한 가지 방법은 읽기 함수를 사용하여 파일에서 사용자의 메모리로 한 번에 1바이트씩 전송하여 각 바이트에서 줄 바꿈 문자를 확인하는 것이다. 이 접근법의 단점은 비효율적이라는 점과 파일 내의 각 바이트를 읽기 위해 커널에 대한 트랩이 필요하다는 것이다.
보다 좋은 방법은 내부의 read buffer에서 텍스트 행을 복사하는 wrapper 함수 (rio_readlineb)를 호출하여 버퍼가 비워질 때마다 자동으로 읽기 호출을 하는 것이다. 텍스트 행과 binary 데이터를 모두 포함하는 파일의 경우 rio_readnb라고 하는 버퍼링 된 버전의 rio_readn을 사용한다. 내부 메모리 버퍼에 부분적으로 캐시 된 파일에서 텍스트 줄과 binary 데이터를 효율적으로 읽는다.
#include "csapp.h"
void rio_readinitb(rio_t *rp, int fd);
Returns: nothing
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
Returns: number of bytes read if OK, 0 on EOF, −1 on error
rio_readinitb 함수는 열려있는 파일 디스크립터 당 한 번 호출된다. fd를 주소 rp의 rio_r 타입의 읽기 버퍼와 연결한다.
rio_readlineb 함수는 파일 rp에서 다음 텍스트 행(종료하는 줄바꿈 문자 포함)을 읽어 메모리 위치 usrbuf에 복사하고 텍스트 행을 NULL 문자로 종료한다. rio_readlineb 함수는 최대 maxlen - 1 바이트를 읽으므로 끝에 NULL문자를 저장할 수 있다. maxlen - 1바이트를 초과하는 텍스트 행은 잘리고 NULL문자로 끝낸다. 특히 network socket에서 텍스트 줄을 읽을 때 유용하다.
종료 조건
- maxlen bytes read
- EOF encountered
- Newline('\n') encountered
rio_readnb 함수는 파일 rp에서 메모리 위치 usrbuf로 최대 n바이트를 읽는다. rio_readlineb와 rio_readb에 대한 호출은 같은 파일 디스크립터로 interleaved 될 수 있다.
종료 조건
- maxlen bytes read
- EOF encountered
Buffered I/O: Implementation
파일에서 읽었지만 사용자 코드로 아직 읽지 않은 바이트를 보관하기 위해 파일에 연결된 버퍼가 있다.
사용자 버퍼가 내부 버퍼를 가지고 있어서 읽은 부분과 읽지 않은 부분을 나눌 수 있다.
Unix 파일 I/O 관점에서 현재 파일을 Current File Positon.까지 읽은 것이다.
파일을 읽을 때 사용자가 한 바이트씩 읽겠다고 하면 시스템 콜을 한번만한 번만 호출하여 512바이트를 파일에서 한 번만 읽어서 사용자 버퍼에 저장하고 그 다음부터 사용자가 한 바이트씩 읽겠다는 요청이 오면 메모리로 한 바이트씩 복제함으로써 시스템 콜 호출 횟수를 줄일 수 있다.
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
RIO Example
텍스트 파일의 행을 표준 입력에서 표준 출력으로 복사하는 예시.
#include "csapp.h"
int main(int argc, char **argv)
{
int n;
rio_t rio;
char buf[MAXLINE];
Rio_readinitb(&rio, STDIN_FILENO);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
Rio_writen(STDOUT_FILENO, buf, n);
exit(0);
}