Linux kernel 如何使读取操作在这里的伪驱动程序中只发生一次?
我正在尝试编写一个内核驱动程序。但是,当我在cat/dev/pSeudoDrv上执行cat操作时。它只是不断打印值。它永远不存在。我假设cat操作在读取一次后应该退出。然而,它永远在阅读。这里有什么问题Linux kernel 如何使读取操作在这里的伪驱动程序中只发生一次?,linux-kernel,kernel,linux-device-driver,Linux Kernel,Kernel,Linux Device Driver,我正在尝试编写一个内核驱动程序。但是,当我在cat/dev/pSeudoDrv上执行cat操作时。它只是不断打印值。它永远不存在。我假设cat操作在读取一次后应该退出。然而,它永远在阅读。这里有什么问题 #include <linux/init.h> #include <linux/module.h> /** needed by all modules **/ #include <linux/kernel.h> /** This is for KERN_AL
#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h> /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h> /** character device **/
#include <linux/device.h> /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>
/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");
static struct cdev basicCdev;
static struct class *basicDriverClass;
static int basicMajorNumber = 0;
/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/
static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);
static int basicOspen(struct inode *inode, struct file *file);
/** File Operations function pointer table **/
/** There are plenty of file operations **/
static struct file_operations fops = {
.read = basicRead,
.write = NULL,
.open = basicOspen,
.release = NULL
};
static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
char msg[1024] = "Hello SJ_read\0";
printk(KERN_ALERT "The Read operation called\r\n");
copy_to_user( buffer, msg, sizeof(msg) );
return sizeof(msg);
}
static int basicOspen(struct inode *inode, struct file *file)
{
printk("Kernel.Basic Driver Opened now!!\r\n");
return 0;
}
static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
int err = -1;
/** MKDEV call creates a device number i.e. combination of major and minor number **/
int devno = MKDEV(basicMajorNumber, minor);
/** Initiliaze character dev with fops **/
cdev_init(dev, fops);
/**owner and operations initialized **/
dev->owner = THIS_MODULE;
dev->ops = fops;
/** add the character device to the system**/
/** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
err = cdev_add (dev, devno, 1);
if (err)
{
printk (KERN_NOTICE "Couldn't add cdev");
}
}
static int chrDriverInit(void)
{
int result;
dev_t dev;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
result = alloc_chrdev_region(&dev, 0, 1, "pSeudoDrv");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
/** Now setup the cdev **/
setup_cdev(&basicCdev, 0, &fops);
return 0;
}
static void chrDriverExit(void)
{
/** A reverse - destroy mechansim -- the way it was created **/
printk("Releasing Simple Devs -- %s\r\n", __FUNCTION__);
/** delete the character driver added **/
cdev_del(&basicCdev);
/** destroy the device created **/
device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
/** destroy the class created **/
class_destroy(basicDriverClass);
/** unregister the chr dev **/
unregister_chrdev(basicMajorNumber, "pSeudoDrv");
}
module_init(chrDriverInit);
module_exit(chrDriverExit);
#包括
#包括所有模块所需的/****/
#包括/**这是针对内核警报的**/
#包含/**用于文件操作**/
#包括/**字符设备**/
#包括/**用于在/dev/和/sys/class中注册系统设备**/
/**用于将\u复制到\u用户**/
#包括
/**要使类注册生效,您需要GPL许可证**/
模块许可证(“GPL”);
静态结构cdev-basicdev;
静态结构类*basicDriverClass;
静态int basicMajorNumber=0;
/**对于读取的原型,当对驱动程序执行读取功能时,将调用该原型**/
/**声明类型是基于文件操作的函数指针读取**/
静态ssize\u t基本增量(结构文件*filp,字符*buffer,大小长度,loff\u t*偏移量);
静态int basicOspen(struct inode*inode,struct file*file);
/**文件操作函数指针表**/
/**有很多文件操作**/
静态结构文件\u操作fops={
.read=basicRead,
.write=NULL,
.open=basicOspen,
.release=NULL
};
静态ssize\u t基本增量(结构文件*filp,字符*buffer,大小长度,loff\u t*偏移量)
{
char msg[1024]=“你好,SJ_读取\0”;
printk(KERN_ALERT“读取操作调用\r\n”);
将_复制到_用户(缓冲区、消息、大小(消息));
返回sizeof(msg);
}
静态int basicOspen(结构inode*inode,结构文件*file)
{
printk(“Kernel.Basic驱动程序现在已打开!!\r\n”);
返回0;
}
静态无效设置\u cdev(结构cdev*dev,int minor,结构文件\u操作*fops)
{
int err=-1;
/**MKDEV调用创建一个设备编号,即主要编号和次要编号的组合**/
int devno=MKDEV(基本主数字,次要);
/**使用fops初始化角色开发**/
cdev_init(dev,fops);
/**所有者和操作已初始化**/
dev->owner=此_模块;
dev->ops=fops;
/**将字符设备添加到系统中**/
/**这里1表示只有1个次要编号,您可以为2个次要设备指定2,最后一个参数是注册的次要编号的计数**/
err=cdev_add(dev,devno,1);
如果(错误)
{
printk(KERN_通知“无法添加cdev”);
}
}
静态int CHRDIVERINIT(无效)
{
int结果;
发展与发展;
printk(“欢迎!!现在设备初始化…”);
/**int alloc_chrdev_区域(dev_t*dev,unsigned int firstminor,unsigned int count,char*name)**/
/**dev->dev_t变量类型,它将获取内核分配的主要编号**/
/**相同的名称将出现在/proc/devices中**/
/**它正在注册字符设备**/
/**将在此处动态分配主要编号**/
结果=alloc_chrdev_区域(&dev,0,1,“伪DRV”);
如果(结果<0)
{
printk(“设备分配错误”);
返回-1;
}
/**从这两个if中,我们避免使用手动mknod命令来创建/dev/**/
/**创建类,然后创建设备将删除调用mknod的依赖关系**/
/**一个很好的方法——mknod方法是折旧的**/
/**mknod的方式是-mknod/dev/c
/**将驱动程序添加到/sys/class/chardrv**/
if((basicDriverClass=class\u create(这个模块,“chardrv”))==NULL)/$ls/sys/class
{
注销chrdev_地区的注册(dev,1);
返回-1;
}
/**将驱动程序添加到/dev/pSeudoDrv--here**/
if(device_create(basicDriverClass,NULL,dev,NULL,“pSeudoDrv”)==NULL)/$ls/dev/
{
销毁类(基本类);
注销chrdev_地区的注册(dev,1);
返回-1;
}
/**让我们看看内核分配了什么主要编号**/
basicMajorNumber=主要(开发);
printk(“内核分配的主要编号为%d..\r\n”,基本主编号);
/**现在设置cdev**/
设置cdev(&basicCdev、0和fops);
返回0;
}
静态void chdriverxit(void)
{
/**一个反向破坏机制——它的创建方式**/
printk(“发布简单dev--%s\r\n”,函数);
/**删除添加的字符驱动程序**/
cdev_del(和基本开发);
/**销毁创建的设备**/
设备销毁(basicDriverClass,MKDEV(basicMajorNumber,0));
/**销毁创建的类**/
销毁类(基本类);
/**注销chr dev**/
注销chrdev(基本注册号,“伪DRV”);
}
模块初始化(chrDriverInit);
模块_出口(CHRRIVEREXIT);
在basicRead
中查看您的返回值
从LDD3:
read的返回值由调用应用程序解释
节目:
- 如果该值等于传递给读取系统调用的count参数,则已传输请求的字节数。这是 最佳情况
- 如果该值为正,但小于计数,则仅传输了部分数据。发生这种情况的原因有很多, 取决于设备。通常,应用程序会重试 阅读。例如,如果使用fread函数进行读取 库函数重新发出系统调用,直到完成 请求的数据传输
- 如果该值为0,则表示已到达文件末尾(并且未读取任何数据)
- 负值表示存在错误。根据,该值指定错误是什么。返回的典型值 on错误包括-EINTR(中断的系统调用)或-EFAULT(错误 地址)