(過去の記事の転載)
recvmmsg というのは、recvmsg(2)を拡張して複数のメッセージをsocketから受け取れるシステムコールで、Linux の 2.6.33 から利用できます。 glibcの version 2.12 からサポートされてます。 これを使うと、要するに1回のシステムコールで複数回recvmsgをすることができるので、messageをどかどか受けるようなアプリケーションでパフォーマンスが上がることがあります。
実際に使ってみて効果が見られたのと、man読んだだけじゃ使い方分かりにくかったので、これを書いておく。
manをみれば使い方はなんとなく分かると思う。manにあるサンプルコードを一番下に貼りました。 複数個受けるだけではなくtimeoutを取れるのがrecvmsgと違うところで、manを読むとまあ普通のタイムアウトのように見えるんだけど、カーネルのソースを読むと厳密なタイムアウトにはなってないようです。(ということを教えてもらいました)
また、すぐに抜けてきて欲しいときに timeoutに0 (tvsec=0, tvnsec=0) をセットすると、1つrecvしただけで返ってきてしまうのも注意。 すぐに抜けて来て欲しいけど、そのときに取れるmessageは全て取りたい、という時には、flagに MSG_DONTWAIT をセットして、timeoutはNULLにして呼び出すと期待している動作になります。
ハマりやすいポイントみたいで、manが分かりにくいという意見もあるようです。
また、sendでも似たようなことをやるためのsendmmsgというのもあります。
以下に、manにあったサンプルコードを載せておく。
#define _GNU_SOURCE #include <netinet/ip.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> int main(void) { #define VLEN 10 #define BUFSIZE 200 #define TIMEOUT 1 int sockfd, retval, i; struct sockaddr_in sa; struct mmsghdr msgs[VLEN]; struct iovec iovecs[VLEN]; char bufs[VLEN][BUFSIZE+1]; struct timespec timeout; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket()"); exit(EXIT_FAILURE); } sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(1234); if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { perror("bind()"); exit(EXIT_FAILURE); } memset(msgs, 0, sizeof(msgs)); for (i = 0; i < VLEN; i++) { iovecs[i].iov_base = bufs[i]; iovecs[i].iov_len = BUFSIZE; msgs[i].msg_hdr.msg_iov = &iovecs[i]; msgs[i].msg_hdr.msg_iovlen = 1; } timeout.tv_sec = TIMEOUT; timeout.tv_nsec = 0; retval = recvmmsg(sockfd, msgs, VLEN, 0, &timeout); if (retval == -1) { perror("recvmmsg()"); exit(EXIT_FAILURE); } printf("%d messages received\n", retval); for (i = 0; i < retval; i++) { bufs[i][msgs[i].msg_len] = 0; printf("%d %s", i+1, bufs[i]); } exit(EXIT_SUCCESS); }