Maison  >  Article  >  Quels sont les périphériques de caractères sous Linux ?

Quels sont les périphériques de caractères sous Linux ?

青灯夜游
青灯夜游original
2023-03-09 10:49:021730parcourir

Les périphériques de caractères Linux comprennent : 1. La souris, qui est un périphérique d'entrée externe pour l'ordinateur et un indicateur pour positionner les coordonnées verticales et horizontales du système d'affichage de l'ordinateur 2. Le clavier, qui est un périphérique d'entrée d'instructions et de données utilisé pour faire fonctionner l'équipement informatique. 3. Terminal de port série, un terminal connecté à l'aide d'un port série d'ordinateur. 5. Console, etc.

Quels sont les périphériques de caractères sous Linux ?

L'environnement d'exploitation de ce tutoriel : système linux7.3, ordinateur Dell G3.

Périphérique de caractères Linux


Le périphérique de caractères est l'un des trois principaux périphériques de Linux (les deux autres sont des périphériques de bloc et des périphériques réseau). Ils sont tous affichés dans le répertoire /dev du système de fichiers sous la forme d'un nœud de fichier (crw--w---- 1 root tty 4, 0 July 11 09:11 tty0 où c représente le type de périphérique caractère).

Le périphérique de caractère fait référence à un périphérique qui peut directement lire et écrire sans mise en mémoire tampon, tel qu'une souris, un clavier, un périphérique de port série, un modem, etc. La différence entre celui-ci et le périphérique de bloc est que l'unité de base du fonctionnement des caractères est l'octet.

Classification des dispositifs à caractères

Les dispositifs à caractères comprennent principalement les terminaux de contrôle et les terminaux série, tels que les consoles et les claviers. En fonction des différences de fonctions et de matériel, les terminaux à caractères sont classés comme suit :

  • Terminal à port série (/dev/ttSn) : un terminal connecté à l'aide d'un port série d'ordinateur. La méthode de transmission de données du périphérique série est la même. 8 bits sont transmis sur une seule ligne. Entrez echo 'hello world' > /dev/ttyS0 sur la ligne de commande pour écrire l'entrée sur le périphérique correspondant.

  • Pseudo terminal (/dev/ttyp, /dev/ptyp) : correspondant au fait qu'il n'y a pas de véritable périphérique matériel au niveau de la couche inférieure, il est utilisé pour fournir une interface de style terminal pour d'autres programmes, tels que entre le serveur réseau et le programme shell lors de la connexion à l'hôte via l'interface du terminal réseau.

  • Terminal de contrôle (/dev/tty) : Le numéro de périphérique principal est 5. Le terminal de contrôle de processus est associé au processus. Par exemple, le processus shell de connexion utilise le terminal /dev/tty.

  • Console (/dev/ttyn, /dev/consol) : Le moniteur pour les entrées et sorties de l'ordinateur Lorsque la console est connectée, tty1 est utilisé, tandis que l'interface graphique Ubuntu utilise tty7.

  • Autres types : Linux actuel possède de nombreux autres types de fichiers spéciaux pour de nombreux appareils différents, tels que le périphérique /dev/ttyIn du périphérique ISIDIN.

La nature et les caractéristiques des périphériques de caractères

  • Les périphériques de caractères sont un type de système de fichiers de périphérique, qui sont équivalents aux fichiers de périphérique logiques fournis par le matériel sous-jacent à la couche supérieure, tout comme la connexion un port de données (registre de données) avec Lorsqu'un fichier est connecté, le pilote de périphérique opère directement sur le fichier, il effectue donc directement des opérations de lecture et d'écriture sur le port. Également en tant que fichier, le pilote de périphérique de caractères doit également implémenter les opérations de base des fichiers telles que open(), close(), write(), read(), etc. Bien entendu, les opérations de redirection de terminal sont également prises en charge.

  • Les fichiers de fichiers de périphérique de caractères sont lus et écrits en unités à un seul octet, et il n'est pas nécessaire de configurer un tampon matériel. Le système d'exploitation accède à l'appareil sous forme de flux d'octets. Le flux d'octets revient à établir un canal de transmission entre le port matériel et le système de fichiers. Les octets sont transmis un par un via le canal et présentés aux lecteurs et aux écrivains. Cette fonctionnalité de streaming est implémentée dans le pilote sous la forme d'une file d'attente tampon. Par exemple : La file d'attente du tampon de lecture et d'écriture dans la structure de la console

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辅助队列(存放规格化后的字符)
};
  • Le périphérique de caractères est identifié par le numéro du périphérique de caractères. Le numéro de périphérique de caractères se compose d'un numéro de périphérique majeur et d'un numéro de périphérique mineur. Par exemple, le numéro de périphérique de /dev/ttyS0 est (4, 64) ; le numéro de périphérique majeur identifie le pilote correspondant au périphérique et le noyau l'utilise. le numéro de périphérique principal pour faire correspondre le périphérique et le pilote de manière individuelle. De manière générale, le numéro de périphérique mineur est utilisé par le pilote pour le code utilisé en interne par le pilote pour différencier les détails du périphérique. Il n'est pas utilisé par d'autres parties. du noyau. La commande

Comment les périphériques de caractères sont reflétés au niveau de la couche d'application

cat /proc/devices peut afficher tous les périphériques de caractères et bloquer les périphériques dans le système actuel.

Quels sont les périphériques de caractères sous Linux ?

Quels sont les périphériques de caractères sous Linux ?

Sous Linux, lors de la connexion de fichiers, les périphériques sont également résumés dans des fichiers. Les fichiers correspondants aux périphériques de caractère et aux périphériques de bloc peuvent être visualisés dans le répertoire /dev/.
Par exemple, il s'agit de deux fichiers de périphérique de port série, utilisez ls -l pour afficher ses détails.
Contrairement aux fichiers ordinaires, les fichiers de périphérique n'ont pas de taille, mais sont remplacés par des numéros de périphérique (numéros de périphérique majeurs et numéros de périphérique mineurs). Les numéros de périphérique peuvent correspondre aux informations contenues dans /proc/devices.

Quels sont les périphériques de caractères sous Linux ?

Comment accéder à un appareil ?

Puisqu'il est résumé dans un fichier, il est naturel d'utiliser le fichier IO (ouvrir, lire, écrire, etc.) pour y accéder.

设备号

Quels sont les périphériques de caractères sous 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 注册。

Quels sont les périphériques de caractères sous 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-Quels sont les périphériques de caractères sous 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 的调用原理

Quels sont les périphériques de caractères sous Linux ?

相关推荐:《Linux视频教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn