我的 Linux 内核模块没有接收到正确的 User-space 应用程序 PID

My Linux kernel module is not receiving the correct User-space Application PID

我正在开发 Linux 内核模块,它与我的用户-space C 应用程序通信。在这个模块中,我正在创建一个线程。此外,我需要知道用户 space 进程的 pid,所以我正在使用 pid_task(find_vpid(pid), PIDTYPE_PID) 函数。

这是我遇到问题的模块:

#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");
    register_chrdev(240, "mod", &my_fops);
    file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);
    f = filp_open(str, O_RDONLY , 0);

    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
pid = 323

但是在我退出后,当我再次尝试重新执行我的用户 space 代码时,它向我显示:

pid = 0 
No such pid

我想知道为什么当我多次执行我的应用程序时它不能以正确的方式工作?这里出了什么问题?我需要一些帮助。谢谢。

因为您使用 PID 的唯一线路是

    int pid =0;
    copy_from_user(mybuf, args->buffer, args->length);
    sscanf(mybuf, "%d", &pid);
    printk("pid = %d\n", pid);

很可能 mybuf 在第二次执行时无效(mybuf 实际上包含 0 或者它是 null 或不可解析为 int)因此你会得到一个错误。你应该打印它来检查它的值。错误可能是用户端

注意:请不要把它当作个人,但这是你两天内的第 3 个线程,与主题相比简单的问题(内核编程并不简单,即使对于 LKM)。我认为你应该在询问之前进一步挖掘。我很确定您可以单独回答您的问题,否则您 难以在内核空间中进行开发。为了简化这个调试过程,您可以使用内核调试器 see this post for setup example(正如 Marco Bonelly 所指出的,只有当您计划在内核空间中进行大量开发时才值得使用内核调试器,因为它的部署可能需要一些时间)。祝你好运。

对于正在寻找此问题解决方案的任何人,经过一些研究我发现必须在线程函数之外使用 copy_from_user 函数。因此,我将它移到代码中的 read_pid 函数中,并声明了它用作全局变量的变量,现在我通常能够接收到我的用户 space 应用程序的正确 PID。