Docker容器内以太网socket mmap内存失败问题求助
问题:Docker容器中AF_PACKET Socket的mmap内存映射失败(错误码11)
环境与复现信息
- Docker启动命令:
docker run --rm -it -e "PYTHONUNBUFFERED=1" --network=host snf
- 程序逻辑:通过AF_PACKET原始Socket配置TPACKET_V3版本、开启混杂模式、设置RX_RING后,启动两个线程各映射64MB内存(总计约128MB)
核心代码片段
int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)); if (packet_socket == -1) { printf("Can't create AF_PACKET socket: %s\n", strerror(errno)); return -1; } int version = TPACKET_V3; int setsockopt_packet_version = setsockopt(packet_socket, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)); if (setsockopt_packet_version < 0) { printf("Can't set packet v3 version\n"); return -1; } int interface_number = get_interface_number_by_device_name(packet_socket, interface_name); if (interface_number == -1) { printf("Can't get interface number by interface name\n"); return -1; } struct packet_mreq sock_params; memset(&sock_params, 0, sizeof(sock_params)); sock_params.mr_type = PACKET_MR_ALLMULTI; sock_params.mr_ifindex = interface_number; int set_promisc = setsockopt(packet_socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *) &sock_params, sizeof(sock_params)); if (set_promisc == -1) { printf("Can't enable promisc mode: %s\n", strerror(errno)); return -1; } struct sockaddr_ll bind_address; memset(&bind_address, 0, sizeof(bind_address)); bind_address.sll_family = AF_PACKET; bind_address.sll_protocol = htons(ETH_P_ALL); bind_address.sll_ifindex = interface_number; struct tpacket_req3 req; memset(&req, 0, sizeof(req)); req.tp_block_size = blocksiz; req.tp_frame_size = framesiz; req.tp_block_nr = blocknum; req.tp_frame_nr = (blocksiz * blocknum) / framesiz; req.tp_retire_blk_tov = 60; // Timeout in msec req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; int setsockopt_rx_ring = setsockopt(packet_socket, SOL_PACKET , PACKET_RX_RING , (void *) &req , sizeof(req)); if (setsockopt_rx_ring == -1) { printf("Can't enable RX_RING for AF_PACKET socket: %s\n", strerror(errno)); return -1; } uint8_t* mapped_buffer = NULL; struct iovec *rd = NULL; mapped_buffer = (uint8_t *) mmap(NULL, req.tp_block_size * req.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, packet_socket, 0); if (mapped_buffer == MAP_FAILED) { printf("mmap failed: %d %s\n", errno, strerror(errno)); return -1; }
报错输出
mem: 64 mb per iface; cores: 2 per iface mmap failed: 11 Resource temporarily unavailable mmap failed: 11 Resource temporarily unavailable
已尝试操作
- 使用sudo运行Docker容器
- 容器内以root身份启动应用
- 宿主机直接运行程序无异常
问题原因与解决方法
错误码11(EAGAIN)的核心原因是Docker容器默认的内存锁定(memlock)限制过低,代码中mmap使用了MAP_LOCKED标志,会将映射区域锁定到物理内存,而容器默认memlock配额远不足以支撑128MB的锁定需求。
解决步骤
启动容器时解除memlock限制
在Docker启动命令中添加--ulimit memlock=-1:-1参数,将内存锁定的软、硬限制设为无限制:docker run --rm -it -e "PYTHONUNBUFFERED=1" --network=host --ulimit memlock=-1:-1 snf验证限制是否生效
进入容器后执行以下命令,输出应为unlimited:ulimit -l可选:调整容器内存配额
如果容器有内存使用上限,需确保分配的内存大于映射需求,例如:docker run --rm -it -e "PYTHONUNBUFFERED=1" --network=host --ulimit memlock=-1:-1 --memory=512m snf
补充说明
宿主机运行正常是因为root用户默认memlock限制较高或无限制,而Docker容器即使以root身份运行,也会继承daemon的默认资源限制,其中memlock默认值通常仅为几十KB,无法满足128MB的锁定需求。
内容的提问来源于stack exchange,提问作者Jessy James stands with Russia
相关产品推荐
相关产品推荐

