【问题标题】:Deadlock with threads calling down on kernel semaphore through sysfs通过 sysfs 调用内核信号量的线程死锁
【发布时间】:2015-03-19 02:41:27
【问题描述】:

来自this question(和my solution),我开始意识到可能存在死锁,但我不明白为什么以及如何避免它。

简而言之,内核空间中有一个semaphore,内核模块(它们实际上是在内核空间中运行的应用程序)可以使用,但用户空间应用程序也需要使用相同的信号量来保护全局共享内存。

我通过公开一个给出正确字符的 sysfs 文件来做到这一点,downup 是内核空间中的信号量。用户空间应用程序只会保持此文件打开,write 是发生锁定的适当字符。

这是一个用于演示的示例内核模块:

#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shahbaz Youssefi");
MODULE_DESCRIPTION("Test module");

static struct kobject *_kobj = NULL;
static struct semaphore sem;

static ssize_t _lock_op(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    switch (buf[0])
    {
    case '0':
        printk("down (%u)\n", sem.count);
        if (down_interruptible(&sem))
            printk("error: sem wait interrupted\n");
        break;
    case '1':
        printk("up (%u)\n", sem.count);
        up(&sem);
        break;
    default:
        printk("error: invalid request %d\n", buf[0]);
    }
    return count;
}

static struct kobj_attribute _lock_attr = __ATTR(test, 0222, NULL, _lock_op);

static int __init _main_init(void)
{
    sema_init(&sem, 1);

    _kobj = kobject_create_and_add("test", NULL);
    if (!_kobj)
    {
        printk("error: failed to create /sys directory for test\n");
        return -ENOMEM;
    }
    if (sysfs_create_file(_kobj, &_lock_attr.attr))
        printk("error: could not create /sys file\n");

    printk("loaded\n");
    return 0;
}

static void __exit _main_exit(void)
{
    if (_kobj)
        kobject_put(_kobj);
    _kobj = NULL;

    printk("unloaded\n");
}

module_init(_main_init);
module_exit(_main_exit);

总的来说,这很有效。用户空间应用程序可以将'0''1' 写入sysfs 文件,它们可以毫无问题地实现互斥。

但是,在一种情况下,这会锁定进程,即同一进程的多个线程尝试获取锁定。

基本上是这样的:

       Thread 1               Thread 2

       write '0'
      system call
       _lock_op
   down_interruptible
   return from syscall
                              write '0'
                             system call
                              _lock_op
                           down_interruptible (blocked)

*go on to release the lock*

                            *return from syscall*
                         *go on to release the lock*

问题在于,在这种情况下,第二个down 发生而第一个尚未释放锁,而不是仅仅第二个线程被阻塞,整个进程被阻塞强>。也就是说,标有* 的步骤不会发生。

这是一个用户空间应用程序,可以在插入上述内核模块时触发:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static int fid;
static volatile sig_atomic_t interrupted = 0;

static void sig_handler(int signum)
{
    interrupted = 1;
}

static void *func(void *arg)
{
    while (!interrupted)
    {
        write(fid, "0", 1);
        write(fid, "1", 1);
        usleep(1000);
    }

    return NULL;
}

int main(void)
{
    pthread_t tid;

    struct sigaction sa = {
        .sa_handler = sig_handler,
    };
    sigemptyset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGHUP, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGUSR1, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);

    fid = open("/sys/test/test", O_WRONLY);
    if (fid < 0)
        return EXIT_FAILURE;

    pthread_create(&tid, NULL, func, NULL);

    while (!interrupted)
    {
        write(fid, "0", 1);
        write(fid, "1", 1);
        usleep(793);
    }

    pthread_join(tid, NULL);

    close(fid);

    return 0;
}

注意:请echo 1 &gt; /sys/test/test 解锁自己;)

我的问题是,为什么 Linux 会阻塞 down 上的整个进程,而不仅仅是调用线程?我该怎么办?


注意:在 x86 上测试,内核 3.8 修补了 RTAI。为了确定,我稍后会尝试使用更新的香草内核,但我怀疑它与 RTAI 无关。

【问题讨论】:

    标签: c linux-kernel


    【解决方案1】:

    我其实已经想出了解决这个问题的方法,但我仍然认为应该有一个适当的解释和解决方案。

    我的解决方法如下:

    在应用程序中使用 pthread 互斥锁。在每个线程上,而不是:

    write(fid, "0", 1);
    /* access */
    write(fid, "1", 1);
    

    pthread_mutex_lock(&mutex);
    write(fid, "0", 1);
    /* access */
    write(fid, "1", 1);
    pthread_mutex_unlock(&mutex);
    

    这使得进程对 sysfs 文件的所有访问都是互斥的。 sysfs 文件确保进程和内核模块之间的访问是互斥的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 2019-10-02
      • 2023-03-09
      • 1970-01-01
      • 2016-11-01
      • 2021-05-09
      • 2015-04-21
      相关资源
      最近更新 更多