Fork vs Clone trên 2.6 Kernel Linux


37

Tôi có một số nhầm lẫn về ngã ba và bản sao. Tôi đã thấy rằng:

  • ngã ba là cho các quy trình và bản sao là cho chủ đề

  • fork chỉ gọi clone, clone được sử dụng cho tất cả các tiến trình và luồng

Là một trong những chính xác? Sự khác biệt giữa 2 tòa nhà này với nhân Linux 2.6 là gì?

Câu trả lời:


52

fork()là cuộc gọi hệ thống UNIX ban đầu. Nó chỉ có thể được sử dụng để tạo các quy trình mới, không phải các chủ đề. Ngoài ra, nó là xách tay.

Trong Linux, clone()là một cuộc gọi hệ thống mới, linh hoạt có thể được sử dụng để tạo ra một luồng thực thi mới. Tùy thuộc vào các tùy chọn được thông qua, luồng thực thi mới có thể tuân thủ ngữ nghĩa của quy trình UNIX, luồng POSIX, thứ gì đó ở giữa hoặc thứ gì đó hoàn toàn khác (như một thùng chứa khác). Bạn có thể chỉ định tất cả các loại tùy chọn ra lệnh cho dù bộ nhớ, mô tả tệp, không gian tên khác nhau, trình xử lý tín hiệu, v.v. được chia sẻ hoặc sao chép.

clone()là cuộc gọi hệ thống superset, việc thực hiện fork()trình bao bọc cuộc gọi hệ thống trong glibc thực sự gọi clone(), nhưng đây là một chi tiết triển khai mà các lập trình viên không cần phải biết. Cuộc fork()gọi hệ thống thực tế vẫn tồn tại trong nhân Linux vì lý do tương thích ngược mặc dù nó đã trở nên dư thừa, bởi vì các chương trình sử dụng các phiên bản libc rất cũ hoặc libc khác ngoài glibc, có thể sử dụng nó.

clone()cũng được sử dụng để thực hiện pthread_create()chức năng POSIX để tạo chủ đề.

Chương trình di động nên gọi fork()pthread_create(), không clone().


2
posix_spawn là một chức năng có liên quan khác - cả hai đều ít di động hơn ngã ba theo một số cách.
Random832

10

Dường như có hai clone()thứ trôi nổi trong Linux 2.6

Có một cuộc gọi hệ thống:

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

Đây là "clone ()" được mô tả bằng cách làm man 2 clone.

Nếu bạn đọc trang người đàn ông đó đủ gần, bạn sẽ thấy điều này:

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

Rõ ràng, bạn phải thực hiện phân luồng bằng cách sử dụng "chức năng thư viện" được xếp lớp trên lệnh gọi hệ thống có tên khó hiểu.

Tôi đã viết một chương trình ngắn:

#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;
}

Biên dịch nó với : c99 -Wall -Wextra, và chạy nó bên dưới strace -fđể xem những gì hệ thống gọi forking thực sự làm. Tôi đã nhận được điều này stracetrên một máy Linux 2.6,18 (CPU x86_64):

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)

Không có cuộc gọi "ngã ba" nào xuất hiện trong straceđầu ra. Cuộc clone()gọi hiển thị trong straceđầu ra có các đối số rất khác với man-page-clone. child_stack=0như đối số đầu tiên là khác nhau int (*fn)(void *).

Dường như fork(2)cuộc gọi hệ thống được triển khai theo nghĩa thực clone() , giống như "chức năng thư viện" clone()được triển khai. Thực tế clone() có một tập hợp các đối số khác nhau từ bản sao con người.

Nói một cách đơn giản, cả hai tuyên bố rõ ràng mâu thuẫn của bạn về fork()clone()đều đúng. "Bản sao" liên quan là khác nhau, mặc dù.


9
"Đây thực sự là một hàm thư viện được xếp lớp trên đầu của lệnh gọi hệ thống clone () bên dưới." - nói chung, điều này áp dụng cho mọi cuộc gọi hệ thống. Các lập trình viên thực sự hầu như luôn gọi các hàm trong libc được đặt tên theo lệnh gọi hệ thống. Điều này là do thực hiện một cuộc gọi hệ thống thực tế trực tiếp từ C yêu cầu ma thuật dành riêng cho nền tảng (thường bằng cách buộc một loại bẫy CPU nào đó, tùy thuộc vào kiến ​​trúc ABI) và mã máy được giao tốt nhất cho libc.
Celada

1
@Celada - vâng, đồng ý. Đó chỉ là man 2 clonecụm từ chính xác theo cách đó, mà tôi nghĩ là gây nhầm lẫn vấn đề, và ngăn người hỏi nhận được câu trả lời tốt.
Bruce Ediger

2
Tôi tin rằng trang này có nghĩa là chỉ ra rằng danh sách đối số của clonechức năng thư viện khác về cơ bản với danh sách đối số được chấp nhận bởi lệnh gọi hệ thống cơ bản. Cụ thể, cuộc gọi hệ thống luôn trả về hai lần trên cùng một ngăn xếp, theo cách truyền thống fork; tất cả các đối số liên quan đến ngăn xếp con được xử lý nghiêm ngặt trong không gian người dùng. Xem ví dụ sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/iêu
zwol

1
Tôi muốn đưa ra câu trả lời tốt nhất của bạn bởi vì nó đá, nhưng tôi đã bị bầy đàn lắc lư và đi với câu trả lời đầu tiên. Cô ấy nhận được điểm cho thời gian đáp ứng. Cảm ơn lời giải thích của bạn.
Gregg Leventhal

6

fork()chỉ là một bộ cờ cụ thể cho cuộc gọi hệ thống clone(). clone()đủ chung để tạo ra một "quy trình" hoặc "luồng" hoặc thậm chí những thứ kỳ lạ nằm ở đâu đó giữa các quy trình và luồng (ví dụ: các "quy trình" khác nhau có chung bảng mô tả tệp).

Về cơ bản, đối với mọi "loại" thông tin liên quan đến bối cảnh thực thi trong kernel, clone()sẽ cho bạn lựa chọn bí danh thông tin đó hoặc sao chép nó. Chủ đề tương ứng với răng cưa, quá trình tương ứng với sao chép. Bằng cách chỉ định các kết hợp trung gian của các cờ clone(), bạn có thể tạo ra những thứ kỳ lạ không phải là chủ đề hoặc quy trình. Bạn thường không nên làm điều này và tôi tưởng tượng đã có một số tranh luận trong quá trình phát triển nhân Linux về việc liệu nó có cho phép một cơ chế chung như vậy không clone().

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.