メジャー番号/マイナ番号の取り扱い
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); }