Im Linux-System kann alles als Datei betrachtet werden und Dateien können unterteilt werden in: normale Dateien, Verzeichnisdateien, Linkdateien und Geräte Dateien. Ein Dateideskriptor ist ein vom Kernel erstellter Index, um offene Dateien effizient zu verwalten. Es handelt sich um eine nicht-negative Ganzzahl (normalerweise eine kleine Ganzzahl), die zum Verweisen auf die geöffnete Datei verwendet wird Gehen Sie die Dateideskriptoren durch. Beim ersten Start des Programms ist 0 die Standardeingabe, 1 die Standardausgabe und 2 der Standardfehler. Wenn Sie zu diesem Zeitpunkt eine neue Datei öffnen, lautet der Dateideskriptor 3.
Dateideskriptoroperationen (z. B. open(), creat(), close(), read())) werden zurückgegeben Ein Dateideskriptor, der eine Ganzzahl vom Typ int ist, d. h. fd. Sein Wesen ist der Index in der Dateideskriptortabelle. Der Prozess findet den fd in der Datei Zeiger, auf den filp zeigt. Jeder Prozess speichert eine Dateideskriptortabelle im PCB (Prozesskontrollblock). Der Dateideskriptor ist der Index dieser Tabelle. Jeder Eintrag in der Dateibeschreibungstabelle hat einen Zeiger auf die geöffnete Datei. Die geöffnete Datei wird durch die Struktur file
im Kernel dargestellt, und der Zeiger in der Dateideskriptortabelle zeigt auf die Struktur file
. Jedes Mal, wenn eine Datei geöffnet wird, wird fd standardmäßig beginnend mit dem kleinsten nicht verwendeten Index zugewiesen. Nachteile von Dateideskriptoren: Sie können nicht auf andere Systeme als UNIX portiert werden und sind nicht intuitiv.
Zeichnen Sie unten ein Bild, um die Beziehung zwischen ihnen zu zeigen:
Und jede Datei enthält hauptsächlich die folgenden Informationen:
verwaltet das Dateistatus-Flag (file
Mitglied der Struktur file
) und die aktuelle Lese- und Schreibposition (f_flags
Mitglied der Struktur file
) in der f_pos
-Struktur. In der Abbildung oben öffnen sowohl Prozess 1 als auch Prozess 2 dieselbe Datei, entsprechen jedoch unterschiedlichen file
-Strukturen, sodass sie unterschiedliche Dateistatus-Flags sowie Lese- und Schreiborte haben können. Die wichtigeren Mitglieder der file
-Struktur sind auch f_count
, die den Referenzzähler darstellen. Wir werden später darüber sprechen, dass Systemaufrufe wie dup
und fork
dazu führen, dass mehrere Dateideskriptoren auf dieselben verweisen file
Struktur, zum Beispiel beziehen sich sowohl fd1
als auch fd2
auf dieselbe file
Struktur, dann ist ihr Referenzzähler 2. Wenn close(fd1)
verwendet wird, wird die file
Struktur nicht freigegeben, aber Reduzieren Sie nur den Referenzzähler auf 1. Wenn Sie erneut close(fd2)
ausführen, wird der Referenzzähler auf 0 reduziert und die file
-Struktur freigegeben. Dadurch wird die Datei tatsächlich geschlossen.
Jede file
-Struktur zeigt auf eine file_operations
-Struktur. Die Mitglieder dieser Struktur sind Funktionszeiger, die auf Kernelfunktionen verweisen, die verschiedene Dateioperationen implementieren. Beispielsweise hat read
in einem Benutzerprogramm einen Dateideskriptor, read
gelangt über einen Systemaufruf in den Kernel, findet dann die file
-Struktur, auf die der Dateideskriptor zeigt, und findet die file
-Struktur, auf die gezeigt wird Rufen Sie anhand der file_operations
-Struktur die Kernelfunktion auf, auf die ihr read
-Mitglied verweist, um die Benutzeranforderung abzuschließen. Wenn Funktionen wie lseek
, read
, write
, ioctl
, open
im Benutzerprogramm aufgerufen werden, ruft der Kernel schließlich die Kernelfunktion auf, auf die jedes Mitglied von file_operations
zeigt, um den Vorgang abzuschließen Benutzerwunsch. Der file_operations
-Member in der release
-Struktur wird verwendet, um die close
-Anfrage des Benutzerprogramms abzuschließen. Der Grund, warum er release
anstelle von close
heißt, liegt darin, dass er die Datei nicht unbedingt schließt reduziert den Referenzzähler. Die Datei wird nur geschlossen, wenn der Referenzzähler auf 0 fällt. Für reguläre Dateien, die im selben Dateisystem geöffnet werden, sollten die Schritte und Methoden von Dateioperationen wie read
und write
gleich sein und die aufgerufenen Funktionen sollten gleich sein, sodass die drei geöffneten Dateien in der Abbildung -Struktur verweist auf dieselbe file
-Struktur. Wenn Sie eine Zeichengerätedatei öffnen, unterscheiden sich ihre Vorgänge file_operations
und read
definitiv von normalen Dateien. Sie lesen und schreiben keine Datenblöcke auf der Festplatte, sondern lesen und schreiben Hardwaregeräte, daher sollte die Struktur write
darauf verweisen unterschiedlich file
Struktur, verschiedene darin enthaltene Dateibetriebsfunktionen werden vom Treiber des Geräts implementiert. file_operations
Jede file
-Struktur hat einen Zeiger auf die dentry
-Struktur. „dentry“ ist die Abkürzung für Verzeichniseintrag. Die Parameter, die wir an Funktionen wie open
und stat
übergeben, sind ein Pfad, wie z. B. /home/akaedu/a
, und der Inode der Datei muss anhand des Pfads gefunden werden. Um die Anzahl der Festplattenlesevorgänge zu reduzieren, speichert der Kernel die Baumstruktur des Verzeichnisses, den so genannten Dentry-Cache, zwischen, in der jeder Knoten eine dentry
-Struktur ist. Suchen Sie einfach entlang jedes Teils des Pfads nach dem Dentry, beginnend mit dem Stammverzeichnis /
Suchen Sie das Verzeichnis home
, dann das Verzeichnis akaedu
und dann die Datei a
. Der Dentry-Cache speichert nur zuletzt aufgerufene Verzeichniseinträge. Wenn sich der gesuchte Verzeichniseintrag nicht im Cache befindet, muss er von der Festplatte in den Speicher gelesen werden.
Jede dentry
-Struktur verfügt über einen Zeiger, der auf die inode
-Struktur zeigt. inode
Die Struktur speichert die vom Festplatten-Inode gelesenen Informationen. Im obigen Beispiel gibt es zwei Einträge, die jeweils /home/akaedu/a
und /home/akaedu/b
darstellen. Sie verweisen beide auf denselben Inode, was darauf hinweist, dass die beiden Dateien fest miteinander verknüpft sind. inode
In der Struktur werden vom Inode der Festplattenpartition gelesene Informationen gespeichert, z. B. Eigentümer, Dateigröße, Dateityp, Berechtigungsbits usw. Jede inode
-Struktur verfügt über einen Zeiger auf die inode_operations
-Struktur, bei der es sich auch um eine Reihe von Funktionszeigern handelt, die auf einige Kernelfunktionen verweisen, die Dateiverzeichnisoperationen abschließen. Im Gegensatz zu file_operations
verweist inode_operations
nicht auf eine Funktion, die eine bestimmte Datei bearbeitet, sondern auf eine Funktion, die das Layout von Dateien und Verzeichnissen beeinflusst, z. B. das Hinzufügen und Löschen von Dateien und Verzeichnissen, das Verfolgen symbolischer Links usw. , die zum selben Dateisystem gehören. Jede inode
-Struktur kann auf dieselbe inode_operations
-Struktur verweisen. Die
inode
-Struktur hat einen Zeiger auf die super_block
-Struktur. super_block
In der Struktur werden Informationen gespeichert, die aus dem Superblock der Festplattenpartition gelesen wurden, z. B. Dateisystemtyp, Blockgröße usw. Das super_block
-Mitglied der s_root
-Struktur ist ein Zeiger auf dentry
und gibt an, wo sich das Stammverzeichnis des Dateisystems mount
befindet. Im obigen Beispiel befindet sich die Partition mount
im Verzeichnis /home
. Runter.
file
, dentry
, inode
, super_block
Diese Strukturen bilden das Kernkonzept von VFS (Virtual Filesystem VFS, Virtual Filesystem).
(1). Linux-Dateideskriptoren anzeigen
1 [root@localhost ~]# sysctl -a | grep -i file-max --color 3 fs.file-max = 392036 5 [root@localhost ~]# cat /proc/sys/fs/file-max 7 392036 9 [root@localhost ~]# ulimit -n11 102413 [root@localhost ~]#
Der maximale Dateideskriptor unter Linux Es gibt zwei Aspekte Bei den Einschränkungen handelt es sich zum einen um Einschränkungen auf Benutzerebene und zum anderen um Einschränkungen auf Systemebene.
Einschränkungen auf Systemebene: Die im Befehl sysctl und im Dateisystem proc angezeigten Werte sind die gleichen. Es handelt sich um eine Einschränkung auf Systemebene Deskriptoren für alle Benutzer
Einschränkungen auf Benutzerebene: Was der Befehl ulimit sieht, ist das maximale Dateideskriptorlimit auf Benutzerebene, was bedeutet, dass die Gesamtzahl der Dateideskriptoren von den ausgeführten Programmen belegt wird Nachdem sich jeder Benutzer angemeldet hat, darf dieser Grenzwert nicht überschritten werden
(2) Ändern Sie den Wert des Dateideskriptors
1 [root@localhost ~]# ulimit-SHn 102402 [root@localhost ~]# ulimit -n3 102404 [root@localhost ~]#
Die obige Änderung betrifft nur aktuelle Sitzung und ist vorübergehend. Wenn es dauerhaft geändert werden muss, müssen Sie es wie folgt ändern:
1 [root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf2 * hard nofile 40963 [root@localhost ~]#
1 //默认配置文件中只有hard选项,soft 指的是当前系统生效的设置值,hard 表明系统中所能设定的最大值2 [root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf3 * hard nofile 102404 * soft nofile 102405 [root@localhost ~]#6 // soft<=hard soft的限制不能比hard限制高
(3) . Ändern Sie die Systemeinschränkungen
1 [root@localhost ~]# sysctl -wfs.file-max=4000002 fs.file-max = 4000003 [root@localhost ~]# echo350000 > /proc/sys/fs/file-max //重启后失效4 [root@localhost ~]# cat /proc/sys/fs/file-max5 3500006 [root@localhost ~]#
//Das Obige ist eine vorübergehende Änderung des Dateideskriptors
//Permanente Änderung, fügen Sie fs.file-max=400000 zu / hinzu etc/sysctl.conf, verwenden Sie sysctl -p
an und öffnet die Datei /home/shenlan/hello.c -Datei. Wenn in diesem Verzeichnis keine hello.c-Datei vorhanden ist, wird das Programm automatisch erstellt und der im Programm zurückgegebene Dateideskriptor ist 3. Denn wenn der Prozess startet, werden drei Dateien, Standardeingabe (0), Standardausgabe (1) und Standardfehlerverarbeitung (2) , geöffnet. Standardmäßig beginnt die Zuweisung von fd ab dem kleinsten nicht verwendeten Index , also ist der zurückgegebene Dateideskriptor 3.
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 int main() 7 { 8 int fd; 9 if((fd = open("/home/shenlan/fd.c",O_CREAT|O_WRONLY|O_TRUNC,0611))<0){10 perror("openfile fd.c error!\n");11 exit(1);12 }13 else{14 printf("openfile fd.c success:%d\n",fd);15 }16 if(close(fd) < 0){17 perror("closefile fd.c error!\n");18 exit(1);19 }20 else21 printf("closefile fd.c success!\n");22 exit(0);23 }
执行结果:
进程通过系统调用open( )来打开一个文件,实质上是获得一个文件描述符,以便进程通过文件描述符为连接对文件进行其他操作。进程打开文件时,会为该文件创建一个file对象,并把该file对象存入进程打开文件表中(文件描述符数组),进而确定了所打开文件的文件描述符。 open( )操作在内核里通过sys_open( )实现的,sys_open( )将创建文件的dentry、inode和file对象,并在file_struct结构体的进程打开文件表fd_array[NR_OPEN_DEFAULT]中寻找一个空闲表项,然后返回这个表项的下标(索引),即文件描述符。创建文件的file对象时,将file对象的f_op指向了所属文件系统的操作函数集file_operations,而该函数集又来自具体文件的i节点,于是虚拟文件系统就与实际文件系统的操作衔接起来了。
C语言中使用的是文件指针而不是文件描述符做为I/O的句柄."文件指针(file pointer)"指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符值.而文件描述符值是文件描述符表中的一个索引.从某种意义上说文件指针就是句柄的句柄。流(如: fopen)返回的是一个FILE结构指针, FILE结构是包含有文件描述符的,FILE结构函数可以看作是对fd直接操作的系统调用的封装, 它的优点是带有I/O缓存。
从文件描述符fd 到文件流 FILE* 的函数是
FILE* fdopen(int filedes,const char* mode);
早期的C标准库中,FILE在stdio.h中定义;Turbo C中,参见谭浩强的《C程序设计》,FILE结构体中包含成员fd,即文件描述符。亦可以在安装的Ubuntu系统的/usr/include/stdio.h中找到struct _IO_FILE结构体,这个结构体比较复杂,我们只关心需要的部分-文件描述符,但是在这个的结构体中,我们并没有发现与文件描述符相关的诸如fd成员变量。此时,类型为int的_fileno结构体成员引起了我们的注意,但是不能确定其为文件描述符。因此写个程序测试是最好的办法,可以用以下的代码测试:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/stat.h> 5 #include<fcntl.h> 6 int main( ) 7 { 8 char buf[50] = {"ILOVE this game!"}; 9 FILE *myfile;10 11 myfile = fopen("2.txt","w+");12 if(!myfile){13 printf("error:openfile failed!\n");14 }15 printf("The openedfile's descriptor is %d\n",myfile->_fileno);16 if(write(myfile->_fileno,buf,50)< 0){17 perror("error:writefile failed!\n");18 exit(1);19 }else{20 printf("writefile successed!\n");21 }22 exit(0);23 }
die Funktion fopen, um die Datei 2.txt zum Lesen und Schreiben zu öffnen nicht existiert, erstellen Sie dieses Dokument. Und es gibt den FILE-Zeiger myfile zurück. Verwenden Sie printf, um den Wert von myfile->_fileno an ein Standardterminal auszudrucken, und übergeben Sie myfile->_fileno als Dateideskriptor an write Systemaufruf zum Schreiben von Pufferdaten in die geöffnete Datei. Verwenden Sie dann den Befehl cat, um den Inhalt von 2.txt anzuzeigen. Das Ausführungsergebnis ist in der Abbildung dargestellt. Der Wert von _fileno ist 3, da die Standardeingabe, -ausgabe und -fehler 0, 1, 2 sind. Das Ausgabeergebnis lautet wie folgt: Daher ist das Mitglied
_fileno das Handle (Windows-System) oder der Dateideskriptor, der vom Betriebssystem beim Öffnen zurückgegeben wird die Datei. Für eingehendere Studien können Sie „CStandard Library“ lesen, herausgegeben vom People's Posts and Telecommunications Publishing House. Natürlich können Sie auch die Datei /glibc-2.9/manual/io.txti lesen. Linux, die Dateideskriptorzuweisung besteht darin, zu prüfen, ob der Dateideskriptor einzeln von klein nach groß verwendet wurde, und ihn dann zuzuweisen. Sie können auch ein Programm zum Testen schreiben.
Die Dateideskriptortabelle wird auch als Dateideskriptor-Array bezeichnet, in dem alle von einem Prozess geöffneten Dateien gespeichert werden. Das Dateideskriptor-Array ist in der vom Prozess geöffneten Dateitabellefiles_struct-Struktur enthalten. ist in /include/linux/fdtable.h definiert und ist ein Array von Zeigern, die auf den Datei-Typ ---fd_array[NR_OPEN_DEFAULT] verweisen, wobei NR_OPEN_DEFAULT auch in fdtable.h definiert ist. Dies ist eine Variable, die sich auf die spezifische CPU-Architektur bezieht, #define NR_OPEN_DEFAULTBITS_PER_LONG.
DATEI Struktur Die Beziehung zwischen dem Dateideskriptor und der Struktur der Datei kann durch die folgende Abbildung dargestellt werden:
Das obige ist der detaillierte Inhalt vonEinführung in das Konzept von Dateideskriptoren und FILE. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!