Hướng dẫn dùng waitpid trong PHP
1. Giới thiệuMột trong các triết lý về lập trình đó là "do one thing and do it well", làm một việc và làm tốt điều đó. Quản lý tiến trình về cơ bản được thực hiện bởi một vài system call, mỗi lệnh có một mục đích (đơn giản). Các lệnh này sau đó có thể được kết hợp để thực hiện các hành vi phức tạp hơn. Show Nội dung chính
Nội dung chính
Những system call sau đây được sử dụng để tạo lập, kết thúc, quản lý tiến trình cơ bản:
2. Quản lý tiến trình conTrong nhiều ứng dụng, một tiến trình cha cần biết được khi nào tiến trình con của nó thay đổi trạng thái (state) để giám sát và đưa ra quyết định thực hiện các hành vi tiếp theo. Điều này có thể thực hiện được thông qua việc sử dụng system call wait() và waitpid() . 2.1. System call wait()System call wait() được sử dụng thể theo dõi trạng thái kết thúc của một trong các tiến trình con mà tiến trình cha tạo ra. Prototype của wait() như sau:
2.2. System call waitpid()System call wait() tồn tại một số hạn chế:
waitpid() được sinh ra để giải quyết các vấn đề này. Prototype của waitpid() như sau:
Về cơ bản, hoạt động của waitpid() cũng giống như wait(). Ngoài ra, chúng ta có thể sử dụng một số macro dưới đây cùng với giá trị "status" nhận từ wait() hoặc waitpid() để xác định cách mà tiến trình con kết thúc.
2.3. Ví dụ 1Ví dụ minh họa về cách sử dụng system call wait() .
Biên dịch và chạy chương trình trên ta được kết quả như sau: Đoạn mã xử lý tiến trình con sẽ in ra PID của nó và sleep 3 giây, trong thời gian này tiến trình cha đang block tại wait(). Sau 3 giây, tiến trình cha in ra PID của tiến trình con thông qua giá trị trả về của wait(). Kết quả sau cùng cho thấy rằng tiến trình con đang kết thúc bình thường (normally termination) .Thay đổi một chút ở trong tiến trình con:
Biên dịch và chạy lại chương trình: Lúc này tiến trình con đang bị block trong while(1). Sau đó chúng ta sẽ kill tiến trình con bằng signal thông qua lệnh "kill - 9 2467" với 9 là SIGKILL và 24767 là PID của tiến trình con. Kết quả trên cho thấy, lúc này tiến trình con đang bị kết thúc một cách bất thường (abnormal termination) bởi SIGKILL.3. Orphane và ZombieVòng đời của các tiến trình cha - con thường không giống nhau. Tiến trình cha sống lâu hơn tiến trình con và ngược lại. Điều này đặt ra hai câu hỏi:
Để trả lời cho hai câu hỏi này, chúng ta tiến hành tìm hiểu hai khái niệm mới về tiến trình dưới đây. 3.1. Tiến trình OrphaneNếu tiến trình cha kết thúc trong khi một hoặc nhiều tiến trình con của nó vẫn đang chạy, khi đó các tiến trình con đó sẽ trở thành các tiến trình mồ côi (orphane). Tiến trình mồ côi sẽ được chấp nhận bởi tiến trình init (có PID 1), và tiến trình init sẽ hoàn thành công việc thu thập trạng thái cho chúng. Mặc dù về mặt kỹ thuật, tiến trình con được init nhận làm "con nuôi" nhưng nó vẫn được gọi là tiến trình mồ côi vì tiến trình cha ban đầu tạo ra nó không còn tồn tại nữa. 3.2. Tiến trình ZombieỞ góc độ của hệ điều hành, mặc dù tiến trình con đã kết thúc công việc của mình, tiến trình cha mẹ vẫn nên được phép gọi wait() để lấy trạng thái kết thúc của tiến trình con, tại thời điểm nào đó sau khi tiến trình con chấm dứt. Tuy nhiên, nếu tiến trình con kết thúc và thu hổi toàn bộ tài nguyên được cấp phát thì tiến trình cha không thể biết con của nó kết thúc như thế nào. Kernel giải quyết vấn đề trên bằng cách biến tiến trình con thành tiến trình thây ma (zoombie process), điều này có nghĩa là hầu hết các tài nguyên do tiến trình con nắm giữ sẽ bị thu hồi và sửa dụng cấp phát cho các tiến trình khác. Tuy nhiên, một vài thông tin cơ bản vẫn được giữ lại như PID và trạng thái kết thúc, các thông tin này sẽ được lấy bởi tiến trình cha rối chúng sẽ bị xóa khỏi hệ thống ngay sau đó thông qua việc sử dụng wait(). Đoạn mã dưới đây mô phỏng việc tạo ra tiến trình zoombie:
Biên dịch và chạy chương trình: Tiến trình con kết thúc ngay lập tức và có PID là 8647, tiến trình cha bị block tại while(1) trước thời điểm wait() được gọi. Sử dụng command "ps aux | grep exam" ta thu được kết quả.
Được lấy cảm hứng từ những bộ phim về zombie, tiến trình thây ma vẫn tồn tại nhưng không thể bị giết (kill) bởi signal (SIGKILL). Lúc này, nếu muốn kết thúc tiến trình zombie ta có thể kill trực tiếp tiến trình cha của nó. Khi đó tiến trình init sẽ nhận tiến trình zombie làm con nuôi và tự động gọi wait(), loại bỏ zombie ra khỏi hệ thống.3.3. Ngăn chặn tạo ra tiến trình zombieCó một bảng process ID (PID) cho mỗi hệ thống. Kích thước của bảng này là hữu hạn. Nếu quá nhiều tiến trình zombie được tạo, thì bảng này sẽ đầy. Tức là hệ thống sẽ không thể tạo ra bất kỳ tiến trình mới nào, khi đó hệ thống sẽ đi đến trạng thái ngưng hoạt động. Do đó, chúng ta cần ngăn chặn việc tạo ra các quy trình zombie. 3.3.1 Sử dụng wait()Thực hiện gọi wait() ở tiến trình cha. Tuy nhiên, wait() sẽ block tiến trình cha cho tới khi nào có một tiến trình con của nó kết thúc, điều đó đồng nghĩa với tiến trình cha có thể phải đợi rất lâu nếu tiến trình con không kết thúc sớm.
3.3.1 Sử dụng SIGCHILDKhi tiến trình con kết thúc, một tín hiệu SIGCHILD sẽ được gửi tới tiến trình cha của nó. Áp dụng nguyên lý này ta sẽ giải quyết được hạn chế của wait().
Kết quả sau khi chương trình được chạy như sau: Một khi tiến trình cha nhận được SIGCHILD, hàm xử lý func (handler) tương ứng được đăng kí bởi signal(SIGCHLD, func) sẽ được kích hoạt và hoạt động độc lập với tiến trình cha. Do đó tiến trình cha sẽ "rảnh rỗi" để làm những việc khác mà vẫn ngăn chặn việc tạo thành tiến trình zombie.4. Kết luậnKết thúc bài viết này, chúng ta cần nắm được cách sử dụng systemcall wait() và waitpid(). Tiến trình orphane, zombie và cách ngăn ngừa tạo ra chúng. |