File descriptor là con số định danh duy nhất một file được mở trong hệ điều hành máy tính. Nó mô tả tài nguyên dữ liệu, và cách tài nguyên đó được truy cập.
Khi một chương trình yêu cầu mở một file – hay tài nguyên dữ liệu khác, như là network socket. Thì khi đó kernel sẽ:
- Cấp phép truy cập
- Tạo ra một mục nhập trong global file table
- Cho software biết vị trí của mục nhập đó trong global file table
File descriptor được định danh bởi một số nguyên không âm, như là 0, 13, hay 345. Ít nhất luôn có một file descriptor tồn tại mỗi khi mở file trên hệ thống.
Nói một cách đơn giản hơn, khi bạn mở một file, hệ điều hành sẽ tạo ra một mục nhập đại diện cho file đó và lưu trữ thông tin về file được mở. Vậy nên nếu có 100 file được mở trong hệ điều hành, khi đó sẽ có 100 mục nhập trong global file table. Những mục nhập này được biểu thị bởi số nguyên không âm (như 100,101,….). Số mục nhập này chính là file descriptor.
Tương tự khi bạn mở một network socket, nó cũng được biểu thị bằng một số nguyên và nó được gọi là Socket Descriptor.
File descriptor được sử dụng trong UNIX và được dùng bởi các hệ điều hành hiện đại như Linux, MacOS và BSD. Trong hệ điều hành windows, file descriptor được biết tới với thuật ngữ khác gọi là file handles.
Tổng quan về file descriptor
Khi một process tạo truy vấn thành công để mở một file, kernel sẽ trả về một file descriptor cho process chỉ đến mục nhập của file đó trong global file table trong kernel. Mục nhập trong file table chứa các thông tin như là inode của file, byte offset và quyền truy cập cho luồng dữ liệu đó (như read-only, write-only,…)
3 Loại file descriptor: Stdin, stdout, and stderr
Trên hệ điều hành như UNIX, theo mặc định, 3 file descriptor đầu tiên là STDIN (standard input), STDOUT (standard output) và STDERR (standard error).
Tên | File descriptor | Mô tả | Tên viết tắt |
---|---|---|---|
Standard input | 0 | Luồng dữ liệu mặc định cho input, ví dụ trong lệnh pipeline. Trong terminal, mặc định bàn phím là input từ người dùng | stdin |
Standard output | 1 | Luồng dữ liệu mặc định cho output, ví dụ khi sử dụng lệnh in lên màn hình. Trong terminal, mặc định tới màn hình người dùng. | stdout |
Standard error | 2 | Luồng dữ liệu mặc định cho output liên quan đến lỗi. Trong terminal, mặc định tới màn hình người dùng. | stderr |
Chuyển hướng file descriptors
File descriptors có thể được truy cập trực tiếp bằng cách sử dụng bash – shell mặc định của Linux, macOS.
Ví dụ, khi bạn dùng lệnh “find“, output sẽ đi tới stdout (file descriptor 1), và thông báo lỗi sẽ đi tới stderr (file descriptor 2). Cả hai luồng hiện thị output giống như mình chạy câu lệnh sau đây:
find / -name '*quang*'
Ở màn hình trên mình nhận được lỗi bởi vì find sẽ cố tìm một vài thư mục hệ thống mà không có quyền đọc. Những dòng thông báo “Permission denied” được ghi tới stderr, và những dòng khác được ghi tới stdout.
Bạn có thể ẩn stderr bằng cách chuyển hướng file descriptor 2 tới /dev/null, là thiết bị đặc biệt trong Linux được gọi là “go nowhere“:
find / -name '*quangvublog.3.zip*' 2>/dev/null
Những thông báo lỗi giống ở câu lệnh đầu tiên mình chạy đã được gửi tới /dev/null, và không được hiển thị.
Hiểu về sự khác biệt giữa stdout và stderr cực kỳ quan trọng khi bạn muốn làm việc với output của chương trình. Ví dụ, nếu bạn thử gõ lệnh “grep” tới output của lệnh “find“, bạn sẽ nhận ra rằng thông báo lỗi sẽ không được lọc, vì chỉ standard output mới được đi qua pipeline tới grep thôi.
find / -name '*quang*' | grep 'vu'
Tuy nhiên, bạn có thể chuyển hướng standard error tới standard output, và khi đó grep sẽ xử lý nội dung của cả hai.
find / -name '*quang*' 2>&1 | grep 'quang'
Lưu ý rằng trong lệnh ở trên, file descriptor mục tiêu (1) có tiền tố bắt đầu bằng dấu “&“.
Cách kiểm tra số lượng file descriptor đang được sử dụng
Khi quản trị hệ thống, đôi khi bạn sẽ muốn biết những gì mà một process đang thực hiện và tìm ra bao nhiêu file descriptor đang được sử dụng. Bạn sẽ ngạc nhiên khi biết rằng process thực hiện mở tất cả các loại file.
- Log file
- UNIX Sockets
- Network Sockets
- File thư viện /lib/lib64
- File thực thi và các chương trình khác
Giờ mình sẽ chỉ cho các bạn cách tính số lượng file descriptor hiện đang được sử dụng trong hệ thông server Linux.
Bước 1: Tìm ra PID
Để tìm ra PID của process mysqld, nhập vào lệnh sau
ps aux | grep mysqld
Hoặc
pidof mysqld
Bước 2: Liệt kê file được mở bởi PID
Sử dụng lệnh lsof hoặc /proc/$PID file system để hiện thị các file descriptor đang mở:
lsof -p 28290
lsof -a -p 28290
Hoặc
cd /proc/28290/fd
ls -l | less
Bước 3: Đếm số file mở, enter lệnh sau
ls -l | wc -l
Mẹo: đếm tất cả file mở trên toàn hệ thống
Để đếm số file mở được xử lý bởi bất kỳ process nào, chạy lệnh sau:
lsof | wc -l
Ví dụ kết quả hiển thị như sau
5436
Liệt kê file descriptor trong kernel memory
Gõ vào lệnh sau:
sysctl fs.file-nr
Kết quả sẽ hiển thị tương tự như sau
fs.file-nr = 1020 0 70000
Trong đó:
- 1020 – số file được cấp phát xử lý
- 0 – số file được cấp phát xử lý nhưng không dùng
- 70000 – số file xử lý tối đa trên toàn hệ thống
Bạn có thể sử dụng lệnh sau để kiểm tra hoặc đặt số lượng file xử lý tối đa trên toàn hệ thống bằng câu lệnh sau:
sysctl fs.file-max
Kết quả tương tự như sau:
fs.file-max = 70000