서버에서 함수 쓸때는 평범한 read말고 다른 거 쓴다
이에 관련해서 csapp 10단원에 나오지만 이거 정리하기에는 괴로워
간단하게 10.5에서 알려주는 RIO 함수만 다루겠다
모 애니메이션 영화가 생각나는 이름이다
RIO 패키지는
shour count 문제1를 자동으로 처리하라고 만든 거다
네트워크 같이 short count가 많은 환경을 위해 만들어졌다
RIO 패키지에는 크게 두 종류의 함수가 들어있다
비버퍼 함수 – 응용 수준의 별도 버퍼링 없이 바로 read/write를 수행하며, 바이너리 데이터를 다룰 때 유용하다
대표적으로 rio_readn
, rio_writen
함수
버퍼 함수 – 내부에 응용 수준 버퍼를 두어 효율적으로 데이터를 읽어들이며, 텍스트 라인이나 이진 데이터를 효과적으로 처리한다
rio_readlineb
, rio_readnb
함수가 이에 해당하며, 사용 전 rio_readinitb
로 버퍼를 초기화해야 한다
형태: 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 - 남은 바이트)
}
형태: 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 반환
}
형태: void rio_readinitb(rio_t *rp, int fd)
rio_readinitb
는 Robust I/O용 내부 버퍼를 초기화하는 함수로, 열려있는 파일 디스크립터 fd
와 rio_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; // 내부 버퍼 포인터를 버퍼 시작 위치로 초기화
}
형태: 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 제외)
}
형태: 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) – 읽고자 하는 최대 바이트 수n
바이트를 모두 읽으면 n
을 반환하고, 도중에 EOF를 만나 n
보다 적게 읽었다면 그만큼의 바이트 수를 반환한다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; // 내부 버퍼 포인터를 버퍼 시작 위치로 초기화
}
출력이나 입력이 중간에 끊기는 등
읽기/쓰기가 요청한 바이트보다 적게 수행되는 경우
↩