【问题标题】:AF_UNIX socket overhead?AF_UNIX 套接字开销?
【发布时间】:2012-06-09 14:50:59
【问题描述】:

我看到一对由调用创建的 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() 返回实际写入的字节数。你应该把这些加起来,而不是假设它们都写好了。
  • 最坏的情况是我写的比我声称的要少,这会使域套接字上的开销更加严重。

标签: c linux sockets networking


【解决方案1】:

查看 socket(7) 手册页。有一段写着:

SO_SNDBUF 设置或获取最大套接字发送缓冲区(以字节为单位)。内核将该值加倍 (为簿记开销留出空间)当它使用 setsockopt(2) 设置时,并且这个 getsockopt(2) 返回加倍的值。默认值由 /proc/sys/net/core/wmem_default 文件,最大允许值由 /proc/sys/net/core/wmem_max 文件。此选项的最小(加倍)值为 2048.

因此,开销似乎只是保存内核的簿记信息。

【讨论】:

  • 我什至不确定这是否适用于本地套接字,并且可用缓冲区空间的简单减半仍然不能解决我看到的所有开销。
  • 手册页不区分 AF_UNIX 或非本地域,所以我假设它适用于所有领域。这就是我能找到的关于这种情况的所有文件。我怀疑如果您需要“确切地”知道开销用于您将不得不查看内核网络代码。
  • 我没有接受这个答案,因为我认为即使有两倍的开销,我仍然看到每条消息太多。即使内核只让我使用 62464 字节,那么我应该能够在填充缓冲区之前写入 15000 多条消息,而我只看到其中的 1/30。
  • 您需要阅读内核网络代码,以了解如何处理我之前提到的开销的所有细节。我只是指出手册页中关于开销的内容。
【解决方案2】:

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

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

【讨论】:

  • 我不知道,不。不过,这适用于这里吗,因为我使用的是 SOCK_STREAM 类型?
  • 不,这应该只适用于数据报套接字,至少在我正在查看的内核版本上。
  • 事实上,我不明白为什么如果达到 wmem_max,unix 数据报套接字写入会变短。
  • 我可以说增加net.unix.max_dgram_qlen 不会改变开销。我的系统的默认值为 10,我将其更改为 200,程序输出或计算开销没有任何变化。
  • 除了重点之外,没有太多错误。不过,你是对的。请停止支持我;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-30
  • 2013-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多