Thư mục là gì, nếu mọi thứ trên Linux là một tệp?


17

Những người mới bắt đầu thường nghe một cụm từ "Mọi thứ đều là tệp trên Linux / Unix". Tuy nhiên, các thư mục sau đó là gì? Chúng khác với các tập tin như thế nào?

Câu trả lời:


22

Lưu ý: ban đầu điều này được viết để hỗ trợ câu trả lời của tôi cho Tại sao thư mục hiện tại trong lslệnh được xác định là được liên kết với chính nó? nhưng tôi cảm thấy rằng đây là một chủ đề xứng đáng để tự mình đứng lên và do đó, đây là câu hỏi và trả lời .

Hiểu hệ thống tệp và tệp Unix / Linux: Mọi thứ đều là inode

Về cơ bản, một thư mục chỉ là một tệp đặc biệt, chứa danh sách các mục và ID của chúng.

Trước khi chúng tôi bắt đầu cuộc thảo luận, điều quan trọng là phải phân biệt giữa một vài thuật ngữ và hiểu thư mục và tệp thực sự đại diện cho điều gì. Bạn có thể đã nghe thấy cụm từ "Mọi thứ là một tệp" cho Unix / Linux. Vâng, những gì người dùng thường hiểu là tập tin này là: /etc/passwd- Một đối tượng có đường dẫn và tên. Trong thực tế, một tên (có thể là một thư mục hoặc tệp, hoặc bất cứ thứ gì khác) chỉ là một chuỗi văn bản - một thuộc tính của đối tượng thực tế. Đối tượng đó được gọi là inode hoặc I-number và được lưu trữ trên đĩa trong bảng inode. Các chương trình mở cũng có bảng inode, nhưng đó không phải là mối quan tâm của chúng tôi bây giờ.

Khái niệm về một thư mục của Unix là như Ken Thompson đã đưa nó vào một cuộc phỏng vấn năm 1989 :

... Và sau đó một số tệp, là các thư mục chỉ chứa tên và số I.

Một quan sát thú vị có thể được thực hiện từ cuộc nói chuyện của Dennis Ritchie năm 1972 rằng

"... thư mục thực sự không nhiều hơn một tệp, nhưng nội dung của nó được kiểm soát bởi hệ thống và nội dung là tên của các tệp khác. (Một thư mục đôi khi được gọi là một danh mục trong các hệ thống khác." "

... nhưng không có đề cập đến inodes ở bất cứ đâu trong cuộc nói chuyện. Tuy nhiên, hướng dẫn năm 1971 về format of directoriescác tiểu bang:

Thực tế là một tập tin là một thư mục được chỉ định bởi một bit trong từ cờ của mục nhập nút i của nó.

Các mục thư mục dài 10 byte. Từ đầu tiên là nút i của tập tin được đại diện bởi mục nhập, nếu không phải là zero zero; nếu không, mục nhập trống.

Vì vậy, nó đã ở đó từ đầu.

Ghép nối thư mục và inode cũng được giải thích trong Cấu trúc thư mục được lưu trữ trong hệ thống tập tin UNIX như thế nào? . bản thân một thư mục là một cấu trúc dữ liệu, cụ thể hơn: danh sách các đối tượng (tệp và số inode) trỏ đến danh sách về các đối tượng đó (quyền, loại, chủ sở hữu, kích thước, v.v.). Vì vậy, mỗi thư mục chứa số inode riêng của nó, sau đó tên tệp và số inode của chúng. Nổi tiếng nhất là inode # 2 là /thư mục . (Lưu ý, mặc dù đó /dev/runlà các hệ thống tệp ảo, vì vậy chúng là các thư mục gốc cho hệ thống tệp của chúng, chúng cũng có inode 2; tức là một nút là duy nhất trên hệ thống tệp riêng của nó, nhưng với nhiều hệ thống tệp được đính kèm, bạn có các nút không duy nhất). sơ đồ mượn từ câu hỏi được liên kết có lẽ giải thích nó ngắn gọn hơn:

Thư mục-iNode-Block

Tất cả thông tin được lưu trữ trong inode có thể được truy cập thông qua stat()các cuộc gọi hệ thống, theo Linux man 7 inode:

Mỗi tệp có một nút chứa siêu dữ liệu về tệp. Một ứng dụng có thể truy xuất siêu dữ liệu này bằng cách sử dụng stat (2) (hoặc các cuộc gọi liên quan), trả về cấu trúc stat hoặc statx (2), trả về cấu trúc statx.

Có thể truy cập một tệp chỉ biết số inode của nó ( ref1 , ref2 )? Trên một số triển khai Unix có thể nhưng nó bỏ qua kiểm tra quyền truy cập và quyền truy cập, vì vậy trên Linux, nó không được triển khai và bạn phải duyệt qua cây hệ thống tập tin ( find <DIR> -inum 1234ví dụ) để lấy tên tệp và inode tương ứng.

Ở cấp độ mã nguồn, nó được xác định trong nguồn nhân Linux và cũng được chấp nhận bởi nhiều hệ thống tệp hoạt động trên các hệ điều hành Unix / Linux, bao gồm các hệ thống tệp ext3 và ext4 (mặc định của Ubuntu). Điều thú vị: với dữ liệu chỉ là khối thông tin, Linux thực sự có chức năng inode_init_always có thể xác định xem một inode có phải là pipe ( inode->i_pipe) hay không. Có, ổ cắm và ống dẫn về mặt kỹ thuật cũng là tệp - tệp ẩn danh, có thể không có tên tệp trên đĩa. Các ổ cắm của FIFOUnix-Domain có tên tệp trên hệ thống tệp.

Dữ liệu có thể là duy nhất, nhưng số inode không phải là duy nhất. Nếu chúng ta có một liên kết cứng đến foo được gọi là foobar, điều đó cũng sẽ trỏ đến inode 123. Bản thân inode này chứa thông tin về những khối không gian đĩa thực sự bị chiếm bởi inode đó. Và đó là về mặt kỹ thuật làm thế nào bạn có thể .được liên kết với tên tệp thư mục. Chà, hầu như: bạn không thể tự tạo liên kết cứng đến các thư mục trên Linux , nhưng các hệ thống tệp có thể cho phép các liên kết cứng đến các thư mục theo cách rất kỷ luật, điều này gây ra sự hạn chế chỉ có ...như các liên kết cứng.

Cây thư mục

Các hệ thống tập tin thực hiện một cây thư mục là một trong các cơ sở dữ liệu cây. Đặc biệt,

  • ext3 và ext4 sử dụng HTree
  • xfs sử dụng B + Tree
  • zfs sử dụng cây băm

Điểm mấu chốt ở đây là bản thân các thư mục là các nút trong cây và các thư mục con là các nút con, với mỗi đứa trẻ có một liên kết quay lại nút cha. Do đó, đối với một liên kết thư mục, số lượng inode là tối thiểu 2 cho một thư mục trống (liên kết đến tên thư mục /home/example/và liên kết đến bản thân /home/example/.) và mỗi thư mục con bổ sung là một liên kết / nút bổ sung:

# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24

Sơ đồ tìm thấy trên trang khóa học của Ian D. Allen cho thấy một sơ đồ rất đơn giản:

WRONG - names on things      RIGHT - names above things
=======================      ==========================

    R O O T            --->         [etc,bin,home]   <-- ROOT directory
   /   |   \                         /    |      \
etc   bin   home       --->  [passwd]  [ls,rm]  [abcd0001]
 |   /   \    \                 |      /    \       |
 |  ls   rm  abcd0001  --->     |  <data>  <data>  [.bashrc]
 |               |              |                   |
passwd       .bashrc   --->  <data>                <data>

Điều duy nhất trong sơ đồ QUYỀN không chính xác là các tệp không được xem là kỹ thuật trên chính cây thư mục: Thêm tệp không có ảnh hưởng đến số lượng liên kết:

$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4

Truy cập các thư mục như thể chúng là tập tin

Để trích dẫn Linus Torvalds :

Toàn bộ vấn đề với "mọi thứ là một tệp" không phải là bạn có một số tên tệp ngẫu nhiên (thực sự, ổ cắm và ống dẫn cho thấy "tệp" và "tên tệp" không liên quan gì đến nhau), nhưng thực tế là bạn có thể sử dụng chung công cụ để hoạt động trên những thứ khác nhau.

Xem xét rằng một thư mục chỉ là một trường hợp đặc biệt của một tệp, tự nhiên phải có các API cho phép chúng ta mở / đọc / ghi / đóng chúng theo cách tương tự như các tệp thông thường.

Đó là nơi dirent.hthư viện C xuất hiện, xác định direntcấu trúc mà bạn có thể tìm thấy ở man 3 readdir :

   struct dirent {
       ino_t          d_ino;       /* Inode number */
       off_t          d_off;       /* Not an offset; see below */
       unsigned short d_reclen;    /* Length of this record */
       unsigned char  d_type;      /* Type of file; not supported
                                      by all filesystem types */
       char           d_name[256]; /* Null-terminated filename */
   };

Do đó, trong mã C của bạn, bạn phải xác định struct dirent *entry_pvà khi chúng tôi mở một thư mục opendir()và bắt đầu đọc nó readdir(), chúng tôi sẽ lưu trữ từng mục vào entry_pcấu trúc đó . Tất nhiên, mỗi mục sẽ chứa các trường được xác định trong mẫu để direnthiển thị ở trên.

Ví dụ thực tế về cách thức hoạt động của điều này có thể được tìm thấy trong câu trả lời của tôi về Cách liệt kê các tệp và số inode của chúng trong thư mục làm việc hiện tại .

Lưu ý rằng hướng dẫn POSIX trên fdopen nói rằng "[t] mục nhập thư mục cho dấu chấm và dấu chấm là tùy chọn" và trạng thái thủ công readdir struct dirent chỉ bắt buộc phải có d_named_inocác trường.

Lưu ý về "ghi" vào thư mục: ghi vào thư mục đang sửa đổi "danh sách" các mục. Do đó, việc tạo hoặc xóa tệp được liên kết trực tiếp với quyền ghi thư mục và thêm / xóa tệp là thao tác ghi trên thư mục đã nói.


2
Tôi từ chối chấp nhận ổ cắm là tệp;) "Mọi thứ có thể truy cập dưới dạng tệp" có chính xác hơn không?
Rinzwind

@Rinzwind Chà, cụm từ "mọi thứ đều có thể truy cập dưới dạng tệp" là chính xác. Các tập tin thông thường có open()read()ổ cắm có connect()read(), quá. Điều chính xác hơn là "tệp" thực sự được sắp xếp "dữ liệu" được lưu trữ trên đĩa hoặc bộ nhớ và một số tệp là ẩn danh - chúng không có tên tệp. Thông thường người dùng nghĩ về các tệp theo biểu tượng đó trên máy tính để bàn, nhưng đó không phải là thứ duy nhất tồn tại. Xem thêm unix.stackexchange.com/a/116616/85039
Sergiy Kolodyazhnyy

Vâng, câu hỏi là về một thư mục là một tập tin. Và nó là. Ổ cắm gần như có thể là một câu hỏi riêng biệt cùng với ống có tên là FIFO.
WinEunuuchs2Unix

Vâng, tôi đã có một câu trả lời về đường ống cho đến nay: Askubfox.com/a/1074550/295286 Có lẽ các FIFO sẽ tiếp theo
Sergiy Kolodyazhnyy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.