最近碰到一个问题,两个进程间需要实时交换一些数据,数据量不是很大,就72个字节。当时估计简单起见,用的是域套接字的方式。
后续性能测试的时候,忽然发现当网络包并发量很大时,性能忽然大幅下降,用strace跟踪发现,忽然有好多的read,write操作,查看代码跟踪到此处,发现是域套接字需要不断的读写操作,虽然保证了数据的安全按序到达,但是此种操作性能太低。自己就想,两者究竟相差多少呢?跑两个程序比较下,一目了然。代码有些是用的网上现成的,水平有限,勿喷。
结果写在前面防止你看不到:域套接字跟共享内存完全不在一个数量级,域套接字是步行的话,共享内存跟跑车差不多。
详细时间值大概相差30多倍(使用perf stat测试)。
1、域套接字客户端:
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/un.h> #include <errno.h> #include <string.h> #define DIS_MSG_FILE "/tmp/.dis_msg" typedef struct _key_args { unsigned int key; union { u_int32_t ipv4_addr; u_int32_t ipv6_addr[4]; } sip; union { u_int32_t ipv4_addr; u_int32_t ipv6_addr[4]; } dip; unsigned short sport; unsigned short dport; unsigned char protocol; unsigned short match_type; unsigned short iptype; //unsigned short mask; int core_id; }key_args_t; typedef struct _dis_msg_buff { long mtype; unsigned int key; int core_num; int msg_type; key_args_t karg; }dis_msg_buff_t; int main() { int i = 0; int fd = 0; int len = 0; int ret = 0; char reerrno = 0; struct sockaddr_un addr; dis_msg_buff_t msg; time_t t; t = time(NULL); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0){ printf("CLIENT: socket error\n"); return -1; } memset(&addr, 0x00, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, DIS_MSG_FILE,strlen(DIS_MSG_FILE)); len = sizeof(addr.sun_family); #ifdef HAVE_SUN_LEN len = addr.sun_len = SUN_LEN(&addr); #else len = sizeof (addr.sun_family) + strlen (addr.sun_path); #endif /* HAVE_SUN_LEN */ ret = connect(fd, (struct sockaddr *)&addr, len); if (ret<0){ printf("CLIENT: connect error\n"); return -1; } /*------------------------通信100w次---------------------------*/ while(i++ < 1000000){ memset(&msg, 0x00, sizeof(msg)); if(write(fd, &msg, sizeof(msg)) < 0) { printf("CLIENT: write error\n"); goto err; } while(1){ int nbytes; reerrno = 0; nbytes = read(fd, &reerrno, sizeof(reerrno)); if (nbytes <= 0 && errno != EINTR) goto err; if (nbytes > 0){ if (reerrno == 1) break; else goto err; } } } /*------------------------通信100w次-结束--------------------------*/ close(fd); printf("CLIENT: success %d\n", time(NULL)-t); return 0; err: close(fd); printf("CLIENT: failed\n"); return -1; }
2、域套接字服务器:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <unistd.h> #include <termios.h> #include <sys/stat.h> /**********定时器头文件***************/ #include <sys/time.h> #include <signal.h> /***********进程间SOCKET通信头文件**********/ #include <sys/socket.h> #include <sys/un.h> #define UNIX_DOMAIN "/tmp/.dis_msg" typedef struct _key_args { unsigned int key; union { u_int32_t ipv4_addr; u_int32_t ipv6_addr[4]; } sip; union { u_int32_t ipv4_addr; u_int32_t ipv6_addr[4]; } dip; unsigned short sport; unsigned short dport; unsigned char protocol; unsigned short match_type; unsigned short iptype; //unsigned short mask; int core_id; }key_args_t; typedef struct _dis_msg_buff { long mtype; unsigned int key; int core_num; int msg_type; key_args_t karg; }dis_msg_buff_t; void main() { socklen_t clt_addr_len; int listen_fd; int com_fd; int ret=0; char reerrno = 0; int i; dis_msg_buff_t msg; int len; struct sockaddr_un clt_addr; struct sockaddr_un srv_addr; //创建用于通信的套接字,通信域为UNIX通信域 listen_fd=socket(AF_UNIX,SOCK_STREAM,0); if(listen_fd<0) { printf("SERVER:cannot create listening socket\n"); return; } //设置服务器地址参数 srv_addr.sun_family=AF_UNIX; strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1); unlink(UNIX_DOMAIN); //绑定套接字与服务器地址信息 ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); if(ret==-1) { printf("cannot bind server socket\n"); close(listen_fd); unlink(UNIX_DOMAIN); return; } //对套接字进行监听,判断是否有连接请求 ret=listen(listen_fd,1); if(ret==-1) { printf("cannot listen the client connect request\n"); close(listen_fd); unlink(UNIX_DOMAIN); return; } chmod(UNIX_DOMAIN,00777);//设置通信文件权限 //当有连接请求时,调用accept函数建立服务器与客户机之间的连接 len=sizeof(clt_addr); com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len); if(com_fd<0) { printf("cannot accept client connect request"); close(listen_fd); unlink(UNIX_DOMAIN); return; } /*------------------------通信100w次---------------------------*/ while(1){ //读取并输出客户端发送过来的连接信息 memset(&msg,0x00,sizeof(msg)); int recv_php_num=read(com_fd,&msg,sizeof(msg)); if (recv_php_num <= 0){ printf("read error\n"); return; } reerrno = 1; write(com_fd,&reerrno,sizeof(reerrno)); reerrno = 0; } }
3、共享内存客户端:
#include <stdio.h> #include <stdlib.h> #include <sys/shm.h> #include <sys/ipc.h> #include <errno.h> #include <string.h> #include <time.h> #define MY_SHM_ID 815 struct shared_use_at{ int written_by_you; char some_text[72]; }; int main() { int shmid, ret; time_t t; t = time(NULL); shmid = shmget(MY_SHM_ID, sizeof(struct shared_use_at), 0666); if(shmid > 0) printf("Create a shared memory segment %d\n", shmid); else if(shmid == EEXIST){ printf("shared memory is exist!\n"); exit(0); } printf("shmid = %d, EEXIST = %d\n", shmid, EEXIST); struct shmid_ds shmds; ret = shmctl(shmid, IPC_STAT, &shmds); if(ret == 0){ printf("Size of memory segment is %d\n", (int)shmds.shm_segsz); printf("Number of attches %d\n", (int)shmds.shm_nattch); }else{ printf("shmctl() call failed\n"); } int running = 1; char buffer[100] = {0}; void *shared_memory = (void *)0; struct shared_use_at *shared_stuff; shared_memory = shmat(shmid, (void *)0, 0); if(shared_memory == (void*)-1){ printf("shmat failed\n"); exit(0); } shared_stuff = (struct shared_use_at *)shared_memory; /*------------------------通信100w次---------------------------*/ while(running++<1000000){ while(shared_stuff->written_by_you == 1){ } memset(shared_stuff->some_text, 0x00, 72); shared_stuff->some_text[0] = 'o'; shared_stuff->written_by_you = 1; } /*------------------------通信100w次--结束-----------------------*/ printf("time = %d\n", (int)(time(NULL) - t)); shared_stuff->some_text[0] = 'k'; shared_stuff->written_by_you = 1; ret = shmdt(shared_memory); if(ret == 0) printf("shmdt memory \n"); else printf("shmdt memory failed \n"); ret = shmctl(shmid, IPC_RMID, 0); if(ret == 0) printf("shared memory removed \n"); else printf("shared memory removed failed\n"); return 0; }
4、共享内存服务器:
#include <stdio.h> #include <stdlib.h> #include <sys/shm.h> #include <sys/ipc.h> #include <errno.h> #define MY_SHM_ID 815 struct shared_use_at{ int written_by_you; char some_text[72]; }; int main() { printf("page size = %d\n", getpagesize()); int shmid, ret; shmid = shmget(MY_SHM_ID, sizeof(struct shared_use_at), 0666|IPC_CREAT); if(shmid > 0) printf("Create a shared memory segment %d\n", shmid); struct shmid_ds shmds; ret = shmctl(shmid, IPC_STAT, &shmds); if(ret == 0){ printf("Size of memory segment is %d\n", (int)shmds.shm_segsz); printf("Number of attches %d\n", (int)shmds.shm_nattch); }else{ printf("shmctl() call failed\n"); } int running = 1; int arunning = 1; void *shared_memory = (void *)0; struct shared_use_at *shared_stuff; shared_memory = shmat(shmid, (void *)0, 0); if(shared_memory == (void*)-1){ printf("shmat failed\n"); exit(0); } shared_stuff = (struct shared_use_at *)shared_memory; shared_stuff->written_by_you = 0; /*------------------------通信100w次---------------------------*/ while(running++){ if(shared_stuff->written_by_you){ if(shared_stuff->some_text[0] == 'o') arunning++; if(shared_stuff->some_text[0] == 'k') break; shared_stuff->written_by_you = 0; } } /*------------------------通信100w次--结束-------------------------*/ printf("arunning = %d, running = %d \n", arunning, running); ret = shmdt(shared_memory); if(ret == 0) printf("shmdt memory \n"); else printf("shmdt memory failed \n"); ret = shmctl(shmid, IPC_RMID, 0); if(ret == 0) printf("shared memory removed \n"); else printf("shared memory removed failed\n"); return 0; }