Rumah >masalah biasa >Apakah peranti watak di bawah Linux?
Peranti aksara Linux termasuk: 1. Tetikus ialah peranti input luaran untuk komputer dan penunjuk untuk meletakkan koordinat menegak dan mendatar sistem paparan komputer 2. Papan kekunci ialah arahan yang digunakan untuk mengendalikan peranti komputer dan peranti input data;
Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.
Peranti aksara ialah salah satu daripada tiga peranti utama dalam Linux (dua yang lain ialah peranti blok dan peranti rangkaian). Kesemuanya dipaparkan dalam direktori /dev sistem fail dalam bentuk nod fail (crw--w---- 1 root tty 4, 0 11 Julai 09:11 tty0 di mana c mewakili jenis peranti aksara).
Peranti aksara merujuk kepada peranti yang boleh membaca dan menulis terus tanpa penimbalan, seperti tetikus, papan kekunci, peranti port bersiri, modem, dll. Perbezaan antara peranti itu dan peranti blok ialah unit asas operasi aksara ialah bait.
Klasifikasi peranti aksara
Peranti aksara terutamanya termasuk peranti terminal kawalan dan peranti terminal bersiri, seperti konsol dan papan kekunci. Berdasarkan perbezaan dalam fungsi dan perkakasan, peranti terminal aksara dikelaskan seperti berikut:
Terminal port bersiri (/dev/ttSn): peranti terminal yang disambungkan menggunakan port bersiri komputer, bersiri kaedah penghantaran data peranti ialah penghantaran satu talian 8-bit dengan aksara yang sama Masukkan gema 'hello world' >
Pseudo terminal (/dev/ttyp, /dev/ptyp): Sejajar dengan fakta bahawa tiada peranti perkakasan sebenar di lapisan bawah, ia digunakan untuk menyediakan terminal- antara muka gaya untuk atur cara lain, seperti log masuk rangkaian ke hos Antara muka terminal antara pelayan rangkaian dan program shell.
Terminal kawalan (/dev/tty): Nombor peranti utama ialah 5. Terminal kawalan proses dikaitkan dengan proses Contohnya, proses shell log masuk menggunakan terminal /dev /tty.
Konsol (/dev/ttyn, /dev/consol): Monitor untuk input dan output komputer Apabila konsol dilog masuk, tty1 digunakan, manakala antara muka grafik ubuntu menggunakan tty7 .
Jenis lain: Linux semasa mempunyai banyak jenis fail khas peranti lain untuk banyak peranti berbeza, seperti peranti /dev/ttyIn bagi peranti ISIDIN.
Sifat dan ciri peranti aksara
Peranti aksara ialah sebahagian daripada peranti sistem fail Ia bersamaan dengan fail peranti logik yang disediakan oleh perkakasan asas ke lapisan atas Ia seperti menyambungkan port data (daftar data) ke fail Pemacu peranti beroperasi secara langsung pada fail, jadi ia secara langsung melakukan pembacaan dan menulis operasi pada pelabuhan. Juga sebagai fail, pemacu peranti aksara juga mesti melaksanakan operasi asas fail seperti open(), close(), write(), read(), dsb. Sudah tentu, operasi ubah hala terminal juga disokong.
Fail fail peranti aksara dibaca dan ditulis dalam unit bait tunggal, dan tidak perlu menyediakan penimbal perkakasan. Peranti diakses oleh sistem pengendalian sebagai aliran bait. Aliran bait adalah seperti menyediakan paip penghantaran antara port perkakasan dan sistem fail dihantar melalui paip satu demi satu dan dibentangkan kepada kedua-dua pembaca dan penulis. Ciri penstriman ini dilaksanakan dalam pemacu sebagai baris gilir penimbal. Contohnya: baris gilir penimbal baca-tulis dalam struktur konsol
struct tty_struct { struct termios termios; int pgrp; int stopped; void (*write)(struct tty_struct * tty); struct tty_queue read_q; //读队列 struct tty_queue write_q; //写队列 struct tty_queue secondary; //tty辅助队列(存放规格化后的字符) };
peranti aksara dikenal pasti melalui nombor peranti aksara. Nombor peranti aksara terdiri daripada nombor peranti utama dan nombor peranti kecil Contohnya, nombor peranti /dev/ttyS0 ialah (4, 64); nombor peranti utama untuk memadankan peranti dan pemandu satu-satu Secara umumnya, nombor peranti kecil digunakan oleh pemandu untuk kod yang digunakan secara dalaman oleh pemandu untuk membezakan antara butiran peranti daripada kernel.
cat /proc/devices
arahan boleh melihat semua peranti aksara dalam sistem semasa dan peranti blok.
Di Linux, apabila menyambungkan fail, peranti juga diabstraksikan ke dalam fail Anda boleh melihat peranti aksara dan Fail yang sepadan untuk blok peranti.
Contohnya, ini adalah dua fail peranti port bersiri Gunakan ls -l untuk melihat maklumat terperincinya.
Berbeza daripada fail biasa, fail peranti tidak mempunyai saiz, tetapi digantikan dengan nombor peranti (nombor peranti utama dan nombor peranti kecil boleh sepadan dengan maklumat dalam /proc/devices).
Bagaimana untuk mengakses peranti?
Memandangkan ia disarikan ke dalam fail, ia secara semula jadi diakses menggunakan fail IO (buka, baca, tulis, dll.).
dev_t dev = MKDEV(major,minor)
major = MAJOR(dev)
minor = MINOR(dev)
设备号由major和minor 组成,总共32位,高12位为major,低20位为minor。每一个设备号都唯一对应着一个cdev结构体。
注册字符设备首先要申请设备号,可以一次批量的申请多个设备号(相同的major,依次递增的minor)。
如果没有指定主设备号的话就使用如下函数来申请设备号:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
baseminor:起始的minor值。
count:一共申请几个设备号。申请到的设备号在(MKDEV(major,baseminor) ~ MKDEV(major,baseminor+count)) 范围内。
(自动申请设备号的原理,其实是传递一个major = 0,然后由内核分配一个空闲的设备号返回)
如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可:int register_chrdev_region(dev_t from, unsigned count, const char *name)
释放设备号:void unregister_chrdev_region(dev_t from, unsigned count)
//include/linux/cdev.h struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
常用
申请一个cdev 内存:struct cdev *cdev_alloc(void);
初始化cdev->ops,即cdev->ops = &xxx_file_operation; :void cdev_init(struct cdev *, const struct file_operations *);
将填充好的cdev 实例,添加到cdev 链表。意味着向内核注册一个字符设备:int cdev_add(struct cdev *, dev_t, unsigned);
//dev_t:添加cdev时必须要,传递一个dev_t,并且它与cdev是唯一对应的。
cdev_add 添加过程中会绑定cdev 与dev_t。
从内核删除一个字符设备:void cdev_del(struct cdev *);
不常用
增加cdev 调用计数:void cdev_put(struct cdev *p);
总结:注册字符设备的主要流程就是,申请设备号dev_t,创建一个cdev、初始化cdev (cdev->ops)、向内核添加 cdev(同时会绑定cdev 与dev_t)。
如果你嫌这些步骤太麻烦的话,内核还提供了一个函数可以一步到位的注册字符设备——__register_chrdev
它会申请多个设备号,创建cdev并初始化它,然后用这多个设备号绑定同一个cdev 注册。
还有一个register_chrdev
,它是对__register_chrdev 的封装,次设备号从基值0开始,直接申请了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); }
字符设备在/dev/ 目录下创建设备文件,并通过struct file_operations 向应用层提供控制接口。应用层对应的open、read 等函数会调用到file_operations 对应的函数。
//include/linux/fs.h struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*mremap)(struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif };
copy_to_user() 与 copy_from_user()
为了安全考虑,应用进程不能直接访问内核数据,需要借助这两个函数拷贝:static inline int copy_to_user(void __user volatile *to, const void *from, unsigned long n)
static inline int copy_from_user(void *to, const void __user volatile *from, unsigned long n)
返回非0 表示错误。
自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在 cdev_add 函数后面添加自动创建设备节点相关代码。首先要创建一个 class 类, class 是个结构体,定义在文件include/linux/device.h 里面。
使用 class_create 创建一个类:
extern struct class * __must_check __class_create(struct module *owner, const char *name, struct lock_class_key *key); #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ })
使用class_destroy 摧毁一个类:extern void class_destroy(struct class *cls);
struct class { const char *name; struct module *owner; struct class_attribute *class_attrs; const struct attribute_group **dev_groups; struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*devnode)(struct device *dev, umode_t *mode); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct kobj_ns_type_operations *ns_type; const void *(*namespace)(struct device *dev); const struct dev_pm_ops *pm; struct subsys_private *p; };
在创建完类后,要创建一个设备,使用 device_create创建一个设备:struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
摧毁一个设备:extern void device_destroy(struct class *cls, dev_t devt);
创建类会在/sys/class/ 目录下生成一个新的文件夹,其中包含属于此类的设备文件夹。
IS_ERR 可以判断一个指针是否为空,PTR_ERR 将指针转化为数值返回。
static inline long __must_check PTR_ERR(const void *ptr) { return (long) ptr; } static inline long __must_check IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); }
#include <linux/fs.h> //file_operations声明 #include <linux/module.h> //module_init module_exit声明 #include <linux/init.h> //__init __exit 宏定义声明 #include <linux/device.h> //class devise声明 #include <linux/uaccess.h> //copy_from_user 的头文件 #include <linux/types.h> //设备号 dev_t 类型声明 #include <asm/io.h> //ioremap iounmap的头文件 #define DEVICE_CNT 0 //设备号个数 struct led_device{ dev_t devid; //设备号 int major; //主设备号 int minor; //次设备号 char* name = "led"; //驱动名 struct cdev led_dev; //cdev 结构体 struct class *class; /* 类 */ struct device* device; //设备 }; struct led_device led; static int led_open(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { return 0; } static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { return 0; } /* 设备操作函数 */ static struct file_operations led_fo = { .owner = THIS_MODULE, .open = led_open, .read = led_read, .write = led_write, }; static int _init led_init() { /*注册字符设备驱动*/ /*1.注册设备号*/ led.major = 0; //由内核自动分配主设备号 if(led.major) //如果分配了的话就注册 { led.devid = MKDEV(led.major,0); register_chrdev_region(led.devid,DEVICE_CNT,led.name); //将驱动注册到内核中 } else{ //如果没有分配的话 //从0号(次设备号)开始申请 alloc_chrdev_region(&led.devid,0,DEVICE_CNT,led.name); //申请设备号 led.major = MAJOR(led.devid); //获取主设备号 led.minor = MANOR(led.devid); //获取次设备号 } printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor); /*2.初始化 cdev 结构体*/ led.led_dev.woner = THIS_MODULE; cdev_init(&led.led_dev,&led_fo); //将操作函数初始化到cdev结构体 /*3.应该是向链表中添cdev*/ cdev_add(&led.led_dev,led.devid,DEVICE_CNT); /*4.创建节点*/ led.class = class_create(THIS_MODULE,led.name); //先创建一个类 led.device = device_create(led.class,NULL,led.devid,NULL); //创建设备 return 0; } static void _exit led_exit() { /* 注销字符设备驱动 */ cdev_del(&newchrled.cdev);/* 删除cdev */ unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */ device_destroy(newchrled.class, newchrled.devid); class_destroy(newchrled.class); } /*注册字符设备入口与卸载入口*/ module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("zhoujianghong");
应用open到file_operations->open 的调用原理
相关推荐:《Linux视频教程》
Atas ialah kandungan terperinci Apakah peranti watak di bawah Linux?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!