C语言-Linux内核:关闭两个k线程导致机器死机

5

编辑:认为我解决了这个问题。明天我上班时会添加我的答案/解决方案。


我正在为Linux内核3.16.x开发一个内核模块。我的模块应该接收和发送以太网帧,因此我创建了两个线程,一个用于接收部分,另一个用于传输部分。它已经工作得很好了。

当我尝试关闭该模块的设备文件并卸载它时,出现了问题。确切地说,当我尝试杀死我的��程时,机器(我在使用Ubuntu 14.04的虚拟机进行测试)就会在没有任何输出之前冻结。然而,当我从代码中删除“线程杀死部分”时,它就不再冻结。这就是为什么我相当确定问题源于我处理kthreads的方式。

下面是我的两个线程:

TX THREAD: tx_sem信号量在代码的另一部分被up'ed。

static int tx_task(void *par)
{
    device->tx_task_running = 1;

    allow_signal(SIGTERM);
    while (!signal_pending(current) && device->tx_task_running) {
        /* wait until there is something to send */
        down_interruptible(&device->tx_sem);

        if (signal_pending(current)) {
            PRINTD("device_TX_task(): Received kill signal\n");
            break;
        }

        /* check if device is still initialized before continuing*/
        if (!device->init_flag) {
            break;
        }
    }
    device->tx_task_running = 0;
    return DEVICE_RET_OK;
}

RX THREAD

static int device_rx_task(void *par)
{
    device->rx_task_running = 1;

    /* task loop */
    allow_signal(SIGTERM);
    while (!signal_pending(current) && device->rx_task_running) {
        rxlen = kernel_recvmsg(device->sock, &msg, 
            (struct kvec *)&iov, 1, DEVICE_PAY_SIZE, 0);

        if (signal_pending(current)) {
            PRINTD("device_rx_task(): Received kill signal\n");
            break;
        }

        if(rxlen < 0) {
            PRINTD("device_RX_task(): Got error when receiving\n");
            break;
        }

        /* check if device is still initialized before continuing*/
        if (!device->init_flag) {
            break;
        }
    }
    device->rx_task_running = 0;
    return DEVICE_RET_OK;
}

这些线程会一直运行,直到我尝试关闭设备。当关闭内核模块时,会调用此函数并导致崩溃。例如,如果我注释掉“send_sig”函数,它就不会崩溃。当我尝试手动杀死这些线程时,它也会崩溃:

int Device_DevTerm(int dev)
{
    device->init_flag = 0;

    send_sig(SIGTERM, device->rx_thread, 0);
    send_sig(SIGTERM, device->tx_thread, 0);
    device->rx_task_running = 0;
    device->tx_task_running = 0;

    return DEVICE_RET_OK;
}

我在谷歌和搜索引擎中花费了很长时间来寻找问题,但至今仍未成功。由于我已经为此问题投入了大量时间,所以我决定向你们寻求帮助。

我在这里做错了什么?

附言:我不认为我曾在这里发布过任何内容,我只是阅读了很多。我希望我的问题足够清晰简明。但可能并不是。


1
如果程序崩溃,请发布堆栈跟踪。如果您的意思是挂起,请使用 SysRq 查找挂起的任务。 - stark
不幸的是,我似乎无法这样做。 当我查看/var/log/syslog/时,没有任何关于挂起的信息。我猜它在任何输出发生之前就已经冻结了。 当我尝试 SysRq(我以前不知道)时,什么也没有发生,它还是冻结的。也许它是一个虚拟机的事实并没有帮助,我不确定。我将继续尝试收集任何有用的信息,但到目前为止我还不能这样做。 - Laughing_Man
1个回答

0

看起来,我找到了我的问题。

在为 kernel_recvmsg() 函数设置 msghdr 时,我以前是这样做的:

struct sockaddr_in client;

MEM_SET(&client, 0x00, sizeof(struct sockaddr_in));

/* setup receive parameter */
msg.msg_name = &client;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

现在我已经将例程更改为:

struct sockaddr_in client[3];

MEM_SET(&client[0], 0x00, 3 *sizeof(struct sockaddr_in));

/* setup receive parameter */
msg.msg_name = &client[1];
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

现在它不会再崩溃了。然后我在我们的代码库中找到了以下评论:

/*
 * NOTE:
 * use three times the size of struct sockaddr_in as workaround
 * for a possible kernel bug which has been seen on a Ubuntu 64bit
 * system
 * after the call of kernel_recvmsg 2 bytes behind the structure
 * client had been corrupted on the stack
 */

我不确定这是否完全涵盖了我的问题,但似乎可以解决它。


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