【问题标题】:My Linux kernel module is not receiving the correct User-space Application PID我的 Linux 内核模块没有收到正确的用户空间应用程序 PID
【发布时间】:2019-11-21 09:02:51
【问题描述】:

我正在开发与我的用户空间 C 应用程序通信的 Linux 内核模块。在这个模块中,我正在创建一个线程。另外,我需要知道用户空间进程的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");

当我在插入这个模块后运行我的用户空间程序时,它似乎第一次没有问题地工作:它在控制台上打印:

Thread is created 
thread name thread91
Thread is awake
pid = 323

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

pid = 0 
No such pid

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

【问题讨论】:

  • 删除您的previous question 并使用 同样不正确的用法(内核线程中的copy_from_user)询问新的copy_from_user 有什么意义,正如 cmets 中所指出的那样上一个问题? Stack Overflow 不鼓励以“ping”为目的重新创建问题。相反,我们更愿意修复旧问题。通过放弃之前的对话(在 cmets 中),您只会阻止其他人帮助您...

标签: c linux-kernel linux-device-driver pid


【解决方案1】:

由于您使用 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),因此您会收到错误。您应该打印它以检查其值。错误可能是用户端的

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

【讨论】:

  • 我不建议新手使用内核调试器,即使内核文档也不鼓励它。 printk 系列函数/宏通常绰绰有余(而且更简单一个数量级)。
  • 作为尝试,我声明了一个unsigned long 变量,它获取copy_from_user 函数输出以查看是否所有字节都被复制。结果是有时有一些字节没有被复制,有时所有字节都被复制了,但是两种情况下的pid仍然等于0
  • @MarcoBonelli 你可能是对的。使用调试器需要熟悉gdb,并且设置需要一些工作(使用kvm和可调试内核,可能使用-g,创建一个脚本到mnt/scp客人的vmlinux和源代码, ...)。在我看来,从长远来看,这是值得的,但它可能不适合你所说的初学者。
  • 是的,如果你真的需要它,它可能是值得的。对于新手来说,设置调试环境来编写内核模块基本上更复杂:P
  • @gaston 好的,所以你有一个不稳定的行为。打印从用户侧发送的内容,收到的内容,您会看到哪一侧是错误的。恕我直言,错误是用户端的。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多