Fork vs Clone บน 2.6 Kernel Linux


37

ฉันมีความสับสนเกี่ยวกับส้อมและโคลน ฉันได้เห็นว่า:

  • fork เป็นกระบวนการและโคลนสำหรับเธรด

  • fork เพียงเรียกการโคลน clone ถูกใช้สำหรับกระบวนการและเธรดทั้งหมด

ถูกต้องหรือไม่ อะไรคือความแตกต่างระหว่าง 2 syscalls ที่มีเคอร์เนล 2.6 Linux?

คำตอบ:


52

fork()เป็นการเรียกระบบ UNIX ดั้งเดิม สามารถใช้เพื่อสร้างกระบวนการใหม่ไม่ใช่เธรดเท่านั้น นอกจากนี้ยังพกพาได้

ใน Linux clone()คือการเรียกใช้ระบบใหม่ที่หลากหลายซึ่งสามารถใช้เพื่อสร้างเธรดการทำงานใหม่ เธรดการเรียกใช้ใหม่สามารถยึดตามซีแมนทิกส์ของกระบวนการ UNIX, เธรด POSIX, สิ่งที่อยู่ระหว่างหรือสิ่งที่แตกต่างกันโดยสิ้นเชิงขึ้นอยู่กับตัวเลือกที่ส่งผ่าน คุณสามารถระบุตัวเลือกทุกประเภทที่กำหนดว่าหน่วยความจำตัวอธิบายไฟล์เนมสเปซต่างๆตัวจัดการสัญญาณและอื่น ๆ จะถูกแชร์หรือคัดลอก

เนื่องจากclone()เป็นการเรียกใช้ระบบ superset การประยุกต์ใช้fork()wrapper การเรียกใช้ระบบใน glibc จะเรียกจริงclone()แต่นี่เป็นรายละเอียดการนำไปปฏิบัติที่โปรแกรมเมอร์ไม่จำเป็นต้องรู้ การfork()เรียกใช้ระบบจริงที่แท้จริงยังคงมีอยู่ในเคอร์เนล Linux สำหรับเหตุผลด้านความเข้ากันได้แบบย้อนหลังแม้ว่าจะกลายเป็นสิ่งซ้ำซ้อนเนื่องจากโปรแกรมที่ใช้ libc เวอร์ชันเก่ามากหรือ libc อื่นนอกเหนือจาก glibc อาจใช้งานได้

clone()ยังใช้เพื่อpthread_create()สร้างฟังก์ชัน POSIX สำหรับสร้างเธรด

โปรแกรมแบบพกพาควรจะเรียกfork()และไม่pthread_create()clone()


2
posix_spawn เป็นอีกฟังก์ชั่นที่เกี่ยวข้อง - ทั้งพกพามากขึ้นและน้อยลงกว่าทางแยกในบางวิธี
Random832

10

ดูเหมือนว่ามีสองclone()สิ่งที่ลอยอยู่ใน Linux 2.6

มีการโทรของระบบ:

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

นี่คือ "โคลน ()" man 2 cloneอธิบายโดยการทำ

หากคุณอ่าน man page นั้นใกล้พอคุณจะเห็นสิ่งนี้:

It is actually a library function layered on top of the
underlying clone() system call.

เห็นได้ชัดว่าคุณควรจะใช้เธรดโดยใช้ "ฟังก์ชั่นห้องสมุด" ในการเรียกระบบที่มีชื่อเหมือนกันอย่างสับสน

ฉันเขียนโปรแกรมสั้น ๆ :

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}

คอมไพล์ด้วย: c99 -Wall -Wextraและเรียกใช้งานภายใต้strace -fเพื่อดูว่าระบบเรียกการฟอร์กทำอะไรได้บ้าง ฉันได้สิ่งนี้จากstraceเครื่อง Linux 2.6.18 (x86_64 CPU):

20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)

ไม่มีการเรียก "fork" ปรากฏในstraceเอาต์พุต การclone()เรียกที่แสดงในstraceเอาต์พุตมีอาร์กิวเมนต์ต่างกันมากจาก man-page-clone เป็นอาร์กิวเมนต์แรกจะแตกต่างกว่าchild_stack=0int (*fn)(void *)

ปรากฏว่ามีการfork(2)เรียกใช้ระบบในแง่ของจริง clone()เช่นเดียวกับ "ฟังก์ชั่นห้องสมุด" clone()มีการใช้งาน จริง clone()มีชุดที่แตกต่างกันของการขัดแย้งจากคนหน้าโคลน

อย่างง่ายงบทั้งสองที่ขัดแย้งกันของคุณเกี่ยวกับfork()และclone()ถูกต้อง แม้ว่า "โคลน" ที่เกี่ยวข้องจะแตกต่างกัน


9
"อันที่จริงแล้วมันเป็นฟังก์ชั่นห้องสมุดที่อยู่ด้านบนของการเรียกระบบต้นแบบโคลน ()" - โดยทั่วไปสิ่งนี้ใช้กับการเรียกระบบทุกครั้ง โปรแกรมเมอร์มักจะเรียกใช้ฟังก์ชั่นใน libc ซึ่งตั้งชื่อตามการเรียกของระบบ เนื่องจากการโทรจริงของระบบโดยตรงจาก C ต้องใช้เวทย์มนตร์เฉพาะแพลตฟอร์ม (โดยปกติจะบังคับให้กับดัก CPU บางชนิดขึ้นอยู่กับสถาปัตยกรรม ABI) และรหัสเครื่องที่ดีที่สุดจากซ้ายไป libc
Celada

1
@Celada - ตกลง มันเป็นเพียงแค่man 2 cloneวลีที่เป็นอย่างนั้นซึ่งฉันคิดว่ามันทำให้เกิดความสับสนและป้องกันไม่ให้ผู้ถามได้รับคำตอบที่ดี
Bruce Ediger

2
ฉันเชื่อว่า manpage หมายถึงการระบุว่ารายการอาร์กิวเมนต์ของcloneฟังก์ชันไลบรารีแตกต่างจากรายการอาร์กิวเมนต์ที่ยอมรับโดยการเรียกระบบพื้นฐาน โดยเฉพาะการโทรระบบเสมอกลับสองครั้งในกองเดียวกันวิธีการแบบดั้งเดิมforkไม่; อาร์กิวเมนต์ทั้งหมดที่เกี่ยวข้องกับ child stack ได้รับการจัดการอย่างเคร่งครัดในพื้นที่ของผู้ใช้ ดูตัวอย่างsourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/…
zwol

1
ฉันต้องการที่จะให้คำตอบที่ดีที่สุดเพราะมันสั่นคลอน แต่ฉันก็โดนฝูงวัวและไปกับคำตอบแรก เธอได้รับคะแนนสำหรับเวลาตอบสนอง ขอบคุณสำหรับคำอธิบายของคุณ
เกร็ก Leventhal

6

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

เป็นหลักสำหรับทุก ๆ "ประเภท" ของข้อมูลที่เกี่ยวข้องกับบริบทการดำเนินการในเคอร์เนลclone()ให้คุณเลือก aliasing ข้อมูลนั้นหรือคัดลอกมัน เธรดสอดคล้องกับนามแฝงกระบวนการสอดคล้องกับการคัดลอก ด้วยการระบุการรวมกันระหว่างสถานะของธงclone()คุณสามารถสร้างสิ่งแปลก ๆ ที่ไม่ใช่กระทู้หรือกระบวนการ โดยปกติคุณไม่ควรทำเช่นนี้และฉันคิดว่ามีการถกเถียงกันบ้างในระหว่างการพัฒนาเคอร์เนล Linux เกี่ยวกับว่าควรอนุญาตให้มีกลไกทั่วไปเช่นclone()นี้หรือไม่

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