Fork() là gì

Khi làm việc với Github, một cộng đồng chia sẻ source code lớn nhất thế giới hiện này, và vừa được mua bởi Microsoft với giá 7.5 tỷ USD. Các bạn sẽ bắt gặp khái niệm Fork. Hôm nay, vinasupport sẽ hướng dẫn các bạn về fork, khái niệm, các sử dụng fork trên Github.

Fork là gì?

Một fork là một bản copy của một repository (Kho chứa source code của bạn trên Github). Việc fork một repository cho phép bạn dễ dàng chỉnh sửa, thay đổi source code mà không ảnh hưởng tới source gốc. 

Một ví dụ về việc sử dụng fork, là khi bạn muốn fix bug source code trên repository của một ai đó, khi đó bạn cần thực hiện theo quy trình sau:

  1. Fork repository đó về tài khoản Github của mình
  2. Thực hiện fix bug
  3. Gửi một Pull Request tới repository gốc

Khi chủ sở hữu của repository nơi bạn fork, sẽ review chỉnh sửa của bạn, và tiến hành merge nội dung chỉnh sửa vào source gốc. 

Hướng dẫn fork repository trên Github

Để fork một repository, các bạn thực hiện theo các bước sau đây.

Bước 1. Truy cập vào repository cần fork. Ở đây ví dụ mình fork repository của Laravel, một PHP phổ biến nhất hiện nay.

https://github.com/laravel/laravel

Bước 2: Click vào button [ Fork ] ở góc phải màn hình

Fork() là gì

Quá trình fork sẽ được thực hiện.

Fork() là gì

Kết quả bạn đã fork thành công repository về tài khoản của mình.

Fork() là gì

Hướng dẫn hủy fork một repository trên Github

Để hủy một repository, các bạn phải xóa repository từ tài khoản của mình. Các bạn thực hiện theo các bước sau đây.

Hệ thống fork fork () được sử dụng để tạo các tiến trình. Nó không có đối số và trả về một ID quá trình. Mục đích của fork () là tạo ra một quy trình mới, trở thành quy trình con của người gọi. Sau khi một tiến trình con mới được tạo, cả hai tiến trình sẽ thực hiện lệnh tiếp theo sau lệnh gọi hệ thống fork (). Do đó, chúng ta phải phân biệt cha mẹ với đứa trẻ. Điều này có thể được thực hiện bằng cách kiểm tra giá trị trả về của fork ():

Nếu fork () trả về giá trị âm, việc tạo một tiến trình con không thành công. fork () trả về 0 cho tiến trình con mới được tạo. fork () trả về giá trị dương, ID tiến trình của tiến trình con, cho cha mẹ. ID tiến trình được trả về là loại pid_t được xác định trong sys / type.h. Thông thường, ID tiến trình là một số nguyên. Hơn nữa, một quy trình có thể sử dụng hàm getpid () để lấy ID tiến trình được gán cho quy trình này. Do đó, sau khi hệ thống gọi tới fork (), một thử nghiệm đơn giản có thể cho biết quá trình nào là con. Xin lưu ý rằng Unix sẽ tạo một bản sao chính xác của không gian địa chỉ của cha mẹ và đưa nó cho đứa trẻ. Do đó, các quá trình cha và con có không gian địa chỉ riêng biệt.

Hãy để chúng tôi hiểu nó với một ví dụ để làm rõ các điểm trên. Ví dụ này không phân biệt các quá trình cha và con.

#include  
#include  
#include  

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

Giả sử chương trình trên thực hiện đến điểm của lệnh gọi tới fork ().

Nếu lệnh gọi fork () được thực hiện thành công, Unix sẽ tạo hai bản sao không gian địa chỉ giống hệt nhau, một cho cha mẹ và một cho con. Cả hai quá trình sẽ bắt đầu thực hiện tại câu lệnh tiếp theo sau lệnh gọi fork (). Trong trường hợp này, cả hai quá trình sẽ bắt đầu thực hiện tại nhiệm vụ

pid = .....;

Cả hai quá trình bắt đầu thực hiện ngay sau khi hệ thống gọi fork (). Vì cả hai quá trình có không gian địa chỉ giống hệt nhau nhưng riêng biệt, các biến đó được khởi tạo trước lệnh gọi fork () có cùng giá trị trong cả hai không gian địa chỉ. Vì mỗi quy trình có không gian địa chỉ riêng, mọi sửa đổi sẽ độc lập với các quy trình khác. Nói cách khác, nếu cha mẹ thay đổi giá trị của biến, việc sửa đổi sẽ chỉ ảnh hưởng đến biến trong không gian địa chỉ của tiến trình cha. Các không gian địa chỉ khác được tạo bởi các lệnh gọi fork () sẽ không bị ảnh hưởng mặc dù chúng có tên biến giống hệt nhau.

Lý do của việc sử dụng write chứ không phải printf là gì? Đó là bởi vì printf () là "bộ đệm", nghĩa là printf () sẽ nhóm đầu ra của một quá trình lại với nhau. Trong khi đệm đầu ra cho tiến trình cha, đứa trẻ cũng có thể sử dụng printf để in ra một số thông tin, cũng sẽ được đệm. Do đó, do đầu ra sẽ không được gửi đến màn hình ngay lập tức, bạn có thể không nhận được thứ tự đúng của kết quả mong đợi. Tồi tệ hơn, đầu ra từ hai quá trình có thể được trộn lẫn theo những cách lạ. Để khắc phục vấn đề này, bạn có thể cân nhắc sử dụng cách viết "không có bộ đệm".

Nếu bạn chạy chương trình này, bạn có thể thấy phần sau trên màn hình:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

ID tiến trình 3456 có thể là quy trình được gán cho cha mẹ hoặc con. Do thực tế là các quy trình này được chạy đồng thời, các dòng đầu ra của chúng được trộn lẫn theo một cách khá khó đoán. Hơn nữa, thứ tự của các dòng này được xác định bởi bộ lập lịch CPU. Do đó, nếu bạn chạy lại chương trình này, bạn có thể nhận được một kết quả hoàn toàn khác.