POSIX Thread Anatomy

ภายในระบบปฏิบัติการลีนุกซ์นั้นจะรองรับการทำงานแบบหลายงานพร้อมกัน (multitasking support) โดยตัวโปร-เซสเองจะสามารถสร้างงานย่อยๆได้เรียกส่วนนี้ว่า เทรด (thread) หรืออีกชื่อหนึ่งว่า “lightweight processes” ข้อดีของการใช้ thread คือจะสามารถทำให้นักพัฒนาโปรแกรมทำการจัดการกับเหตุการณ์ที่เกิดขึ้นในเวลาที่ไม่แน่นอน (asynchronous events) ได้อย่างทันทีและมีประสิทธิภาพ นอกจากนั้นสามารถใช้ในการประมวลในลักษณะขนาน (parallel computing) บนเครื่องที่มีหน่วยประมวลผลแบบหลายหน่วยประมวลผล (multi-core CPU) ที่ใช้หน่วยความจำร่วมกันได้ดี (shared-memory multiprocessor)

โดยทั่วไปแล้วโปรเซสภายในระบบปฏิบัติการลีนุกซ์จะประกอบด้วยสถานะของหน่วยประมวลผล (เช่นค่าภายในตัวรีจิสเตอร์ AX,BX,DX) รายละเอียดของการจองหน่วยความจำ (สำหรับเก็บ code, globals, heap และ stack) และรายละเอียดที่เกี่ยวข้องกับระบบปฏิบัติการ (เช่น การเปิดไฟล์, หมายเลขโปรเซส) ซึ่งเทรดก็จะคล้ายกัน แต่จะต่างกันตรงที่โปรเซสแต่ละตัวจะแยกการเก็บสถานะกันอย่างชัดเจน ในขณะที่แต่ละเทรดจะมีการใช้ code, globals และ heap ร่วมกัน

ซึ่งเทรดจะสามารถทำงานได้ดีในกรณีที่เครื่องคอมพิวเตอร์นั้นมีมากกว่าหนึ่งหน่วยประมวลผล โดยในรูปข้างล่างแสดงการทำงานของเทรดแต่ละตัว (ได้แก่ main(), function1() และ function2()) ซึ่งถ้าเครื่องมีหน่วยประมวลผลเพียงตัวเดียวการทำงานของโปรแกรมนี้ก็จะทำตามลำดับของคำสั่งแต่ละบรรทัด (program counter) ซึ่งอาจจะใช้เวลาทั้งสิ้น 2 นาที แต่ถ้าเครื่องมีหน่วยประมวลผลจำนวน 3 ตัวทั้งสามส่วนก็จะแยกกันทำงานไปแต่ละหน่วยประมวลผลซึ่งอาจจะใช้เวลาทั้งสิ้นเพียง 50 วินาทีเท่านั้น

รูปเปรียบเทียบการประมวลผลโปรแกรมแบบ Single-Thread และ Multi-Thread
รูปแสดงการใช้พื้นที่ในส่วนของ stack ระหว่างโปรเซสและเทรด

ตัวเทรดแต่ละตัวภายในโปรเซสจะใช้ทรัพยาการเดียวกับโปรเซส แต่อย่างไรก็ตามระบบปฏิบัติการก็สามารถจัดตารางให้การทำงานของแต่ thread ได้อย่างอิสระ เนื่องจากพวกมันเพียงแค่คัดลอกเอาทรัพยากรไปเพียงเล็กน้อยเท่านั้นเพื่อแยกไปเป็นโปรแกรมเล็กอีกตัวดังแสดงในรูปข้างบน

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

ในขณะที่

  • การสื่อสารระหว่างโปรเซสจะสามารถติดต่อผ่านกันได้ต้องผ่านทางระบบปฏิบัติการเท่านั้น เช่นผ่านไฟล์, ผ่าน pipe หรือผ่าน socket เป็นต้น

POSIX threads

ไลบรารี POSIX thread ถือว่าเป็นตัวมาตราฐานหลักในการเขียนเทรดสำหรับโปรแกรมภาษา C/C++ ซึ่งเป็นไลบรารีที่จัดเตรียมฟังก์ชันต่างๆเกี่ยวกับการจัดการเทรดและจะทำงานได้มีประสิทธิภาพมากสำหรับระบบคอมพิวเตอร์ที่มีหลายหน่วยประมวลผลกลาง (multi-processor หรือ multi-core systems) เนื่องจากมีฟังก์ชันในการจัดตารางการทำงานของโปรเซสบนแต่ละหน่วยประมวลผลกลางที่อยู่ภายในระบบคอมพิวเตอร์ตัวเดียวกันและเทรดทุกตัวที่อยู่ภายในโปรเซสเดียวกันนั้นก็จะใช้พื้นที่ในหน่วยความจำเดียวกัน (address space) ซึ่งแตกต่างจากเทคโนโลยีในการเขียนโปรแกรมแบบขนาน (Parallel programming) เช่น MPI และ PVM ที่ถูกใช้ในสภาพแวดล้อมการคำนวณแบบกระจาย (distributed computing) ไปยังระบบคอมพิวเตอร์เครื่องอื่นๆ ที่อยู่ไกลออกไปหรือต่างสถานที่กัน

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

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

  • ฟังก์ชัน int pthread_create(pthread_t *new_thread_ID, const pthread_attr_t *attr, void * (*start_func)(void *), void *arg);

    • สำหรับสร้าง thread ตัวใหม่ (new_thread_ID) และกำหนดฟังก์ชัน (*start_func) ที่จะให้ทำงานเมื่อ thread ถูกเรียกขึ้นมา โดยสามารถระบุพารามิเตอร์ที่จะส่งไปยังฟังก์ชันของ thread ได้ (arg)

  • ฟังก์ชัน int pthread_join(pthread_t target_thread, void **status);

    • สำหรับเรียกให้ thread นั้น (target_thread) ทำงานขึ้นมา

  • ฟังก์ชัน void pthread_exit(void *retval);

    • สำหรับสั่งให้ thread สิ้นสุดการทำงาน

ขบวนการทำงานของเทรดนั้นจะประกอบไปด้วย สร้างเทรด (thread creation), สิ้นสุดการทำงาน (termination), ทำงานตามจังหวะ (thread synchronization ด้วยวิธีการแบบ joins, blocking เป็นต้น), การจัดลำดับการทำงาน (scheduling), การจัดการข้อมูล (data management) และ การติดต่อกันระหว่างกัน (process interaction)

รูปแสดงฟังก์ชันที่เกี่ยวข้องของการทำงานของเทรดลูกตั้งแต่เริ่มจนสิ้นสุด

พื้นที่ที่ใช้งานร่วมกันของเทรดทั้งหมดภายในโปรเซสประกอบไปด้วย

  • ชุดคำสั่งโปรเซส (Process instructions)

  • ค่า files descriptors ที่มีการเปิดไว้

  • สัญญาณ (signals) และตัวดำเนินการ (signal handlers)

  • ไดเรดทอรี่ปัจจุบัน (current working directory)

  • หมายเลข User และ Group

รูปแสดงรายละเอียดของโครงสร้างภายในโปรเซสที่มีเทรดย่อย

โดยแต่ละเทรดจะมี:

  • หมายเลขเทรด (Thread ID)

  • กลุ่มตัวแปรรีจิสเตอร์ (set of registers) และ stack pointer

  • สแต็คสำหรับเก็บค่าตัวแปร (local variables)

  • signal mask

  • ค่า priority

  • ค่าสถานะที่ส่งกลับ: errno

และเมื่อต้องการคอมไพล์โปรแกรมจะต้องมีการอ้างอิงไลบรารี Posix threads ด้วย -lpthread ดังตัวอย่าง

เปรียบเทียบประสิทธิภาพระหว่าง fork() และ pthread_create()

เมื่อเปรียบเทียบการสร้างและจัดการโปรเซสแล้ว การใช้ thread จะมีการใช้ทรัพยากรของระบบน้อยกว่ามาก และเกิด overhead กับระบบปฏิบัติการที่น้อยกว่าอย่างเห็นได้ชัด จากตัวอย่างโปรแกรมข้างล่างจะแสดงระยะเวลาของการสร้างโปรเซสและ thread ด้วยจำนวน 50,000 ตัว ภายใต้สภาพแวดล้อมเดียวกัน โดยจะมีการนับหน่วยเวลาทั้งสามแบบคือ real time, user time และ system time

แสดงผลการทดสอบบนหน่วยประมวลผลรุ่นต่างๆ (Ref:https://computing.llnl.gov/tutorials/pthreads/)

ตาราง 5-3 แสดงผลการทดสอบบนหน่วยประมวลผลรุ่นต่างๆ

Ref: https://computing.llnl.gov/tutorials/pthreads/

Last updated

Was this helpful?