1 คะแนน โดย GN⁺ 2024-07-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • izabera/ps เป็น อิมพลีเมนเทชันด้วย Bash สำหรับจำลองเอาต์พุตที่ใกล้เคียงกับ ps aux ภายใน Bash แม้อยู่ในสถานการณ์ที่ไม่สามารถสร้างโปรเซสใหม่ได้
  • เงื่อนไขหลักคืออยู่บนเครื่องที่เชื่อมต่อผ่าน ssh และมี bash shell ที่เชื่อถือได้ แต่ PID อื่น ๆ ถูกใช้หมดแล้วจนไม่สามารถสร้างโปรเซสใหม่ได้
  • README นำเสนอสถานการณ์นี้เป็นตัวอย่าง คำถามสัมภาษณ์ สำหรับตำแหน่งที่ต้องใช้ความรู้ด้าน Bash/Linux
  • เครื่องมือนี้ถูกอธิบายว่าช่วยให้สามารถ “แสร้งทำเหมือนเข้าถึง ps aux ที่ใช้งานได้” และไม่ได้รับประกันว่าเป็นอิมพลีเมนเทชันทดแทนที่สมบูรณ์
  • ประโยคที่ว่า “ทำงานได้ 100% บนทุกเครื่องและทุกสถานการณ์” ถูกใช้เป็น การรับประกันเชิงล้อเล่น อย่างชัดเจน

โปรเจกต์นี้ทำอะไร

  • เป็นโปรเจกต์ที่เขียน ps aux ด้วย Bash ล้วน ๆ
  • ชื่อใน README คือ “ps aux written entirely in bash without ever forking”
  • จุดเด่นหลักของโปรเจกต์คือระหว่างการทำงานจะไม่ fork เลย

สถานการณ์ที่ตั้งสมมติฐานไว้

  • สถานการณ์ตัวอย่างมีดังนี้
    • เชื่อมต่อเข้าเครื่องผ่าน ssh
    • ผู้ใช้อยู่ใน bash shell ที่คุ้นเคย
    • แต่ PID อื่น ๆ ถูกใช้หมดแล้ว จึงไม่สามารถสร้างโปรเซสใหม่ได้เลย
  • README แนะนำว่าในเงื่อนไขแบบนี้อาจต้องการฟังก์ชันที่คล้าย ps aux

ขอบเขตที่คาดหวังได้และ caveat

  • เครื่องมือนี้ถูกแนะนำสำหรับใช้เพื่อ “kinda sorta pretend” ว่ามี ps aux ที่ใช้งานได้
  • ข้อความใน README ที่ว่า “ทำงานได้อย่างสมบูรณ์ในทุกสถานการณ์บนเครื่อง 100%, รับประกัน” ถูกใช้เป็น มุกตลก แบบเกินจริง
  • ดังนั้นสาระสำคัญตามคำอธิบายคือ ไม่ใช่ความเข้ากันได้อย่างสมบูรณ์ แต่เป็นการจำลอง ps aux ด้วย Bash ล้วน ๆ ในสภาพแวดล้อมสุดขั้วที่ไม่สามารถสร้างโปรเซสใหม่ได้

1 ความคิดเห็น

 
GN⁺ 2024-07-30
ความเห็นจาก Hacker News
  • มุกที่ว่าปัญหาที่ยากที่สุดในวิทยาการคอมพิวเตอร์สุดท้ายแล้วคือ การจัดแนวให้ตรงกัน นี่โดนใจมาก
    เคยเขียนฟังก์ชันจัดคอลัมน์ให้ตรงกันมาแล้วนับครั้งไม่ถ้วนในหลายภาษา และทุกครั้งก็ทรมานไปหมด ทั้งที่ในหัวมันดูเหมือนง่ายแค่ว่า “หาความยาวสูงสุดของแต่ละคอลัมน์ แล้วเติมช่องว่างจนถึงพหุคูณถัดไปของขนาดแท็บ”
    ต่อให้ใช้ f-string และความสามารถเรื่อง padding ของ Python โค้ดก็มักซับซ้อนและอ่านยากอย่างรวดเร็ว แถมแค่กำลังเขียนตัวอย่างประกอบคอมเมนต์ใหม่ก็ยังต้องแก้บั๊กไปหลายจุดจนรู้สึกสยอง

    • เคยถึงขั้นเพิ่ม Pandas เข้าโปรเจกต์เพราะไม่อยากเขียนโค้ดแบบนี้เองแค่เพื่อพิมพ์ตารางให้ดูสวย
      เป็นงานที่พบบ่อยขนาดนี้ก็น่าจะมีไลบรารีอยู่แล้ว และพูดตรง ๆ ว่าน่าแปลกใจที่ไม่มีใน standard library
    • https://perldoc.perl.org/perlform
    • เมื่อก่อนเคยตอบใน Stack Overflow ด้วย วิธีแก้แบบ O(n): https://stackoverflow.com/questions/10865483/print-results-i...
      วิธีคือดึงความกว้างคอลัมน์กับชื่อคอลัมน์จาก description ของ database cursor แล้วสร้างเส้นคั่นกับ format string ก่อนพิมพ์แต่ละแถว ยังไม่แน่ใจว่ามีพลาดบั๊กร้ายแรงอะไรไปหรือเปล่า แต่ก็ไม่ได้ดูเป็นปัญหาที่ยากเป็นพิเศษ
    • ถ้าจะเอาให้ง่ายกว่านั้น ก็ใช้ zip(*table) พลิกข้อมูลเป็นตามคอลัมน์เพื่อหาความยาวสูงสุดของแต่ละคอลัมน์ แล้วค่อยพิมพ์ด้วย f"{r:<{w}}" ให้จัดตรงกัน
      ผลลัพธ์ตัวอย่างก็จะเป็นตารางที่ความกว้างคอลัมน์ตรงกัน เช่น agony | kick | pump
    • ในทางกลับกัน ถ้าต้องคอย พาร์สข้อมูลที่จัดเรียงเป็นคอลัมน์ อยู่บ่อย ๆ เรื่องนี้ก็ไม่ง่ายเหมือนกัน
      ค่าข้อมูลอาจมีช่องว่างอยู่ข้างใน มีการ padding ด้วยช่องว่าง บางทีก็จัดไม่ตรง และบางครั้งข้อมูลก็ล้นคอลัมน์
      สุดท้ายถ้าตกลงกันไปเลยว่าอย่าใช้ข้อมูลแบบจัดคอลัมน์ แต่ใช้ฟอร์แมตที่คนอ่านได้และง่ายกว่านี้ ก็น่าจะดีกับทุกฝ่าย
  • ถ้า SSH เข้าเครื่องไปแล้ว Bash shell ยังอยู่ แต่ PID ถูกใช้จนหมดจนสร้างโปรเซสใหม่ไม่ได้ ผมคงไล่ดูไฟล์ใน /proc/[pid]/ เพื่อหาว่าโปรเซสไหนกำลังทำให้ พื้นที่ PID หมด
    kill ของ Bash เป็นคำสั่ง built-in ของ shell จึงไม่ต้อง fork โปรเซสใหม่แบบ /bin/kill
    ถ้าหา parent process ที่คอยสร้างลูกจนกิน PID หมดได้ ก็หยุดมันแล้วเอาระบบกลับมาอยู่ในการควบคุมได้อีกครั้ง
    สคริปต์นี้ก็พาร์ส /proc เหมือนกัน และยังไม่มีทั้ง pipe หรือการแทนค่าด้วย $(...) ที่ต้องสร้าง Bash subshell ใหม่ด้วย จึงดูค่อนข้างสะอาดดี

    • เคยตอบในสัมภาษณ์ว่า “exec Python
      แบบนั้นจะเรียกใช้ฟังก์ชัน POSIX ที่ต้องการได้โดยไม่ต้องรันคำสั่งแยกต่างหาก และได้รับการตอบรับที่ดี
    • พูดตามตรง ถ้าเป็นผมคง รีบูต ไปเลย
      มันอาจเร็วกว่าไปไล่หา parent process แล้วฆ่าทิ้งภายใต้ข้อจำกัดมากมาย และถ้า PID หมดแล้ว อย่างอื่นก็น่าจะพังอยู่ไม่น้อยเหมือนกัน
    • ถ้าต้องการดูแค่ PID กับชื่อคำสั่ง แบบเกือบมินิมัลก็ทำได้ประมาณนี้: ps(){ (cd /proc;for i in [0-9]*;do echo $i: $(tr '\0' ' ' < $i/cmdline);done); }
    • ประเด็นว่าจะดูจาก /proc/[pid]/ เพื่อหาโปรเซสที่ทำให้พื้นที่ PID หมดนั้นถูกต้อง แต่จากคอมเมนต์ในซอร์สโค้ด ผู้เขียนบอกว่าตอนแรกหวังว่าแค่ /proc/*/status ก็น่าจะพอ ทว่าไม่มีค่าพวก อัตราการใช้ CPU อยู่ในนั้น
    • เรื่อง subprocess นี่อยากรู้จริง ๆ ว่า [[ $cmdline ]] && exec {cmdline}>&- กับ exec {cmdline}< "$dir"/cmdline || continue ทำงานอย่างไร
  • ในปี 2011 เคยไปสัมภาษณ์ตำแหน่ง SRE กับบริษัทเทคขนาดค่อนข้างใหญ่ในสหรัฐ ตอนนั้นยังไม่เคยได้ยินคำว่า SRE มาก่อนเลย
    บริษัทนั้นกำลังทำทางเลือกแทน MS Office แบบบนเบราว์เซอร์ และหลัง phone screening ก็ต้องคุยกับผู้สัมภาษณ์พร้อมเขียนโปรแกรมสด ๆ ภายในตัวแก้ไขเอกสารของบริษัทเอง
    เพราะในแบบประเมินตนเองให้คะแนน shell scripting กับ Linux ไว้สูง จึงได้โจทย์ให้ทำตัวแทน netstat ด้วย Bash แต่ตอนนั้นไม่รู้เลยว่าข้อมูล socket อยู่ตรงไหนใน /proc/ และอยู่ในรูปแบบไหน จึงตัดสินใจอย่างรวดเร็วว่าคงทำไม่ทัน
    เลยเสนอว่าจะทำ ps กับ fuser ฉบับย่อแทน และคำตอบที่พิมพ์ลงในโปรแกรมประมวลผลคำบนเบราว์เซอร์อันแสนเลวร้ายนั้นก็ถูกยอมรับจนได้ไปสัมภาษณ์ onsite ต่อ
    ตอนนี้ย้อนมาคิดก็ไม่แน่ใจว่าสถานการณ์สมมติที่เป็นแรงบันดาลใจของแบบฝึกหัดนี้อาจมีรากฐานในโลกจริงมากกว่าที่คิด

    • ยูทิลิตีระบบพวกนี้บางตัวก็น่าจะ อ่าน procfs/sysfs อยู่ภายในอยู่แล้ว
      ถ้าเป็นผมก็คงเริ่มจากตรงนั้นเหมือนกัน
  • เคยทำ เว็บไซต์แบบอินเทอร์แอ็กทีฟ เล่น ๆ สำหรับสำรวจสถานการณ์ที่ SSH เข้าไปแล้วสร้างโปรเซสใหม่ไม่ได้: https://oops.cmdchallenge.com

    • "echo *" ไม่ได้แสดงทุกไฟล์ในไดเรกทอรี
      ต้องใช้ "echo .* *"
    • ไอเดียเจ๋งดี แต่พอเคลียร์ผ่านแล้วมันเด้งกลับไปด่านแรกและดูด่านอื่นไม่ได้ เลยรู้สึกหงุดหงิด
      อยากดูรายการ "View Solutions" ของด่านอื่นเพื่อดูว่ามีวิธีเข้าหาแบบอื่นอย่างไรบ้าง
  • Izabera เป็นหนึ่งในตัวเทพของ #bash@libera
    ตั้งแต่สมัย freenode ก็ได้เรียนรู้อะไรเยอะมากจากคนเก่ง ๆ แบบนั้นตลอด 10 ปีที่ผ่านมา

  • นี่เป็น Bash ที่สะอาดดี มาก
    จากประสบการณ์ โค้ด Bash ส่วนใหญ่มักเขียนได้ไม่ดีและไม่มีประสิทธิภาพ แต่นี่ดูเป็นตัวอย่างที่ดีว่าไม่จำเป็นต้องเป็นแบบนั้น

    • แต่ถ้าจะเรียกว่า Bash ที่สะอาด ก็ควรพกพาได้ด้วย ซึ่งสคริปต์นี้ ใช้ได้เฉพาะบน Linux และนอกนั้นน่าจะพังหนัก
  • ถ้าเข้าไปอยู่ใน POSIX shell ที่เชื่อถือได้แต่ไม่มี Bash จะทำอย่างไร?
    สคริปต์ Bash นี้ไม่เข้ากันกับ POSIX

  • สคริปต์นี้ ใช้กับ Bash 3.2 ไม่ได้ แต่ทำงานได้บน Bash 4.2
    บน Bash 3.2 จะขึ้นข้อผิดพลาด printf: '(': invalid format character โดยสภาพแวดล้อมตัวอย่างคือ bash-3.2-33.el5_11.4.0.1

    • การไม่รองรับสายรีลีสของ Bash ที่อายุ 18 ปี และสายรีลีสของระบบปฏิบัติการที่อายุ 17 ปี ก็ดูสมเหตุสมผลดี
  • การใช้งานที่ดีกว่าน่าจะเป็น ใช้ดูรายชื่อโปรเซสบนระบบที่ไม่ได้ติดตั้ง procps
    ก็โอเคดี

  • คุณยังสามารถทำ listener และ client ด้วย Bash ได้เหมือนกัน
    แต่ไม่แนะนำให้ใช้จริง