Un fichier de périphérique est un fichier spécial dans le système Linux. Il est utilisé pour représenter l'interface du périphérique afin que les programmes de l'espace utilisateur puissent accéder au périphérique via des opérations sur les fichiers. L'implémentation des fichiers de périphérique implique trois structures importantes : inode, file et file_operations. La structure inode est utilisée pour stocker les métadonnées des fichiers de périphérique, telles que le numéro de périphérique, les autorisations, la taille, etc. La structure de fichiers est utilisée pour stocker les informations d'état des fichiers de l'appareil, telles que l'emplacement actuel, le mode ouvert, les données privées, etc. La structure file_operations est utilisée pour stocker les fonctions d'opération de fichier de périphérique, telles que l'ouverture, la lecture, l'écriture, la fermeture, etc. Dans cet article, nous présenterons les définitions et les fonctions de ces trois structures, et donnerons des exemples de leur utilisation et de leurs précautions.
Le pilote contrôle le matériel vers le bas et fournit des interfaces vers le haut. Les interfaces fournies ici correspondent finalement à la couche application de trois manières : Fichiers de périphérique, /proc, /sys, dont la plus couramment utilisée est d'utiliser des fichiers de périphérique, et Linux Le périphérique le plus couramment utilisé est le périphérique de caractère. Cet article prend le périphérique de caractère comme exemple pour analyser le mécanisme interne de création et d'ouverture d'un fichier de périphérique de caractère.
Tout sous Linux est un fichier. Lorsque nous créons un fichier sous Linux, un inode sera créé dans le système de fichiers correspondant pour lui correspondre L'entité fichier et l'inode du fichier sont en correspondance biunivoque. . Une fois qu'un inode est créé, stocké dans la mémoire, la première ouverture aura une sauvegarde de l'inode dans la mémoire. Le même fichier est ouvert plusieurs fois et plusieurs inodes ne seront pas générés lorsque tous les fichiers ouverts sont fermés. sera dans la mémoire. L'instance sera libérée. Dans ce cas, lorsque nous utilisons mknod (ou d'autres méthodes) pour créer un fichier de périphérique, un inode sera également créé dans le système de fichiers. Cet inode, comme les autres inodes, est utilisé pour stocker des informations statiques (informations immuables) sur le fichier. . ), y compris le numéro de périphérique correspondant à ce fichier de périphérique, le chemin du fichier et l'objet pilote correspondant, etc. En tant que l'un des quatre objets majeurs de VFS, l'inode doit rarement être rempli par nous-mêmes lors du développement du pilote. Il est plus nécessaire de le visualiser dans la méthode open() et de remplir notre structure de fichiers si nécessaire.
Pour différents types de fichiers, le contenu des membres remplis de l'inode sera également différent. En prenant comme exemple la création d'un périphérique de caractère, nous savons que add_chrdev_region connecte en fait un objet pilote et un (groupe) numéro de périphérique. La création d'un fichier d'appareil relie en fait le fichier d'appareil et le numéro d'appareil ensemble. À ce stade, ces trois éléments sont liés. De cette façon, le noyau a la capacité de créer une instance de struct inode Ce qui suit est l'inode du noyau 4.8.5. Cet inode est l'inode de VFS, qui est une encapsulation supplémentaire de l'inode du système de fichiers le plus spécifique. C'est également l'inode concerné par le développement du pilote. Pour les systèmes de fichiers spécifiques, il existe également des structures telles que struct ext2_inode_info.
//include/linux/fs.h 596 /* 597 * Keep mostly read-only and often accessed (especially for 598 * the RCU path lookup and 'stat' data) fields at the beginning 599 * of the 'struct inode' 600 */ 601 struct inode { 602 umode_t i_mode; 603 unsigned short i_opflags; 604 kuid_t i_uid; 605 kgid_t i_gid; 606 unsigned int i_flags; 607 608 #ifdef CONFIG_FS_POSIX_ACL 609 struct posix_acl *i_acl; 610 struct posix_acl *i_default_acl; 611 #endif 612 613 const struct inode_operations *i_op; 614 struct super_block *i_sb; 615 struct address_space *i_mapping; 616 617 #ifdef CONFIG_SECURITY 618 void *i_security; 619 #endif 620 621 /* Stat data, not accessed from path walking */ 622 unsigned long i_ino; 623 /* 624 * Filesystems may only read i_nlink directly. They shall use the 625 * following functions for modification: 626 * 627 * (set|clear|inc|drop)_nlink 628 * inode_(inc|dec)_link_count 629 */ 630 union { 631 const unsigned int i_nlink; 632 unsigned int __i_nlink; 633 }; 634 dev_t i_rdev; 635 loff_t i_size; 636 struct timespec i_atime; 637 struct timespec i_mtime; 638 struct timespec i_ctime; 639 spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ 640 unsigned short i_bytes; 641 unsigned int i_blkbits; 642 blkcnt_t i_blocks; 643 644 #ifdef __NEED_I_SIZE_ORDERED 645 seqcount_t i_size_seqcount; 646 #endif 647 648 /* Misc */ 649 unsigned long i_state; 650 struct rw_semaphore i_rwsem; 651 652 unsigned long dirtied_when; /* jiffies of first dirtying */ 653 unsigned long dirtied_time_when; 654 655 struct hlist_node i_hash; 656 struct list_head i_io_list; /* backing dev IO list */ 657 #ifdef CONFIG_CGROUP_WRITEBACK 658 struct bdi_writeback *i_wb; /* the associated cgroup wb */ 659 660 /* foreign inode detection, see wbc_detach_inode() */ 661 int i_wb_frn_winner; 662 u16 i_wb_frn_avg_time; 663 u16 i_wb_frn_history; 664 #endif 665 struct list_head i_lru; /* inode LRU list */ 666 struct list_head i_sb_list; 667 struct list_head i_wb_list; /* backing dev writeback list */ 668 union { 669 struct hlist_head i_dentry; 670 struct rcu_head i_rcu; 671 }; 672 u64 i_version; 673 atomic_t i_count; 674 atomic_t i_dio_count; 675 atomic_t i_writecount; 676 #ifdef CONFIG_IMA 677 atomic_t i_readcount; /* struct files open RO */ 678 #endif 679 const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 680 struct file_lock_context *i_flctx; 681 struct address_space i_data; 682 struct list_head i_devices; 683 union { 684 struct pipe_inode_info *i_pipe; 685 struct block_device *i_bdev; 686 struct cdev *i_cdev; 687 char *i_link; 688 unsigned i_dir_seq; 689 }; 690 691 __u32 i_generation; 692 693 #ifdef CONFIG_FSNOTIFY 694 __u32 i_fsnotify_mask; /* all events this inode cares about */ 695 struct hlist_head i_fsnotify_marks; 696 #endif 697 698 #if IS_ENABLED(CONFIG_FS_ENCRYPTION) 699 struct fscrypt_info *i_crypt_info; 700 #endif 701 702 void *i_private; /* fs or device private pointer */ 703 };
Les principaux membres liés à cet article sont :
«
struct inode
–602–>i_mode indique le contrôle des autorisations d'accès
–604–>UID
–605–>GID
–606–>indicateurs du système de fichiers i_flags
–630–> Nombre de liens physiques
–635–>i_size taille du fichier en octets
–636–>Heure du dernier accès
–637–>Heure de la dernière modification
–638–>Heure du dernier changement
–669–>i_dentry; //Liste des colliers du catalogue
–673–>i_count nombre de références, lorsque le nombre de références devient 0, l'instance d'inode sera libérée
–675–>i_writecount nombre d'écrivains
–679–>Lors de la création d'un fichier de périphérique, i_fops est rempli avec l'un des def_chr_fops, blk_blk_fops, def_fifo_fops et bad_sock_fops Voir init_special_inode() appelé pendant le processus de création
. –683–>Types de fichiers spéciaux tels que union, pipe, cdev, blk.link etc. i_cdev indique que cet inode appartient à un fichier de périphérique de caractères lors de la création du fichier de périphérique dans cet article, l'objet pilote cdev du numéro de périphérique associé. sera utilisé. Remplissage
–702–>données privées d’inode”
Parmi les membres ci-dessus, seul struct def_chr_fops vaut la peine d'être poursuivi, il sera d'une grande utilité plus tard :
//fs/char_dev.c 429 const struct file_operations def_chr_fops = { 430 .open = chrdev_open, 431 .llseek = noop_llseek, 432 };
Linux内核会为每一个进程维护一个文件描述符表,这个表其实就是struct file[]的索引。open()的过程其实就是根据传入的路径填充好一个file结构并将其赋值到数组中并返回其索引。下面是file的主要内容
//include/linux/fs.h 877 struct file { 878 union { 879 struct llist_node fu_llist; 880 struct rcu_head fu_rcuhead; 881 } f_u; 882 struct path f_path; 883 struct inode *f_inode; /* cached value */ 884 const struct file_operations *f_op; 885 886 /* 887 * Protects f_ep_links, f_flags. 888 * Must not be taken from IRQ context. 889 */ 890 spinlock_t f_lock; 891 atomic_long_t f_count; 892 unsigned int f_flags; 893 fmode_t f_mode; 894 struct mutex f_pos_lock; 895 loff_t f_pos; 896 struct fown_struct f_owner; 897 const struct cred *f_cred; 898 struct file_ra_state f_ra;f 904 /* needed for tty driver, and maybe others */ 905 void *private_data; 912 struct address_space *f_mapping; 913 } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
“
struct file
–882–>f_path里存储的是open传入的路径,VFS就是根据这个路径逐层找到相应的inode
–883–>f_inode里存储的是找到的inode
–884–>f_op里存储的就是驱动提供的file_operations对象,这个对象在open的时候被填充,具体地,应用层的open通过层层搜索会调用inode.i_fops->open,即chrdev_open()
–891–>f_count的作用是记录对文件对象的引用计数,也即当前有多少个使用CLONE_FILES标志克隆的进程在使用该文件。典型的应用是在POSIX线程中。就像在内核中普通的引用计数模块一样,最后一个进程调用put_files_struct()来释放文件描述符。
–892–>f_flags当打开文件时指定的标志,对应系统调用open的int flags,比如驱动程序为了支持非阻塞型操作需要检查这个标志是否有O_NONBLOCK。
–893–>f_mode;对文件的读写模式,对应系统调用open的mod_t mode参数,比如O_RDWR。如果驱动程序需要这个值,可以直接读取这个字段。
–905–>private_data表示file结构的私有数据”
我在Linux设备管理(二)_从cdev_add说起一文中已经分析过chrdev_open(),这里仅作概述。
//fs/chr_dev.c 348 /* 349 * Called every time a character special file is opened 350 */ 351 static int chrdev_open(struct inode *inode, struct file *filp) 352 { /* 搜索cdev */ ... 390 replace_fops(filp, fops); 391 if (filp->f_op->open) { 392 ret = filp->f_op->open(inode, filp); 393 if (ret) 394 goto out_cdev_put; 395 } ... 402 }
可以看出,这个函数有三个任务(划重点!!!):
“
chrdev_open()
–352-389–>利用container_of等根据inode中的成员找到相应的cdev
–390–>用cdev.fops替换filp->f_op,即填充了一个空的struct file的f_op成员。
–392–>回调替换之后的filp->f_op->open,由于替换,这个其实就是cdev.fops”
至此,我们知道了我们写的驱动中的open()在何时会被回调,这样我们就可以实现很多有意思的功能,比如,
我们可以在open中通过inode->cdev来识别具体的设备,并将其私有数据隐藏到file结构的private_data中,进而识别同一个驱动操作一类设备;
我们也可以在回调cdev.fops->open()阶段重新填充file结构的fop,进而实现同一个驱动操作不同的设备,这种思想就是内核驱动中常用的分层!
最后总结一下这些结构之间的关系:
“
”
Grâce à cet article, nous avons découvert les trois structures importantes des fichiers de périphérique : inode, file et file_operations. Ils peuvent être utilisés pour gérer et exploiter les fichiers de l'appareil. Nous devons choisir la structure appropriée en fonction des besoins réels et suivre certains principes de base, tels que l'utilisation du bon numéro d'appareil, l'utilisation d'autorisations raisonnables, l'utilisation de fonctions d'exploitation efficaces, etc. Les trois structures de fichiers de périphérique constituent l'un des concepts les plus fondamentaux du système Linux. Elles peuvent abstraire et encapsuler le périphérique, et peuvent également améliorer l'unité et la flexibilité du système. J’espère que cet article pourra vous être utile et inspirant.
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!