Pipe Programming

ประเภทการกลไกสื่อสาร IPC

ไปป์ (Pipe)

หลายๆครั้งที่ผู้ใช้งานระบบปฏิบัติการลีนุกซ์จะต้องมีการใช้คำสั่งมากกว่าหนึ่งคำสั่งเพื่อต้องการที่จะกรองข้อมูลที่ต้องการ โดยเชื่อมชุดคำสั่งเหล่านั้นด้วยเครื่องหมาย “|” หรือเรียกว่าไปป์ (Pipe) ดังตัวอย่างข้างล่าง ซึ่งผลลัพธ์ของคำสั่งด้านซ้ายจะส่งเข้าเป็นอินพุทต่อไปของคำสั่งด้านขวาอย่างนี้ไปเรื่อยๆจนกระทั่งถึงคำสั่งสุดท้ายผลลัพธ์ก็จะแสดงออกหน้าจอในที่สุด

ท่อ (Pipe) เป็นกระบวนการที่ใช้ในการติดต่อระหว่างโปรเซสที่ง่ายที่สุดคือการนำผลลัพธ์ที่ได้จากโปรแกรมหนึ่งไปเป็นอินพุทของอีกโปรแกรมหนึ่ง โดยไปป์จะเป็นตัวกลางในการส่งข้อมูลระหว่างโปรเซสซึ่งการส่งข้อมูลจะเป็นแบบทิศทางเดียว (unidirectional) ดังรูป

โดยเมื่อโปรเซสหนึ่งต้องการสร้างไปป์ขึ้นมา ขบวนการของเคอร์เนลก็จะทำการสร้าง file descriptor (fd) ขึ้นมาสองไฟล์สำหรับใช้ในการสร้าง pipe ระหว่างโปรเซสและเคอร์เนล โดยไฟล์ fd ตัวแรกจะถูกใช้เป็นเส้นทางการป้อนข้อมูลหรือเขียนข้อมูลเข้าไปใน pipe (write) ส่วนไฟล์ fd ตัวที่สองจะใช้เป็นตัวรับข้อมูลที่ได้มาจาก pipe (read) ทำให้โปรเซสนั้นสามารถใช้ pipe ในการส่งข้อมูลไปมาให้กันเองภายในโปรเซสได้ ดังแสดงในรูปข้างล่าง

การส่งข้อมูลจากโปรเซสไปยังไฟล์ fd

จากรูปข้างต้นแสดงให้เห็นว่าเมื่อไฟล์ fd ทั้งสอง (pfd0 และ pfd1) ถูกเชื่อมถึงกันแล้ว เมื่อโปรเซสต้องการที่จะส่งข้อมูลผ่าน pipe (pfd1) ข้อมูลก็จะถูกส่งผ่านเคอร์เนลที่สร้างเป็น pipe ขึ้นมาแล้วผ่านข้อมูลนั้นต่อไปยังอีกด้านของ pipe เพื่อกลายเป็นอินพุทของโปรเซสมายังไฟล์ pfd0 ดังแสดงในรูปข้างล่าง

แสดงการส่งข้อมูลแบบทิศทางเดียวด้วย Pipe

ในการใช้งานจริงนั้น pipe จะนำมาใช้มากในการสื่อสารข้อมูลระหว่างโปรเซสแม่และโปรเซสลูก เนื่องจากโปรเซสจะได้รับสืบทอด (inherit) ในทรัพยากรต่างๆจากโปรเซสแม่ เช่น file descriptors ทั้งหลายที่โปรเซสแม่ได้สร้างเอาไว้ทันที ดังแสดงในรูปข้างล่าง

สื่อสารข้อมูลระหว่างโปรเซสแม่และโปรเซสลูก

จากรูปข้างต้นจะสังเกตเห็นว่าทั้งสองโปรเซสมีการเข้าใช้งานไฟล์ pfd0 และ pfd1 ดังนั้นเพื่อให้การสื่อสารระหว่างทั้งสองอยู่ในกฏเกณฑ์ของ pipe คือแบบทิศทางเดียว (unidirectional) จะต้องกำหนดว่าโปรเซสใดเป็นคนส่งและโปรเซสใดเป็นคนรับ ดังตัวอย่างข้างล่างแสดงให้เห็นถึงตัวโปรเซสลูกจะทำหน้าที่ส่งข้อมูล (write) ผ่าน pipe ไปยังโปร-เซสแม่ (read)

การส่งข้อมูลในรูปแบบทางเดียวระหว่างโปรเซส

แต่อย่างไรก็ตามก็สามารถสร้างการสื่อสารข้อมูลในอยู่ในแบบสองทิศทางได้ (bi-directional) โดยการเพิ่ม pipe ขึ้นมาอีกท่อ โดยกำหนดให้โปรเซสแม่เป็นคนส่งข้อมูล (write) ไปยังโปรเซสลูก (read) ได้เช่นกัน ซึ่ง system call ที่ใช้ในการเขียนและอ่านข้อมูลก็ใช้ชื่อฟังก์ชันว่า write() และ read() ตรงๆได้ทันที ยกเว้นไม่สามารถใช้ฟังก์ชัน lseek() ได้กับ file descriptor ของ pipe ได้ ดังนั้นถ้าต้องการใช้โปรเซสสามารถสื่อสารกันได้ทั้งสองทิศทาง (bi-directional) จะต้องใช้ pipe จำนวน n*(n-1) โดยที่ n คือจำนวนโปรเซส

ฟังก์ชันและตัวแปรที่เกี่ยวข้อง

  • ไลบรารีที่เกี่ยวข้องคือ unistd.h

  • ฟังก์ชัน int pipe(int *fd_couple)

    • ทำหน้าที่สร้าง pipe และเก็บรายละเอียดของ file descriptors ทั้งสอง

การพัฒนาโปรแกรมสื่อสารด้วยวิธีการสตรีมข้อมูลระหว่างโพรเซสชนิดทางเดียวนี้จะใช้ฟังก์ชัน pipe ที่อยู่ภายในไฟล์ไลบรารีชื่อว่า unistd.h

สถานะการทำงานของ pipe

0 - ดำเนินการได้สำเร็จ

-1 - การดำเนินการล้มเหลว

จากรูปข้างบนแสดงการทำงานของฟังก์ชัน pipe ซึ่งจะสร้างช่องทางสื่อสารชนิดทางเดียวระหว่างโปรเซสโดยจะคืนค่า file descriptor ทั้งสองฝั่ง ผ่านตัวแปร pfd[] กล่าวคือ ปลายหนึ่งสำหรับอ่าน (pfd[0]) และอีกปลายหนึ่งสำหรับเขียน (pfd[1]) ค่าของ file descriptor เป็นชนิดจำนวนเต็ม (int) ที่ระบบปฏิบัติการลีนุกซ์จะใช้ในการอ้างอิงถึงแฟ้มที่มีการเปิดใช้งานดังนั้นเมื่อเรียกใช้งานฟังก์ชัน pipe จะต้องส่งอาเรย์ของจำนวนเต็มที่มีสมาชิก 2 ตัวให้แก่ ฟังก์ชัน pipe()

ตัวอย่างที่ 1

แสดงตัวอย่างการสร้าง pipe โดยใช้คำสั่ง pipe() เพื่อเป็นท่อเชื่อมระหว่างโปรเซสแม่และโปรเซสลูก โดยที่โปรเซสลูกจะทำการส่งข้อมูลไปยังโปรเซสแม่ โดยการเขียนลง pfd[1] ดังรูปข้างล่าง

คอมไพล์โปรแกรม pipe.c และทดสอบการส่งข้อมูลระหว่างโปรเซสทั้งสอง (โปรเซส parent และโปรเซส child)

ฟังก์ชันและตัวแปรที่เกี่ยวข้อง

นอกจากนั้นการสร้าง pipe ยังสามารถสร้างไฟล์ pipe สำหรับชุดคำสั่งโปรเซสที่ต้องการโดยใช้ฟังก์ชัน popen() ซึ่งภายในฟังก์ชัน popen() จะมีการเรียกฟังก์ชัน pipe() อยู่ภายใน และจะต้องระบุด้วยว่าจะให้ชุดคำสั่งโปรเซสนี้เป็นตัวรับข้อมูล (write) หรือส่งข้อมูล (read) ให้ชัดเจน โดยชุดคำสั่งโปรเซสจะถูกนำไปรันอยู่บน shell ของระบบอีกทอดหนึ่ง ดังนั้นเมื่อมีการสร้าง pipe ด้วยคำสั่ง popen ก็จะต้องใช้ฟังก์ชัน pclose() เพื่อทำลาย pipe ที่สร้างขึ้นมา

  • ฟังก์ชัน FILE *popen(char *command, char *type)

  • ฟังก์ชัน int pclose(FILE *stream)

ตัวอย่างที่ 2

แสดงตัวอย่างการสร้าง pipe สำหรับชุดคำสั่ง sort ที่จะรับข้อมูล (write) จากค่าใช้ตัวแปรอาเรย์ เพื่อทำการจัดเรียงข้อมูล

เนื่องจากฟังก์ชัน popen() จะใช้ shell ในการรันคำสั่งที่ระบุไปให้ ดังนั้นการใช้ชุดคำสั่งก็สามารถทำได้เหมือนกับการพิมพ์คำสั่งบน shell (command line) ได้เช่นกัน ตัวอย่างเช่น

ตัวอย่างที่ 3

แสดงตัวอย่างการใช้ popen() เพื่อรับข้อมูลที่อ่านได้จากคำสั่ง ls แล้วส่งต่อไปเป็นอินพุทให้กับอีกคำสั่ง (sort) เพื่อทำการจัดเรียงข้อมูลต่อไป

ตัวอย่างที่ 4

แสดงตัวอย่างการรับอาร์กิวเมนต์จากผู้ใช้ผ่านการเรียกโปรแกรมจาก command line โดยอาร์กิวเมนต์ตัวแรกจะเป็นคำสั่งที่ต้องการให้รับข้อมูล (write) และอาร์กิวเมนต์ที่สองจะเป็นไฟล์ข้อมูลที่ต้องการจะให้เปิดอ่าน (rt)

ตัวอย่างที่ 5

แสดงตัวอย่างการประยุกต์การส่งข้อความ (message) ระหว่างโปรเซสต่างๆ โดยโปรเซสที่ i ต้องการส่งไปยังโปรเซสที่ j โดยถ้าโปรเซสใดรับข้อความเพียงสองครั้งแล้วก็จะสิ้นสุดการทำงาน

Last updated

Was this helpful?