星期一, 4月 20, 2009

NoneBlock sendfile


一般copy 的狀況下
  1. 從HW 的資料透過DMA 搬移至Kernel
  2. 再從 Kernel 搬至User space,送到Socket 時再搬依次。
  3. CPU Copy x 2 + DMA x 2
使用mmap + write 的狀況:
  • 從HW 的資料透過DMA 搬移至Kernel
  • 直接從 Kernel space 將Buffer搬至Socket buffer。
  • 缺點:當User/Kernel space 兩塊同時操作時須有解決方案,多了sendfile 一次的context switch。
  • 優點:當要送至socket時有sendfile的效能,user space也能存取資料。
  • 花費:CPU Copy x 1 + DMA x 2

使用Sendfile 的狀況:
  • 從HW 的資料透過DMA 搬移至Kernel
  • 直接從 Kernel space 將Buffer搬至Socket buffer。
  • 缺點: User space 無法取得資料。
  • 花費:CPU Copy x 1 + DMA x 2
較佳的方式:
  • 從HW 的資料透過DMA 搬移至Kernel
  • 利用HW support gather 的方式,直接從 socket/kernel dscr pointer 利用DMA gather的方式將資料直送底層。
  • 這個步驟目前還不確定方式,目前看來從AP層好像沒有現成的,要從Driver request DMA channel,setup, start 等動作,要找一下。
    asfd
  • 缺點: 需要HW support。
  • 花費:DMA x 2

DMA的操作,基本上DMA的動作,需要follow HW Handshaking,基本上會跑類似下列流程:

  • request_dma()
  • dma_setup_handlers()
  • dma_config_src()
  • dma_config_dst()
  • dma_setup_single
  • dma_enable
  • ==> wait for transfer complete event
  • dma_disable
  • dma_free
  • MultiBlock 的話還需要設定 setup_sg or setup_mlist








下面例子用以前寫的socket function測試,listen/connect 不列出來了,主要是測試 NoneBlock sendfile 的狀況,以及 EAGAIN 旗標產生的主因。( client/server在同一台機器)

以下四種狀況都會影響到Client side 每次下send 的大小。
  • Client 端 connect target ip = 192.168.1.85 or 127.0.0.1
  • Server端收到就丟棄。
  • Server端收到後存檔。
  • Server端收到後印出收到Size。

Server Side Code:

#include <stdio.h>
#include <stdlib.h>
#include <socketServer.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int fd, times=0;
void callback( int fd, unsigned char *buf, int size ){
// printf("MyFunction:\n FD[%d] str=%s size=%d\n", fd, buf, size );
// printf("[%d] MyFunction:\n FD[%d] size=%d\n", times, fd, size );
// fd = open("server.dat", O_WRONLY | O_APPEND );
// write( fd, buf, size );
// close( fd );
times++;
}

int main( int argc, char ** arg ) {
SOCKET_SERVER server;
int ret;
system("rm server.dat;touch server.dat");
// use User Define Callback
#if 1
ret = server.initServer( 9600, callback );
#else
ret = server.initServer( 9600);
#endif
printf(" server ret =%d\n", ret );

while ( 1 ) {
server.clientList();
sleep(3);
}
return EXIT_SUCCESS;
}



Client Side Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <socketClient.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <errno.h>

int main( int argc, char ** arg ) {

SOCKET_CLIENT client;
int ret;
int fd;
//init sock
//ret = client.initClient( "127.0.0.1", 9600 );
ret = client.initClient( "192.168.1.85", 9600 );
if( ret != EXIT_SUCCESS ){
printf("Client create FAIL ret=%d\n", ret );
}

//send mydata
fd = open( "mydata", O_RDONLY);
int times=0;
size_t r;
off_t offset = 0;

struct timeval tv;
fd_set wfds;

while( 1 ){
FD_ZERO( &wfds );
FD_SET( client.m_iFD, &wfds);
tv.tv_sec = 1; tv.tv_usec = 0;
ret = select( client.m_iFD+1, NULL, &wfds, NULL, &tv);
if ( ret <= 0 ) printf("timeout or ret=%d\n", ret );

r = sendfile( client.m_iFD, fd, &offset, 1024*1024);
if( r == 0 ) break;
if( r == -1 ){//&& errno == EAGAIN ){
printf("EAGAIN\n");
continue;
}
printf("[%d]sendfile offset=%d r=%d \n", times,(int)offset, r);
times++;
}
return EXIT_SUCCESS;
}



Run: 可以看到連下兩道後的結果並不相同,但Server端是相同的,且不會出EAGAIN,EAGAIN發生的時機點為連下兩道sendfile才會發生,select 當次即使是送不完也不會發生。也就是說EAGAIN這個flag 是在ret = -1 時產生,buf 裡的位置為0 才會發生。
[root@192 ipSock_noneblock_sendfile]# ./client
[0]sendfile offset=1048576 r=1048576
[1]sendfile offset=2097152 r=1048576
[2]sendfile offset=2179072 r=81920
[3]sendfile offset=2785280 r=606208
[4]sendfile offset=2949120 r=163840
[5]sendfile offset=3112960 r=163840
[6]sendfile offset=3276800 r=163840
[7]sendfile offset=3440640 r=163840
[8]sendfile offset=3604480 r=163840
[9]sendfile offset=3768320 r=163840
[10]sendfile offset=3932160 r=163840
[11]sendfile offset=4096000 r=163840
[12]sendfile offset=4259840 r=163840
[13]sendfile offset=4423680 r=163840
[14]sendfile offset=4538368 r=114688
[15]sendfile offset=4685824 r=147456
[16]sendfile offset=4833280 r=147456
[17]sendfile offset=4964352 r=131072
[18]sendfile offset=5095424 r=131072
[19]sendfile offset=5226496 r=131072
[20]sendfile offset=5554176 r=327680
[21]sendfile offset=5898240 r=344064
[22]sendfile offset=6045696 r=147456
[23]sendfile offset=6193152 r=147456
[24]sendfile offset=6324224 r=131072
[25]sendfile offset=6455296 r=131072
[26]sendfile offset=6586368 r=131072
[27]sendfile offset=6717440 r=131072
[28]sendfile offset=6848512 r=131072
[29]sendfile offset=6979584 r=131072
[30]sendfile offset=7110656 r=131072
[31]sendfile offset=7241728 r=131072
[32]sendfile offset=7372800 r=131072
[33]sendfile offset=7503872 r=131072
[34]sendfile offset=7634944 r=131072
[35]sendfile offset=7766016 r=131072
[36]sendfile offset=7897088 r=131072
[37]sendfile offset=8028160 r=131072
[38]sendfile offset=8159232 r=131072
[39]sendfile offset=8191034 r=31802
[root@192 ipSock_noneblock_sendfile]# ./client
[0]sendfile offset=1048576 r=1048576
[1]sendfile offset=2097152 r=1048576
[2]sendfile offset=3145728 r=1048576
[3]sendfile offset=4194304 r=1048576
[4]sendfile offset=4538368 r=344064
[5]sendfile offset=5586944 r=1048576
[6]sendfile offset=6635520 r=1048576
[7]sendfile offset=7684096 r=1048576
[8]sendfile offset=8191034 r=506938


Conclusion:
  • 查了一下原因,也許跟下面有關,不確定,把wmem_max值加大,當busy時send出去的值就會接近設定的值。
    cat /proc/sys/net/core/wmem_max
    131071
  • 為何要用sendfile
    1.減少TLB cache flush,使用read/write function會導致 cashe 異動。
    2.減少kernel/user space context switch 次數。
    3.Copy 次數減少。
  • 長遠來看 IPCam/ DVR 長時間處理影像傳輸,應該使用第四種方式,減少CPU Loading,除了增加Performance,還可以增加 Client 的 Throughput。

沒有留言: