在 Linux 内核模块中使用线程的问题

Probelm with using Threads in Linux kernel Module

我正在开发 Linux 内核模块,它与我的用户-space C 应用程序通信。在这个模块中,我正在创建一个线程。这是我遇到问题的模块:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/siginfo.h>    //siginfo
#include <linux/rcupdate.h>    //rcu_read_lock
#include <linux/sched/signal.h>    //find_task_by_pid_type
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include<linux/slab.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/fs.h> 
#include <linux/random.h> 
#include <linux/kthread.h> 

#define SIG_TEST 44    // we choose 44 as our signal number (real-time signals are in the range of 33 to 64)
#define BTN_FILE_PATH "/dev/input/event0"


char *str = BTN_FILE_PATH;
int file;

struct file *f;   // keyboard driver

// prototypage des fonctions read_in_thread & read_pid
int read_in_thread(void *data);
static ssize_t read_pid(struct file *pfile, char __user *buffer, size_t length, loff_t *offset);


static ssize_t write_pid(struct file *pfile, const char __user *buffer,
                                size_t length, loff_t *offset)
{
   return 0;
}



struct read_args {
    struct file *pfile;
    const char __user *buffer;
    size_t length;
    loff_t *offset;
};


static ssize_t read_pid(struct file *pfile, char __user *buffer, size_t length, loff_t *offset)
{
  // création de la structure des arguments
    struct read_args args ;
    args.pfile = pfile;
    args.buffer = buffer;
    args.length = length;
    args.offset = offset;

struct task_struct *thread1;
char our_thread[20];
unsigned int rand;

get_random_bytes(&rand, sizeof(rand));
rand = rand % 250;
sprintf(our_thread, "thread%u", rand);

if(thread1==NULL)
{
thread1 = kthread_create(read_in_thread,&args,our_thread);
    if((thread1))
        {
            printk(KERN_INFO "Thread is created\n");
        printk("thread name %s\n", our_thread);
// lancement du thread
            wake_up_process(thread1);
        printk(KERN_INFO "Thread is awake\n");
        }
}

else 
printk("\nTHREAD1 IS NOT NULL!!! CAN NOT CREATE THREAD!!!\n"); 

return 0;                   
}



int read_in_thread(void *data) {

/************************** récupération des arguments *******************/

    struct read_args *const args = data;

/***************************   corps de la fonction ***********************/


// init des variables 

    char mybuf[10];
    enum { MAX_BUF_SIZE = 4096 };
    size_t buf_size = 0;
    char *buf = NULL;
    ssize_t total = 0;
    ssize_t rc = 0;
    struct task_struct *t;
    struct input_event ev[64];
    int yalv;

    int ret;
    struct siginfo info;
    int pid =0; 
    size_t amount = sizeof(ev);    


// récupération de l'ID du processus appelant

/* read the value from user space */
    if(args->length > 10)
        return -EINVAL;
    copy_from_user(mybuf, args->buffer, args->length);
    sscanf(mybuf, "%d", &pid);
    printk("pid = %d\n", pid);

    // the signal 
    memset(&info, 0, sizeof(struct siginfo));
    info.si_signo = SIG_TEST;
    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. 
    info.si_int = 260;          //real time signals may have 32 bits of data.

    rcu_read_lock();
    t = pid_task(find_vpid(pid), PIDTYPE_PID);  //find the task_struct associated with this pid
    if(t == NULL){
        printk("no such pid\n");
        rcu_read_unlock();
        return -ENODEV;
    }
    rcu_read_unlock();


// lecture blocquante

    rc = kernel_read(f, ev, amount, &f->f_pos);

// récupération de l'événement

    if (rc > 0) {
            for (yalv = 0; yalv < (int) (rc / sizeof(struct input_event)); yalv++) {
            if (ev[yalv].type == EV_KEY) {
                if (ev[yalv].value == 0)
                     //eval_keycode(ev[yalv].code);
                     info.si_int = ev[yalv].code;  

// envoie du signal vers le processus appelant avec les événements lu

                                         ret = send_sig_info(SIG_TEST, &info, t);    //send the signal
                         printk("signal was send\n");
                     if (ret < 0) {
                            printk("error sending signal\n");
                        kfree(buf);
                            return ret;
                         }              
            }
        }

                if (rc < amount) {
                    /* Didn't read the full amount, so terminate early. */
                    rc = 0;
                }

    } 

    /* Free temporary buffer. */
        kfree(buf);

return 0;
}




static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .write = write_pid,
    .read = read_pid,
    //.open = open_pid,
};



static int __init signalexample_module_init(void)
{
     printk(KERN_INFO "Initializing LKM");
    /* we need to know the pid of the user space process
      * -> we use debugfs for this. As soon as a pid is written to 
      * this file, a signal is sent to that pid
      */
    /* only root can write to this file (no read) */
    register_chrdev(240, "mod", &my_fops);
    file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);
    f = filp_open(str, O_RDONLY , 0);
    //printk("%d",f);

    return 0;
}



static void __exit signalexample_module_exit(void)
{
    unregister_chrdev(240, "mod");
    debugfs_remove(file);

}

module_init(signalexample_module_init);
module_exit(signalexample_module_exit);
MODULE_LICENSE("GPL");

当我第一次执行我的 user-space 程序时,一切正常,它在控制台上为我打印:

Thread is created 
thread name thread91
Thread is awake 

但是当我尝试再次执行它时,它会打印:

THREAD1 IS NOT NULL!!! CAN NOT CREATE THREAD!!!

我认为问题在 task_struct *thread1 中,它仍然保存着我第一次执行程序时创建的第一个线程的信息。

谁能帮我解决这个问题?我如何编辑我的代码以使其正确 运行 多次?

谢谢。

正如您所提到的,该线程被正确标记为非空,因为它 已经 运行 宁。所以行为是有意的。

因此,为了解决您的问题,您必须描述您的实际预期行为。

如果你想让它 运行 多次,你可能想使用局部变量来 运行 线程(例如将它存储在一个数组中以供以后通信)。

您还可以等待上一个线程完成,然后再运行下一个线程。

您的内核代码存在于您的用户 space 代码开始和完成到 运行 之前和之后。所以当你第一次执行你的命令时,内核线程被创建,因为它是第一次调用 read_pid 所以线程被创建。但是在 read_pid 完成后,内核并没有停止。所以线程仍然存在。看起来您应该在驱动程序的 probe 函数中创建线程,并以只为 read_pid 调用准备数据的方式编写线程逻辑。在您的情况下,信号使用看起来完全没有必要。