用于生成随机数的字符设备实现

标签 c makefile kernel-module insmod

根据我的学术项目,我当前的任务是使用内核模块生成 10 个随机数,并且我的用户空间程序(c 程序)应该能够显示这些数字。我一直在学习内核空间和用户空间程序。我遇到了字符设备的创建。我使用此命令创建了一个设备。

mknod /dev/my_device c 222 0

据我了解,该设备充当用户空间和内核空间程序之间的中介。所以我创建了一个内核模块来注册和取消注册我的字符设备。另存为 my_dev.c

#include<linux/module.h>
#include<linux/init.h>
#include"my_dev.h"

MODULE_AUTHOR("Krishna");
MODULE_DESCRIPTION("A simple char device");

static int r_init(void);
static void r_cleanup(void);

module_init(r_init);
module_exit(r_cleanup);


static int r_init(void)
{
printk("<1>hi\n");
if(register_chrdev(222,"my_device",&my_fops)){
    printk("<1>failed to register");
}
return 0;
}
static void r_cleanup(void)
{
printk("<1>bye\n");
unregister_chrdev(222,"my_device");
return ;
}

我用于编译此模块的 Make 文件是

obj-m += my_dev.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

使用 insmod 命令编译该内核模块并将其加载到内存中。

这是一个向用户缓冲区写入和读取一些文本并保存为 my_dev.h 的程序。

/*
 * my device header file 
 */
#ifndef _MY_DEVICE_H
#define _MY_DEVICE_H

#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/current.h>
#include <asm/segment.h>
#include <asm/uaccess.h>

char my_data[80]="heloooo"; /* our device */

int my_open(struct inode *inode,struct file *filep);
int my_release(struct inode *inode,struct file *filep);
ssize_t my_read(struct file *filep,char *buff,size_t count,loff_t *offp );
ssize_t my_write(struct file *filep,const char *buff,size_t count,loff_t *offp );

struct file_operations my_fops={
    open: my_open,
    read: my_read,
write: my_write,
release:my_release,
};

int my_open(struct inode *inode,struct file *filep)
{  
    /*MOD_INC_USE_COUNT;*/ /* increments usage count of module */
return 0;
}

int my_release(struct inode *inode,struct file *filep)
{
/*MOD_DEC_USE_COUNT;*/ /* decrements usage count of module */
return 0;
}
ssize_t my_read(struct file *filep,char *buff,size_t count,loff_t *offp )
{
/* function to copy kernel space buffer to user space*/
if ( copy_to_user(buff,my_data,strlen(my_data)) != 0 )
    printk( "Kernel -> userspace copy failed!\n" );
return strlen(my_data);

}
ssize_t my_write(struct file *filep,const char *buff,size_t count,loff_t *offp )
{
/* function to copy user space buffer to kernel space*/
if ( copy_from_user(my_data,buff,count) != 0 )
    printk( "Userspace -> kernel copy failed!\n" );
return 0;
}
#endif

这是我的用户空间程序acs.c,它在运行时通过从上述程序的内核缓冲区读取文本来打印“heloooo”。

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

int main()
{
int fd=0,ret=0;
char buff[80]="";

fd=open("/dev/my_device",O_RDONLY);

printf("fd :%d\n",fd);

ret=read(fd,buff,10);
buff[ret]='\0';

printf("buff: %s ;length: %d bytes\n",buff,ret);
close(fd);
}

现在我的问题是我需要编写一个用户空间程序,该程序在运行时打印 10 个随机数。但这些数字应该使用内核模块生成。所以基本上以上三个代码可以正常工作并打印“helooo”。我需要做的是获取随机数作为输出,而不是“helooo”。

这是一个内存模块,它使用线性同余生成器算法生成一些随机数。 LCG.c

#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */

int init_module(void)
{
int M = 8;  //Modulus,    M>0
    int a = 9;  //Multiplier, 0 <= a < M.
    int c = 3;  //Increment,  0 <= c < M.
    int X = 1;  //seed value, 0 <= X(0) < M
    int i;      //iterator,   i < M 
    for(i=0; i<8; i++)
    {
            X = (a * X + c) % M;
            printk(KERN_INFO "%d\n",X);

    }
    return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Task Done ! :D.\n");
}

我有所有的代码。但我不知道如何将这个随机数生成器代码放入我的字符设备调用代码中。当我运行程序 acs.c 时,我需要通过使用字符设备来获取内存模块 LCG.c 的输出。请帮我找到解决方案。

最佳答案

尝试进行这些更改,我刚刚添加了更改:

重构LCG.C,使随机数生成器成为一个单独的函数,并确保该函数不是静态的。您还应该需要导出此符号。

void generate_random_lcg(char* output_str)
{
    static const int M = 8;  //Modulus,    M>0
    static const int a = 9;  //Multiplier, 0 <= a < M.
    static const int c = 3;  //Increment,  0 <= c < M.
    static int X = 1;  //seed value, 0 <= X(0) < M

    int i;      //iterator,   i < M 
    ssize_t index = 0;
    for(i=0; i<8; i++)
    {
        X = (a * X + c) % M;
        index += sprintf(output_str + index, "%d\n", X);
    }
    output_str[index] = '\0';
}

EXPORT_SYMBOL(generate_random_lcg);

这样,该函数就可以直接由 LCG 模块调用,也可以从外部调用。

现在要从模块 my_dev 调用此函数并返回输出,您需要进行以下更改:

my_dev.c:

static int r_init(void)
{                                                                 
    printk("<1>hi\n");
    if(register_chrdev(222,"my_device",&my_fops)){                
            printk("<1>failed to register");                      
    }  

    memset(output_str, 0, MAX_SIZE);                              

    return 0;                                                     
}

my_dev.h

extern void generate_random_lcg(char* output_str);

#define MAX_SIZE 1024
static char output_str[MAX_SIZE];

ssize_t my_read(struct file *filep,char *buff,size_t count,loff_t *offp )
{
    ssize_t output_str_size = 0;
    generate_random_lcg(output_str);
    output_str_size = strlen(output_str);
    /* function to copy kernel space buffer to user space*/
    if (copy_to_user(buff,output_str,output_str_size) != 0 )
    {
        printk( "Kernel -> userspace copy failed!\n" );
        return 0;
    }   
    return output_str_size;
}

需要记住的几点:

  • 根据您的需要更新 MAX_SIZE 的值。如果数字变得非常大,您可以考虑使用 kmalloc 来获取内存。
  • 在 .h 文件中定义函数实现(内联函数除外)是一种非常糟糕的编码实践。
  • copy_from_usercopy_to_user 失败时,返回 0 而不是 strlen 的输出。

上面只是一个非常粗略的实现,当使用 sprintf 打印字符串时,您可能还需要额外的检查来检查缓冲区溢出。

关于用于生成随机数的字符设备实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15837547/

相关文章:

linux - 如何在不重命名传递给 insmod 的 .ko 的情况下重命名内核模块名称?

linux - 启动时从本地文件夹加载 Linux 内核模块

c - OpenMp 运算符 != 的无效控制谓词

c++ - 无法使用 C++ stdlib 系统调用运行 make

c++ - 如何使用 __attribute__((visibility ("default")))?

bash - Makefile 有什么有趣的用途可以分享吗?

linux - kprobe 处理程序未针对特定功能触发

数组指针的常量正确性?

c - 在 Briceno 等人的 A5/2 实现中,他们延迟了 LSFR 周期而不运行时钟周期函数。有人可以帮我理解吗?

c - LPC1788 - 引导加载程序和共享库