โปรดอธิบายฟังก์ชัน exec () และตระกูล


102

อะไรคือสิ่งที่exec()ฟังก์ชั่นและครอบครัวของตนหรือไม่ เหตุใดจึงใช้ฟังก์ชันนี้และทำงานอย่างไร

กรุณาทุกคนอธิบายฟังก์ชันเหล่านี้


4
ลองอ่านสตีเวนส์อีกครั้งและชี้แจงสิ่งที่คุณไม่เข้าใจ
vlabrecque

คำตอบ:


250

อย่างง่ายใน UNIX คุณมีแนวคิดเกี่ยวกับกระบวนการและโปรแกรม กระบวนการคือสภาพแวดล้อมที่โปรแกรมดำเนินการ

แนวคิดง่ายๆเบื้องหลัง "รูปแบบการดำเนินการ" ของ UNIX คือมีสองการดำเนินการที่คุณสามารถทำได้

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

ประการที่สองคือการexec()แทนที่โปรแกรมในกระบวนการปัจจุบันด้วยโปรแกรมใหม่ล่าสุด

จากการดำเนินการง่ายๆสองอย่างนั้นสามารถสร้างโมเดลการดำเนินการ UNIX ทั้งหมด


หากต้องการเพิ่มรายละเอียดเพิ่มเติมที่ด้านบน:

การใช้fork()และexec()เป็นตัวอย่างเจตนารมณ์ของ UNIX ซึ่งเป็นวิธีที่ง่ายมากในการเริ่มกระบวนการใหม่

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

กระบวนการใหม่ (เรียกว่าชายด์) ได้รับ ID กระบวนการ (PID) ที่แตกต่างกันและมี PID ของกระบวนการเก่า (พาเรนต์) เป็น PID หลัก (PPID)

เนื่องจากกระบวนการทั้งสองกำลังรันโค้ดเดียวกันทุกประการพวกเขาจึงต้องสามารถบอกได้ว่าอันไหน - โค้ดส่งคืนของการfork()ให้ข้อมูลนี้ - เด็กได้รับ 0 พาเรนต์ได้รับ PID ของเด็ก (หากfork()ล้มเหลวไม่ เด็กถูกสร้างขึ้นและพาเรนต์ได้รับรหัสข้อผิดพลาด)

ด้วยวิธีนี้ผู้ปกครองจะรู้ PID ของเด็กและสามารถสื่อสารกับมันฆ่ามันรอมันและอื่น ๆ (เด็กสามารถค้นหากระบวนการหลักได้ตลอดเวลาด้วยการโทรไปหาgetppid())

การexec()โทรจะแทนที่เนื้อหาปัจจุบันทั้งหมดของกระบวนการด้วยโปรแกรมใหม่ โหลดโปรแกรมลงในพื้นที่กระบวนการปัจจุบันและเรียกใช้จากจุดเริ่มต้น

ดังนั้นfork()และexec()มักใช้ตามลำดับเพื่อให้โปรแกรมใหม่ทำงานเป็นลูกของกระบวนการปัจจุบัน โดยทั่วไปfindเชลล์จะทำสิ่งนี้ทุกครั้งที่คุณพยายามรันโปรแกรมเช่น- เชลล์ฟอร์กจากนั้นเด็กจะโหลดfindโปรแกรมลงในหน่วยความจำตั้งค่าอาร์กิวเมนต์บรรทัดคำสั่งทั้งหมด I / O มาตรฐานและอื่น ๆ

แต่ไม่จำเป็นต้องใช้ร่วมกัน เป็นที่ยอมรับอย่างสมบูรณ์สำหรับโปรแกรมที่จะเรียกfork()โดยไม่มีสิ่งต่อไปนี้exec()ตัวอย่างเช่นโปรแกรมมีทั้งรหัสหลักและรหัสย่อย (คุณต้องระมัดระวังในสิ่งที่คุณทำการใช้งานแต่ละครั้งอาจมีข้อ จำกัด )

สิ่งนี้ถูกใช้ค่อนข้างมาก (และยังคงเป็น) สำหรับ daemons ซึ่งเพียงแค่ฟังบนพอร์ต TCP และแยกสำเนาของตัวเองเพื่อประมวลผลคำขอเฉพาะในขณะที่ผู้ปกครองกลับไปฟัง สำหรับสถานการณ์นี้โปรแกรมมีทั้งรหัสหลักและรหัสลูก

ในทำนองเดียวกันโปรแกรมที่รู้ว่าพวกเขาทำเสร็จแล้วและเพียงแค่ต้องการที่จะเรียกใช้โปรแกรมอื่นทำไม่จำเป็นต้องfork(), exec()และจากนั้นwait()/waitpid()สำหรับเด็ก พวกเขาสามารถโหลดเด็กลงในพื้นที่กระบวนการปัจจุบันโดยตรงด้วยexec()ไฟล์.

การใช้งาน UNIX บางอย่างมีการปรับให้เหมาะสมfork()ซึ่งใช้สิ่งที่พวกเขาเรียกว่า copy-on-write นี่เป็นเคล็ดลับในการชะลอการคัดลอกพื้นที่ในกระบวนการfork()จนกว่าโปรแกรมจะพยายามเปลี่ยนแปลงบางสิ่งในช่องว่างนั้น สิ่งนี้มีประโยชน์สำหรับโปรแกรมเหล่านั้นโดยใช้เพียงอย่างเดียวfork()และexec()ไม่จำเป็นต้องคัดลอกพื้นที่กระบวนการทั้งหมด ภายใต้ลินุกซ์fork()สร้างสำเนาของตารางเพจและโครงสร้างงานใหม่เท่านั้นexec()จะทำงานหนักในการ "แยก" หน่วยความจำของทั้งสองกระบวนการ

หากสิ่งexec นี้เรียกว่าการติดตามfork(และนี่คือสิ่งที่เกิดขึ้นเป็นส่วนใหญ่) นั่นจะทำให้เกิดการเขียนลงในพื้นที่กระบวนการและจากนั้นจะถูกคัดลอกสำหรับกระบวนการลูกก่อนที่จะอนุญาตให้แก้ไขได้

ลินุกซ์ยังมี a ที่vfork()ได้รับการปรับให้เหมาะสมยิ่งขึ้นซึ่งแบ่งปันทุกอย่างระหว่างสองกระบวนการ ด้วยเหตุนี้จึงมีข้อ จำกัด บางประการในสิ่งที่เด็กสามารถทำได้และผู้ปกครองจะหยุดจนกว่าเด็กจะเรียกexec()หรือ_exit()หรือ

ต้องหยุดพาเรนต์ (และเด็กไม่ได้รับอนุญาตให้กลับจากฟังก์ชันปัจจุบัน) เนื่องจากกระบวนการทั้งสองใช้สแต็กเดียวกัน นี้เป็นเพียงเล็กน้อยที่มีประสิทธิภาพมากขึ้นสำหรับกรณีการใช้งานคลาสสิกของตามทันทีโดยfork()exec()

ทราบว่ามีคนในครอบครัวของexecสาย ( execl, execle, execveและอื่น ๆ ) แต่execในบริบทที่นี่หมายถึงของพวกเขา

แผนภาพต่อไปนี้แสดงให้เห็นถึงการfork/execดำเนินการทั่วไปที่bashเชลล์ใช้เพื่อแสดงรายการไดเร็กทอรีด้วยlsคำสั่ง:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V

12
ขอบคุณสำหรับคำอธิบายอย่างละเอียด :)
Faizan

2
ขอขอบคุณสำหรับการอ้างอิงเชลล์ด้วยโปรแกรมค้นหา สิ่งที่ฉันต้องเข้าใจ
ผู้ใช้

เหตุใดexecยูทิลิตี้นี้จึงใช้สำหรับเปลี่ยนเส้นทาง IO ของกระบวนการปัจจุบัน กรณี "null" ที่เรียกใช้ exec โดยไม่มีอาร์กิวเมนต์ถูกนำมาใช้สำหรับอนุสัญญานี้อย่างไร
เรย์

@ เรย์ฉันคิดมาตลอดว่ามันเป็นการขยายธรรมชาติ หากคุณคิดว่าexecเป็นวิธีการแทนที่โปรแกรมปัจจุบัน (เชลล์) ในกระบวนการนี้ด้วยโปรแกรมอื่นการไม่ระบุว่าโปรแกรมอื่นที่จะแทนที่อาจหมายความว่าคุณไม่ต้องการแทนที่
paxdiablo

ฉันเข้าใจว่าคุณหมายถึงอะไรถ้าโดย "การขยายตามธรรมชาติ" คุณหมายถึงบางสิ่งตามแนว "การเติบโตตามธรรมชาติ" ดูเหมือนว่าจะมีการเพิ่มการเปลี่ยนเส้นทางเพื่อรองรับฟังก์ชันการแทนที่โปรแกรมและฉันสามารถเห็นพฤติกรรมนี้ที่เหลืออยู่ในกรณีที่เสื่อมสภาพของexecการถูกเรียกโดยไม่มีโปรแกรม แต่มันค่อนข้างแปลกในสถานการณ์นี้เนื่องจากประโยชน์ดั้งเดิมของการเปลี่ยนเส้นทางสำหรับโปรแกรมใหม่ - โปรแกรมที่จะใช้งานได้จริงexec- หายไปและคุณมีสิ่งประดิษฐ์ที่มีประโยชน์เปลี่ยนเส้นทางโปรแกรมปัจจุบัน - ซึ่งไม่ได้ใช้execหรือเริ่มต้น ขึ้น แต่อย่างใด - แทน
Ray

36

หน้าที่ในครอบครัว exec () มีพฤติกรรมที่แตกต่างกัน:

  • l: อาร์กิวเมนต์ถูกส่งผ่านเป็นรายการสตริงไปยัง main ()
  • v: อาร์กิวเมนต์ถูกส่งผ่านเป็นอาร์เรย์ของสตริงไปยัง main ()
  • p: path / s เพื่อค้นหาโปรแกรมที่กำลังทำงานใหม่
  • e: ผู้โทรสามารถระบุสภาพแวดล้อมได้

คุณสามารถผสมได้ดังนั้นคุณจึงมี:

  • int execl (เส้นทาง const char *, const char * arg, ... );
  • int execlp (ไฟล์ const char *, const char * arg, ... );
  • int execle (เส้นทาง const char *, const char * arg, ... , ถ่าน * const envp []);
  • int execv (เส้นทาง const char *, ถ่าน * const argv []);
  • int execvp (ไฟล์ const char *, char * const argv []);
  • int execvpe (ไฟล์ const char *, char * const argv [], ถ่าน * const envp []);

สำหรับทั้งหมดอาร์กิวเมนต์เริ่มต้นคือชื่อของไฟล์ที่จะดำเนินการ

สำหรับข้อมูลเพิ่มเติมอ่านexec (3) man page :

man 3 exec  # if you are running a UNIX system

1
ที่น่าสนใจคือคุณพลาดexecve()รายการของคุณซึ่งกำหนดโดย POSIX และคุณได้เพิ่มexecvpe()ซึ่ง POSIX ไม่ได้กำหนดไว้ (ส่วนใหญ่เป็นเหตุผลของแบบอย่างในอดีตมันทำให้ชุดของฟังก์ชันสมบูรณ์) มิฉะนั้นคำอธิบายที่เป็นประโยชน์เกี่ยวกับหลักการตั้งชื่อสำหรับครอบครัวซึ่งเป็นส่วนเสริมที่เป็นประโยชน์สำหรับคำตอบของpaxdiabloซึ่งจะอธิบายเพิ่มเติมเกี่ยวกับการทำงานของฟังก์ชัน
Jonathan Leffler

1
และในการป้องกันของคุณผมเห็นว่าหน้าคนสำหรับลินุกซ์execvpe()(et al,) ไม่ได้รายการexecve(); มันมีหน้าคนแยกต่างหาก (อย่างน้อยใน Ubuntu 16.04 LTS) - ความแตกต่างคือexec()ฟังก์ชั่นตระกูลอื่น ๆแสดงอยู่ในส่วนที่ 3 (ฟังก์ชัน) ในขณะที่execve()แสดงอยู่ในส่วนที่ 2 (การเรียกระบบ) โดยทั่วไปทุกฟังก์ชั่นอื่น ๆ execve()ในครอบครัวจะดำเนินการในแง่ของการเรียกไปยัง
Jonathan Leffler

19

execครอบครัวของฟังก์ชั่นให้กระบวนการของคุณรันโปรแกรมที่แตกต่างกันเปลี่ยนโปรแกรมเก่ามันก็ทำงาน คือถ้าโทร

execl("/bin/ls", "ls", NULL);

จากนั้นlsโปรแกรมจะดำเนินการด้วยรหัสกระบวนการผู้ควบคุมการทำงานปัจจุบันและผู้ใช้ / กลุ่ม (สิทธิ์การเข้าถึง) ของกระบวนการที่เรียกว่าexeclของกระบวนการที่เรียกว่า หลังจากนั้นโปรแกรมเดิมจะไม่ทำงานอีกต่อไป

ในการเริ่มกระบวนการใหม่forkระบบจะใช้การเรียกระบบ การรันโปรแกรมโดยไม่ต้องแทนที่เดิมคุณต้องแล้วforkexec


ขอบคุณที่เป็นประโยชน์จริงๆ ฉันกำลังทำโปรเจ็กต์ที่ต้องการให้เราใช้ exec () และคำอธิบายของคุณทำให้ฉันเข้าใจมากขึ้น
TwilightSparkleTheGeek

7

หน้าที่ผู้บริหารและครอบครัวคืออะไร

execครอบครัวฟังก์ชั่นการทำงานทั้งหมดที่ใช้ในการเรียกใช้ไฟล์เช่นexecl, execlp, execle, execvและexecvp.They เป็น frontends ทั้งหมดexecveและให้วิธีการที่แตกต่างกันของการเรียกมันว่า

เหตุใดจึงใช้ฟังก์ชันนี้

ฟังก์ชัน Exec ใช้เมื่อคุณต้องการเรียกใช้งาน (เรียกใช้) ไฟล์ (โปรแกรม)

และมันทำงานอย่างไร

ซึ่งทำงานโดยการเขียนทับอิมเมจกระบวนการปัจจุบันด้วยภาพที่คุณเปิดตัว พวกเขาแทนที่ (โดยสิ้นสุด) กระบวนการที่กำลังทำงานอยู่ (กระบวนการที่เรียกว่าคำสั่ง exec) ด้วยกระบวนการใหม่ที่เปิดตัว

สำหรับรายละเอียดเพิ่มเติม: ดูลิงค์นี้


7

exec มักใช้ร่วมกับ forkซึ่งฉันเห็นว่าคุณถามถึงด้วยดังนั้นฉันจะพูดถึงเรื่องนี้โดยคำนึงถึงสิ่งนี้

execเปลี่ยนกระบวนการปัจจุบันเป็นโปรแกรมอื่น ถ้าคุณเคยดู Doctor Who นี่ก็เหมือนกับตอนที่เขางอกใหม่ร่างกายเก่าของเขาจะถูกแทนที่ด้วยร่างใหม่

สิ่งนี้เกิดขึ้นกับโปรแกรมของคุณและexecเป็นทรัพยากรจำนวนมากที่เคอร์เนลระบบปฏิบัติการตรวจสอบเพื่อดูว่าไฟล์ที่คุณส่งผ่านไปexecเป็นอาร์กิวเมนต์ของโปรแกรม (อาร์กิวเมนต์แรก) สามารถเรียกใช้งานได้โดยผู้ใช้ปัจจุบัน (id ผู้ใช้ของกระบวนการ การexecโทร) และหากเป็นเช่นนั้นจะแทนที่การแม็พหน่วยความจำเสมือนของกระบวนการปัจจุบันด้วยหน่วยความจำเสมือนให้กระบวนการใหม่และคัดลอกargvและenvpข้อมูลที่ส่งผ่านในการexecโทรไปยังพื้นที่ของการแม็พหน่วยความจำเสมือนใหม่นี้ สิ่งอื่น ๆ อาจเกิดขึ้นที่นี่เช่นกัน แต่ไฟล์ที่เปิดไว้สำหรับโปรแกรมที่เรียกexecจะยังคงเปิดอยู่สำหรับโปรแกรมใหม่และจะแชร์ ID กระบวนการเดียวกัน แต่โปรแกรมที่เรียกexecจะหยุดทำงาน (เว้นแต่ว่า exec จะล้มเหลว)

ด้วยเหตุผลที่ว่านี้จะทำวิธีนี้ก็คือว่าโดยการแยกการทำงาน ใหม่ โปรแกรมออกเป็นสองขั้นตอนเช่นนี้คุณสามารถทำบางสิ่งบางอย่างระหว่างคนทั้งสองขั้นตอน สิ่งที่ต้องทำบ่อยที่สุดคือตรวจสอบให้แน่ใจว่าโปรแกรมใหม่เปิดไฟล์บางไฟล์เป็นตัวอธิบายไฟล์บางไฟล์ (โปรดจำไว้ว่า file descriptors จะไม่เหมือนกับFILE *แต่เป็นintค่าที่เคอร์เนลรู้) การทำเช่นนี้คุณสามารถ:

int X = open("./output_file.txt", O_WRONLY);

pid_t fk = fork();
if (!fk) { /* in child */
    dup2(X, 1); /* fd 1 is standard output,
                   so this makes standard out refer to the same file as X  */
    close(X);

    /* I'm using execl here rather than exec because
       it's easier to type the arguments. */
    execl("/bin/echo", "/bin/echo", "hello world");
    _exit(127); /* should not get here */
} else if (fk == -1) {
    /* An error happened and you should do something about it. */
    perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */

สิ่งนี้ทำงานได้สำเร็จ:

/bin/echo "hello world" > ./output_file.txt

จากเชลล์คำสั่ง


5

เมื่อกระบวนการใช้ fork () มันจะสร้างสำเนาของตัวมันเองขึ้นมาและรายการที่ซ้ำกันนี้จะกลายเป็นลูกของกระบวนการ fork () ถูกนำไปใช้โดยใช้ clone () system call ใน linux ซึ่งส่งคืนสองครั้งจากเคอร์เนล

  • ค่าที่ไม่ใช่ศูนย์ (ID กระบวนการของลูก) จะถูกส่งกลับไปยังพาเรนต์
  • ค่าศูนย์จะถูกส่งกลับไปยังเด็ก
  • ในกรณีที่สร้างลูกไม่สำเร็จเนื่องจากปัญหาใด ๆ เช่นหน่วยความจำเหลือน้อย -1 จะถูกส่งกลับไปที่ fork ()

มาทำความเข้าใจกับตัวอย่าง:

pid = fork(); 
// Both child and parent will now start execution from here.
if(pid < 0) {
    //child was not created successfully
    return 1;
}
else if(pid == 0) {
    // This is the child process
    // Child process code goes here
}
else {
    // Parent process code goes here
}
printf("This is code common to parent and child");

ในตัวอย่างเราสันนิษฐานว่า exec () ไม่ได้ใช้ในกระบวนการย่อย

แต่แม่และลูกแตกต่างกันในคุณลักษณะบางอย่างของ PCB (บล็อกควบคุมกระบวนการ) เหล่านี้คือ:

  1. PID - ทั้งเด็กและผู้ปกครองมีรหัสกระบวนการที่แตกต่างกัน
  2. สัญญาณรอดำเนินการ - เด็กไม่ได้รับสัญญาณรอดำเนินการของผู้ปกครอง จะว่างเปล่าสำหรับกระบวนการลูกเมื่อสร้างขึ้น
  3. Memory Locks - เด็กไม่ได้รับการล็อกหน่วยความจำของผู้ปกครอง ล็อกหน่วยความจำคือการล็อกที่สามารถใช้เพื่อล็อกพื้นที่หน่วยความจำจากนั้นไม่สามารถสลับพื้นที่หน่วยความจำนี้ไปยังดิสก์ได้
  4. Record Locks - เด็กไม่ได้รับการล็อกบันทึกของผู้ปกครอง การล็อกเร็กคอร์ดเชื่อมโยงกับบล็อกไฟล์หรือทั้งไฟล์
  5. การใช้ทรัพยากรกระบวนการและเวลาที่ใช้ CPU ถูกตั้งค่าเป็นศูนย์สำหรับเด็ก
  6. เด็กยังไม่ได้รับช่วงเวลาจากผู้ปกครอง

แต่ความจำตอนเด็กล่ะ? พื้นที่ที่อยู่ใหม่สร้างขึ้นสำหรับเด็กหรือไม่

คำตอบในไม่ หลังจากส้อม () ทั้งพาเรนต์และเด็กแชร์พื้นที่แอดเดรสหน่วยความจำของพาเรนต์ ใน linux พื้นที่แอดเดรสเหล่านี้จะแบ่งออกเป็นหลายเพจ เฉพาะเมื่อเด็กเขียนไปยังเพจหน่วยความจำพาเรนต์เท่านั้นการสร้างซ้ำของเพจนั้นจะถูกสร้างขึ้นสำหรับเด็ก สิ่งนี้เรียกอีกอย่างว่า copy on write (คัดลอกพาเรนต์เพจเฉพาะเมื่อเด็กเขียนเท่านั้น)

มาทำความเข้าใจกับตัวอย่างการ copy on write

int x = 2;
pid = fork();
if(pid == 0) {
    x = 10;
    // child is changing the value of x or writing to a page
    // One of the parent stack page will contain this local               variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page.  
}
else {
    x = 4;
}

แต่ทำไม copy on write จึงจำเป็น?

การสร้างกระบวนการโดยทั่วไปเกิดขึ้นผ่านการรวมกันของ fork () - exec () มาทำความเข้าใจกันก่อนว่า exec () ทำอะไร

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

หากไม่มีสำเนาบนกลไกการเขียนที่เกี่ยวข้องกับ fork () เพจที่ซ้ำกันจะถูกสร้างขึ้นสำหรับเด็กและข้อมูลทั้งหมดจะถูกคัดลอกไปยังเพจของเด็ก การจัดสรรหน่วยความจำใหม่และการคัดลอกข้อมูลเป็นกระบวนการที่มีราคาแพงมาก (ใช้เวลาของโปรเซสเซอร์และทรัพยากรระบบอื่น ๆ ) เรารู้ด้วยว่าในกรณีส่วนใหญ่เด็กจะเรียก exec () และนั่นจะแทนที่หน่วยความจำของเด็กด้วยโปรแกรมใหม่ ดังนั้นสำเนาแรกที่เราทำอาจจะเสียเปล่าถ้าไม่มี copy on write

pid = fork();
if(pid == 0) {
    execlp("/bin/ls","ls",NULL);
    printf("will this line be printed"); // Think about it
    // A new memory space will be created for the child and that   memory will contain the "/bin/ls" program(text section), it's stack, data section and heap section
else {
    wait(NULL);
    // parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue
}
return 1; // Both child and parent will exit with status code 1.

เหตุใดผู้ปกครองจึงรอกระบวนการย่อย

  1. ผู้ปกครองสามารถมอบหมายงานให้กับลูกและรอจนกว่าจะเสร็จสิ้น จากนั้นก็สามารถทำงานอื่น ๆ ได้
  2. เมื่อลูกสิ้นสุดลงทรัพยากรทั้งหมดที่เกี่ยวข้องกับชายด์จะถูกปล่อยให้เป็นอิสระยกเว้นบล็อกควบคุมกระบวนการ ตอนนี้เด็กอยู่ในสภาพซอมบี้ การใช้ wait () ผู้ปกครองสามารถสอบถามเกี่ยวกับสถานะของเด็กจากนั้นขอให้เคอร์เนลปล่อย PCB ในกรณีที่ผู้ปกครองไม่ใช้การรอเด็กจะยังคงอยู่ในสภาพซอมบี้

เหตุใดการเรียกระบบ exec () จึงจำเป็น?

ไม่จำเป็นต้องใช้ exec () กับ fork () หากโค้ดที่เด็กจะเรียกใช้นั้นอยู่ในโปรแกรมที่เกี่ยวข้องกับพาเรนต์ไม่จำเป็นต้องใช้ exec ()

แต่ลองนึกถึงกรณีที่เด็กต้องเรียกใช้หลายโปรแกรม มาดูตัวอย่างโปรแกรมเชลล์กัน รองรับหลายคำสั่งเช่น find, mv, cp, date เป็นต้นจะรวมโค้ดโปรแกรมที่เกี่ยวข้องกับคำสั่งเหล่านี้ไว้ในโปรแกรมเดียวหรือให้ลูกโหลดโปรแกรมเหล่านี้ลงในหน่วยความจำเมื่อจำเป็น?

ทั้งหมดขึ้นอยู่กับกรณีการใช้งานของคุณ คุณมีเว็บเซิร์ฟเวอร์ที่ให้อินพุต x ที่ส่งคืน 2 ^ x ไปยังไคลเอนต์ สำหรับแต่ละคำขอเว็บเซิร์ฟเวอร์จะสร้างลูกใหม่และขอให้คำนวณ คุณจะเขียนโปรแกรมแยกต่างหากเพื่อคำนวณสิ่งนี้และใช้ exec () หรือไม่? หรือคุณจะเขียนโค้ดการคำนวณภายในโปรแกรมหลัก?

โดยปกติการสร้างกระบวนการจะเกี่ยวข้องกับการเรียกใช้ fork (), exec (), wait () และ exit () เข้าด้วยกัน


4

exec(3,3p)ฟังก์ชั่นเปลี่ยนกระบวนการปัจจุบันอีกด้วย นั่นคือกระบวนการปัจจุบันหยุดลงและอีกกระบวนการหนึ่งทำงานแทนโดยยึดทรัพยากรบางส่วนที่โปรแกรมเดิมมีอยู่


6
ไม่มาก จะแทนที่อิมเมจกระบวนการปัจจุบันด้วยอิมเมจกระบวนการใหม่ กระบวนการนี้เป็นกระบวนการเดียวกันกับ pid เดียวกันสภาพแวดล้อมเดียวกันและตารางตัวอธิบายไฟล์เดียวกัน สิ่งที่เปลี่ยนไปคือหน่วยความจำเสมือนและสถานะ CPU ทั้งหมด
JeremyP

@JeremyP "ตัวอธิบายไฟล์เดียวกัน" มีความสำคัญที่นี่อธิบายว่าการเปลี่ยนเส้นทางทำงานอย่างไรในเชลล์! ฉันงงงวยว่าการเปลี่ยนเส้นทางสามารถทำงานได้อย่างไรหาก exec เขียนทับทุกอย่าง! ขอบคุณ
FUD
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.