首页 > 解决方案 > Linux 守护进程似乎挂在“recvfrom”,发现进程 procfs 网络损坏且无法访问

问题描述

我有一个长时间运行的 linux 守护程序,它在端口 65445 上创建了一个“非阻塞”套接字绑定,等待 UDP 数据包。它可以在大部分时间工作。

现在,我遇到了一个问题,该进程在一段时间后变为“D”(不确定是哪条消息导致它,但可以肯定的是,守护进程大部分时间都可以正确处理此类消息,只是在某个随机点,它失败了)

在这个阶段,该进程没有任何信号,所以我不能杀死它,转储内核堆栈:

Kernel Status:
[<ffffffff80385ff3>] number.isra.2+0x2d3/0x300
[<ffffffff802e0228>] address_space_init_once+0x88/0x120
[<ffffffff802e0200>] address_space_init_once+0x60/0x120
[<ffffffff802df880>] inode_wait+0x0/0x10
[<ffffffff802df889>] inode_wait+0x9/0x10
[<ffffffff802df880>] inode_wait+0x0/0x10
[<ffffffff80275dc0>] wake_bit_function+0x0/0x30
[<ffffffff802e0abd>] iget_locked+0x11d/0x180
[<ffffffff8030e1f0>] proc_get_inode+0x10/0xf0
[<ffffffff80313555>] proc_lookup_de+0x75/0xf0
[<ffffffff802d31bc>] d_alloc_and_lookup+0x3c/0x90
[<ffffffff802deeee>] d_lookup+0x2e/0x60
[<ffffffff802d3ea6>] do_lookup+0x296/0x3a0
[<ffffffff802ddcee>] dput+0x1e/0x190
[<ffffffff802d49eb>] link_path_walk+0x12b/0x850
[<ffffffff802dddb2>] dput+0xe2/0x190
[<ffffffff802d7437>] path_openat+0xb7/0x370
[<ffffffff802b4cfe>] tlb_finish_mmu+0xe/0x50
[<ffffffff802d7824>] do_filp_open+0x44/0xb0
[<ffffffff802e2905>] alloc_fd+0x45/0x130
[<ffffffff802c8a5c>] do_sys_open+0xec/0x1d0
[<ffffffff805a8afb>] system_call_fastpath+0x16/0x1b
[<ffffffffffffffff>] 0xffffffffffffffff

说明procfs有问题,经过进一步排查,发现这个进程的procfs的net目录已经损坏,我什至做不到ls /proc/*pid*/net,bash也挂在那里。

我缩小了进程可能会挂在“recvfrom”上的范围,因为它是一个非阻塞套接字,所以我无法理解,我的部分代码如下:

fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
    return ret;
}

if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, DEV, sizeof(DEV))
    < 0) {
    goto Exit;
}

rt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf, sizeof(sock_buf));
if (rt < 0) {
    goto Exit;
}

rt = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf, sizeof(sock_buf));
if (rt < 0) {
    goto Exit;
}

val = fcntl(fd, F_GETFL, 0);
if (val < 0) 
    return -1;
if (val & O_NONBLOCK)
    return 0;
val |= O_NONBLOCK;
fcntl(fd, F_SETFL, val);

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET, addr.sin_port = PORT;
addr.sin_addr.s_addr = INADDR_ANY;

if (bind(fd, (void *)&addr, sizeof(addr)) < 0) {
    goto Exit;
}

我在 epoll 中添加了这个套接字

struct epoll_event e;
e.events = EPOLLIN;
e.data.ptr = comm_handle;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &e);

有事件时接收数据包:

socklen_t from_len = sizeof(struct sockaddr_in);
memset(recv_buf, 0, 65000);
len = recvfrom(e->fd, recv_buf, 65000, 0, (struct sockaddr *)&from, &from_len);
if (len <= 0) {
    return -1;
}

标签: clinuxsocketsprocfs

解决方案


推荐阅读