आइए मिलकर एक प्रोजेक्ट बनाते हैं!
ps, top और netstat जैसे लिनक्स टूल्स बहुत उपयोगी हैं।
ये आपके सिस्टम के बारे में ढेर सारी जानकारी देते हैं।
- लेकिन ये काम कैसे करते हैं?
- ये अपनी सारी जानकारी कहाँ से प्राप्त करते हैं?
- हम अपने खुद के टूल्स बनाने के लिए इसका उपयोग कैसे कर सकते हैं?
इस पोस्ट में हम मिलकर तीन लोकप्रिय लिनक्स टूल्स को फिर से बनाएंगे।
आपको एक साथ दोहरा लाभ मिलेगा: कुछ रूबी ट्रिक्स सीखेंगे और उपयोगी लिनक्स ज्ञान भी प्राप्त करेंगे! 🙂
स्टेटस जानकारी ढूंढना
तो आइए उस सवाल का जवाब देने की कोशिश करते हैं कि ये सभी टूल्स अपनी जानकारी कहाँ से पाते हैं।
जवाब है proc फाइलसिस्टम में!
यदि आप /proc डायरेक्टरी के अंदर देखें, तो यह आपके कंप्यूटर पर किसी अन्य डायरेक्टरी की तरह ही डायरेक्टरीज़ और फाइलों का एक समूह लगेगा।
ये असली फाइलें नहीं हैं, यह केवल लिनक्स कर्नेल का उपयोगकर्ताओं को डेटा उजागर करने का एक तरीका है।
यह बहुत सुविधाजनक है क्योंकि इन्हें सामान्य फाइलों की तरह माना जा सकता है, जिसका अर्थ है कि आप इन्हें किसी विशेष टूल के बिना पढ़ सकते हैं।
लिनक्स की दुनिया में बहुत सी चीज़ें इसी तरह काम करती हैं।
यदि आप एक और उदाहरण देखना चाहते हैं तो /dev डायरेक्टरी पर एक नज़र डालें।
अब जब हम समझ गए हैं कि हम किससे निपट रहे हैं, तो आइए /proc डायरेक्टरी की सामग्री पर एक नज़र डालते हैं…
1 10 104 105 11 11015 11469 11474 11552 11655
यह सिर्फ एक छोटा सा नमूना है, लेकिन आप जल्दी से एक पैटर्न देख सकते हैं।
वे सभी संख्याएं क्या हैं?
ठीक है, पता चला है कि ये PID (प्रोसेस आईडी) हैं।
प्रत्येक एंट्री में एक विशिष्ट प्रोसेस के बारे में जानकारी होती है।
यदि आप ps चलाते हैं तो आप देख सकते हैं कि कैसे हर प्रोसेस से एक PID जुड़ा होता है:
PID TTY TIME CMD 15952 pts/5 00:00:00 ps 22698 pts/5 00:00:01 bash
इससे हम यह निष्कर्ष निकाल सकते हैं कि ps क्या करता है: यह केवल /proc डायरेक्टरी पर पुनरावृति करता है और जो जानकारी पाता है उसे प्रिंट करता है।
आइए देखते हैं कि उन नंबर वाली डायरेक्टरीज़ में से एक के अंदर क्या है:
attr autogroup auxv cgroup clear_refs cmdline comm cpuset cwd environ exe fd
यह सिर्फ जगह बचाने के लिए एक नमूना है, लेकिन मैं आपको पूरी सूची देखने के लिए प्रोत्साहित करता हूं।
यहां कुछ दिलचस्प एंट्रीज़ हैं:
| एंट्री | विवरण |
|---|---|
| comm | प्रोग्राम का नाम |
| cmdline | इस प्रोसेस को लॉन्च करने के लिए इस्तेमाल की गई कमांड |
| environ | पर्यावरण चर जिनके साथ यह प्रोसेस शुरू किया गया था |
| status | प्रोसेस स्टेटस (रनिंग, स्लीपिंग…) और मेमोरी उपयोग |
| fd | डायरेक्टरी जिसमें फाइल डिस्क्रिप्टर्स होते हैं (खुली फाइलें, सॉकेट…) |
अब जब हम यह जानते हैं तो हम कुछ टूल्स लिखना शुरू कर सकते हैं!
चल रहे प्रोग्राम्स को कैसे सूचीबद्ध करें
आइए /proc के तहत सभी डायरेक्टरीज़ की सूची प्राप्त करके शुरू करते हैं।
हम इसे Dir क्लास का उपयोग करके कर सकते हैं।
उदाहरण:
Dir.glob("/proc/[0-9]*")
ध्यान दें कि मैंने एक नंबर रेंज का उपयोग कैसे किया, कारण यह है कि /proc के तहत अन्य फाइलें हैं जिनकी हमें अभी परवाह नहीं है, हम केवल नंबर वाली डायरेक्टरीज़ चाहते हैं।
अब हम इस सूची पर पुनरावृति कर सकते हैं और दो कॉलम प्रिंट कर सकते हैं, एक PID के साथ और दूसरा प्रोग्राम नाम के साथ।
उदाहरण:
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
और यह आउटपुट है:
PID CMD --------------- 1 systemd 2 kthreadd 3 ksoftirqd/0 5 kworker/0 7 migration/0 8 rcu_preempt 9 rcu_bh 10 rcu_sched
अरे, ऐसा लगता है हमने अभी ps बना लिया! हां, यह मूल से सभी फैंसी विकल्पों का समर्थन नहीं करता, लेकिन हमने कुछ काम करने वाला बना लिया।
कौन सुन रहा है?
आइए अब netstat को दोहराने की कोशिश करते हैं, यह आउटपुट कैसा दिखता है ( -ant फ्लैग्स के साथ)।
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
हमें यह जानकारी कहाँ मिल सकती है? यदि आपने /proc के अंदर कहा तो आप सही हैं! अधिक विशिष्ट होने के लिए आप इसे /proc/net/tcp में पा सकते हैं।
लेकिन एक छोटी सी समस्या है, यह netstat आउटपुट की तरह बिल्कुल नहीं दिखता!
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
इसका मतलब है कि हमें रेगुलर एक्सप्रेशन्स के साथ कुछ पार्सिंग करने की आवश्यकता है। अभी के लिए आइए केवल लोकल एड्रेस और स्टेटस के बारे में चिंता करें।
यहां वह रेगुलर एक्सप्रेशन है जो मैं लेकर आया:
LINE_REGEX = /\s+\d+: (?<local_addr>\w+):(?<local_port>\w+) \w+:\w+ (?<status>\w+)/
यह हमें कुछ हेक्साडेसिमल मान देगा जिन्हें हमें दशमलव में बदलने की आवश्यकता है। आइए एक क्लास बनाते हैं जो हमारे लिए यह करेगा।
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
# हेक्स को नियमित IP नोटेशन में बदलें
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
# इसके बारे में ज्यादा चिंता न करें। यह कुछ बाइनरी गणित है।
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
बाकी केवल एक सुंदर टेबल फॉर्मेट में परिणाम प्रिंट करना है।
require 'table_print' tp connections
उदाहरण आउटपुट:
STATUS | LOCAL_PORT | LOCAL_ADDR ------------|------------|-------------- LISTENING | 5432 | 127.0.0.1 ESTABLISHED | 39530 | 192.168.88.46
हां, यह जेम बहुत बढ़िया है!
मुझे बस इसके बारे में पता चला और लगता है कि मुझे फिर से ljust / rjust के साथ उलझना नहीं पड़ेगा 🙂
मेरे पोर्ट का उपयोग करना बंद करो!
क्या आपने कभी यह संदेश देखा है?
Address already in use - bind(2) for "localhost" port 5000
उम्म…
मुझे आश्चर्य है कि कौन सा प्रोग्राम उस पोर्ट का उपयोग कर रहा है।
आइए पता लगाते हैं:
fuser -n tcp -v 5000 PORT USER PID ACCESS CMD 5000/tcp rubyguides 30893 F.... nc
अरे, तो ये है हमारा अपराधी!
अब हम इस प्रोग्राम को रोक सकते हैं अगर हम नहीं चाहते कि यह चलता रहे और वह हमारा पोर्ट खाली कर देगा। fuser प्रोग्राम को कैसे पता चला कि इस पोर्ट का उपयोग कौन कर रहा था?
आपने सही अनुमान लगाया!
/proc फाइलसिस्टम फिर से।
वास्तव में, यह उन दो चीजों को जोड़ता है जिन्हें हमने पहले ही कवर किया है: प्रोसेस सूची के माध्यम से चलना और /proc/net/tcp से सक्रिय कनेक्शन पढ़ना।
हमें बस एक अतिरिक्त कदम की आवश्यकता है:
खुले पोर्ट की जानकारी का PID से मिलान करने का एक तरीका खोजें।
यदि हम /proc/net/tcp से प्राप्त होने वाले TCP डेटा को देखें, तो वहां PID नहीं है। लेकिन हम इनोड नंबर का उपयोग कर सकते हैं।
“एक इनोड एक डेटा संरचना है जिसका उपयोग फाइलसिस्टम ऑब्जेक्ट को दर्शाने के लिए किया जाता है।” – विकिपीडिया
हम इनोड का उपयोग करके मिलान करने वाली प्रोसेस कैसे ढूंढ सकते हैं? यदि हम एक प्रोसेस के fd डायरेक्टरी के तहत देखते हैं जिसे हम जानते हैं कि उसके पास एक खुला पोर्ट है, तो हमें इस तरह की एक लाइन मिलेगी:
/proc/3295/fd/5 -> socket:[12345]
कोष्ठक के बीच की संख्या इनोड नंबर है। तो अब हमें बस सभी फाइलों पर पुनरावृति करनी है और हम मिलान करने वाली प्रोसेस ढूंढ लेंगे।
इसे करने का एक तरीका यहां दिया गया है:
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})"
उदाहरण आउटपुट:
Port 5432 in use by /usr/bin/postgres (474)
कृपया ध्यान दें कि आपको इस कोड को रूट के रूप में या प्रोसेस मालिक के रूप में चलाने की आवश्यकता होगी।
अन्यथा आप /proc के अंदर प्रोसेस विवरण पढ़ने में सक्षम नहीं होंगे।
निष्कर्ष
इस पोस्ट में आपने सीखा कि लिनक्स वर्चुअल /proc फाइलसिस्टम के माध्यम से बहुत सारा डेटा उजागर करता है। आपने यह भी सीखा कि /proc के तहत डेटा का उपयोग करके ps, netstat और fuser जैसे लोकप्रिय लिनक्स टूल्स को कैसे फिर से बनाया जाए।
नीचे न्यूज़लेटर की सदस्यता लेना न भूलें ताकि आप अगली पोस्ट से न चूकें। 🙂