AF_UNIX套接字开销是多少?

21

我发现一对用类似以下方式创建的AF_UNIX套接字存在一些奇怪的问题:

 socketpair(AF_UNIX, SOCK_STREAM, 0, sfd); 

sfd是一个int [2]数组,用于文件描述符。

首先,默认的缓冲区大小似乎恰好为122K(124928字节),而不是来自/proc/sys/net的任何内容(例如设置为128K的wmem_default)。有人知道这个奇怪的缓冲区大小的原因吗?

其次,在通过套接字写入小消息(8个字节)时,我只能写入423个消息,然后写入就会阻塞,这只有8 * 423 = 3384字节,另一个奇怪的大小。这些消息的作用好像每个消息占用295 +一点字节。这种开销的来源是什么?

在RHEL6(2.6.32,64位)上运行。

我编写了一个程序来尝试不同大小的数据以比较开销成本:

#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define DATA_SIZE 4

void run(size_t size) {
    int sfd[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
        perror("error");
    }


    int sndbuf, sbsize = sizeof(sndbuf);
    getsockopt(sfd[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, (socklen_t*)&sbsize);

    printf("Data Size: %zd\n", size);
    char buff[size];   
    size_t wrote=0;
    for (size_t ii=0; ii < 32768; ii++) {
        if ((send(sfd[0], buff, size, MSG_DONTWAIT) == -1) && (errno == EAGAIN)) {
            wrote = ii;
            break;
        }
    }

    printf("Wrote:     %zd\n", wrote);

    if (wrote != 0) { 
        int bpm = sndbuf/wrote;
        int oh  = bpm - size;

        printf("Bytes/msg: %i\n",  bpm);
        printf("Overhead:  %i\n",  oh);
        printf("\n");
    }

    close(sfd[0]); close(sfd[1]);
}

int main() {
    int sfd[2];
    socketpair(AF_UNIX, SOCK_STREAM, 0, sfd);

    int sndbuf, sbsize = sizeof(sndbuf);
    getsockopt(sfd[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, (socklen_t*)&sbsize);

    printf("Buffer Size: %i\n\n", sndbuf);
    close(sfd[0]); close(sfd[1]);

    for (size_t ii=4; ii <= 4096; ii *= 2) {
        run(ii);
    }
}

这会产生:

Buffer Size: 124928

Data Size: 4
Wrote:     423
Bytes/msg: 295
Overhead:  291

Data Size: 8
Wrote:     423
Bytes/msg: 295
Overhead:  287

Data Size: 16
Wrote:     423
Bytes/msg: 295
Overhead:  279

Data Size: 32
Wrote:     423
Bytes/msg: 295
Overhead:  263

Data Size: 64
Wrote:     423
Bytes/msg: 295
Overhead:  231

Data Size: 128
Wrote:     348
Bytes/msg: 358
Overhead:  230

Data Size: 256
Wrote:     256
Bytes/msg: 488
Overhead:  232

Data Size: 512
Wrote:     168
Bytes/msg: 743
Overhead:  231

Data Size: 1024
Wrote:     100
Bytes/msg: 1249
Overhead:  225

Data Size: 2048
Wrote:     55
Bytes/msg: 2271
Overhead:  223

Data Size: 4096
Wrote:     29
Bytes/msg: 4307
Overhead:  211

使用管道肯定会有很多开销:

Data Size: 4
Wrote:     16384
Bytes/msg: 4
Overhead:  0

Data Size: 8
Wrote:     8192
Bytes/msg: 8
Overhead:  0

Data Size: 16
Wrote:     4096
Bytes/msg: 16
Overhead:  0

Data Size: 32
Wrote:     2048
Bytes/msg: 32
Overhead:  0

Data Size: 64
Wrote:     1024
Bytes/msg: 64
Overhead:  0

Data Size: 128
Wrote:     512
Bytes/msg: 128
Overhead:  0

Data Size: 256
Wrote:     256
Bytes/msg: 256
Overhead:  0

Data Size: 512
Wrote:     128
Bytes/msg: 512
Overhead:  0

Data Size: 1024
Wrote:     64
Bytes/msg: 1024
Overhead:  0

Data Size: 2048
Wrote:     32
Bytes/msg: 2048
Overhead:  0

Data Size: 4096
Wrote:     16
Bytes/msg: 4096
Overhead:  0

send() 返回实际写入的字节数。你应该将它们累加起来,而不是仅仅假设所有内容都被写入了。 - user207421
1
最坏的情况是我写的比我声称的还要少,这会使域套接字的开销变得更糟。 - gct
2个回答

7
请查看socket(7)手册页。其中有一节:
SO_SNDBUF 设置或获取套接字发送缓冲区的最大字节数。当使用setsockopt(2)设置时,内核将此值加倍(以留出用于簿记开销的空间),并且getsockopt(2)返回这个加倍后的值。默认值由/proc/sys/net/core/wmem_default文件设置,允许的最大值由/proc/sys/net/core/wmem_max文件设置。此选项的最小(加倍)值为2048。
因此,似乎开销只是为了保存内核的簿记信息。

我甚至不确定这是否适用于本地套接字,而且简单地减少可用缓冲区空间的一半仍然无法解释我所看到的所有开销。 - gct
1
手册没有区分AF_UNIX或非本地域,所以我假设它适用于所有情况。这是我能找到的有关情况的所有文档。如果您需要知道“确切”开销用于什么,我怀疑您将不得不查看内核网络代码。 - Chimera
2
我还没有接受这个答案,因为我认为即使有两倍的开销,每条消息仍然太多了。即使内核只允许我使用62464字节,那么在填满缓冲区之前,我应该能够写入15000多条消息,但我只看到了其中的1/30。 - gct
您需要阅读内核网络代码,以获取关于开销的详细信息,正如我之前提到的。我只是指出手册中关于开销的内容。 - Chimera

0

您有没有查看过net.unix.max_dgram_qlen sysctl的值?

内核对在飞行中的AF_UNIX datagrams的最大数量施加了限制。在我的系统上,该限制实际上非常低:只有10个。


我不知道那个,不是很清楚。因为我正在使用SOCK_STREAM类型,所以这是否适用于这里呢? - gct
1
不,那只适用于数据报套接字,至少在我查看的内核版本上是这样。 - Kristof Provost
1
事实上,我无法理解为什么Unix数据报套接字写入在达到wmem_max时会出现短路的情况。 - Kristof Provost
我可以说增加 net.unix.max_dgram_qlen 不会改变开销。我的系统默认值为10,我将其更改为200,但程序输出或计算的开销没有任何变化。 - Chimera
1
不是完全错误,只是离题了。不过,你说得对。请不要再给我点赞 ;) - Kristof Provost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接