# 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="/files/2qBluAqGwes247fSr48Z" alt=""><figcaption><p><em>รูปเปรียบเทียบการประมวลผลโปรแกรมแบบ Single-Thread และ Multi-Thread</em></p></figcaption></figure>

<figure><img src="/files/DzJLUbqG7O4cy88xij06" 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="/files/CsZ4gSq62gF3JfG2uAkb" 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="/files/ix1GPWNdT6YYTY506JPx" 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="/files/FRyUzYpdccy82uxFkUcQ" 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/>*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/computer-operation-systems/linux-os-dev.-engineer/posix-threads/posix-thread-anatomy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
