メジャー番号/マイナ番号の取り扱い
Rev.4を表示中。最新版はこちら。
デバイスファイルのメジャー/マイナー番号は、inode->i_rdev=(((ma) << MINORBITS) | (mi))と設定されます。でメジャー番号でドライバを特定し、マイナー番号でドライバに依存した処理がなされる。と理解していました。しかし実装は、メジャー番号/マイナー番号の組み合わせで(厳密にはマイナー番号の範囲)で、ドライバーが特定できる事も可能となるような実装となっています。メジャー番号のみでドライバーをhoge_fopsとし、ドライバ側でマイナー番号に依存する処理を行っています。マイナー番号は255まで可能です。
#include <linux/module.h>
#include <linux/genhd.h>
#include <linux/uaccess.h>
#define HOGE_MAJOR 100
static int hoge_open (struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t hoge_write (struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
unsigned char buf[64];
unsigned int minor = iminor(inode);
}
static ssize_t hoge_write (struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
unsigned char buf[64];
unsigned int minor = iminor(inode);
if (_copy_from_user(buf, user_buf, count )) {
return -EFAULT;
}
buf[count - 1] = 0;
printk("<0>write %s by minor %d\n", buf, minor);
return count;
}
static struct file_operations hoge_fops = {
.owner = THIS_MODULE,
.write = hoge_write,
.open = hoge_open,
};
static int hoge_init (void)
{
if (register_chrdev(HOGE_MAJOR, "hoge", &hoge_fops)) {
printk("<0>chrdev register err\n");
return -1;
}
return 0;
}
static void __exit hoge_exit (void)
{
unregister_chrdev(HOGE_MAJOR, "hoge");
}
module_init(hoge_init);
module_exit(hoge_exit);
メジャー/マイナー番号は100/1の時、hoge_fops1を、100/2の時、hoge_fops2となります。指定できるマイナー番号は1/2のみで、hoge3ではエラーとなっています。
#include <linux/module.h>
#include <linux/genhd.h>
#include <linux/uaccess.h>
#define HOGE_MAJOR 100
static int hoge_open (struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t hoge_write1 (struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
unsigned char buf[64];
if (_copy_from_user(buf, user_buf, count )) {
return -EFAULT;
}
buf[count - 1] = 0;
printk("<0>write %s by minor 1\n", buf);
return count;
}
static ssize_t hoge_write2 (struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
unsigned char buf[64];
if (_copy_from_user(buf, user_buf, count )) {
return -EFAULT;
}
buf[count - 1] = 0;
printk("<0>write %s by minor 2\n", buf);
return count;
}
static struct file_operations hoge_fops1 = {
.owner = THIS_MODULE,
.write = hoge_write1,
.open = hoge_open,
};
static struct file_operations hoge_fops2 = {
.owner = THIS_MODULE,
.write = hoge_write2,
.open = hoge_open,
};
static int hoge_init (void)
{
if(__register_chrdev(HOGE_MAJOR, 1, 1, "hoge1", &hoge_fops1)) {
printk("<0>chrdev register hoge1 err\n");
return -1;
}
if(__register_chrdev(HOGE_MAJOR, 2, 1, "hoge2", &hoge_fops2)) {
printk("<0>chrdev register hoge2 err\n");
return -1;
}
return 0;
}
static void __exit hoge_exit (void)
{
__unregister_chrdev(HOGE_MAJOR, 1, 1, "hoge1");
__unregister_chrdev(HOGE_MAJOR, 2, 1, "hoge2");
}
module_init(hoge_init);
module_exit(hoge_exit);
デバイスファイルの作成[root@localhost lkm]# mknod hoge1 c 100 1 [root@localhost lkm]# mknod hoge2 c 100 2 [root@localhost lkm]# mknod hoge3 c 100 3パターン1
[root@localhost lkm]# insmod hoge.ko [root@localhost lkm]# echo "abc" > hoge1 [root@localhost lkm]# Message from syslogd@localhost at Sep 1 02:36:48 ... kernel:[ 4858.277987] write abc by minor 1 [root@localhost lkm]# echo "abc" > hoge2 [root@localhost lkm]# Message from syslogd@localhost at Sep 1 02:36:56 ... kernel:[ 4865.619893] write abc by minor 2 [root@localhost lkm]# echo "abc" > hoge3 [root@localhost lkm]# Message from syslogd@localhost at Sep 1 02:36:59 ... kernel:[ 4869.280976] write abc by minor 3パターン2
[root@localhost lkm]# insmod hoge.ko [root@localhost lkm]# echo "abc" > hoge1 [root@localhost lkm]# Message from syslogd@localhost at Sep 1 02:42:00 ... kernel:[ 5169.970628] write abc by minor 1 [root@localhost lkm]# echo "abc" > hoge2 [root@localhost lkm]# Message from syslogd@localhost at Sep 1 02:42:04 ... kernel:[ 5173.357401] write abc by minor 2 [root@localhost lkm]# echo "abc" > hoge3 -bash: hoge3: そのようなデバイスやアドレスはありません
補足
カーネルがメジャー番号/マイナー番号でドライバが特定してくれれば、ドライバーの読み書き毎に、マイナー番号による処理を必要としません。入出力頻度の多いドライバを考慮しての実装かと思います。実際、ttyの/dev/ttyと/dev/console(メジャー番号は同じ)では利用されています。register_chrdev()は__register_chrdev()をマイナー番号数を256としてコールしています。
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}




