【问题标题】:Linked list in Linux kernel freezes machineLinux内核中的链表冻结机器
【发布时间】:2013-10-04 13:29:56
【问题描述】:

我编写了一个需要将消息推送到用户空间的内核模块。这个想法是内核模块缓冲消息并向用户空间程序发出信号,然后用户空间程序通过netlink套接字请求消息并获取消息。我的问题是缓冲90条消息后,机器锁死,需要重启。我不知道我做错了什么,并且我在内核模块的其他地方成功地使用了链表。

//
// A message from the kernel space to user space.
//
typedef struct CoreLinkMessage
{

    unsigned int id;
    char* data;
    unsigned int length;

    struct list_head list; // kernel's list structure

} CoreLinkMessage;

这个函数初始化列表和信号量:

// Constructor
void
ctsRtNetlinkSystem_init( void )
{
    sema_init(&cmd_sem_, 1);    
    INIT_LIST_HEAD(&cmd_list_.list);
}

这一定是导致问题的函数。它只是将一个项目推到链表的尾部。如果我注释掉向链表添加项目并且只调用一个信号,程序会无限期地运行,所以我认为问题不是信号。

//
// Allows the kernel module to buffer messages until requested by
// the user space
//
void
ctsRtNetlinkSystem_addMessage(char* data, unsigned int length)
{

    CoreLinkMessage* msg;
    int sem_ret;
    BOOL doSignal = FALSE;

    //
    // LOCK the semaphore
    //
    sem_ret = down_interruptible(&cmd_sem_);

    if ( !sem_ret )
    {

    msg = (CoreLinkMessage*)kmalloc(sizeof(CoreLinkMessage), GFP_KERNEL );
    if ( msg == NULL )
    {
        PRINTF(CTSMSG_INFO
            "ctsRtNetlinkSystem_addMessage failed to allocate memory! \n" );
        goto unlock;
    }
            memset( msg, 0, sizeof(CoreLinkMessage) );
            msg->data = (char*)kmalloc( length, GFP_KERNEL );
    if ( msg->data == NULL )
    {                        
        kfree( msg );
        PRINTF(CTSMSG_INFO
            "ctsRtNetlinkSystem_addMessage failed to allocate data memory!\n" );
        goto unlock;
    }

    memcpy( msg->data, data, length );
    msg->length = length;

    lastMessageId_ += 1;
    msg->id = lastMessageId_;

    list_add_tail(&(msg->list), &cmd_list_.list);   
    doSignal = TRUE;

unlock:

    up( &cmd_sem_ );

    if ( doSignal )
        sendMessageSignal( msg->id );


    }
    else
    {
    PRINTF(CTSMSG_INFO
        "CtsRtNetlinkSystem_addMessage -- failed to get semaphore\n" );
    }


}




//
// Signal the user space that a message is waiting. Pass along the message
// id
//
static BOOL
sendMessageSignal( unsigned int id )
{
    int ret;
    struct siginfo info;
    struct task_struct *t;

    memset(&info, 0, sizeof(struct siginfo));
    info.si_signo = SIGNAL_MESSAGE;
    info.si_code = SI_QUEUE;    // this is bit of a trickery: 
                            // SI_QUEUE is normally used by sigqueue 
                            // from user space,
                            // and kernel space should use SI_KERNEL. 
                            // But if SI_KERNEL is used the real_time data 
                            // is not delivered to the user space signal 
                            // handler function. 

    // tell the user space application the index of the message
    // real time signals may have 32 bits of data.
    info.si_int = id;       

    rcu_read_lock();

    //find the task_struct associated with this pid
    t = // find_task_by_pid_type( PIDTYPE_PID, registeredPid_ );  
    // find_task_by_pid_type_ns(PIDTYPE_PID, nr, &init_pid_ns);
    pid_task(find_vpid(registeredPid_), PIDTYPE_PID); 
    if(t == NULL)
    {
    PRINTF(CTSMSG_INFO
        "CtsRtNetlinkSystem::sendMessageSignal -- no such pid\n");
    rcu_read_unlock();
    registeredPid_ = 0;
    return FALSE;
    }

    rcu_read_unlock();

    //send the signal
    ret = send_sig_info(SIGNAL_MESSAGE, &info, t);    
    if (ret < 0) 
    {
    PRINTF(CTSMSG_INFO
        "CtsRtNetlinkSystem::sendMessageSignal -- \n"
        "\t error sending signal %d \n", ret );
    return FALSE;
    }

    return TRUE;    
}

我目前正在虚拟机上测试程序,所以我创建了一个计时器,它每 7 秒计时一次,并向缓冲区添加一条消息。

//
// Create a timer to call the process thread
// with nanosecond resolution.
//

static void
createTimer(void)
{
    hrtimer_init(
    &processTimer_,     // instance of process timer
    CLOCK_MONOTONIC,    // Pick a specific clock. CLOCK_MONOTONIC is
                // guaranteed to move forward, no matter what.
                // It's akin to jiffies tick count
                // CLOCK_REALTIME matches the current real-world time
    HRTIMER_MODE_REL ); // Timer mode (HRTIMER_ABS or HRTIMER_REL)

    processTimer_.function = &cyclic_task;

    processTimerNs_ =  ktime_set(1, FREQUENCY_NSEC);

    //
    // Start the timer. It will callback the .function
    // when the timer expires.
    //
    hrtimer_start(
    &processTimer_,     // instance of process timer
    processTimerNs_,    // time, nanosecconds                       
    HRTIMER_MODE_REL );     // HRTIMER_REL indicates that time should be
                // interpreted relative
                // HRTIMER_ABS indicates time is an 
                // absolute value


}

static enum hrtimer_restart
cyclic_task(struct hrtimer* timer)
{

    char msg[255];
    sprintf(msg, "%s", "Testing the buffer.");


    ctsRtNetlink_send( &msg[0], strlen(msg) );

    hrtimer_forward_now(
            &processTimer_,
            processTimerNs_ );


    return HRTIMER_RESTART; 

}

提前感谢您的帮助。

【问题讨论】:

  • 诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺诺。用户空间和内核空间是神圣分离的。一个不只是在两者之间传递信息。此外,您似乎忽略了 memcpys 和 _sends 中的空终止符 ('\0')。永远记住:strlen(str) + 1.
  • 现在没有任何东西被推入内核;问题完全在内核中。
  • 使用 GFP_ATOMIC 而不是 GFP_KERNEL 似乎已经解决了这个问题。也许是因为我是从 hrtimer 回调中调用的,kmalloc 不能被允许休眠?

标签: c linux linux-kernel linked-list


【解决方案1】:

虽然从问题上看你的代码流程不是很清楚,但我觉得列表添加可能不是问题。您必须在其他地方处理列表,您必须从列表中删除消息等。我怀疑在您的列表添加和删除等之间存在某种死锁情况。此外,请检查您将消息复制到的位置用户空间并从列表中删除并释放它。我想,您并没有试图直接从用户空间引用您的消息作为上面建议的评论员之一。

还有,

   memset( msg, 0, sizeof(CoreLinkMessage) );

if ( msg == NULL )
{

这两行必须颠倒它的顺序,否则,如果 alloc 失败了,你的系统就注定了。

【讨论】:

  • 哎哟。那是倒退。我会解决的。
【解决方案2】:

对 kmalloc 使用 GFP_ATOMIC 而不是 GFP_KERNEL 解决了这个问题。到目前为止运行了三天,没有崩溃。我怀疑一个人无法在 hrtimer 触发的线程中睡觉。

msg = (CoreLinkMessage*)kmalloc(sizeof(CoreLinkMessage), GFP_ATOMIC );

感谢大家的见解!

【讨论】:

    【解决方案3】:

    分配的内存不足

    确保为字符串长度 + 1 分配足够的内存来存储它的终止符。
    发送时,可能需要length + 1

    // ctsRtNetlink_send( &msg[0], strlen(msg) );
    ctsRtNetlink_send( &msg[0], strlen(msg) + 1);  // +1 for \0
    

    【讨论】:

    • 许多其他未显示的例程,如list_add_tail(),可能有问题。 @joe 关于memset() 是正确的。在没有看到 CoreLinkMessage 结构、样本输入和其他附近的例程的情况下,这个问题的解决方案很难辨别。
    • list_add_tail() 是 Linux 内核的一部分。我添加了所有其他正在运行的内容。
    • @user761576 感谢list_add_tail() - 认为这是您的代码。也许将“内核”标签更改为“linux-kernel”,因为这篇文章是特定于该内核的,而不是一般的内核。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 2019-03-24
    相关资源
    最近更新 更多