jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. RIO (Robus I/O)
    1. 비버퍼 함수
      1. rio_readn
      2. rio_writen
    2. 버퍼 함수
      1. rio_readinitb
      2. rio_readlineb
      3. rio_readnb

서버에서 함수 쓸때는 평범한 read말고 다른 거 쓴다

이에 관련해서 csapp 10단원에 나오지만 이거 정리하기에는 괴로워

간단하게 10.5에서 알려주는 RIO 함수만 다루겠다

모 애니메이션 영화가 생각나는 이름이다


RIO (Robus I/O)

RIO 패키지는
shour count 문제1를 자동으로 처리하라고 만든 거다
네트워크 같이 short count가 많은 환경을 위해 만들어졌다

RIO 패키지에는 크게 두 종류의 함수가 들어있다

  • 비버퍼 함수 – 응용 수준의 별도 버퍼링 없이 바로 read/write를 수행하며, 바이너리 데이터를 다룰 때 유용하다
    대표적으로 rio_readn, rio_writen 함수

  • 버퍼 함수 – 내부에 응용 수준 버퍼를 두어 효율적으로 데이터를 읽어들이며, 텍스트 라인이나 이진 데이터를 효과적으로 처리한다
    rio_readlineb, rio_readnb 함수가 이에 해당하며, 사용 전 rio_readinitb로 버퍼를 초기화해야 한다


비버퍼 함수

rio_readn

형태: ssize_t rio_readn(int fd, void *usrbuf, size_t n)

파일 디스크립터 fd로부터 최대 n 바이트를 읽어 메모리 버퍼 usrbuf에 저장하는 함수다
요청 바이트 모두 읽거나 EOF(End-of-File)에 도달할 때까지 읽는다

인자

  • fd (int) – 읽기를 수행할 파일 디스크립터 (예: 파일 또는 소켓 식별자)
  • usrbuf (void *) – 읽어들인 데이터를 저장할 사용자 버퍼의 시작 주소
  • n (size_t) – 읽고자 하는 바이트 수 (최대 읽을 바이트의 개수)

반환값

  • 성공 시: 실제로 읽어들인 바이트 수를 반환한다
    요청한 n 바이트를 모두 읽었다면 n을 반환하고, 도중에 EOF를 만나 일부만 읽었다면 그만큼의 바이트 수(요청보다 작은 값)를 반환한다
    만약 EOF에 바로 도달하여 한 바이트도 읽지 못했다면 0을 반환한다

  • 실패 시: -1을 반환하며, 이때 전역 변수 errno에 오류 정보를 설정한다
    (예: 읽기 중 발생한 실제 오류 코드)


코드

ssize_t rio_readn(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;          // 남은 바이트 수를 요청한 n으로 초기화
    ssize_t nread;             // read 함수가 반환한 바이트 수 (읽은 바이트 수)
    char *bufp = usrbuf;       // 사용자 버퍼를 가리킬 포인터

    while (nleft > 0) {        // 남은 바이트를 모두 읽을 때까지 반복
        // fd에서 최대 nleft 바이트 읽어서 bufp에 저장
        if ((nread = read(fd, bufp, nleft)) < 0) {  
            if (errno == EINTR) {      // 읽는 중 시그널 인터럽트 발생한 경우
                nread = 0;             // nread를 0으로 설정하고 루프를 계속 (다시 read 시도)
            } else {
                return -1;             // 기타 읽기 오류 발생 시 -1 반환하고 종료
            }
        } else if (nread == 0) {
            break;                     // EOF인 경우 (읽을 데이터 없음) -> 루프 종료
        }
        nleft -= nread;                // 읽어온 만큼 남은 바이트 수 감소
        bufp += nread;                 // 사용자 버퍼 포인터를 읽은 바이트 수만큼 이동
    }
    return (n - nleft);                // 총 읽은 바이트 수 반환 (요청한 n - 남은 바이트)
}


rio_writen

형태: ssize_t rio_writen(int fd, void *usrbuf, size_t n)

rio_writen은 메모리 버퍼 usrbuf로부터 n 바이트를 읽어 파일 디스크립터 fd에 쓰는 함수다
요청한 모든 바이트가 전송될 때까지 쓰기를 계속한다

인자

  • fd (int) – 쓰기를 수행할 파일 디스크립터 (출력 대상 파일 또는 소켓 식별자)
  • usrbuf (void *) – 출력할 데이터가 담긴 메모리 버퍼의 시작 주소
  • n (size_t) – 출력하고자 하는 바이트 수

반환 값

  • 성공 시: 요청한 바이트 수 n을 반환한다
    rio_writen은 지정한 n 바이트를 모두 쓰도록 구현되어 있으므로, 정상적으로 함수를 복귀했다면 항상 n 바이트가 전송된 상태
    (short count 없이 완료)

  • 실패 시: -1을 반환하며, 이때까지 일부만 전송되었더라도 함수는 오류로 간주하고 -1을 리턴
    (errno에 오류 원인이 설정)


코드

ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;           // 아직 보내지 않은 바이트 수 초기화
    ssize_t nwritten;           // write 함수의 반환 값 (쓴 바이트 수)
    char *bufp = usrbuf;        // 보낼 데이터의 현재 위치를 가리킬 포인터

    while (nleft > 0) {         // 남은 모든 바이트를 보낼 때까지 반복
        // fd로부터 nleft 바이트를 bufp에서 전송 시도
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {  
            if (errno == EINTR) {       // 쓰기 도중 시그널로 인터럽트된 경우
                nwritten = 0;          // nwritten을 0으로 설정하고 루프 지속 (재시도)
            } else {
                return -1;             // 다른 쓰기 오류 발생 시 -1 반환
            }
        }
        nleft -= nwritten;             // 보낸 만큼 남은 바이트 수 감소
        bufp += nwritten;             // 버퍼 포인터를 보낸 바이트 수만큼 이동
    }
    return n;                          // 모든 바이트 전송 완료 시 요청한 n 반환
}


버퍼 함수

rio_readinitb

형태: void rio_readinitb(rio_t *rp, int fd)

rio_readinitb는 Robust I/O용 내부 버퍼를 초기화하는 함수로, 열려있는 파일 디스크립터 fdrio_t 구조체를 연결한다

인자

  • rp (rio_t *) – 읽기 버퍼 정보를 담을 RIO 구조체의 포인터
    rio_t는 내부 버퍼(rio_buf), 그 버퍼에서 아직 읽지 않은 바이트 수(rio_cnt), 현재 읽을 위치 포인터(rio_bufptr), 그리고 대상 파일 디스크립터(rio_fd)를 필드로 갖는 구조체다

  • fd (int) – rp와 연결할 파일 디스크립터
    이 디스크립터로부터 읽을 때 rio_readlineb, rio_readnb 등이 rp의 내부 버퍼를 활용하게 된다

반환값

그런 거 없다 (void함수)

코드

void rio_readinitb(rio_t *rp, int fd) {
    rp->rio_fd = fd;                 // RIO 내부 구조체에 파일 디스크립터 저장
    rp->rio_cnt = 0;                 // 내부 버퍼에 남은 데이터 양을 0으로 초기화 (버퍼 비움)
    rp->rio_bufptr = rp->rio_buf;    // 내부 버퍼 포인터를 버퍼 시작 위치로 초기화
}


rio_readlineb

형태: ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)

rio_readlineb는 버퍼링된 입력 함수로, rp에 연결된 파일 (파일 디스크립터 rp->rio_fd)로부터 텍스트 한 줄을 읽어와 usrbuf에 저장한다
줄바꿈 문자('\n')까지 포함하여 한 줄을 읽고, 그 뒤에 NULL 문자('\0')를 추가해 문자열을 종료한다

인자

  • rp (rio_t *) – Robust I/O 읽기 버퍼 구조체
  • usrbuf (void *) – 읽은 한 줄의 텍스트를 저장할 사용자 메모리 버퍼
    문자열로 사용할 수 있도록 함수가 끝에 '\0'를 붙여준다
  • maxlen (size_t) – usrbuf최대 크기 (읽을 수 있는 최대 문자 수 + 1)
    함수는 개행을 포함하여 최대 maxlen-1개의 문자만 읽고, 마지막에 NULL을 추가한다

반환값

  • 성공 시: 읽어들인 라인의 길이를 바이트 단위로 반환한다
    이 길이에는 개행 문자 '\n'이 포함되며, 문자열 종료용 NULL 문자는 제외된다
    예를 들어 한 줄 “Hello\n”을 읽었다면 6을 반환합니다 (개행 포함 6바이트, '\0' 미포함)
    만약 개행 없이 EOF나 버퍼 한계로 종료되었다면 읽은 만큼의 바이트 수를 반환한다

  • EOF 시: 파일의 끝에 도달하여 아무 데이터도 읽지 못한 경우 0을 반환한다
    (예를 들어 EOF에서 호출하면 0을 리턴)

  • 실패 시: -1을 반환하며, 내부적으로 rio_read에서 설정한 errno 값이 유지된다
    (메모리 접근 오류나 기타 읽기 오류 등)


코드

ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    // 1부터 시작하여 maxlen-1 글자까지 루프 (n은 읽은 문자 개수+1로 사용)
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {   // 내부 버퍼에서 1바이트 읽기 성공 (rc=1)
            *bufp++ = c;                        // 읽은 문자를 사용자 버퍼에 저장
            if (c == '\n') {                    // 개행 문자를 읽은 경우 (한 줄의 끝)
                n++;                            // 현재까지 읽은 문자 개수에 개행 포함시키고
                break;                          // 루프 종료 (한 줄 읽기 완료)
            }
        } else if (rc == 0) {    // EOF에 도달한 경우 (더 읽을 데이터 없음)
            if (n == 1) {
                return 0;       // EOF이고 한 글자도 읽지 못했으면 0 반환
            } else {
                break;          // EOF지만 일부 문자는 읽은 경우 루프 종료
            }
        } else {                // rc < 0, 즉 rio_read에서 오류 발생 (-1 반환)
            return -1;          // 오류 발생 시 -1 반환
        }
    }
    *bufp = '\0';               // 읽은 문자열의 끝에 NULL 문자 추가
    return n - 1;               // 개행 문자를 포함한 읽은 바이트 수 반환 (NULL 제외)
}


rio_readnb

형태: ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)

rio_readnb는 버퍼링된 바이너리 읽기 함수로, rp에 연결된 파일에서 최대 n 바이트를 읽어와 usrbuf에 저장
rio_readn과 달리 데이터를 직접 읽는 대신 내부 버퍼(rio_t 구조체의 rio_buf)를 이용

인자

  • rp (rio_t *) – Robust I/O 버퍼 구조체
    rio_readinitb로 초기화되어 있으며, 대상 파일 디스크립터와 내부 버퍼를 포함하고 있다
  • usrbuf (void *) – 읽어들인 데이터를 저장할 메모리 버퍼의 시작 주소
  • n (size_t) – 읽고자 하는 최대 바이트 수
    함수는 이 만큼의 데이터를 읽으려고 시도하며, EOF나 오류가 없다면 정확히 n바이트를 채운다

반환값

  • 성공 시: 실제로 읽은 바이트 수를 반환한다
    요청한 n바이트를 모두 읽으면 n을 반환하고, 도중에 EOF를 만나 n보다 적게 읽었다면 그만큼의 바이트 수를 반환한다
    (예를 들어 EOF 직전까지 50바이트만 읽고 요청은 100바이트였다면 50을 반환)
  • EOF 시: 호출 시점에 EOF로 더 이상 읽을 데이터가 없어서 한 바이트도 읽지 못하면 0을 반환한다
  • 실패 시: -1을 반환하며, 이때의 errno를 통해 오류 원인을 확인할 수 있다
    (내부적으로 read 호출 실패 시의 에러가 전달됨)


코드

void rio_readinitb(rio_t *rp, int fd) {
    rp->rio_fd = fd;                 // RIO 내부 구조체에 파일 디스크립터 저장
    rp->rio_cnt = 0;                 // 내부 버퍼에 남은 데이터 양을 0으로 초기화 (버퍼 비움)
    rp->rio_bufptr = rp->rio_buf;    // 내부 버퍼 포인터를 버퍼 시작 위치로 초기화
}






출력이나 입력이 중간에 끊기는 등

  1. 읽기/쓰기가 요청한 바이트보다 적게 수행되는 경우