read 재시작 함수 관련(r_read)

asleea의 이미지

ssize_t r_read(int fd, void *buf, size_t size)
{
   ssize_t retval;
   while(retval = read(fd, buf, size), retval == -1 && errno == EINTR);
   return retval;
}

공부를 하다가 책에 나온 code를 보고 의문점이 생겨서 글을 쓰게 되었습니다.
read함수가 수행도중에 인터럽트로 인해 실패되었을 때, 재시작 하도록 하는 함수 입니다.

만약
fd파일에는 1234567890이 입력되어있고,(r_read(fd, buf, 100)
read함수가 123까지 읽다가 인터럽트로 인해 함수를 재시작 하게되었다고 하면

다시 read를 수행할 때는 fd의 offset이 4를 가리키고 있어서 4부터 읽어서 buf에 쓰게되어 4567890이 buf에 저장되는거 아닌가요.?

romandev의 이미지

read함수가 123까지 읽다가 인터럽트로 인해 함수가 종료될때는 123을 읽은만큼의 사이즈3이 아닌 -1을 리턴합니다. 즉, 읽기전으로 돌려놓는 것이지요.

즉, 질문자님이 예를 드신 상황을 보면 fd 파일에는 1234567890 총 10바이트 길이의 데이터가 입력되어있고,

r_read(fd, buf, 100)을 호출하였다면 해당 함수 내부에서 다시 read(fd, buf, 100)을 호출 할 것이고 read함수 수행도중에 인터럽트가 걸리면 내부에서 123까지만 읽었더라도

100바이트를 못채웠기 때문에 -1을 리턴합니다. 즉, 파일 디스크립터의 offset은 읽기 전으로 되돌아간다는 의미지요. 100바이트를 못채워도 정상 리턴할려면 EOF(파일의 끝)를 만나는 수 밖에 없습니다.

설명이 미흡해서 이해가 가실런지 모르겠군요.

asleea의 이미지

일단 감사드립니다.

그럼 buf에 이미 123이 쓰였더라도 시그널 인터럽트로 인해 함수가 error로 종료되었기 때문에

fd의 offset을 변화 시키기 않는다는 말씀이시 군요.

죄송하지만 한가지만 더 질문 드리겠습니다.

write함수 재시작 함수는 read와 형태가 다른데 이유가 무엇일까요, read와 같은 형태로 하면 문제가 되는 부분이 있는건 가요?

ssize_t r_write(int fd, void *buf, size_t size) {
   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;
 
   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) {
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten) == -1 && (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   }
   return totalbytes;
}
익명 사용자의 이미지

먼저 read 혹은 write 호출시 결과로 얻을 수 있는 케이스들은 다음과 같은 네가지 경우로 간추려 질 수 있을 것 같습니다. (제가 보는 관점이구요. 실제로는 더 많은 케이스를 가질 수도 있습니다)
1. 정상 read / write
2. 부분적인 read / write
3. -1 and EINTR
4. -1 (기타 에러)

먼저 3번, 4번의 경우는 결국 에러로 인해 오프셋 변경이 발생하지 않습니다.
1번의 경우는 정상적으로 read, write가 된 경우로 뭐 역시 큰 문제가 되지 않습니다. (기대했던 가장 이상적인 경우)
2번의 경우가 발생하는 것이 문제인데 read, write가 데이터를 기록하기 시작한 후 시그널 신호를 받으면 EINTR이 아닌 읽기 성공한 혹은 쓰기 성공한 만큼의 데이터를 리턴합니다. 즉, 에러가 아니며 정상 처리한 것으로 볼 수 있습니다.

본론으로 들어가 r_read와 r_write의 차이를 알아보기 위하여 먼저 r_write를 살펴보면

r_write는 먼저 for문 속에 첫번째 if문을 통하여 4번 케이스를 처리하며 두번째 if문을 통해 3번 케이스를 처리합니다.
또한 byteswritten과 bytestowrite를 이용하여 실제 기록된 데이터의 크기와 기록되길 기대하는 크기를 비교하여 기대값을 만족할 때까지 지속적인 write를 시도합니다(즉, 1번과 2번을 모두 처리하고 있습니다).

r_read의 경우는 먼저 직관적으로 3번 케이스가 발생하면 다시 read를 시도하도록 처리하고 있습니다.
1번, 2번, 3번 케이스가 발생하는 경우는 -1을 리턴하거나 읽어들인 바이트 수 만큼 리턴하도록 처리하고 있습니다.

r_write와 r_read의 차이점은 2번 케이스에 관한 처리입니다. r_write는 size파라메터를 통해 전달한 기대값만큼
내부의 write가 모두 처리할 수 있을 때까지 반복하는데 비해 r_read의 경우는 size 파라메터를 통해 전달한 기대값만큼
내부의 read가 모두 처리하지 않더라도(즉, 부분 read가 발생하더라도) 반복하지 않습니다.

제가 asleea님이 참조하시는 책의 내용을 읽어보지 않아 확답은 드릴 수 없으나 여러 정황을 종합하여 추측해서 답변을 드리자면
r_write는 기대값만큼 데이터를 쓰는 경우에만 정상처리라고 생각하는 것 같습니다. 그러나 r_read의 경우 EOF를 만나면 기대값 이하로 데이터를 읽어들이는 케이스가 발생할 수 있으며 이 또한 매우 정상적입니다.
r_read를 r_write 와 같이 기대값만큼 지속적으로 반복시도를 하는 경우 EOF로 인해 문제가 발생할 수 있겠지요.
그래서 r_read는 기대값만큼 읽는 것을 함수 내부가 아닌 외부로 위임한 것이 아닌가 생각해봅니다.

혹은 EOF를 만나는 예외케이스가 아닌 경우에는 기대값만큼 처리할 수 있도록 asleea님이 직접 r_read를 수정하실 수도 있겠군요.

저도 님 덕분에 많이 배우게 됩니다. 감사드립니다 :)

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.