fork() tạo ra một bản sao của process hiện tại. Process con có không gian địa chỉ riêng biệt, tách biệt hoàn toàn với process cha. Sau khi gọi fork(), cả cha và con đều tiếp tục thực thi từ vị trí sau lệnh fork().
- fork: tạo một process mới (con).
- execlp: thay thế nội dung process con bằng chương trình khác (ở đây là
ls -l). - wait/waitpid: là một blocking call, process cha chờ process con kết thúc
Ưu điểm:
- Cách ly an toàn: Nếu process con crash (segfault, memory corruption), process cha không bị ảnh hưởng. (sandbox).
- Khi cần chạy chương trình khác bằng
exec()saufork() - Khi muốn tận dụng đa tiến trình thay vì chia sẻ bộ nhớ.
Nhược điểm
- Chi phí cao: Tạo process nặng hơn tạo thread (cần copy bảng trang bộ nhớ, quản lý PID)
- Khó chia sẻ dữ liệu: Vì không gian bộ nhớ tách biệt, muốn trao đổi dữ liệu phải dùng IPC (pipe, socket, shared memory).
- Quản lý phức tạp: Cần xử lý zombie process bằng
wait()hoặcwaitpid().
Ví dụ
Do fork tạo song song, thứ tự hiển thị có thể hơi khác nhau (ví dụ, dòng "Process con đang chạy..." có thể xuất hiện trước hoặc sau "Process cha, chờ con hoàn thành..." tùy vào lịch trình CPU).
Trong process con nên có “return;” nếu không có sau khi process con thực hiện xong tác vụ , nó sẽ tiếp tục chạy xuống phần code của process cha. Điều này gây ra lỗi logic vì process con sẽ gọi wait() vốn chỉ dành cho process cha.
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cerrno>
#include <cstring>
void mylog(const std::string& message)
{
std::cout << "[" << getpid() << "] " << message << std::endl;
}
int main() {
pid_t pid = fork(); // tạo process con
if (pid < 0)
{
mylog("ERROR!!! fork() thất bại vì " + std::string(strerror(errno)));
return 1;
}
if (pid == 0)
{
// Đây là process con
mylog("Process con đang chạy...process cha là:" + std::to_string(getppid()));
execlp("/bin/ls", "ls", "-l", NULL); // thay nội dung process con bằng chương trình khác (vd: ls -l)
mylog("execlp thất bại vì " + std::string(strerror(errno)));
return 1; // kết thúc process con với mã lỗi
}
else
{
// Đây là process cha
mylog("Process cha, chờ con hoàn thành...");
wait(NULL); // chờ process con kết thúc
mylog("Process con đã kết thúc.");
}
return 0;
}
[12345] Process cha, chờ con hoàn thành...
[12346] Process con đang chạy... process cha là: 12345
(total 12)
-rw-r--r-- 1 user user 1234 Sep 6 15:00 file1.txt
...
[12345] Process con đã kết thúc.Ví dụ: tạo 2 processes để xử lý nhiều tác vụ song song
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cerrno>
#include <cstring>
void mylog(const std::string& msg) {
std::cout << "[" << getpid() << "] " << msg << std::endl;
}
void renderImage() {
mylog("renderImage");
sleep(3);
mylog("renderImage done");
}
void renderVideo() {
mylog("renderVideo");
sleep(5);
mylog("renderVideo done");
}
void renderFinal() {
mylog("completed");
}
int main() {
const int NUM_PROCESS = 2;
pid_t pids[2];
// spawn 2 processes
mylog("spawning 2 processes");
for (int i = 0; i < NUM_PROCESS; i++) {
pid_t pid = fork();
if (pid < 0) {
mylog("ERROR !!! fork() thất bại vì " + std::string(strerror(errno)));
return 1;
}
if (pid == 0)
{
// Đây là server process
switch (i) {
case 0: renderImage(); break; // process 1 chạy renderImage()
case 1: renderVideo(); break; // process 2 chạy renderVideo()
default: mylog("Undefined!"); break;
}
return 0; // kết thúc server
}
else
{
// Đây là parent process
mylog("spanwed process " + getpid());
// lưu PID của process con
pids[i] = pid;
}
}
// chờ cả 2 processes kết thúc
for (int i = 0; i < NUM_PROCESS; i++) {
waitpid(pids[i], nullptr, 0);
mylog("process " + std::to_string(pids[i]) + " đã kết thúc");
}
// sau khi cả 2 server xong, broker renderFinal
renderFinal();
return 0;
}
[12345] spawning 2 processes
[12345] spanwed process 12345
[12345] spanwed process 12345
[12346] renderImage
[12347] renderVideo
[12346] renderImage done
[12345] process 12346 đã kết thúc
[12347] renderVideo done
[12345] process 12347 đã kết thúc
[12345] completedChú ý
wait() thứ tự không cố định, process nào chạy nhanh hơn thì sẽ nhận PID của process đó
waitpid() chờ lần lượt process 1, rồi process 2, theo đúng thứ tự trong mảng pids.
wait(NULL) nghĩa là process cha không cần lấy chi tiết trạng thái (status).
// wait() thứ tự không cố định, process nào chạy nhanh hơn thì sẽ nhận PID của process đó
for (int i = 0; i < NUM_PROCESS; i++)
{
pid_t l_pid = wait(NULL);
cout << "process " << std::to_string(l_pid) << " đã kết thúc" << endl;
}
// lấy trạng thái
for (int i = 0; i < NUM_PROCESS; i++) {
int status;
pid_t l_pid = wait(&status);
cout << "process " << std::to_string(finished) << " đã kết thúc với status " << std::to_string(status);
}// waitpid() chờ lần lượt process 1, rồi process 2, theo đúng thứ tự trong mảng pids.
for (int i = 0; i < NUM_PROCESS; i++)
{
waitpid(pids[i], nullptr, 0);
cout << "process " << std::to_string(pids[i]) << " đã kết thúc" << endl;
}
// lấy trạng thái
for (int i = 0; i < NUM_PROCESS; i++) {
int status;
waitpid(pids[i], &status, 0);
cout << "process " << std::to_string(pids[i]) << " đã kết thúc với status " << std::to_string(status));
}