- Đọc/Ghi ngày, tháng, năm
b) Sử dụng cấu trúc Semaphore:
Semaphore là cấu trúc đƣợc Dijkstra đề xuất vào 1965, semaphore s là một biến cĩ các thuộc tính sau:
- Một giá trị nguyên dƣơng e
- Một hàng đợi f lƣu danh sách các tiến trình đang chờ trên semaphore s - Cĩ hai thao tác đƣợc định nghĩa trên semaphore s:
Down(s): e=e-1. Nếu e < 0 thì tiến trình phải chờ trong f, ngƣợc lại tiến trình tiếp tục. Up(s): e=e+1. Nếu e<=0 thì chọn một tiến trình trong f cho tiếp tục thực hiện (đánh thức). Gọi P là tiến trình thực hiện thao tác Down(s) hay Up(s):
Down(s)
{ e = e - 1; if (e < 0)
{ status(P)= blocked; //chuyển P sang trạng thái bị khố (chờ) enter(P,f); //cho P vào hàng đợi f
} } Up(s) { e = e + 1; if (e<= 0 ) {
exit(Q,f); //lấy một tt Q ra khỏi hàng đợi f theo một thuật tốn nào đĩ (FIFO,…) status (Q) = ready; //chuyển Q sang trạng thái sẵn sàng
enter(Q,ready-list); //đƣa Q vào danh sách sẵn sàng của hệ thống }
Nhận xét:
- Hệ điều hành cần cài đặt các thao tác Down, Up là độc quyền. Để cài đặt sự độc quyền cĩ thể dùng kỹ thuật cấm ngắt (1 cpu) hoặc các giải pháp phần mềm, hoặc lệnh TSL (nhiều cpu). Nếu dùng giải pháp phần mềm thì giải pháp semaphore vẫn là giải pháp "busy and waiting" nhƣng tách vịng lặp chờ ra khỏi chƣơng trình.
- Hàng đợi cĩ thể cài đặt là một con trỏ trỏ tới danh sách các khối PCB của các tiến trình đang chờ trên s, khi đĩ semaphore cĩ dạng:
class semaphore {
int e;
PCB * f; //ds riêng của semaphore public:
down(); up(); };
|e| = số tiến trình đang chờ trên f.
Cĩ thể dùng semaphore để giải quyết bài tốn miền găng hay đồng bộ các tiến trình.
* Giải quyết bài tốn miền găng bằng Semaphores:
Dùng một semaphore s, e đƣợc khởi gán là 1. Tất cả các tiến trình áp dụng cùng cấu trúc chƣơng trình sau:
semaphore s=1; //nghĩa là e của s=1 while (1) { Down(s); critical-section (); Up(s); Noncritical-section (); }
* Giải quyết bài tốn đồng bộ bằng Semaphores:
Ví dụ cĩ hai tiến trình đồng hành P1 và P2, P1 thực hiện cơng việc 1, P2 thực hiện cơng việc 2. Giả sử muốn cv1 làm trƣớc rồi mới làm cv2, ta cĩ thể cho hai tiến trình dùng chung một semaphore s, khởi gán e(s)= 0:
semaphore s=0; //dùng chung cho hai tiến trình P1:
{
job1();
Up(s); //đánh thức P2 }
P2: {
Down(s); // chờ P1 đánh thức job2();
}
Nhận xét: Nhờ lệnh down, up là độc quyền, semaphore đã giải quyết đƣợc vấn đề tín hiệu "đánh thức" bị thất lạc. Tuy nhiên sử dụng semaphore cũng khơng đơn giản, chƣơng trình dễ bị lỗi mà khơng dự đốn đƣợc. Ta xét một số tình huống gây ra lỗi sau:
- Nếu đặt Down và Up sai vị trí hoặc thiếu thì cĩ thể bị sai. Ví dụ nếu P1 để Up() lên trƣớc lệnh job1() thì nếu P1 thực hiện trƣớc P2, cĩ thể job1 chƣa thực hiện xong mà job2 đƣợc thực hiện.
Xét ví dụ khác e(s)=1;
while (1) {
Down(s); critical-section (); Noncritical-section (); }
Tiến trình quên gọi Up(s), và kết quả là khi ra khỏi miền găng nĩ sẽ khơng cho tiến trình khác vào miền găng!
- Sử dụng semaphore cĩ thể gây ra tình trạng tắc nghẽn. Ví dụ cĩ hai tiến trình P1, P2 sử dụng chung hai semaphore s1=s2=1
P1: { down(s1); down(s2); …. up(s1); up(s2); } P2: { down(s2); down(s1); …. up(s2); up(s1); }
Nếu thứ tự thực hiện nhƣ sau: P1: down(s1), P2: down(s2) , P1: down(s2), P2: down(s1) khi đĩ s1=s2=-1 nên P1,P2 đều chờ mãi
- Sử dụng semaphore cĩ thể gây ra tình trạng đĩi CPU khi giải thuật chọn tiến trình đang đợi là giải thuật LIFO.
Semaphore xây dựng nhƣ trên gọi là semaphore đếm (counting semaphore) giá trị e khơng giới hạn. Semaphore nhị phân (binary semaphore) cĩ e=0,1 dễ cài đặt hơn vì đƣợc sự hỗ trợ của phần cứng. Semaphore đếm cĩ thể cài đặt bằng semaphore nhị phân nhƣ sau:
//các biến dùng chung
binary semaphore s1=1, s2=0;
int c=giá trị ban đầu e của semaphore đếm; down() //down of counting semaphore {
down(s1); //down of binary semaphore c--;
if (c<0) down(s2); //down of binary semaphore up(s1); //up of binary semaphore
}
up() //up of counting semaphore {
down(s1); //down of binary semaphore c++;
if (c<=0) up(s2); //up of binary semaphore up(s1);
}
down(s1); up(s1); để đảm bảo độc quyền truy xuất miền găng .