file_operations構造
いくつかのファイルを調べましたoperations構造の文章、例えばこの文章は、実は翻訳された英語の文章で、Googleでその原文を見つけました.いい文章です.ここが住所です.翻訳していないで、少し小さい表示をして、コードのフォーマットを少しいじって、まだそんなに乱れていないように見えます.
Character Device Drivers
Character Device Drivers
The file_operations Structure
The file_operations structure is defined in linux/fs.h, andholds pointers to functions defined by the driver that perform various operations on the device. Each field of thestructure corresponds to the address of some function defined by the driver to handle a requested operation.
For example, every character driver needs to define a function that reads from the device. Thefile_operations structure holds the address of the module's function that performs that operation. Here iswhat the definition looks like for kernel 2.4.2: struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
loff_t *);
};
Some operations are not implemented by a driver. For example, a driver that handles a video card won't need to readfrom a directory structure. The corresponding entries in thefile_operations structure should be set toNULL.
There is a gcc extension that makes assigning to this structure more convenient. You'll see it in modern drivers,and may catch you by surprise. This is what the new way of assigning to the structure looks like: struct file_operations fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
};
However, there's also a C99 way of assigning to elements of a structure, and this is definitely preferred over usingthe GNU extension. The version of gcc I'm currently using,2.95, supports the new C99 syntax. Youshould use this syntax in case someone wants to port your driver. It will help with compatibility: struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
The meaning is clear, and you should be aware that any member of the structure which you don't explicitly assignwill be initialized toNULL by gcc.
A pointer to a struct file_operations is commonly namedfops.
The file structure
Each device is represented in the kernel by a file structure, which is defined inlinux/fs.h. Be aware that afile is a kernel level structure and never appears in auser space program. It's not the same thing as aFILE, which is defined by glibc and would never appear in akernel space function. Also, its name is a bit misleading; it represents an abstract open `file', not a file on a disk,which is represented by a structure namedinode.
A pointer to a struct file is commonly named filp. You'll also see itrefered to as struct file file. Resist the temptation.
Go ahead and look at the definition of file. Most of the entries you see, likestruct dentry aren't used by device drivers, and you can ignore them. This is because drivers don'tfillfile directly; they only use structures contained in file which are createdelsewhere.
Registering A Device
As discussed earlier, char devices are accessed through device files, usually located in/dev[1]. The major number tells you which driver handles which device file. The minor numberis used only by the driver itself to differentiate which device it's operating on, just in case the driver handles morethan one device.
Adding a driver to your system means registering it with the kernel. This is synonymous with assigning it a majornumber during the module's initialization. You do this by using theregister_chrdev function,defined bylinux/fs.h. int register_chrdev(unsigned int major, const char *name,
struct file_operations *fops);
where unsigned int major is the major number you want to request,const char*name is the name of the device as it'll appear in/proc/devices andstructfile_operations *fops is a pointer to thefile_operations table for your driver. A negativereturn value means the registertration failed. Note that we didn't pass the minor number toregister_chrdev. That's because the kernel doesn't care about the minor number; only our driver usesit.
Now the question is, how do you get a major number without hijacking one that's already in use? The easiest waywould be to look throughDocumentation/devices.txt and pick an unused one. That's a bad way of doingthings because you'll never be sure if the number you picked will be assigned later. The answer is that you can ask thekernel to assign you a dynamic major number.
If you pass a major number of 0 to register_chrdev, the return value will be the dynamicallyallocated major number.The downside is that you can't make a device file in advance, since you don't know what the majornumber will be.ここでは3つの を していますが、 も な は、 に り てた 、printkで しい り てられたプライマリ・デバイス を し、dmesgコマンドで することです. に、デバイスファイルを するときにプライマリ・デバイス を します.There are a couple of ways to do this. First, the driver itself can print the newly assigned number andwe can make the device file by hand. Second, the newly registered device will have an entry in/proc/devices, and we can either make the device file by hand or write a shell script to read thefile in and make the device file. The third method is we can have our driver make the the device file using themknod system call after a successful registration and rm during the call tocleanup_module.
Unregistering A Device
We can't allow the kernel module to be rmmod'ed whenever root feels like it. If thedevice file is opened by a process and then we remove the kernel module, using the file would cause a call to the memorylocation where the appropriate function (read/write) used to be. If we're lucky, no other code was loaded there, andwe'll get an ugly error message. If we're unlucky, another kernel module was loaded into the same location, which means ajump into the middle of another function within the kernel. The results of this would be impossible to predict, but theycan't be very positive.
Normally, when you don't want to allow something, you return an error code (a negative number) from the functionwhich is supposed to do it. Withcleanup_module that's impossible because it's a void function.However, there's a counter which keeps track of how many processes are using your module. You can see what it's value isby looking at the 3rd field of/proc/modules. If this number isn't zero,rmmodwill fail. Note that you don't have to check the counter from withincleanup_module because thecheck will be performed for you by the system callsys_delete_module, defined inlinux/module.c. You shouldn't use this counter directly, but there are macros defined inlinux/modules.h which let you increase, decrease and display this counter: MOD_INC_USE_COUNT: Increment the use count. MOD_DEC_USE_COUNT: Decrement the use count. MOD_IN_USE: Display the use count.
It's important to keep the counter accurate; if you ever do lose track of the correct usage count, you'll never beable to unload the module; it's now reboot time, boys and girls. This is bound to happen to you sooner or later during amodule's development.
chardev.c
The next code sample creates a char driver named chardev. You cancat itsdevice file (oropen the file with a program) and the driver will put the number of times the devicefile has been read from into the file. We don't support writing to the file (likeecho "hi">/dev/hello), but catch these attempts and tell the user that the operation isn't supported. Don't worry if youdon't see what we do with the data we read into the buffer; we don't do much with it. We simply read in the data andprint a message acknowledging that we received it.
Example 4-1. chardev.c /* chardev.c: Creates a read-only char device that says how many times
* you've read from the dev file
*
* Copyright (C) 2001 by Peter Jay Salzman
*
* 08/02/2006 - Updated by Rodrigo Rubira Branco <[email protected]>
*/
/* Kernel Programming */
#define MODULE
#define LINUX
#define __KERNEL__
#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
#include <linux/modversions.h>
#define MODVERSIONS
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* for put_user */
#include <asm/errno.h>
/* Prototypes - this would normally go in a .h file */
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
#define SUCCESS 0
#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */
#define BUF_LEN 80 /* Max length of the message from the device */
/* Global variables are declared as static, so are global within the file. */
static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open? Used to prevent multiple
access to the device */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
/* Functions */
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk ("Registering the character device failed with %d
", Major);
return Major;
}
printk("<1>I was assigned major number %d. To talk to
", Major);
printk("<1>the driver, create a dev file with
");
printk("'mknod /dev/hello c %d 0'.
", Major);
printk("<1>Try various minor numbers. Try to cat and echo to
");
printk("the device file.
");
printk("<1>Remove the device file and module when done.
");
return 0;
}
void cleanup_module(void)
{
/* Unregister the device */
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0) printk("Error in unregister_chrdev: %d
", ret);
}
/* Methods */
/* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open) return -EBUSY;
Device_Open++;
sprintf(msg,"I already told you %d times Hello world!
", counter++);
msg_Ptr = msg;
MOD_INC_USE_COUNT;
return SUCCESS;
}
/* Called when a process closes the device file */
static int device_release(struct inode *inode, struct file *file)
{
Device_Open --; /* We're now ready for our next caller */
/* Decrement the usage count, or else once you opened the file, you'll
never get get rid of the module. */
MOD_DEC_USE_COUNT;
return 0;
}
/* Called when a process, which already opened the dev file, attempts to
read from it.
*/
static ssize_t device_read(struct file *filp,
char *buffer, /* The buffer to fill with data */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we're at the end of the message, return 0 signifying end of file */
if (*msg_Ptr == 0) return 0;
/* Actually put the data into the buffer */
while (length && *msg_Ptr) {
/* The buffer is in the user data segment, not the kernel segment;
* assignment won't work. We have to use put_user which copies data from
* the kernel data segment to the user data segment. */
put_user(*(msg_Ptr++), buffer++);
length--;
bytes_read++;
}
/* Most read functions return the number of bytes put into the buffer */
return bytes_read;
}
/* Called when a process writes to dev file: echo "hi" > /dev/hello */
static ssize_t device_write(struct file *filp,
const char *buff,
size_t len,
loff_t *off)
{
printk ("<1>Sorry, this operation isn't supported.
");
return -EINVAL;
}
MODULE_LICENSE("GPL");
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
loff_t *);
};
Some operations are not implemented by a driver. For example, a driver that handles a video card won't need to readfrom a directory structure. The corresponding entries in thefile_operations structure should be set toNULL.
There is a gcc extension that makes assigning to this structure more convenient. You'll see it in modern drivers,and may catch you by surprise. This is what the new way of assigning to the structure looks like: struct file_operations fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
};
However, there's also a C99 way of assigning to elements of a structure, and this is definitely preferred over usingthe GNU extension. The version of gcc I'm currently using,2.95, supports the new C99 syntax. Youshould use this syntax in case someone wants to port your driver. It will help with compatibility: struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
The meaning is clear, and you should be aware that any member of the structure which you don't explicitly assignwill be initialized toNULL by gcc.
A pointer to a struct file_operations is commonly namedfops.
The file structure
Each device is represented in the kernel by a file structure, which is defined inlinux/fs.h. Be aware that afile is a kernel level structure and never appears in auser space program. It's not the same thing as aFILE, which is defined by glibc and would never appear in akernel space function. Also, its name is a bit misleading; it represents an abstract open `file', not a file on a disk,which is represented by a structure namedinode.
A pointer to a struct file is commonly named filp. You'll also see itrefered to as struct file file. Resist the temptation.
Go ahead and look at the definition of file. Most of the entries you see, likestruct dentry aren't used by device drivers, and you can ignore them. This is because drivers don'tfillfile directly; they only use structures contained in file which are createdelsewhere.
Registering A Device
As discussed earlier, char devices are accessed through device files, usually located in/dev[1]. The major number tells you which driver handles which device file. The minor numberis used only by the driver itself to differentiate which device it's operating on, just in case the driver handles morethan one device.
Adding a driver to your system means registering it with the kernel. This is synonymous with assigning it a majornumber during the module's initialization. You do this by using theregister_chrdev function,defined bylinux/fs.h. int register_chrdev(unsigned int major, const char *name,
struct file_operations *fops);
where unsigned int major is the major number you want to request,const char*name is the name of the device as it'll appear in/proc/devices andstructfile_operations *fops is a pointer to thefile_operations table for your driver. A negativereturn value means the registertration failed. Note that we didn't pass the minor number toregister_chrdev. That's because the kernel doesn't care about the minor number; only our driver usesit.
Now the question is, how do you get a major number without hijacking one that's already in use? The easiest waywould be to look throughDocumentation/devices.txt and pick an unused one. That's a bad way of doingthings because you'll never be sure if the number you picked will be assigned later. The answer is that you can ask thekernel to assign you a dynamic major number.
If you pass a major number of 0 to register_chrdev, the return value will be the dynamicallyallocated major number.The downside is that you can't make a device file in advance, since you don't know what the majornumber will be.ここでは3つの を していますが、 も な は、 に り てた 、printkで しい り てられたプライマリ・デバイス を し、dmesgコマンドで することです. に、デバイスファイルを するときにプライマリ・デバイス を します.There are a couple of ways to do this. First, the driver itself can print the newly assigned number andwe can make the device file by hand. Second, the newly registered device will have an entry in/proc/devices, and we can either make the device file by hand or write a shell script to read thefile in and make the device file. The third method is we can have our driver make the the device file using themknod system call after a successful registration and rm during the call tocleanup_module.
Unregistering A Device
We can't allow the kernel module to be rmmod'ed whenever root feels like it. If thedevice file is opened by a process and then we remove the kernel module, using the file would cause a call to the memorylocation where the appropriate function (read/write) used to be. If we're lucky, no other code was loaded there, andwe'll get an ugly error message. If we're unlucky, another kernel module was loaded into the same location, which means ajump into the middle of another function within the kernel. The results of this would be impossible to predict, but theycan't be very positive.
Normally, when you don't want to allow something, you return an error code (a negative number) from the functionwhich is supposed to do it. Withcleanup_module that's impossible because it's a void function.However, there's a counter which keeps track of how many processes are using your module. You can see what it's value isby looking at the 3rd field of/proc/modules. If this number isn't zero,rmmodwill fail. Note that you don't have to check the counter from withincleanup_module because thecheck will be performed for you by the system callsys_delete_module, defined inlinux/module.c. You shouldn't use this counter directly, but there are macros defined inlinux/modules.h which let you increase, decrease and display this counter: MOD_INC_USE_COUNT: Increment the use count. MOD_DEC_USE_COUNT: Decrement the use count. MOD_IN_USE: Display the use count.
It's important to keep the counter accurate; if you ever do lose track of the correct usage count, you'll never beable to unload the module; it's now reboot time, boys and girls. This is bound to happen to you sooner or later during amodule's development.
chardev.c
The next code sample creates a char driver named chardev. You cancat itsdevice file (oropen the file with a program) and the driver will put the number of times the devicefile has been read from into the file. We don't support writing to the file (likeecho "hi">/dev/hello), but catch these attempts and tell the user that the operation isn't supported. Don't worry if youdon't see what we do with the data we read into the buffer; we don't do much with it. We simply read in the data andprint a message acknowledging that we received it.
Example 4-1. chardev.c /* chardev.c: Creates a read-only char device that says how many times
* you've read from the dev file
*
* Copyright (C) 2001 by Peter Jay Salzman
*
* 08/02/2006 - Updated by Rodrigo Rubira Branco <[email protected]>
*/
/* Kernel Programming */
#define MODULE
#define LINUX
#define __KERNEL__
#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
#include <linux/modversions.h>
#define MODVERSIONS
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* for put_user */
#include <asm/errno.h>
/* Prototypes - this would normally go in a .h file */
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
#define SUCCESS 0
#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */
#define BUF_LEN 80 /* Max length of the message from the device */
/* Global variables are declared as static, so are global within the file. */
static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open? Used to prevent multiple
access to the device */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
/* Functions */
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk ("Registering the character device failed with %d
", Major);
return Major;
}
printk("<1>I was assigned major number %d. To talk to
", Major);
printk("<1>the driver, create a dev file with
");
printk("'mknod /dev/hello c %d 0'.
", Major);
printk("<1>Try various minor numbers. Try to cat and echo to
");
printk("the device file.
");
printk("<1>Remove the device file and module when done.
");
return 0;
}
void cleanup_module(void)
{
/* Unregister the device */
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0) printk("Error in unregister_chrdev: %d
", ret);
}
/* Methods */
/* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open) return -EBUSY;
Device_Open++;
sprintf(msg,"I already told you %d times Hello world!
", counter++);
msg_Ptr = msg;
MOD_INC_USE_COUNT;
return SUCCESS;
}
/* Called when a process closes the device file */
static int device_release(struct inode *inode, struct file *file)
{
Device_Open --; /* We're now ready for our next caller */
/* Decrement the usage count, or else once you opened the file, you'll
never get get rid of the module. */
MOD_DEC_USE_COUNT;
return 0;
}
/* Called when a process, which already opened the dev file, attempts to
read from it.
*/
static ssize_t device_read(struct file *filp,
char *buffer, /* The buffer to fill with data */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we're at the end of the message, return 0 signifying end of file */
if (*msg_Ptr == 0) return 0;
/* Actually put the data into the buffer */
while (length && *msg_Ptr) {
/* The buffer is in the user data segment, not the kernel segment;
* assignment won't work. We have to use put_user which copies data from
* the kernel data segment to the user data segment. */
put_user(*(msg_Ptr++), buffer++);
length--;
bytes_read++;
}
/* Most read functions return the number of bytes put into the buffer */
return bytes_read;
}
/* Called when a process writes to dev file: echo "hi" > /dev/hello */
static ssize_t device_write(struct file *filp,
const char *buff,
size_t len,
loff_t *off)
{
printk ("<1>Sorry, this operation isn't supported.
");
return -EINVAL;
}
MODULE_LICENSE("GPL");