Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

แนวคิดโครงการ Ruby:สร้างเครื่องมือ Linux ของคุณเอง

มาทำโปรเจกต์กันเถอะ!

เครื่องมือ Linux เช่น ps , top &netstat ดีมาก

พวกเขาให้ข้อมูลมากมายเกี่ยวกับสิ่งที่เกิดขึ้นกับระบบของคุณ

  • แต่มันทำงานอย่างไร
  • พวกเขาได้ข้อมูลทั้งหมดมาจากไหน?
  • เราจะใช้สิ่งนี้เพื่อสร้างเครื่องมือของเราเองได้อย่างไร

ในโพสต์นี้ เราจะสร้างเครื่องมือ Linux ยอดนิยมสามตัวด้วยกัน

คุณจะได้รับอาหาร 2x1 เรียนรู้เคล็ดลับของ Ruby และรับความรู้เกี่ยวกับ Linux ที่มีประโยชน์ไปพร้อม ๆ กัน! 🙂

การค้นหาข้อมูลสถานะ

ลองตอบคำถามว่าเครื่องมือเหล่านี้หาข้อมูลได้จากที่ใด

คำตอบอยู่ในระบบไฟล์ proc!

หากคุณดูภายใน /proc ไดเร็กทอรี มันจะดูเหมือนกลุ่มของไดเร็กทอรีและไฟล์ เช่นเดียวกับไดเร็กทอรีอื่นๆ ในคอมพิวเตอร์ของคุณ

นี่ไม่ใช่ไฟล์จริง แต่เป็นเพียงวิธีที่เคอร์เนล Linux เปิดเผยข้อมูลแก่ผู้ใช้

สะดวกมากเพราะสามารถจัดการได้เหมือนไฟล์ทั่วไป ซึ่งหมายความว่าคุณสามารถอ่านได้โดยไม่ต้องใช้เครื่องมือพิเศษใดๆ

ในโลกของ Linux มีหลายสิ่งหลายอย่างที่ทำงานในลักษณะนี้

หากคุณต้องการดูตัวอย่างอื่นให้ดูที่ /dev ไดเรกทอรี

ตอนนี้เราเข้าใจสิ่งที่เรากำลังเผชิญอยู่แล้ว มาดูเนื้อหาของ /proc ไดเรกทอรี…

110104105111101511469114741155211655

นี่เป็นเพียงตัวอย่างเล็กๆ แต่คุณสามารถสังเกตเห็นรูปแบบได้อย่างรวดเร็ว

ตัวเลขทั้งหมดนั้นคืออะไร

ปรากฎว่านี่คือ PID (รหัสกระบวนการ)

ทุกรายการมีข้อมูลเกี่ยวกับกระบวนการเฉพาะ

หากคุณเรียกใช้ ps คุณสามารถดูได้ว่าทุกกระบวนการมี PID เกี่ยวข้องอย่างไร:

PID TTY TIME CMD15952 pts/5 00:00:00 ps22698 pts/5 00:00:01 ทุบตี

จากนี้เราสามารถสรุปได้ว่าสิ่งที่ ps ทำนั้นเป็นเพียงการวนซ้ำ /proc ไดเร็กทอรี &พิมพ์ข้อมูลที่พบ

มาดูกันว่ามีอะไรอยู่ในไดเร็กทอรีที่มีลำดับเลขตัวใดตัวหนึ่ง:

attrautogroupauxvcgroupclear_refscmdlinecommcpusetcwdenvironexefd

นั่นเป็นเพียงตัวอย่างเพื่อประหยัดพื้นที่ แต่เราขอแนะนำให้คุณดูรายการทั้งหมด

นี่คือรายการที่น่าสนใจบางส่วน :

รายการ คำอธิบาย
comm ชื่อโปรแกรม
cmdline คำสั่งที่ใช้เปิดกระบวนการนี้
สิ่งแวดล้อม ตัวแปรสภาพแวดล้อมที่กระบวนการนี้เริ่มต้นด้วย
สถานะ สถานะกระบวนการ (กำลังทำงาน กำลังสลีป…) &การใช้หน่วยความจำ
fd ไดเร็กทอรีที่มี file descriptor (เปิดไฟล์, sockets…)

เมื่อรู้อย่างนี้แล้ว เราก็เริ่มเขียนเครื่องมือได้เลย!

วิธีการแสดงรายการโปรแกรมที่ทำงานอยู่

เริ่มจากการรับรายการไดเร็กทอรีทั้งหมดภายใต้ /proc .

เราสามารถทำได้โดยใช้ Dir ชั้นเรียน

ตัวอย่าง :

Dir.glob("/proc/[0-9]*")

สังเกตว่าฉันใช้ช่วงตัวเลขอย่างไร เหตุผลก็คือมีไฟล์อื่นภายใต้ /proc ที่เราไม่สนใจในตอนนี้ เราต้องการแค่ไดเร็กทอรีที่มีหมายเลขเท่านั้น

ตอนนี้ เราสามารถวนซ้ำรายการนี้และพิมพ์สองคอลัมน์ คอลัมน์แรกมี PID และอีกคอลัมน์หนึ่งมีชื่อโปรแกรม

ตัวอย่าง :

pids =Dir.glob("/proc/[0-9]*")ใส่ "PID\tCMD" ใส่ "-" * 15pids.each ทำ |pid| cmd =File.read(pid + "/comm") pid =pid.scan(/\d+/).first ใส่ "#{pid}\t#{cmd}"end

และนี่คือผลลัพธ์ :

PID CMD ----------------1 systemd2 kthreadd3 ksoftirqd/05 kworker/07 การย้ายข้อมูล/08 rcu_preempt9 rcu_bh10 rcu_sched

เฮ้ ดูเหมือนว่าเราเพิ่งสร้าง ps ! ใช่ มันไม่รองรับตัวเลือกแฟนซีทั้งหมดจากต้นฉบับ แต่เราได้ทำบางสิ่งให้ได้ผล

ใครกำลังฟังอยู่

มาลองจำลอง netstat . กัน ทีนี้ นี่คือผลลัพธ์ที่ออกมา (ด้วย -ant เป็นธง)

การเชื่อมต่ออินเทอร์เน็ตที่ใช้งาน (เซิร์ฟเวอร์และที่จัดตั้งขึ้น)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 ก่อตั้ง

เราจะหาข้อมูลนี้ได้จากที่ไหน? ถ้าคุณพูดว่า “inside /proc " คุณถูก! หากต้องการเจาะจงมากขึ้น คุณสามารถค้นหาได้ใน /proc/net/tcp .

แต่มีปัญหานิดหน่อย ดูไม่เหมือน netstat เอาท์พุต!

<ก่อน>0:0100007F:1538 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1001 0 92161:2E58A8C0:9A6A 9FBB0EB9:0016 01 00000000:00000000 00:00000000 00000000 1000 0 258603

สิ่งนี้หมายความว่าเราต้องแยกวิเคราะห์ด้วยนิพจน์ทั่วไป ตอนนี้เรามากังวลเกี่ยวกับที่อยู่ในท้องถิ่นและสถานะกัน

นี่คือ regex ที่ฉันใช้ :

LINE_REGEX =/\s+\d+:(?\w+):(?\w+) \w+:\w+ (?\w+)/

นี่จะให้ค่าเลขฐานสิบหกที่เราจำเป็นต้องแปลงเป็นทศนิยม มาสร้างชั้นเรียนที่จะทำสิ่งนี้ให้เรากันเถอะ

คลาส 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 =@data["status"] STATUSES.fetch(code, "UNKNOWN") สิ้นสุด # อย่ากังวลเรื่องนี้มากเกินไป มันเป็นคณิตศาสตร์เลขฐานสอง def decimal_to_ip(ทศนิยม) ip =[] ip <<(ทศนิยม>> 24 &0xFF) ip <<(ทศนิยม>> 16 &0xFF) ip <<(ทศนิยม>> 8 &0xFF) ip <<(ทศนิยม &0xFF) ip.join(".") สิ้นสุด

เหลือเพียงพิมพ์ผลลัพธ์ในรูปแบบตารางที่สวยงาม

ต้องการการเชื่อมต่อ 'table_print'tp

ตัวอย่างผลลัพธ์ :

สถานะ | LOCAL_PORT | LOCAL_ADDR ------------|-----------|-------------- กำลังฟัง | 5432 | 127.0.0.1 ก่อตั้งแล้ว | 39530 | 192.168.88.46

ใช่ อัญมณีชิ้นนี้ยอดเยี่ยมมาก!

ฉันเพิ่งค้นพบและดูเหมือนว่าฉันจะไม่ต้องคลำหา ljust / rjust อีกครั้ง 🙂

หยุดใช้พอร์ตของฉัน!

คุณเคยเห็นข้อความนี้ไหม

มีการใช้งานอยู่แล้ว - bind(2) สำหรับพอร์ต "localhost" 5000

อืม…

ฉันสงสัยว่าโปรแกรมใดใช้พอร์ตนั้นอยู่

มาหาคำตอบกัน :

fuser -n tcp -v 5000PORT USER PID ACCESS CMD5000/tcp rubyguides 30893 F.... nc

อา มีคนร้ายของเราแล้ว!

ตอนนี้ เราสามารถหยุดโปรแกรมนี้ได้ ถ้าเราไม่ต้องการให้มันทำงาน &นั่นจะทำให้พอร์ตของเราว่าง โปรแกรม "ฟิวเซอร์" รู้ได้อย่างไรว่าใครกำลังใช้พอร์ตนี้อยู่

คุณเดามัน!

/proc ระบบไฟล์อีกครั้ง

อันที่จริง มันรวมสองสิ่งที่เราได้กล่าวถึงไปแล้ว:การเดินผ่านรายการกระบวนการ &อ่านการเชื่อมต่อที่ใช้งานอยู่จาก /proc/net/tcp .

เราต้องการอีกหนึ่งขั้นตอน :

ค้นหาวิธีจับคู่ข้อมูลพอร์ตที่เปิดอยู่กับ PID

ถ้าเราดูข้อมูล TCP ที่เราได้จาก /proc/net/tcp , ไม่มี PID แต่เราสามารถใช้เลขไอโหนดได้

“ไอโหนดคือโครงสร้างข้อมูลที่ใช้แทนออบเจกต์ระบบไฟล์” – วิกิพีเดีย

เราจะใช้ไอโหนดเพื่อค้นหากระบวนการจับคู่ได้อย่างไร ถ้าเราดูภายใต้ fd ไดเร็กทอรีของกระบวนการที่เรารู้ว่ามีพอร์ตเปิดอยู่ เราจะพบบรรทัดดังนี้:

/proc/3295/fd/5 -> socket:[12345]

ตัวเลขระหว่างวงเล็บคือหมายเลขไอโหนด ตอนนี้ สิ่งที่เราต้องทำคือวนซ้ำไฟล์ทั้งหมด &เราจะพบกระบวนการจับคู่

นี่คือวิธีหนึ่งในการทำเช่นนั้น :

x =Dir.glob("/proc/[0-9]*/fd/*").find do |fd| ไฟล์.readlink(fd).include? "socket:[#{socket_inode}]" rescue nilendpid =x.scan(/\d+/).firstname =File.readlink("/proc/#{pid}/exe") วาง "พอร์ต #{hex_port.to_i() 16)} ถูกใช้โดย #{name} (#{pid})"

ตัวอย่างผลลัพธ์:

พอร์ต 5432 ที่ใช้งานโดย /usr/bin/postgres (474)

โปรดทราบว่าคุณจะต้องเรียกใช้โค้ดนี้ในฐานะรูทหรือในฐานะเจ้าของกระบวนการ

มิฉะนั้น คุณจะไม่สามารถอ่านรายละเอียดกระบวนการภายใน /proc .

บทสรุป

ในโพสต์นี้ คุณได้เรียนรู้ว่า Linux เปิดเผยข้อมูลจำนวนมากผ่านเสมือน /proc ระบบไฟล์ คุณยังได้เรียนรู้วิธีสร้างเครื่องมือ Linux ยอดนิยมเช่น ps, netstat &fuser โดยใช้ข้อมูลภายใต้ /proc .

อย่าลืมสมัครรับจดหมายข่าวด้านล่างเพื่อไม่ให้พลาดโพสต์ต่อไป 🙂