> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/computer-operation-systems/linux-os-dev.-engineer/posix-threads/posix-thread-anatomy.md).

# POSIX Thread Anatomy

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

โดยทั่วไปแล้วโปรเซสภายในระบบปฏิบัติการลีนุกซ์จะประกอบด้วยสถานะของหน่วยประมวลผล (เช่นค่าภายในตัวรีจิสเตอร์ `AX,BX,DX`) รายละเอียดของการจองหน่วยความจำ (สำหรับเก็บ code, globals, heap และ stack) และรายละเอียดที่เกี่ยวข้องกับระบบปฏิบัติการ (เช่น การเปิดไฟล์, หมายเลขโปรเซส) ซึ่งเทรดก็จะคล้ายกัน <mark style="color:red;">แต่จะต่างกันตรงที่โปรเซสแต่ละตัวจะแยกการเก็บสถานะกันอย่างชัดเจน</mark> ในขณะที่แต่ละเทรดจะมีการใช้ code, globals และ heap <mark style="color:orange;">**ร่วมกัน**</mark>&#x20;

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

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2F5MT1iygs6YUmjCkj4O74%2Fsingle_thread.png?alt=media&amp;token=a259a25f-f81f-4416-acbf-434d37e55769" alt=""><figcaption><p><em>รูปเปรียบเทียบการประมวลผลโปรแกรมแบบ Single-Thread และ Multi-Thread</em></p></figcaption></figure>

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2Fhh7GN4MneVBlwnm2SE5Q%2FUNIX_process_thread.png?alt=media&amp;token=9e68fc13-bd3d-45a8-8821-e659d8665c70" alt=""><figcaption><p><em>รูปแสดงการใช้พื้นที่ในส่วนของ stack ระหว่างโปรเซสและเทรด</em></p></figcaption></figure>

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

{% hint style="info" %}

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

&#x20;ในขณะที่

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

## POSIX threads

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

{% hint style="info" %}
**ฟังก์ชันและตัวแปรที่เกี่ยวข้อง**

* ไลบรารีที่เกี่ยวข้องคือ <mark style="color:purple;">**`pthread.h`**</mark>
* ฟังก์ชัน <mark style="color:orange;">**`int pthread_create(pthread_t *new_thread_ID, const pthread_attr_t *attr, void * (*start_func)(void *), void *arg);`**</mark>
  * สำหรับสร้าง thread ตัวใหม่ (`new_thread_ID`) และกำหนดฟังก์ชัน (`*start_func`) ที่จะให้ทำงานเมื่อ thread ถูกเรียกขึ้นมา โดยสามารถระบุพารามิเตอร์ที่จะส่งไปยังฟังก์ชันของ thread ได้ (`arg`)
* ฟังก์ชัน <mark style="color:orange;">**`int pthread_join(pthread_t target_thread, void **status);`**</mark>
  * สำหรับเรียกให้ thread นั้น (`target_thread`) ทำงานขึ้นมา
* ฟังก์ชัน <mark style="color:orange;">**`void pthread_exit(void *retval);`**</mark>
  * สำหรับสั่งให้ thread สิ้นสุดการทำงาน
    {% endhint %}

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

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FE0yAwvxUSTvOEIZu0W6h%2Fpthread_create.png?alt=media&amp;token=a0bc3607-8d4e-44c4-8703-0f9fbaa64b4f" alt=""><figcaption><p><em>รูปแสดงฟังก์ชันที่เกี่ยวข้องของการทำงานของเทรดลูกตั้งแต่เริ่มจนสิ้นสุด</em> </p></figcaption></figure>

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

* ชุดคำสั่งโปรเซส (Process instructions)&#x20;
* ค่า files descriptors ที่มีการเปิดไว้
* สัญญาณ (signals) และตัวดำเนินการ (signal handlers)
* ไดเรดทอรี่ปัจจุบัน (current working directory)&#x20;
* หมายเลข User และ Group

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FVMs0Vx0ve20w682MR7dK%2Fpthread_structure.png?alt=media&amp;token=e9cde1fa-4a49-4e59-9a23-732ee90747e6" alt="" width="563"><figcaption><p><em>รูปแสดงรายละเอียดของโครงสร้างภายในโปรเซสที่มีเทรดย่อย</em></p></figcaption></figure>

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

* หมายเลขเทรด (Thread ID)
* กลุ่มตัวแปรรีจิสเตอร์ (set of registers) และ stack pointer
* สแต็คสำหรับเก็บค่าตัวแปร (local variables)
* signal mask
* ค่า priority
* ค่าสถานะที่ส่งกลับ: errno

และเมื่อต้องการคอมไพล์โปรแกรมจะต้องมีการอ้างอิงไลบรารี Posix threads ด้วย <mark style="color:orange;">**`-lpthread`**</mark> ดังตัวอย่าง

```shell-session
$ gcc -o main main.c –lpthread
```

### เปรียบเทียบประสิทธิภาพระหว่าง fork() และ pthread\_create()&#x20;

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

```c
/*
 * =========================================================================
 * C Code for fork() creation test
 * =========================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#define NFORKS 50000

void do_nothing() {
	int i;
	i = 0;
}

int main(int argc, char *argv[]) {
	int pid, j, status;

	for (j = 0; j < NFORKS; j++) {

		/*** error handling ***/
		if ((pid = fork()) < 0) {
			printf("fork failed with error code= %d\n", pid);
			exit(0);
		}

		/*** this is the child of the fork ***/
		else if (pid == 0) {
			do_nothing();
			exit(0);
		}

		/*** this is the parent of the fork ***/
		else {
			waitpid(pid, status, 0);
		}
	}
}
```

```c
/*
 * =========================================================================
 * C Code for pthread_create() test
 * =========================================================================
 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NTHREADS 50000

void* do_nothing(void *null) {
	int i;
	i = 0;
	pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
	int rc, i, j, detachstate;
	pthread_t tid;
	pthread_attr_t attr;

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	for (j = 0; j < NTHREADS; j++) {
		rc = pthread_create(&tid, &attr, do_nothing, NULL);
		if (rc) {
			printf("ERROR; return code from pthread_create() is %d\n", rc);
			exit(-1);
		}

		/* Wait for the thread */
		rc = pthread_join(tid, NULL);
		if (rc) {
			printf("ERROR; return code from pthread_join() is %d\n", rc);
			exit(-1);
		}
	}

	pthread_attr_destroy(&attr);
	pthread_exit(NULL);

}
```

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FofJW2WsKNt2R7PuTSI4N%2Fperformance.png?alt=media&amp;token=81a54e9b-5f94-4414-9f15-dea63cd81cec" alt=""><figcaption><p><em>แสดงผลการทดสอบบนหน่วยประมวลผลรุ่นต่างๆ (</em>Ref:<em>https://computing.llnl.gov/tutorials/pthreads/)</em></p></figcaption></figure>

<br>

<br>

<br>

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

&#x20;

Ref: *<https://computing.llnl.gov/tutorials/pthreads/>*
