目錄
linux字元設備
字元裝置在應用層如何體現" >字元裝置在應用層如何體現
设备号" >设备号
字符设备结构体 cdev" >字符设备结构体 cdev
struct file_operations" >struct file_operations
自动创建设备文件" >自动创建设备文件
IS_ERR 和 PTR_ERR" >IS_ERR 和 PTR_ERR
首頁 常見問題 linux下字元裝置有哪些

linux下字元裝置有哪些

Mar 09, 2023 am 10:49 AM
linux 字元設備

linux字元設備有:1、滑鼠,是電腦的一種外接輸入設備,也是電腦顯示系統縱橫座標定位的指示器;2、鍵盤,是用來操作電腦設備運作的一種指令及資料輸入裝置;3、序列埠終端,使用電腦序列埠連接的終端設備;4、控制終端;5、控制台等。

linux下字元裝置有哪些

本教學操作環境:linux7.3系統、Dell G3電腦。

linux字元設備


字元設備是Linux三大設備之一(另外兩種是區塊設備,網路設備)。它們皆以一個檔案節點形式顯示在檔案系統的/dev目錄下(crw--w---- 1 root tty 4, 0 7月 11 09:11 tty0 其中c代表字元裝置類型)。

字元設備是指設備無需緩衝即可直接進行讀寫的設備, 如滑鼠,鍵盤,串列設備、調變解調器等, 它與區塊設備的區別在於是字元操作的基本單位是位元組.

字元設備的分類

字元設備主要包括控制終端設備和序列終端設備, 例如控制台和鍵盤。依據功能與硬體上的差異, 字元終端設備有下列分類:

  • 序列埠終端(/dev/ttSn):使用電腦序列埠連接的終端設備,序列裝置資料傳輸方式為相同字元8個bit單線傳輸, 在指令列輸入echo 'hello world' > /dev/ttyS0可將輸入寫入對應裝置。

  • 偽終端(/dev/ttyp,/dev/ptyp): 對應底層不存在真實的硬體設備, 用於為其他程式提供終端式樣的接口,如網路登陸主機時網路伺服器和shell程式之間的終端介面。

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

  • 控制台(/dev/ttyn,/dev/consol): 電腦輸入輸出的顯示器,當控制台登陸時, 使用的就是tty1, 而ubuntu 圖形介面所使用的tty7 。

  • 其他類型:現行的linux針對許多不同的裝置建有許多其他種類的裝置特殊文件,如ISIDIN裝置的/dev/ttyIn裝置。

字元設備的性質及特性

  • 字元裝置屬於裝置檔案系統的一種, 相當於底層硬體向上層提供的邏輯設備文件, 宛如將一個資料埠(資料暫存器)與一個文件對接起來,設備驅動程式直接對文件操作, 於是便直接對埠進行了讀寫操作。同樣作為文件, 字元設備驅動也必須實現文件的基本的操作open(),close(),write(),read()等,當然終端重定向操作也是支援的。

  • 字元裝置檔案檔案的讀寫是以單一位元組為單位的, 不需要設立硬體緩衝區。設備像存取位元組流一樣被作業系統存取。位元組流就像在硬體連接埠和檔案系統搭建起了一個傳送管道, 位元組逐個通過管道傳輸並呈現給讀寫雙方。這個流特性在驅動程式中是以緩衝佇列來實現的。例如: 控制台的結構體中的讀寫緩衝佇列

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辅助队列(存放规格化后的字符)
};
登入後複製
  • #字元裝置由字元裝置號碼標識。字元設備號由主設備號和次設備號構成, 例如/dev/ttyS0的設備號為(4,64); 主設備號標識設備對應驅動程序, 內核透過主設備號將設備和驅動程序一一對應起來, 次設備號由驅動程式使用, 用於驅動程式內部區分設備細節差別使用的程式碼,內核其他部分不使用它。

字元裝置在應用層如何體現

cat /proc/devices 指令可以查看目前系統中所有的字元設備和區塊設備。

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)。
如果没有指定主设备号的话就使用如下函数来申请设备号:
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)

字符设备结构体 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 调用计数:
void cdev_put(struct cdev *p);

总结:注册字符设备的主要流程就是,申请设备号dev_t,创建一个cdev、初始化cdev (cdev->ops)、向内核添加 cdev(同时会绑定cdev 与dev_t)。

如果你嫌这些步骤太麻烦的话,内核还提供了一个函数可以一步到位的注册字符设备——__register_chrdev
它会申请多个设备号,创建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/ 目录下创建设备文件,并通过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/ 目录下生成一个新的文件夹,其中包含属于此类的设备文件夹。

5-linux下字元裝置有哪些

IS_ERR 和 PTR_ERR

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下字元裝置有哪些

相关推荐:《Linux视频教程

以上是linux下字元裝置有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

無法以 root 身份登錄 mysql 無法以 root 身份登錄 mysql Apr 08, 2025 pm 04:54 PM

無法以 root 身份登錄 MySQL 的原因主要在於權限問題、配置文件錯誤、密碼不符、socket 文件問題或防火牆攔截。解決方法包括:檢查配置文件中 bind-address 參數是否正確配置。查看 root 用戶權限是否被修改或刪除,並進行重置。驗證密碼是否準確無誤,包括大小寫和特殊字符。檢查 socket 文件權限設置和路徑。檢查防火牆是否阻止了 MySQL 服務器的連接。

C語言條件編譯:新手入門到實戰應用的詳盡指南 C語言條件編譯:新手入門到實戰應用的詳盡指南 Apr 04, 2025 am 10:48 AM

C語言條件編譯是一種根據編譯時條件選擇性編譯代碼塊的機制,入門方法有:使用#if和#else指令根據條件選擇代碼塊。常用條件表達式包括STDC、_WIN32和linux。實戰案例:根據操作系統打印不同消息。根據系統位數使用不同的數據類型。根據編譯器支持不同的頭文件。條件編譯增強了代碼的可移植性和靈活性,使其適應編譯器、操作系統和CPU架構變化。

【Rust自學】簡介 【Rust自學】簡介 Apr 04, 2025 am 08:03 AM

1.0.1前言這個項目(包括代碼和註釋)是在我自學Rust的過程中記錄的。可能有不准確或表述不清的地方,還請大家諒解。如果您從中受益,那就更好了。 1.0.2為什麼使用RustRust可靠且高效。 Rust可以取代C和C,性能相似但安全性更高,並且不需要像C和C那樣頻繁重新編譯來檢查錯誤。主要優點包括:內存安全(防止空指針取消引用、懸空指針和數據爭用)。線程安全(確保多線程代碼在執行前是安全的)。避免未定義的行為(例如,數組越界、未初始化的變量或訪問已釋放的內存)。 Rust提供現代語言功能(例如泛型

Linux的5個基本組件是什麼? Linux的5個基本組件是什麼? Apr 06, 2025 am 12:05 AM

Linux的五個基本組件是:1.內核,管理硬件資源;2.系統庫,提供函數和服務;3.Shell,用戶與系統交互的接口;4.文件系統,存儲和組織數據;5.應用程序,利用系統資源實現功能。

mysql 無法啟動怎麼解決 mysql 無法啟動怎麼解決 Apr 08, 2025 pm 02:21 PM

MySQL啟動失敗的原因有多種,可以通過檢查錯誤日誌進行診斷。常見原因包括端口衝突(檢查端口占用情況並修改配置)、權限問題(檢查服務運行用戶權限)、配置文件錯誤(檢查參數設置)、數據目錄損壞(恢復數據或重建表空間)、InnoDB表空間問題(檢查ibdata1文件)、插件加載失敗(檢查錯誤日誌)。解決問題時應根據錯誤日誌進行分析,找到問題的根源,並養成定期備份數據的習慣,以預防和解決問題。

c語言函數庫在什麼位置? c語言函數庫怎麼添加? c語言函數庫在什麼位置? c語言函數庫怎麼添加? Apr 03, 2025 pm 11:39 PM

C語言函數庫是一個包含各種函數的工具箱,這些函數被組織在不同的庫文件中。添加函數庫需要通過編譯器的命令行選項來指定,例如 GCC 編譯器使用 -l 選項,後跟庫名的縮寫。如果庫文件不在默認搜索路徑下,則需要使用 -L 選項指定庫文件路徑。庫有靜態庫和動態庫之分,靜態庫在編譯時直接鏈接到程序中,而動態庫在運行時被加載。

MySQL安裝在特定係統版本上報錯的解決途徑 MySQL安裝在特定係統版本上報錯的解決途徑 Apr 08, 2025 am 11:54 AM

MySQL安裝報錯的解決方法是:1.仔細檢查系統環境,確保滿足MySQL的依賴庫要求,不同操作系統和版本需求不同;2.認真閱讀報錯信息,根據提示(例如缺少庫文件或權限不足)採取對應措施,例如安裝依賴或使用sudo命令;3.必要時,可嘗試源碼安裝並仔細檢查編譯日誌,但這需要一定的Linux知識和經驗。最終解決問題的關鍵在於仔細檢查系統環境和報錯信息,並參考官方文檔。

mysql 可以在 android 上運行嗎 mysql 可以在 android 上運行嗎 Apr 08, 2025 pm 05:03 PM

MySQL無法直接在Android上運行,但可以通過以下方法間接實現:使用輕量級數據庫SQLite,由Android系統自帶,無需單獨服務器,資源佔用小,非常適合移動設備應用。遠程連接MySQL服務器,通過網絡連接到遠程服務器上的MySQL數據庫進行數據讀寫,但存在網絡依賴性強、安全性問題和服務器成本等缺點。