欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          linux下字符設備有哪些

          linux字符設備有:1、鼠標,是計算機的一種外接輸入設備,也是計算機顯示系統(tǒng)縱橫坐標定位的指示器;2、鍵盤,是用于操作計算機設備運行的一種指令和數(shù)據(jù)輸入裝置;3、串行端口終端,使用計算機串行端口連接的終端設備;4、控制終端;5、控制臺等。

          linux下字符設備有哪些

          本教程操作環(huán)境:linux7.3系統(tǒng)、Dell G3電腦。

          linux字符設備


          字符設備是Linux三大設備之一(另外兩種是塊設備,網(wǎng)絡設備)。它們均以一個文件節(jié)點形式顯示在文件系統(tǒng)的/dev目錄下(crw–w—- 1 root tty 4, 0 7月 11 09:11 tty0 其中c代表字符設備類型)。

          字符設備是指設備無需緩沖即可直接進行讀寫的設備, 如鼠標,鍵盤,串口設備、調制解調器等, 它與塊設備的區(qū)別在于是字符操作的基本單位是字節(jié)。

          字符設備的分類

          字符設備主要包括控制終端設備和串行終端設備, 例如控制臺和鍵盤。依據(jù)功能和硬件上的差別, 字符終端設備有如下分類:

          • 串行端口終端(/dev/ttSn):使用計算機串行端口連接的終端設備, 串行設備數(shù)據(jù)傳輸方式為同一字符8個bit單線傳輸, 在命令行輸入 echo 'hello world' > /dev/ttyS0可將輸入寫入到對應設備。

          • 偽終端(/dev/ttyp,/dev/ptyp): 對應底層不存在真實的硬件設備, 用于為其他程序提供終端式樣的接口,如網(wǎng)絡登陸主機時網(wǎng)絡服務器和shell程序之間的終端接口。

          • 控制終端(/dev/tty):主設備號為5, 進程控制終端,與進程相關聯(lián),如登陸shell進程使用的就是終端/dev/tty。

          • 控制臺(/dev/ttyn,/dev/consol): 計算機輸入輸出的顯示器,當控制臺登陸時, 使用的就是tty1, 而ubuntu 圖形界面使用的tty7。

          • 其他類型:現(xiàn)行的linux針對許多不同的設備建有許多其他種類的設備特殊文件,如ISIDIN設備的/dev/ttyIn設備。

          字符設備的性質及特點

          • 字符設備屬于設備文件系統(tǒng)的一種, 相當于底層硬件向上層提供的邏輯設備文件, 宛如將一個數(shù)據(jù)端口(數(shù)據(jù)寄存器)與一個文件對接起來,設備驅動程序直接對文件操作, 于是便直接對端口進行了讀寫操作。 同樣作為文件, 字符設備驅動也必須實現(xiàn)文件的基本的操作open(),close(),write(),read()等,當然終端重定向操作也是支持的。

          • 字符設備文件文件的讀寫是以單個字節(jié)為單位的, 不需要設立硬件緩沖區(qū)。 設備像訪問字節(jié)流一樣被操作系統(tǒng)訪問。 字節(jié)流就像在硬件端口和文件系統(tǒng)搭建起了一個傳送管道, 字節(jié)逐個通過管道傳輸并呈現(xiàn)給讀寫雙方。 這個流特性在驅動程序中是以緩沖隊列來實現(xiàn)的。例如: 控制臺的結構體中的讀寫緩沖隊列

          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輔助隊列(存放規(guī)格化后的字符) };
          登錄后復制

          • 字符設備由字符設備號標識。字符設備號由主設備號和次設備號構成, 例如/dev/ttyS0的設備號為(4,64); 主設備號標識設備對應驅動程序, 內核通過主設備號將設備和驅動程序一一對應起來, 次設備號由驅動程序使用, 用于驅動程序內部區(qū)分設備細節(jié)差別使用的代碼,內核其他部分不使用它。

          字符設備在應用層如何體現(xiàn)

          cat /proc/devices 命令可以查看當前系統(tǒng)中所有的字符設備和塊設備。

          linux下字符設備有哪些

          linux下字符設備有哪些

          在Linux 中一切接文件,設備也被抽象成文件,在/dev/ 目錄下可以查看到字符設備和塊設備的對應的文件。
          例如這是兩個串口設備文件,使用ls -l 查看它的詳細信息。
          與普通文件不同的是設備文件沒有大小,而是被替換成了設備號(主設備號和次設備號),設備號可以與/proc/devices 中的信息相對應。

          linux下字符設備有哪些

          如何訪問一個設備?

          既然它被抽象成了一個文件,那么自然就用文件IO (open、read、write 等等) 來訪問。

          設備號

          linux下字符設備有哪些

          dev_t dev = MKDEV(major,minor)
          major = MAJOR(dev)
          minor = MINOR(dev)

          設備號由major和minor 組成,總共32位,高12位為major,低20位為minor。每一個設備號都唯一對應著一個cdev結構體。

          注冊字符設備首先要申請設備號,可以一次批量的申請多個設備號(相同的major,依次遞增的minor)。
          如果沒有指定主設備號的話就使用如下函數(shù)來申請設備號:
          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,然后由內核分配一個空閑的設備號返回)

          如果給定了設備的主設備號和次設備號就使用如下所示函數(shù)來注冊設備號即可:
          int register_chrdev_region(dev_t from, unsigned count, const char *name)

          釋放設備號:
          void unregister_chrdev_region(dev_t from, unsigned count)

          字符設備結構體 cdev

          //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 調用計數(shù):
          void cdev_put(struct cdev *p);

          總結:注冊字符設備的主要流程就是,申請設備號dev_t,創(chuàng)建一個cdev、初始化cdev (cdev->ops)、向內核添加 cdev(同時會綁定cdev 與dev_t)。

          如果你嫌這些步驟太麻煩的話,內核還提供了一個函數(shù)可以一步到位的注冊字符設備——__register_chrdev
          它會申請多個設備號,創(chuàng)建cdev并初始化它,然后用這多個設備號綁定同一個cdev 注冊。

          linux下字符設備有哪些

          還有一個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); }

          struct file_operations

          字符設備在/dev/ 目錄下創(chuàng)建設備文件,并通過struct file_operations 向應用層提供控制接口。應用層對應的open、read 等函數(shù)會調用到file_operations 對應的函數(shù)。

          //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()

          為了安全考慮,應用進程不能直接訪問內核數(shù)據(jù),需要借助這兩個函數(shù)拷貝:
          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 表示錯誤。

          自動創(chuàng)建設備文件

          自動創(chuàng)建設備節(jié)點的工作是在驅動程序的入口函數(shù)中完成的,一般在 cdev_add 函數(shù)后面添加自動創(chuàng)建設備節(jié)點相關代碼。首先要創(chuàng)建一個 class 類, class 是個結構體,定義在文件include/linux/device.h 里面。

          使用 class_create 創(chuàng)建一個類:

          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; };
          登錄后復制

          在創(chuàng)建完類后,要創(chuàng)建一個設備,使用 device_create創(chuàng)建一個設備:
          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);

          創(chuàng)建類會在/sys/class/ 目錄下生成一個新的文件夾,其中包含屬于此類的設備文件夾。

          linux下字符設備有哪些

          IS_ERR 和 PTR_ERR

          IS_ERR 可以判斷一個指針是否為空,PTR_ERR 將指針轉化為數(shù)值返回。

          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	//設備號個數(shù)  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; }    /* 設備操作函數(shù) */ 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=%drn",newchrled.major, newchrled.minor);	  	/*2.初始化 cdev 結構體*/ 	led.led_dev.woner = THIS_MODULE; 	cdev_init(&led.led_dev,&led_fo);	//將操作函數(shù)初始化到cdev結構體  	/*3.應該是向鏈表中添cdev*/ 	cdev_add(&led.led_dev,led.devid,DEVICE_CNT);	  	/*4.創(chuàng)建節(jié)點*/ 	led.class = class_create(THIS_MODULE,led.name);		//先創(chuàng)建一個類 	led.device = device_create(led.class,NULL,led.devid,NULL);	//創(chuàng)建設備  	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下字符設備有哪些

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號