readv와 writev 함수도 효율성을 향샹시키는데 도움이 되는 함수들이다. 일단 사용 방법부터
살펴보고 어떠한 경우에 사용되는지 알아보도록 하자.
이 함수들은 한 마디로 표현하면 "데이터를 모아서 전송하고 데이터를 나누어서 수신하는 함수"
라고 말할 수 있다. 즉 writev 함수를 사용하게 되면 여러 버퍼에 저장되어 있는 데이터를 한 번에
전송하게 되고, 또 readv 함수를 사용하면 데이터를 여러 버퍼에 나누어서 수신할 수 있게 된다.
------------- writev 함수 선언 -------------------
#include <sys/uio.h>
int writev(int fd, const struct iovec *vector, int count);
리턴값 : 성공시 전송한 바이트 수, 실패시 -1 리턴
인자값 :
- fd : 데이터 전송의 목적지를 나타내는 소켓의 파일 디스크립터를 전달한다.
반드시 소켓에만 제한되는 함수가 아니다. read, write 함수처럼 파일이나
콘솔을 입, 출력 대상으로 할 수도 있다.
- vector : 일반적으로 iovec 구조체 배열의 이름을 인자로 전달하는데, iovec 구조체에는
전송하고자 하는 데이터에 대한 정보가 담겨진다.
- count : 데이터를 전송하기 위해 참고할 iovec 구조체 변수의 수를 지정한다. 만약에 3이 인자로
전달되면, vector가 가리키는 iovec 구조체 변수를 시작으로 총 3개의 iovec 변수를 참고하여
데이터를 전송하게 된다.
------------------------------------------------
----------------- iovec는 구조체의 선언 ------------
struct iovec
{
ptr_t iov_base;
size_t iov_len;
};
iov_base : 전송할 데이터의 시작 주소를 가리킨다.
iov_len : iov_base가 가리키는 위치를 시작으로 전송하고자 하는 바이트 수를 대입한다.
뒤에나오는 readv 함수에서는 수신하고자 하는 최대 바이트 수를 나타내는 변수로 그의미가
달라진다.
-------------------------------------------------------
-------------- writev 함수 사용 예제 -------------------
#include <sys/uio.h>
#include <stdio.h>
#include <string.h>
int main()
{
struct iovec iov[2] = {0};
char buf1[] = "Hello!!! ";
char buf2[] = "I am Hacker!!\n";
iov[0].iov_base = buf1;
iov[0].iov_len = strlen(buf1);
iov[1].iov_base = buf2;
iov[1].iov_len = strlen(buf2);
writev(1,iov,2);
return 0;
}
-------------------------------------------------------
-------------------------------------------------
[결과화면]
[root@localhost readv_writev]# ./writev.exe
Hello!!! I am Hacker!!
--------------------------------------------------------
이번에는 readv 함수에 대해 살펴보자. readv 함수는 writev 함수의 인자와 인자 값의
의미를 반대로 생각하면된다.
--------------------- readv 함수 선언 ----------------
#include <sys/uio.h>
int readv(int fd, const struct iovec *vector, int count);
리턴 값 : 성공시 수신한 바이트 수, 실패 시 -1 리턴
인자값 :
- fd : 데이터를 수신할 파일(혹은 소켓)의 파일 디스크립터를 인자로 전달한다.
- vector : writev 함수에서 사용되던 용도와 비슷화다. 어디에다가 얼마큰 데이터를 수신할 것인에
대한 정보를 iovec 구조체 변수에다가 넣어서 그 배열의 이름을 인자로 전달한다.
- count : 데이터를 수신하기 위해서 참고할 iovec 구조체 변수의 수를 지정한다. 만약에 3이 인자로
들어가면 vector가 가리키는 iovec 구조체 변수를 시작으로 총 3개의 iovec 변수를 참고하여
데이터를 수신하게 된다.
----------------------------------------------------------------------
--------------------- readv 사용 예제 -------------------
#include <stdio.h>
#include <sys/uio.h>
int main()
{
struct iovec iov[2];
char buf1[10] = {0};
char buf2[10] = {0};
int strLen = 0;
iov[0].iov_base = buf1;
iov[0].iov_len = sizeof(buf1) - 1;
iov[1].iov_base = buf2;
iov[1].iov_len = sizeof(buf2) - 1;
strLen = readv(0,iov,2);
printf("total : %d\n",strLen);
printf("buf1 : %s\n",buf1);
printf("buf2 : %s\n",buf2);
return 0;
}
--------------------------------------------------------------------------------
------------------------------------------------------------------------------------
[결과화면]
[root@localhost readv_writev]# ./readv.exe
I am Hacker!!
total : 14
buf1 : I am Hack
buf2 : er!!
------------------------------------------------------------------------------------
[ readv & writev 함수의 적절한 사용 ]
어떠한 경우가 readv와 writev 함수를 적절히 사용한 예에 해당할까? 사용할 수 있는 모든 경우가
다 적절한 사용의 예라고 한다.. (뭐 지.. ㅋ)
즉 전송해야 할 데이터가 여러 개의 배열에 나뉘어서 저당되어 있는 경우에는, 모두 전송하기 위해서
write 함수를 여러 번 호출해야 하는데, 이것보다는 한 번의 writev 함수 호출이 더 효율적이다.
또한 입력 버퍼에 수신된 데이터를 여러 배열에 나누어서 읽어 들이고 싶은 경우에도
read 함수를 여러 번 호출하는 것보다 readv 함수를 여러번 호출하는 것이 더 효율적이다.
그렇다면 어떤 이유에서 보다 효율적이라고 말할 수 있는가 ?
일단 간단하게 C언어의 차원에서 생각해 봐도, 함수 호출이 적으면 그만큼 성능 향상을 가져오므로
이득일 것이다. 그러나 전송되는 패킷의 수를 줄일 수 있다는 것이 더큰 이유가 된다.
서버의 성능 향상을 위해서 Nagle 알고리즘을 사용을 오프(Off : 사용하지 않음) 시켰을 때를
예로 들어보자. 사실 writev 함수는 Nagle 알고리즘을 오프(Off)시켰을 때 더 사용가치가 높다.
한 번에 전송하고 싶은 데이터가 있는데 모두 세곳의 배열에 나누어 저장되어 있다고 가정해 보자
모두 다 전송하기 위해서는 세 번의 write 함수를 호출해야만 한다. 그런데 속도 향상을 목적으로
이미 Nagle 알고리즘이 오프(Off)되어 있었다면 총 몇 개의 패킨이 생성되어 전송되겠는가?
Nagle 알고리즘을 적용하지 않는 경우에는 출력 버퍼에 데이터가 들어오자 마자 데이터를 블록화
해서 전송하기 때문에 총 세 개의 패킷이 생성되어 전송된다. 여기서 우리는 한꺼번에 블록화해서
전송해도 되는 데이터를 세 개의 패킷으로 나누어 보낸 것이 된다. 이는 전체 네트워크 입장에서도
수신하는 호스트 입장에서도 그리 좋은 결과가 아니다. 같은 양의 데이터를 전송하더라도
나누어서 전송하게 될 경우에는 전송되는 헤더의 수가 그 만큼 많이지기 때문에 네트워크 입장에서는
트래픽이 증가하게되고, 수신하는 호스트 입장에서는 처리해야하는 패킷의 수가 증가하게 된다.
이러한 경우에 write함수를 여러 번 호출 하는 것 대신에, writev 함수를 사용하게 되면
여러 배열에 저장되어있던 데이터를 하나의 패킷에 모두 담아서 한 번에 전송할 수 있 게된다.
비록 Nagle 알고리즘이 동작하고 있는 상태라도 말이다.
한 가지 더 댕각해 볼것은 여러 배열에 나뉘어 있는 데이터를 모두 담을 수 있는 큰 배열을 생성해서
그 안에다가 작은 배열에 존재하는 데이터를 차례로 복사한 다음에 write 함수를 호출해서
한 번에 전송하면 어떨까 하는 것이다. writev 함수를 호출한 것은 같은 효과를 얻을 수 있지 않을까?
그렇다 그러나 그것보다는 writev 함수를 사용하는 것이 더 편리하다. 뿐만 아니라.
불필요하게 큰 배열을 할당할 필요도 없게 된다.
자! 다시 결론은 하나다. writev 함숨와 readv 함수를 사용할 상황이 되면 적극 사용하도록 하자.
RECENT COMMENT