Maison  >  Article  >  Tutoriel système  >  24 heures pour en savoir plus sur le noyau Linux et les problèmes liés à l'implémentation du système de fichiers Linux

24 heures pour en savoir plus sur le noyau Linux et les problèmes liés à l'implémentation du système de fichiers Linux

WBOY
WBOYavant
2024-02-05 16:00:03909parcourir

L'utilisation de Linux et la programmation de programmes en espace utilisateur sont étroitement liées au système de fichiers. Vous connaissez peut-être déjà le concept de système de fichiers, je ne vais donc pas trop l'expliquer. Après tout, tant que vous comprenez ces concepts, ceux qui souhaitent en savoir plus peuvent toujours obtenir plus d'informations via les moteurs de recherche tels que Baidu. Je vais maintenant me concentrer sur le système de fichiers virtuel de Linux.

Le système de fichiers virtuel est l'une des fonctionnalités importantes de Linux, qui prend en charge de nombreux systèmes de fichiers différents. La structure du système de fichiers est illustrée dans la figure ci-dessous : [Voir le texte original pour l'image] 24 heures pour en savoir plus sur le noyau Linux et les problèmes liés à limplémentation du système de fichiers Linux

Le VFS (Virtual File System) dans l'image ci-dessus s'appuie sur des structures de données pour enregistrer sa représentation générale d'un système de fichiers. Les structures de données sont répertoriées comme suit :

.
  • Structure de super blocs : stocke les informations relatives au système de fichiers installé ;

  • Structure du nœud d'index : stocke les informations sur les fichiers

  • Structure des fichiers : stocke les informations sur les fichiers ouverts par le processus ;

  • Structure des entrées de répertoire : stocke des informations sur le nom du chemin et le fichier pointé par le nom du chemin.
  • Le noyau Linux utilise des variables globales pour enregistrer les pointeurs vers les structures mentionnées précédemment. Toutes les structures sont enregistrées dans des listes doublement chaînées. Le noyau enregistre le pointeur vers la tête de la liste chaînée et l'utilise comme point d'accès de la liste chaînée. Ces structures utilisent le champ de type list_head, utilisez-le pour pointer vers l'élément précédent dans la liste chaînée. Le tableau suivant présente les variables globales enregistrées par le noyau et les types de listes chaînées pointées par ces variables (variables globales liées à VFS). )

Variables globalessuper_blockssystèmes_de_fichiersdentry_unusedvfsmntlistinode_in_useinode_unused

Les structures Super_block, file_system_type, dentry et vfsmoubt sont toutes stockées dans leurs propres listes chaînées. Les nœuds d'index peuvent se retrouver sur le inode_in_use global ou inode_unused, ou sur leurs listes chaînées locales ultra-rapides correspondantes.

En plus de la structure principale de VFS, il existe plusieurs autres structures qui interagissent avec VFS, fs_struct et files_struct, namespace, fd_set. La figure ci-dessous explique comment les descripteurs de processus sont associés aux structures liées aux fichiers.

24 heures pour en savoir plus sur le noyau Linux et les problèmes liés à limplémentation du système de fichiers Linux

Tout d'abord, introduisons la structure fs_struct. La structure fs_struct peut être référencée par plusieurs descripteurs de processus. Le code suivant se trouve dans include/Linux/fs_struct.h. Si vous ne comprenez pas bien le code, veuillez m'en donner. conseil

struct fs_struct{
    atomic_t count;  //保存引用特定fs_struct的进程描述符数目
    rwlock_t lock;
    int umask;  //保存一个掩码,表示将要在打开文件上设置的许可权
    struct dentry * root, *pwd ,*altroot;  //都是指针,,,,
    struct vfsmount * rootmnt, *pwdmnt,  *altrootmnt;  //指针,
};

files_struct contient des informations sur les fichiers ouverts et leurs descripteurs, il utilise ces collections pour regrouper ses descripteurs. Le code suivant peut être consulté dans include/linux/file.h

struct files_struct{
    atomic_t count;  //与fs_struct类似
    spinlock_t file_lock;
    int max_fds;  //表示进程能够打开的文件的最大数
    int max_fdset;  //表示描述符的最大数
    int next_fd;  //保存下一个将要分配的文件描述符的值
    struct file ** fd;  //fd数组指向打开的文件对象的数组
    fd_set *close_on_exec; //是指向文件描述符集的一个指针,这些文件描述符在exec()时候就被标志位将要关闭,如果在exec()时候被标志位“打开”的文件描述符数超过close_on_exec_init域的大小,则改变close_on_exec域的值;
    fd_set *open_fds; //是一个指针,指向被标记为“打开”的文件描述符集合,
    fd_set close_on_exec_init;  //保存一个位域,表示打开文件对应的文件描述符
    fd_set open_fds_init;    //这些都是fd_set类型的域,其实都不懂,,,
    struct file *fd_array[NR_OPEN_DEFAULT];//fd_array数组指针指向前32个打开的文件描述法
};

Initialisez la structure fs_struct via la macro INIT_FILES :

#define INIT_FILES \
{
    .count = ATOMIC_INIT(1),
    .file_lock = SPIN_LOCK_UNLOCKED,
    .max_fds = NR_OPEN_DEFAULT,
    .max_fdset = __FD_SETSIZE,
    .next_fd = 0,
    .fd = &init_files.fd_array[0];
    .close_on_exec = &init_files.close_on_exec_init,
    .open_fds = &init_files.open_fds_init,
    .close_on_exec_init = {{0, }},
    .open_fda_init = {{0, }},
    .fd_array = {NULL, }
}

La définition globale de NR_OPEN_DEFAULT est définie sur BITS_PER_LONG, soit 32 dans les systèmes 32 bits et 64 dans les systèmes 64 bits.

Présentons la mise en mémoire tampon des pages. Voyons maintenant comment elle fonctionne et est implémentée. Sous Linux, la mémoire est divisée en partitions. Chacune a une liste chaînée de pages actives et une liste chaînée inactive. Lorsque la page est inactive, elle sera réécrite sur le disque.

image-2024020222103970824 heures pour en savoir plus sur le noyau Linux et les problèmes liés à limplémentation du système de fichiers Linux

Le cœur de la mise en mémoire tampon des pages est l'objet adresse_espace, et son code peut être consulté dans include/linux/fs.h (je ne comprends pas très bien ce code, veuillez me donner quelques conseils) :

struct address_space{    
    struct inode *host;
    struct radix_tree_root page_tree;
    spinlock_t tree_lock;
    unsigned long nrpages;
    pgoff_t writeback;
    struct address_space_operations *a_ops;
    struct prio_tree_root i_map;
    unsigned inr i_map_lock;
    struct list_head i_mmap_nonlinear;
    spinlock_t i_mmap_lock;
    atomic_t truncate_count;
    unsigned long flags;
    struct backing_dev_info *backing_dev_info;
    spinlock_t private_lock;
    struct list_head private_list;
    struct address_space *assoc_mapping;
};

Le noyau Linux représente également chaque secteur du périphérique bloc sous la forme d'une structure buffer_head. La zone physique utilisée par la structure buffer_head est le bloc logique b_blocknr du périphérique b_dev. La mémoire physique référencée est constituée des données de mémoire b_data à partir de la taille de bloc de b_size. bloc d'octets, ce bloc mémoire est dans la page physique b_page, et sa structure est la suivante :

Enfin, parlons de l'appel système VFS et de la couche du système de fichiers, et suivons leur exécution jusqu'au niveau du noyau. Nous devons d'abord comprendre quatre fonctions : open(), close(), read() et write(). 24 heures pour en savoir plus sur le noyau Linux et les problèmes liés à limplémentation du système de fichiers Linux

fonction open() :

La fonction open est utilisée pour ouvrir et créer des fichiers. Ce qui suit est une brève description de la fonction ouverte

#include 
int open(const char *pathname, int oflag, ... );

Valeur de retour

 : en cas de succès, renvoie le descripteur de fichier, sinon renvoie -1 Pour la fonction open, le troisième paramètre (...) n'est utilisé que lors de la création d'un nouveau fichier et permet de spécifier les bits d'autorisation d'accès du fichier. pathname est le chemin du fichier à ouvrir/créer (par exemple C:/cpp/a.cpp) ; oflag est utilisé pour spécifier le mode d'ouverture/création du fichier. Ce paramètre peut être composé des constantes suivantes (définies). dans fcntl.h) via OU logique.

    O_RDONLY mode lecture seule
  • O_WRONLY mode écriture seule
  • Mode lecture et écriture O_RDWR
  • Lors de l'ouverture/création d'un fichier, vous devez utiliser au moins une des trois constantes ci-dessus. Les constantes suivantes sont facultatives :
  • O_APPEND Chaque opération d'écriture est écrite à la fin du fichier
  • O_CREAT Si le fichier spécifié n'existe pas, créez ce fichier
  • O_EXCL Si le fichier à créer existe déjà, retournez -1 et modifiez la valeur de errno
  • O_TRUNC Si le fichier existe et est ouvert en mode écriture seule/lecture-écriture, effacez tout le contenu du fichier
  • O_NOCTTY Si le chemin d'accès pointe vers un périphérique terminal, n'utilisez pas ce périphérique comme terminal de contrôle.
  • O_NONBLOCK Si le nom du chemin pointe vers FIFO/fichier de bloc/fichier de caractères, définissez l'ouverture du fichier et les E/S ultérieures en mode non bloquant (mode non bloquant)
  • Les trois constantes suivantes sont également sélectionnées, elles sont utilisées pour synchroniser l'entrée et la sortie
  • O_DSYNC attend la fin des E/S physiques avant d'écrire. Sans affecter la lecture des données nouvellement écrites, n'attendez pas les mises à jour des attributs du fichier.
  • O_RSYNC read attend que toutes les opérations d'écriture dans la même zone soient terminées avant de continuer
  • O_SYNC attend la fin des E/S physiques avant d'écrire, y compris les E/S pour mettre à jour les attributs du fichier
  • Le descripteur de fichier renvoyé par open doit être le plus petit descripteur inutilisé.

    Si NAME_MAX (longueur maximale du nom de fichier, hors '

    POSIX.1 引入常量 _POSIX_NO_TRUNC 用于决定是否截断长文件名/长路径名。如果_POSIX_NO_TRUNC 设定为禁止截断,并且路径名长度超过 PATH_MAX(包括 ‘\0’),或者组成路径名的任意文件名长度超过 NAME_MAX,则返回错误信息,并且把 errno 置为 ENAMETOOLONG。

close()函数

进程使用完文件后,发出close()系统调用:

sysopsis

#include 
int close(int fd);

参数:fd文件描述符

函数返回值:0成功,-1出错

参数fd是要关闭的文件描述符。需要说明的是:当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

read()函数

当用户级别程序调用read()函数时,Linux把它转换成系统调sys_read():

功能描述:从文件读取数据。
所需头文件: #include

函数原型:ssize_t read(int fd, void *buf, size_t count);

参数

  • fd: 将要读取数据的文件描述词。

  • buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。

  • count:表示调用一次read操作,应该读多少数量的字符。

  • 返回值:返回所读取的字节数;0(读到EOF);-1(出错)。

  • 以下几种情况会导致读取到的字节数小于 count :

  • 读取普通文件时,读到文件末尾还不够 count 字节。例:如果文件只有 30 字节,而我们想读取 100,字节,那么实际读到的只有 30 字节, 函数返回 30 。此时再使用 read 函数作用于这个文件会导致 read 返回 0

  • 从终端设备(terminal device)读取时,一般情况下每次只能读取一行。

  • 从网络读取时,网络缓存可能导致读取的字节数小于 count字节。

  • 读取 pipe 或者 FIFO 时,pipe 或 FIFO 里的字节数可能小于 count 。

  • 从面向记录(record-oriented)的设备读取时,某些面向记录的设备(如磁带)每次最多只能返回一个记录。

  • 在读取了部分数据时被信号中断,读操作始于 cfo 。在成功返回之前,cfo 增加,增量为实际读取到的字节数。

    例程如下(程序是网上找的例子,贴下来以以供大家理解一下)::

#include 
#include 
#include 
#include 
#include 
#include 
int main(void)
{
    void* buf ;
    int handle;
    int bytes ;
    buf=malloc(10);
    /*
    LooksforafileinthecurrentdirectorynamedTEST.$$$andattempts
    toread10bytesfromit.Tousethisexampleyoushouldcreatethe
    fileTEST.$$$
    */
    handle=open("TEST.$$$",O_RDONLY|O_BINARY,S_IWRITE|S_IREAD);
    if(handle==-1)
    {
        printf("ErrorOpeningFile\n");
        exit(1);
    }
    bytes=read(handle,buf,10);
    if(bytes==-1)
    {
        printf("ReadFailed.\n");
        exit(1);
    }
    else 
    {
        printf("Read:%dbytesread.\n",bytes);
    }
    return0 ;
}

write()函数

功能描述:向文件写入数据。
所需头文件: #include

函数原型:ssize_t write(int fd, void *buf, size_t count);

返回值:写入文件的字节数(成功);-1(出错)

功能:write 函数向 filedes 中写入 count 字节数据,数据来源为 buf 。返回值一般总是等于 count,否则就是出错了。常见的出错原因是磁盘空间满了或者超过了文件大小限制。对于普通文件,写操作始于 cfo 。如果打开文件时使用了 O_APPEND,则每次写操作都将数据写入文件末尾。成功写入后,cfo 增加,增量为实际写入的字节数。

例程如下(程序是网上找的例子,贴下来以以供大家理解一下):

#include 
#include 
#include 
#include 
#include 
#include 
int main(void)
{
int *handle; char string[40];
int length, res;/* Create a file named "TEST.$$$" in the current directory and write a string to it. If "TEST.$$$" already exists, it will be overwritten. */
if ((handle = open("TEST.$$$", O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE)) == -1)
{
printf("Error opening file.\n");
exit(1);
}
strcpy(string, "Hello, world!\n");
length = strlen(string);
if ((res = write(handle, string, length)) != length)
{
printf("Error writing to the file.\n");
exit(1);
}
printf("Wrote %d bytes to the file.\n", res);
close(handle); return 0; }

小结

今天看的代码不多,差不多都是网上找的代码,有些解释也是查阅资料写上去的,有些还是不懂,希望各路大神指教,这里我总结了有关Linux文件系统实现的问题,但是具体的细节方面并没有提及到,大家看了之后应该只能有一个大致的最Linux文件系统的了解,有读者问我看的是哪些书,这里我说明一下,看了Linux内核编程,还有深入理解Linux内核以及网上各种资料或者其他大牛写的好的博客。这里我是总结了一下,并且把自己不懂的还有觉得重要的说了一下,希望各位大神给些建议,thanks~

Type de structure
super_block
file_systems_type
denterie
vfsmount
inode
inode

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:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer