Process API Programming
ฟังก์ชัน Getpid(), getppid()
Getpid(), getppid()ในเชิงการเขียนโปรแกรมสามารถที่จะเรียกดูหมายเลขของโปรเซสได้โดยเรียกใช้ฟังก์ชันของภาษาซี/ซีพลัสพลัส ได้แก่ฟังก์ชัน getpid() สำหรับหมายเลขตัวที่ถูกสร้างและฟังก์ชัน getppid() สำหรับหมายเลขตัวสร้างโปรเซส ดังตัวอย่างข้างล่างนี้
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("My pid = %d. My parent's pid = %d\n", getpid(), getppid());
return 0;
}แต่ละครั้งที่รันโปรแกรม showid ก็จะแสดง pid ที่แตกต่างกันไป (เพิ่มขึ้น) แต่ยังคงเป็น parent pid หมายเลขเดิม
$ gcc -o showpid showpid.c -Wall
$ ./showpid
My pid = 854. My parent's pid = 381
$ ./showpid
My pid = 855. My parent's pid = 381
$ ./showpid
My pid = 856. My parent's pid = 381
$ ./showpid
My pid = 857. My parent's pid = 381
$ ps x
...
381 p2 S 0:01 -sh (csh)
...
$ ภายในระบบปฏิบัติการลีนุกซ์ชุดเครื่องมือหลักสำหรับการจัดการโปรเซสดังแสดงในแผนผังข้างล่างนี้โดยจะเรียกใช้ System Call ตัวอย่างเช่น การสร้างโปรเซสจะเรียกใช้ fork() เป็นต้น

โดยมีหลักการคือเมื่อโปรเซสใหม่เกิดขึ้น ซึ่งเรียกว่าโปรเซสลูก (child process) จะมีหลายเลขประจำโปรเซสเพิ่มต่อมาจากหมายเลขของโปรเซสที่สร้างหรือเรียกว่าโปรเซสแม่ (parent process) ตัวโปรเซสใหม่จะทำการคัดลอกค่าของหน่วยความจำ (code, globals, heap และ stack) ของโปรเซสแม่ นอกจากนั้นทั้งโปรเซสแม่และโปรเซสลูกจะใช้ทรัพยากรร่วมกัน

การทำงานของทั้งสองโปรเซสจะทำงานไปพร้อมๆกัน (concurrency) โดยโปรเซสแม่จะรอให้โปรเซสลูกทำงานเสร็จสิ้น จากรูปข้างล่างแสดงการคัดลอกพื้นที่ของตำแหน่งหน่วยความจำเสมือน (virutal address space)

การเรียกใช้งาน fork() เพื่อสร้างโปรเซสลูก
ผลการรันโปรแกรมเมื่อโปรเซสแม่ได้เข้าครอบครอง CPU ผลที่ออกมาจะแสดงหมายเลขโปรเซสที่ถูกสร้างขึ้น ซึ่งในกรณีนี้คือหมายเลข 915 โดยที่โปรเซสแม่ (simpfork) จะมีหมายเลข 914 และ bash shell จะมีหมายเลข 381 ซึ่งเมื่อโปรเซสแม่ออกจากการครอบครอง CPU แล้ว ตัวโปรเซสลูกที่เกิดใหม่ (หมายเลข 915) ก็จะเข้าไปครอบครอง CPU ต่อ จึงทำให้มีการส่งค่ากลับมาจาก fork() เท่ากับศูนย์ (0) โดยที่หมายเลขโปรเซสหลักก็จะเป็นเลขของมันเอง (915) แล้วตามด้วยหมายเลขโปรเซสแม่ ซึ่งในที่นี้ก็คือ โปรแกรม simpfork
แต่อย่างไรก็ตามในบางครั้งเมื่อมีการรันโปรแกรม simpfork แต่ละครั้งการแสดงผลอาจจะไม่เป็นตามลำดับซะทีเดียว กล่าวคือ โปรเซสลูกอาจจะได้เข้าครอบครอง CPU ก่อนโปรเซสแม่ ก็เป็นไปได้ ดังตัวอย่างข้างล่างนี้
เพื่อคำสั่งหน่วงเวลาประมาณ 5 วินาที (sleep(5);) ไว้ในตัวโปรเซสลูก ซึ่งจะทำให้โปรเซสแม่ทำงานสิ้นสุดไปก่อน โดยไม่ได้รอให้โปรเซสลูกเสร็จก่อน
เมื่อทำการรันโปรแกรมขึ้นมา โปรเซสลูกก็จะหลับไปชั่วขณะประมาณ 5 วินาที แต่เมื่อตืนขึ้นมาปรากฏว่าตัวโปรเซสแม่ได้ทำงานเสร็จสิ้นไปเรียบร้อยแล้ว จึงทำให้ค่าหมายเลขโปรแกรมเมื่อใช้คำสั่ง getppid() ไม่สามารถส่งค่ากลับของโปรเซสแม่เดิม (simpfork2) ได้ ดังนั้นโปรเซสลูกจึงกำพร้าแม่ทันที และถูกขึ้นไปอยู่ภายใต้การดูแลแทนด้วยโปรเซสตัวแรกของระบบที่มีหมายเลขโปรเซส 1 ซึ่งรู้จักกันในนาม init
เมื่อมีการสร้างโปรเซสลูกขึ้นมา พื้นที่ในหน่วยความจำของโปรเซสลูกจะถูกคัดลอกมาจากโปรเซสแม่ หลังจากที่มีการเรียก fork() แล้วทั้งสองโปรเซสก็จะมีพื้นที่หน่วยความจำสำหรับเก็บตัวแปร j และ K แยกกันไป โดยไม่กระทบกัน ดังตัวอย่างโปรแกรมข้างล่างนี้
เมื่อรันโปรแกรม simpfork3 ดังข้างบน จะสังเกตว่าผลลัพธ์ที่เกิดขึ้นคือ ค่าในตัวแปร j และ K ของแต่ละโปรเซสจะเปลี่ยนแปลงแยกออกจากกัน
เพื่อให้เห็นพฤติกรรมในการคัดลอกพื้นที่ในหน่วยความจำจากโปรเซสแม่ได้ชัดเจนขึ้น สามารถทดสอบรันโปรแกรมใหม่อีกครั้งโดยให้แสดงผลลัพธ์ไปเก็บลงในไฟล์แทน ดังตัวอย่างข้างล่างนี้
จากผลลัพธ์จะสังเกตเห็นได้ว่า ภายในไฟล์ output จะเก็บข้อความซ้ำกันเกิดขึ้น เนื่องจากว่าในระหว่างที่มีการเรียกคำสั่ง fork() นั้นข้อความที่จะถูกพิมพ์ออกไปยังไฟล์ (standard output) ด้วยคำสั่ง printf("Before forking: j = %d, K = %d ", j, K); นั้นจะถูกพักเก็บไว้ชั่วคราว (buffer) เอาไว้ก่อน ซึ่งถ้ายังไม่เต็ม buffer (ที่มีขนาดประมาณ 4KB ถึง 8KB) ก็ยังไม่ส่งออกไปยังไฟล์ก่อน แต่ในขณะนั้นเองตัวโปรเซสลูกก็ได้ทำการคัดลอกค่าหน่วยความจำทั้งหมดของโปรเซสแม่ไว้แล้ว ซึ่งก็ติดค่าที่พักเก็บไว้ใน buffer นั้นด้วยเช่นกัน ดังนั้นเมื่อโปรเซสลูกทำงานและใช้คำสั่ง printf("After forking, child: j = %d, K = %d\n", j, K); ข้อมูลเดิมที่ค้างอยู่ใน buffer ที่ถูกคัดลอกมาจึงถูกเขียนลงไฟล์ซ้ำอีกครั้งนั่นเอง
แสดงตัวอย่างเมื่อทั้งโปรเซสแม่และโปรเซสลูก ต้องเข้าใช้ทรัพยากรร่วมกัน ตัวอย่างเช่น การเข้าถึงไฟล์ เป็นต้น
จากโปรแกรมข้างต้น เมื่อรันโปรแกรม simpfork4 แล้ว โปรเซสแม่จะเริ่มทำดารสร้างไฟล์ใหม่ชื่อว่า tmpfile หลังจากนั้นก็จะเริ่มสร้างโปรเซสใหม่ (โปรเซสลูก) ด้วยคำสั่ง fork() โดยทั้งสองโปรเซสต่างก็เข้าใช้ไฟล์นี้ในการเขียนร่วมกัน
การสิ้นสุดของโปรเซสเกิดขึ้นได้หลายกรณี ตัวอย่างเช่น
โปรเซสดำเนินการชุดคำสั่งจนถึงบรรทัดสุดท้าย (last statement) ของฟังก์ชัน
main()โดยทั่วไปจะเป็นส่งค่ากลับเป็นศูนย์ (exit (0);)มีการสิ้นสุดโปรเซสที่ผิดพลาด (error exit) โดยตั้งใจ ซึ่งจะเป็นการส่งค่ากลับที่ไม่ใช่เลขศูนย์ เช่นใช้คำสั่ง
exit (2);หรือexit (-1);เป็นต้นมีการสิ้นสุดของโปรเซสที่ล้มเหลว (fatal exit) โดยไม่ตั้งใจ เช่นในกรณีการคำนซรทางคณิตศาสตร์ เช่นกรณีการหารด้วยศูนย์ (divided by zero) หรือกรณีเกิดความผิดพลาดในหน่วยใช้งานหน่วยความจำ เป็นต้น
มีการสิ้นสุดของโปรเซสในกรณีที่มีโปรเซสอื่นทำการฆ่า (kill) หรือสั่งให้หยุดและสิ้นสุดการทำงานโปรเซส
แสดงตัวอย่างโปรแกรมการสร้างโปรเซสลูกตามลำดับที่กำหนดดังรูปข้างล่าง โดยใช้ฟังก์ชัน wait()ในการควบคุมการเกิดโปรเซสตามลำดับที่กำหนด

เมื่อรันโปรแกรมตัวโปรเซสแม่ (A) จะสร้างโปรเซสลูกตามลำดับ B-->D และ C-->E
แสดงตัวอย่างโปรแกรมที่มีการเรียกใช้ฟังก์ชัน exec(), wait() และ exit()
เมื่อรันโปรแกรมตัวโปรเซสแม่จะสร้างโปรเซสลูกเพื่อให้ทำการรันคำสั่ง ls จนกว่าจะทำงานเสร็จสิ้น โดยการใช้คำสั่ง wait(NULL); เพื่อรอโปรเซสลูก
แสดงตัวอย่างโปรแกรมที่มีการเรียกใช้คำสั่งที่ไม่มีอยู่ในระบบปฏิบัติการ
แสดงตัวอย่างการเขียนโปรแกรมในลักษณะ shell อย่างง่ายเพื่อรับคำสั่งที่ป้อนเข้ามา (command line) แล้วทำการสร้างโปรเซสลูกในการดำเนินการคำสั่งนั้น โดยทั้งสองตัวอย่างข้างล่างจะขียนในรูปแบบภาษา C และภาษา C++
Last updated
Was this helpful?