Bài 40.228 phút đọc
fork và Copy-on-Write
Sau bài này bạn sẽ làm được
- Giải thích fork trả về gì ở cha và ở con
- Giải thích Copy-on-Write làm fork nhanh thế nào
- Tránh bẫy bộ đệm stdio bị nhân đôi
- Không bao giờ viết fork bomb
fork là lời gọi hệ thống kỳ lạ nhất trong C: bạn gọi nó một lần, và nó trả về hai lần, ở hai tiến trình khác nhau. Hiểu điều đó là chìa khoá của mọi lập trình đa tiến trình.
#Một lời gọi, ba giá trị
fork.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
printf("Truoc fork: PID=%d\n", getpid());
pid_t pid = fork(); /* MOT loi goi, HAI tien trinh tra ve */
if (pid < 0) {
perror("fork");
return 1;
} else if (pid == 0) {
/* TIEN TRINH CON: fork tra ve 0 */
printf("CON : PID=%d, cha=%d\n", getpid(), getppid());
_exit(0); /* dung _exit trong con, xem Bai 40.1 */
} else {
/* TIEN TRINH CHA: fork tra ve PID cua con */
printf("CHA : PID=%d, con=%d\n", getpid(), pid);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Con thoat voi ma %d\n", WEXITSTATUS(status));
}
return 0;
}terminal
gcc fork.c -o fork && ./fork
Truoc fork: PID=25104 CHA : PID=25104, con=25105 CON : PID=25105, cha=25104 Con thoat voi ma 0
| fork trả về | Ở đâu | Nghĩa |
|---|---|---|
| < 0 | Tiến trình gọi | Lỗi: hết bộ nhớ hoặc quá giới hạn tiến trình |
| == 0 | Tiến trình CON | Đây là con |
| > 0 | Tiến trình CHA | Giá trị là PID của con |
#Copy-on-Write
Copy-on-Write (COW)
Kỹ thuật làm
fork nhanh. Nhân không sao chép bộ nhớ ngay. Nó đánh dấu mọi trang là chỉ đọc và cho hai tiến trình dùng chung. Chỉ khi một bên ghi vào một trang, nhân mới nhân bản đúng trang đó./* Neu fork phai chep TOAN BO bo nho cua tien trinh (co the hang
GB), no se rat cham. Copy-on-Write tranh viec do:
Ngay sau fork:
Cha va con CHIA SE moi trang bo nho, danh dau CHI DOC.
fork gan nhu tuc thoi, chi chep bang trang.
Khi mot ben GHI vao mot trang:
Nhan phat hien ghi vao trang chi doc, NHAN BAN dung trang do,
va cho ben ghi ban rieng. Chi trang bi ghi moi duoc chep.
He qua:
- fork nhanh, gan nhu khong ton bo nho luc dau
- neu con exec ngay (thay chuong trinh), gan nhu khong trang
nao bi chep -> cuc ky hieu qua
Do la ly do mau fork + exec (Bai 40.3) re, du fork nghe co ve
phai chep ca tien trinh. */terminal
# Do thoi gian fork 1000 lan (con exit ngay)
./do_fork
1000 lan fork + exit: 42 ms trung binh 42 micro giay moi fork (COW: khong chep bo nho, chi bang trang)
#Bẫy bộ đệm
#Fork bomb
Không kiểm tra fork
for (int i = 0; i < n; ++i) {
if (fork() == 0) {
lam_viec(i);
/* QUEN _exit! con chay tiep vong for, fork THEM */
}
}
/* Con khong thoat sau khi lam viec -> no cung fork tiep,
sinh so tien trinh bung no. */
Luôn kiểm tra và có điều kiện dừng
for (int i = 0; i < n; ++i) {
pid_t pid = fork();
if (pid < 0) { perror("fork"); break; } /* kiem tra loi */
if (pid == 0) {
lam_viec(i);
_exit(0); /* con THOAT, khong fork tiep */
}
/* chi CHA chay tiep vong for */
}
for (int i = 0; i < n; ++i) wait(NULL); /* cho moi con */
Tự làm thử
- Chạy
fork.cnhiều lần và quan sát thứ tự in của cha và con thay đổi. - Sửa một biến trong con và chứng minh cha không thấy thay đổi.
- Đo thời gian
forkmột nghìn lần và tính trung bình. - Tái hiện bẫy bộ đệm bằng
printfkhông xuống dòng rồi chuyển hướng ra tệp. - Tạo cây tiến trình ba tầng: ông, cha, con, mỗi tầng in PID và PPID.
- Đặt
ulimit -u 50trong một shell riêng và thử một vòng fork có kiểm soát tới giới hạn đó.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
forkgọi một lần, trả về hai lần: 0 ở con, PID của con ở cha, âm khi lỗi.- Sau
forkcả hai tiến trình chạy tiếp từ cùng một dòng, chỉ khác giá trị trả về; thứ tự chạy không đoán trước được. - Copy-on-Write làm
forknhanh: chia sẻ trang chỉ đọc, chỉ nhân bản trang khi có ghi. - Bẫy bộ đệm:
printfkhông\ntrướcforkbị in hai lần; xả bộ đệm hoặc dùng\n, và dùng_exittrong con. - Không bao giờ viết
while(1) fork(); luôn kiểm tra giá trị trả về và có điều kiện dừng.