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 함수를 사용할 상황이 되면 적극 사용하도록 하자.