Hãy cùng nhau thực hiện một dự án!
Các công cụ Linux như ps
, top
&netstat
thật tuyệt.
Họ cung cấp cho bạn nhiều thông tin về những gì đang diễn ra với hệ thống của bạn.
- Nhưng chúng hoạt động như thế nào?
- Họ lấy tất cả thông tin từ đâu?
- Làm cách nào chúng ta có thể sử dụng điều này để xây dựng các công cụ của riêng mình?
Trong bài đăng này, chúng ta sẽ cùng nhau tạo lại ba công cụ Linux phổ biến.
Bạn sẽ có được bữa ăn 2 × 1, học một số thủ thuật Ruby và đồng thời nhận được kiến thức hữu ích về Linux! 🙂
Tìm thông tin trạng thái
Vì vậy, hãy thử trả lời câu hỏi về nơi mà tất cả các công cụ này tìm thấy thông tin của chúng.
Câu trả lời là trong hệ thống tệp proc!
Nếu bạn nhìn vào bên trong /proc
thư mục nó sẽ giống như một loạt các thư mục và tệp, giống như bất kỳ thư mục nào khác trên máy tính của bạn.
Đây không phải là các tệp thực, nó chỉ là một cách để nhân Linux hiển thị dữ liệu cho người dùng.
Điều này rất tiện lợi vì chúng có thể được coi như các tệp bình thường, có nghĩa là bạn có thể đọc chúng mà không cần bất kỳ công cụ đặc biệt nào.
Trong thế giới Linux, rất nhiều thứ hoạt động như thế này.
Nếu bạn muốn xem một ví dụ khác, hãy xem /dev
thư mục.
Bây giờ chúng tôi hiểu những gì chúng tôi đang xử lý, hãy xem nội dung của /proc
thư mục…
1 10 104 105 11 11015 11469 11474 11552 11655
Đây chỉ là một mẫu nhỏ, nhưng bạn có thể nhanh chóng nhận ra một mẫu.
Tất cả những con số đó là gì?
Chà, hóa ra đây là PID (ID quy trình).
Mỗi mục nhập đều chứa thông tin về một quy trình cụ thể.
Nếu bạn chạy ps
bạn có thể thấy mọi quy trình có PID được liên kết với nó như thế nào:
PID TTY TIME CMD 15952 pts/5 00:00:00 ps 22698 pts/5 00:00:01 bash
Từ đó, chúng ta có thể suy ra rằng những gì ps làm chỉ là lặp qua /proc
thư mục &in thông tin mà nó tìm thấy.
Hãy xem những gì bên trong một trong những thư mục được đánh số đó:
attr autogroup auxv cgroup clear_refs cmdline comm cpuset cwd environ exe fd
Đó chỉ là một ví dụ để tiết kiệm dung lượng, nhưng tôi khuyến khích bạn xem danh sách đầy đủ.
Đây là một số mục nhập thú vị :
Mục nhập | Mô tả |
---|---|
comm | Tên chương trình |
cmdline | Lệnh được sử dụng để khởi chạy quá trình này |
môi trường | Các biến môi trường mà quá trình này đã được bắt đầu |
trạng thái | Trạng thái xử lý (đang chạy, ngủ…) và sử dụng bộ nhớ |
fd | Thư mục chứa trình mô tả tệp (tệp đang mở, ổ cắm…) |
Bây giờ chúng tôi biết điều này, chúng tôi sẽ có thể bắt đầu viết một số công cụ!
Cách liệt kê các chương trình đang chạy
Hãy bắt đầu bằng cách chỉ lấy danh sách tất cả các thư mục trong /proc
.
Chúng tôi có thể làm điều này bằng cách sử dụng Dir
lớp học.
Ví dụ :
Dir.glob("/proc/[0-9]*")
Lưu ý cách tôi đã sử dụng một dải số, lý do là có các tệp khác trong /proc
mà chúng tôi không quan tâm ngay bây giờ, chúng tôi chỉ muốn các thư mục được đánh số.
Bây giờ chúng ta có thể lặp lại danh sách này và in ra hai cột, một cột có PID và một cột khác có tên chương trình.
Ví dụ :
pids = Dir.glob("/proc/[0-9]*") puts "PID\tCMD" puts "-" * 15 pids.each do |pid| cmd = File.read(pid + "/comm") pid = pid.scan(/\d+/).first puts "#{pid}\t#{cmd}" end
Và đây là kết quả đầu ra :
PID CMD --------------- 1 systemd 2 kthreadd 3 ksoftirqd/0 5 kworker/0 7 migration/0 8 rcu_preempt 9 rcu_bh 10 rcu_sched
Này, có vẻ như chúng ta vừa tạo ps
! Vâng, nó không hỗ trợ tất cả các tùy chọn ưa thích từ bản gốc, nhưng chúng tôi đã làm một cái gì đó hoạt động.
Ai đang lắng nghe?
Hãy thử tái tạo netstat
bây giờ, đây là kết quả trông như thế nào (với -ant
dưới dạng cờ).
Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN tcp 0 0 192.168.1.82:39530 182.14.172.159:22 ESTABLISHED
Chúng ta có thể tìm thông tin này ở đâu? Nếu bạn nói “inside /proc
" bạn đúng! Để cụ thể hơn, bạn có thể tìm thấy nó trong /proc/net/tcp
.
Nhưng có một vấn đề nhỏ, cái này trông không giống với netstat
đầu ra!
0: 0100007F:1538 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1001 0 9216 1: 2E58A8C0:9A6A 9FBB0EB9:0016 01 00000000:00000000 00:00000000 00000000 1000 0 258603
Điều này có nghĩa là chúng ta cần thực hiện một số phân tích cú pháp với các biểu thức chính quy. Bây giờ, chúng ta hãy chỉ lo lắng về địa chỉ địa phương và trạng thái.
Đây là regex tôi đã nghĩ ra :
LINE_REGEX = /\s+\d+: (?<local_addr>\w+):(?<local_port>\w+) \w+:\w+ (?<status>\w+)/
Điều này sẽ cung cấp cho chúng ta một số giá trị thập lục phân mà chúng ta cần chuyển đổi thành thập phân. Hãy tạo một lớp học sẽ làm điều này cho chúng tôi.
class TCPInfo def initialize(line) @data = parse(line) end def parse(line) line.match(LINE_REGEX) end def local_port @data["local_port"].to_i(16) end # Convert hex to regular IP notation def local_addr decimal_to_ip(@data["local_addr"].to_i(16)) end STATUSES = { "0A" => "LISTENING", "01" => "ESTABLISHED", "06" => "TIME_WAIT", "08" => "CLOSE_WAIT" } def status code = @data["status"] STATUSES.fetch(code, "UNKNOWN") end # Don't worry too much about this. It's some binary math. def decimal_to_ip(decimal) ip = [] ip << (decimal >> 24 & 0xFF) ip << (decimal >> 16 & 0xFF) ip << (decimal >> 8 & 0xFF) ip << (decimal & 0xFF) ip.join(".") end end
Việc còn lại duy nhất là in kết quả ở định dạng bảng đẹp.
require 'table_print' tp connections
Đầu ra mẫu :
STATUS | LOCAL_PORT | LOCAL_ADDR ------------|------------|-------------- LISTENING | 5432 | 127.0.0.1 ESTABLISHED | 39530 | 192.168.88.46
Vâng, viên ngọc này thật tuyệt vời!
Tôi vừa mới tìm thấy về nó và có vẻ như tôi sẽ không phải mò mẫm với ljust
/ rjust
một lần nữa 🙂
Ngừng sử dụng cổng của tôi!
Bạn đã bao giờ thấy thông báo này chưa?
Address already in use - bind(2) for "localhost" port 5000
Ừm…
Tôi không biết chương trình nào đang sử dụng cổng đó.
Hãy cùng tìm hiểu :
fuser -n tcp -v 5000 PORT USER PID ACCESS CMD 5000/tcp rubyguides 30893 F.... nc
À, thì ra là thủ phạm của chúng ta!
Bây giờ chúng tôi có thể dừng chương trình này nếu chúng tôi không muốn nó chạy và điều đó sẽ giải phóng cổng của chúng tôi. Làm cách nào mà chương trình “fuser” tìm ra ai đang sử dụng cổng này?
Bạn đoán nó!
/proc
lại hệ thống tệp.
Trên thực tế, nó kết hợp hai thứ mà chúng tôi đã đề cập:xem qua danh sách quy trình và đọc các kết nối đang hoạt động từ /proc/net/tcp
.
Chúng tôi chỉ cần thêm một bước :
Tìm cách khớp thông tin cổng đang mở với PID.
Nếu chúng ta xem dữ liệu TCP mà chúng ta có thể lấy từ /proc/net/tcp
, PID không có ở đó. Nhưng chúng ta có thể sử dụng số inode.
“Một inode là một cấu trúc dữ liệu được sử dụng để đại diện cho một đối tượng hệ thống tệp.” - Wikipedia
Làm cách nào chúng ta có thể sử dụng inode để tìm quá trình đối sánh? Nếu chúng ta xem dưới fd
thư mục của một tiến trình mà chúng tôi biết có một cổng đang mở, chúng tôi sẽ tìm thấy một dòng như thế này:
/proc/3295/fd/5 -> socket:[12345]
Số giữa các dấu ngoặc là số inode. Vì vậy, bây giờ tất cả những gì chúng tôi phải làm là lặp lại tất cả các tệp và chúng tôi sẽ tìm thấy quy trình phù hợp.
Đây là một cách để làm điều đó :
x = Dir.glob("/proc/[0-9]*/fd/*").find do |fd| File.readlink(fd).include? "socket:[#{socket_inode}]" rescue nil end pid = x.scan(/\d+/).first name = File.readlink("/proc/#{pid}/exe") puts "Port #{hex_port.to_i(16)} in use by #{name} (#{pid})"
Ví dụ đầu ra:
Port 5432 in use by /usr/bin/postgres (474)
Xin lưu ý rằng bạn sẽ cần chạy mã này với tư cách là người chủ hoặc với tư cách là chủ sở hữu quá trình.
Nếu không, bạn sẽ không thể đọc chi tiết quy trình bên trong /proc
.
Kết luận
Trong bài đăng này, bạn đã biết rằng Linux hiển thị rất nhiều dữ liệu qua /proc
ảo hệ thống tập tin. Bạn cũng đã học cách tạo lại các công cụ Linux phổ biến như ps, netstat &fuser bằng cách sử dụng dữ liệu trong /proc
.
Đừng quên đăng ký nhận bản tin bên dưới để không bỏ lỡ những bài viết tiếp theo. 🙂