라이브러리 함수(fopen, fread, fwrite)와 시스템 함수(open, read, write)의 차이

익명 사용자의 이미지

궁금해서 구글링하다가 KLDP에 유사한 질문과 답변이 있어서 참고했는데 사실 이해가 그렇게 잘 되지 않아서요.

https://kldp.org/node/2864

답을 달아주신 분의 답변을 보면 fread, fwrite의 경우 중간 버퍼/경유 버퍼(intermediate buffer)가 존재해서 작은 데이터 양이면서 잦은 호출이 발생할 때 read, write보다 더 빠른 속도로 처리를 할 수 있다고 하셨는데요.

이것을 저는 이렇게 이해했습니다. read, write도 버퍼가 존재하며 그 사이즈를 정할 수 있도록 함수의 인자값으로 받죠. fread, fwrite는 라이브러리 함수가 앞의 시스템 함수와 다른 점은 내부적으로 read, write를 사용하면서 또 하나의 버퍼를 생성시켜서 데이터를 다룬다는 것으로 해석했습니다.

그런데 여기서 조금 이해가 되질 않는 부분이 있는데요.

버퍼를 두 개 생성하면 정말 처리 속도가 빨라지냐는 것인데요. 이건 제가 라이브러리 함수를 보고서 말씀드리는 것은 아닙니다. 제 예측으로는 fread, fwrite 함수 내부에 별도로 버퍼를 생성하고 시스템 함수인 read, write에는 일괄적인 크기의 버퍼(작지는 않을 것이라고 봅니다.) 사이즈로 호출할 것 같습니다.

버퍼의 장점은 여러 번 데이터를 전달하는 것을 한 번의 데이터 뭉치 전달로, 즉 오버헤드를 줄이기 위함이라고 알고 있는데 이게 버퍼를 몇 개 두느냐에 따라 더 성능이 좋아진다는 것에 잘 공감이 되지 않는다고 해야 할까요.

정리해서 말씀드리자면 다이렉트로 시스템 콜 함수를 호출해서 사용할 수 있음에도 불구하고 라이브러리 함수를 사용하는 이유인, 중간 버퍼의 장점이 대체 무엇인지 모르겠다는 것이에요.

익명 사용자의 이미지

집 앞 수퍼마켓에 이미 냉장고가 있는데 왜 집에도 또 냉장고가 있어야 하느냐는 질문과 비슷합니다.

1. read, write와 같은 syscall은 기본적으로 비쌉니다. syscall 자체의 동작에 필요한 비용과 상관없이, syscall을 호출할 때 유저-커널모드 장벽을 넘어가며 데이터를 주고받는 데 고정적인 비용이 들어가기 때문입니다. 유저모드 안에서만 노는 C library 함수들(fread, fwrite)과는 비교가 안 됩니다.

따라서 고작 몇 바이트 읽고 쓰자고 read, write를 거듭 호출하기는 매우 비효율적입니다. 음료수 한 캔 마시려고 매번 외출복 차려입고 나갔다 오는 셈이죠. 한번에 잔뜩 사다 두면 편하잖아요?

2. C library는 더 다양하고 고차원적인 기능을 제공할 수 있습니다.

예컨대, C언어에서 한 번 읽은 글자를 다시 무를 수도 있다는 걸 알고 계시는지요:
https://en.cppreference.com/w/c/io/ungetc

이런 기능이 있는 이유는 종종 입력을 어디에서 끊어야 하는지 판단하기 위해 다음 글자를 볼 필요가 있는데, 정말 입력을 끊는다면 미리 봤던 "다음 글자"는 다음을 위해 남겨둬야 하는 경우가 있기 때문이죠. Lookahead parser가 대표적인 경우입니다만, 모르셔도 상관없습니다. 아무튼.

무엇이 syscall이어야 하는가에 대한 관점은 사람마다 대체로 다르겠지만, 이런 기능까지 syscall로 일일히 만들어 두는 건 별로 바람직하지 않은 설계라는 점에 아마도 동의하실 거라고 생각합니다. 언어 라이브러리 차원에서 구현하는 게 합리적이지요.

대충 비유하자면, 가정용 냉장고엔 얼음 정수기 기능 같은 게 있을 수 있는 겁니다. 수퍼마켓 냉장고엔 없겠지만요.

익명 사용자의 이미지

답변 정말 감사합니다.

제가 알고 있는 선상에서 답변해주신 것을 정리하자면

시스템 콜을 사용하면 유저모드에서 커널모드로 변경해야 하고 또 함수 수행 완료시 다시 유저모드로 돌아와야하는 부분에서 큰 비용이 발생하기 때문에
최대한 시스템 콜을 적재적소한 시기에 사용하기 위해 라이브러리 함수를 통해(정확히는 중간 버퍼의 생성으로) 컨트롤 한다는 것으로 이해했습니다.

입출력 버퍼가 비워지는 시점과 무관하게 시스템 콜 호출만으로 여러 작업을 수행해야 하기 때문에 일반 응용 프로그램에서 발생하는 데이터 입출력을 처리하기 위해선 라이브러리 함수를 사용하는 게 더 현명하다고 말씀하시는 것 같습니다.

틀린 부분이 있다면 말씀해주시고 다시 한번 감사합니다. 명쾌하게 해소되네요!

익명 사용자의 이미지

유감입니다만 저는 귀하께서 정리하신 내용을 이해할 수가 없군요. 특히 아래 두 대목이 그렇습니다.

Quote:
최대한 시스템 콜을 적재적소한 시기에 사용하기 위해 라이브러리 함수를 통해(정확히는 중간 버퍼의 생성으로) 컨트롤 한다

Quote:
입출력 버퍼가 비워지는 시점과 무관하게 시스템 콜 호출만으로 여러 작업을 수행해야 하기 때문에 일반 응용 프로그램에서 발생하는 데이터 입출력을 처리하기 위해선 라이브러리 함수를 사용하는 게 더 현명하다

익명 사용자의 이미지

답변 달아주신 분이신지는 모르겠으나

1. 최대한 시스템 콜을 적재적소한 시기에 사옹하기 위해 라이브러리 한수로 컨트롤 한다.

좀 더 풀어서 말씀드리자면 답변해주신 것처럼 시스템 콜 함수는 커널 모드로 진입해서 현재 프로그램의 수행 흐름을 변경해야 합니다.
커널 모드의 진입은 cpu의 mode 변환뿐만 아니라 많은 일들을 포함하고 있잖아요? 굳이 설명드리지 않아도(커널 모드 진입 이전의 프로세스 상태 보존을 위해 커널 스택에 담고 pc값도 바꿔야하고 등등..) 알고 있으실거라 생각됩니다.
좋은 프로그램의 기준은 개발자마다 다르겠지만 서버 프로그램이 아닌 조금 낮은 레벨에서 작은 데이터 입출력을 위해 시스템 함수를 빈번히 호출한다먼 조심스럽지만 바람직하지 않다고 볼 수 있다는 것이죠. 그래서 최대한 시스템 콜을 나중에 사용하기 위해 라이브러리 함수로 중간 버퍼를 생성해서 관리한다고 해야 할까요. 그런 의미로 말씀 드린 것이었어요.

2. 입출력 버퍼가 비워지는 시점과 무관하게 시스템 콜 함수 호출만으로 여러 작업을 수행해야 해서 라이브러리 함수를 사용하는 기 좋다.

앞에 내용과 비슷한데 현재 입출력 함수에 대해 말씀드리고 있었습니다. 이또한 아실거라 생각됩니다만 read, write 함수 호출만으로 바로 데이터가 전송된다(버퍼가 비워진다는 것)라고 할 수 없음을 의미하는 것이었습니다. 그래서 입출력 버퍼의 flush 시잠과 관계없이 앞에 언급한 루틴을 수행해야 해서 큰 데이터가 오가는 서버 수준의 프로그램이 아니라면 굳이 시스템 함수를 사용할 필요가 있느냐는 것이죠.

사실 결과만 따지면 라이브러리 함수를 사용하든 시스템 콜 함수를 사용하든 동일하게 지정한 곳으로 데이터가 입출력되겠죠.
하지만 성능을 고려한다면 조금 다른 결론이 지어질 수 있을 것 같아요. 이는 개발자가 고려해서 선택할 부분이죠. 저의 질문의 요지는 라이브러리 함수를 사용하는 게 좋긴한데 그게 중간버퍼 생성과 어떤 연관이 있는지 궁금했던 거였어요.

저의 얕은 수준의 지식으로 댓글을 달았는데 혹시 틀린 부분이 있다면 말씀 부탁드리고 감사합니다!

지나가는 나그네의 이미지

1. 유저모드와 커널모드간의 전환 비용(Context Switching.. 등)은 꽤 비싸다.
2. 시스템 콜은 이런 유저모드와 커널모드 전환 비용의 문제를 고스란히 가지고 있다.
3. 시스템 콜을 최대한 줄여서 성능 하락을 피하고 싶다.
4. 유저모드 입/출력 함수에서 별도의 버퍼를 생성하여 시스템 콜의 호출 횟수를 낮춘다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.