Khoa Công Nghệ Thông Tin & Truyền Thông
Đại học Cần Thơ
Giảng viên: Hà Duy An
1.
2.
3.
4.
5.
6.
Vấn đề miền tương trục
Giải pháp phần mềm
Giải pháp phần cứng
Semaphores
Các bài toán đồng bộ hóa cổ điển
Monitors
10/29/2013
2
Chương 5: Đồng bộ hóa
• Các tiến trình được thực thi đồng thời
o Có thể bị ngắt tại bất cứ vị trí nào
• Các tiến trình đồng thời truy cập lên dữ liệu chia sẻ → tình
trạng không nhất quán dữ liệu (inconsistency).
• Việc duy trì sự nhất quán dữ liệu yêu cầu các cơ chế để đảm
bảo sự thực thi một cách có thứ tự của các tiến trình có hợp
tác với nhau.
• Xét trường hợp: Bài toán Người sản xuất – Người tiêu thụ
(Producer – Consumer Problem) với vùng đệm có kích thước
giới hạn (bounded-buffer).
10/29/2013
4
Chương 5: Đồng bộ hóa
• Dữ liệu chia sẻ:
#define BUFFER_SIZE 10
typedef struct {
. . .
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int counter = 0;
10/29/2013
5
Chương 5: Đồng bộ hóa
while (true) {
/* produce an item in next produced */
while (counter == BUFFER_SIZE) ;
/* do nothing */
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
counter++;
}
10/29/2013
6
Chương 5: Đồng bộ hóa
while (true) {
while (counter == 0)
; /* do nothing */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
counter--;
/* consume the item in next consumed */
}
10/29/2013
7
Chương 5: Đồng bộ hóa
•
counter++ có thể được cài đặt:
register1 = counter
register1 = register1 + 1
counter = register1
•
Counter-- có thể được cài đặt:
register2 = counter
register2 = register2 - 1
counter = register2
•
Xét sự thực thi đan xen nhau với “count = 5” là giá trị khởi tạo:
S0: producer execute register1 = counter
{register1 = 5}
S1: producer execute register1 = register1 + 1 {register1 = 6}
S2: consumer execute register2 = counter
{register2 = 5}
S3: consumer execute register2 = register2 – 1 {register2 = 4}
S4: producer execute counter = register1
{counter = 6 }
S5: consumer execute counter = register2
{counter = 4}
10/29/2013
8
Chương 5: Đồng bộ hóa
• Nếu cả hai producer và consumer cố gắng cập nhật vùng đệm
đồng thời, các phát biểu assembly có thể bị phủ lấp.
• Sự phủ lấp phụ thuộc vào cách producer và consumer được
định thời.
• Tình trạng đua tranh (Race Condition): là tình trạng mà vài
tiến trình cùng truy cập và thay đổi lên dữ liệu được chia sẻ và
giá trị cuối cùng của dữ liệu chia sẻ phụ thuộc vào tiến trình
nào hoàn thành cuối cùng.
Để ngăn chặn tình trạng đua tranh, các tiến trình cạnh tranh
phải được đồng bộ hóa.
10/29/2013
9
Chương 5: Đồng bộ hóa
• Xét hệ thống có n tiến trình {p0, p1, … pn-1}
• Mỗi tiến trình có một đoạn mã lệnh được gọi là miền tương
trục (critical section):
o Tiến trình có thể cập nhật các dữ liệu dùng chung
o Khi một tiến trình đang trong miền tương trục, thì không tiến
trình nào khác được phép thực thi trong miền tương trục của
chúng
=> Vấn đề miền tương trục (critical-section problem): thiết kế
giao thức giải quyết vấn đề này
10/29/2013
10
Chương 5: Đồng bộ hóa
• Cấu trúc tổng quát của tiến trình Pi là:
• Mỗi tiến trình phải kiểm
tra sự hợp lệ trong phần
entry section để đi vào
miền tương trục, tiếp theo
sau khi vào miền tương
trục tiến trình sẽ thực hiện
thao tác thoát khỏi miền
tương trục exit section, và
tiếp tục thực thi phần còn
lại remainder section
10/29/2013
11
Chương 5: Đồng bộ hóa
Giải pháp cho vấn đề miền tương trục phải thỏa các yêu cầu:
1. Loại trừ hỗ tương (Mutual Exclusion). Nếu tiến trình Pi đang
thực thi trong miền tương trục, thì không có tiến trình nào khác có
thể thực thi trong miền tương trục của chúng.
2. Tiến triển (Progress). Nếu không có tiến trình nào đang thực thi
trong miền tương trục và tồn tại vài tiến trình muốn được thực thi
trong miền tương trục của chúng, thì việc lựa chọn một tiến trình
được vào miền tương trục của nó không thể bị trì hoãn mãi được.
3. Chờ đợi hữu hạn (Bounded Wait). Không có tiến trình nào phải
chờ đợi vĩnh viễn để có thể đi vào miền tương trục của nó.
• Hai hướng tiếp cận giải quyết vấn đề phụ thuộc vào nhân HĐH:
o Non-preemptive kernel: không cho phép tiến trình bị trưng dụng khi
nó đang chạy ở chế độ nhân => vấn đề được giải quyết cho các cấu trúc
dữ liệu trong nhân
o Preemptive kernel: cho phép các tiến trình bị trưng dụng khi đang
chạy ở chế độ nhân
10/29/2013
12
Chương 5: Đồng bộ hóa
• Các biến chung:
o boolean lock;
khởi đầu lock = false;
• Tiến trình Pi:
do {
while (lock) ;
lock = true;
critical section
lock = false;
remainder section
} while (1);
=> Không giải quyết được vấn đề
10/29/2013
15
Chương 5: Đồng bộ hóa
• Các biến chung:
o int turn;
khởi đầu turn = 0
o turn = i ⇒Pi có thể bước vào miền tương trục của nó
•
Tiến trình Pi
do {
while (turn != i) ;
critical section
turn = j;
remainder section
} while (1);
=> Không thõa điều kiện 2
10/29/2013
16
Chương 5: Đồng bộ hóa
• Các biến chia sẻ:
o boolean flag[2];
khởi đầu flag[0] = flag[1] = false.
o flag[i] = true ⇒Pi sẵn sàng bước vào miền tương trục của nó
• Tiến trình P1:
do {
flag[i] := true;
while (flag[j]) ;
critical section
flag [i] = false;
remainder section
} while (1);
=> Không thỏa mãn điều kiện 2
10/29/2013
17
Chương 5: Đồng bộ hóa
• Giải quyết được vấn đề miền tương trục (thỏa mãn cả 3 điều
kiện) cho 2 tiến trình
• Giả sử các lệnh load và store là nguyên tử (không thể bị
ngắt)
• Hai tiến trình chia sẽ hai biến:
o int turn;
o Boolean flag[2]
• turn – chỉ định tiến trình nào được phép vào miền tương trục
• flag – cho biết tiến trình nào đã sẵn sàng vào miền tương
trục
o flag [i] = true ⇒Pi sẵn sàng bước vào miền tương trục của nó
10/29/2013
18
Chương 5: Đồng bộ hóa
do {
flag[i] = true;
turn = j;
while (flag[j] && turn == j);
critical section
flag[i] = false;
remainder section
} while (true);
•
Nhận xét:
1. Loại trừ hỗ tương được đảm bảo
2. Yêu cầu tiến triển được thỏa mãn
3. Yêu cầu chờ đợi hữu hạn được đáp ứng
10/29/2013
19
Chương 5: Đồng bộ hóa
•
•
•
•
•
Nhiều hệ thống cung cấp phần cứng hỗ trợ đồng bộ hóa
Tất cả các giải pháp này dựa trên ý tưởng bảo vệ miền tương trục bằng
"khóa"
Vô hiệu hóa các ngắt: khi một tiến trình bắt đầu thực thi trong miền tương
trục thì các ngắt bị vô hiệu hóa đến khi tiến trình thoát khỏi miền tương
trục
o Cho phép tiến trình người dùng có khả năng vô hiệu các ngắt => có thể tác
động xấu đến sự vận hành hệ thống
o Không giải quyết được vấn đề trên hệ thống đa xử lý
Các hệ thống máy tính hiện đại cung cấp các thao tác nguyên tử (atomic
hardware instructions):
o test_and_set
o compare_and_swap
Nếu các lệnh có tính chất nguyên tử thực thi cùng lúc thì thứ tự thực hiện
của chúng luôn được đảm bảo – một lệnh được hoàn thành trước khi lệnh
khác được thực thi
o Atomic = non-interruptible
10/29/2013
21
Chương 5: Đồng bộ hóa
• Lệnh test_and_set đọc và sửa đổi nội dung của một word
một cách nguyên tử:
boolean test_and_set(boolean *target) {
boolean rv = *target;
*target = true;
return rv;
}
10/29/2013
22
Chương 5: Đồng bộ hóa
• Dữ liệu chia sẻ:
boolean lock = false;
• Tiến trình Pi:
do {
while (Test_And_Set(lock)) ; /* do nothing
*/
/* critical section */
lock = false;
/* remainder section */
} while(1);
=>không thỏa mãn yêu cầu chờ đợi hữu hạn
10/29/2013
23
Chương 5: Đồng bộ hóa
• Tự động hoán chuyển (swap) hai biến:
int compare_and_swap(int *value,int expected,int new value){
int temp = *value;
if (*value == expected)
*value = new value;
return temp;
}
10/29/2013
24
Chương 5: Đồng bộ hóa
• Dữ liệu chia sẻ (khởi tạo là false):
boolean lock;
• Tiến trình Pi
do {
while (compare_and_swap(&lock, 0, 1) != 0) ;
/* do nothing */
/* critical section */
lock = 0;
/* remainder section */
} while (true);
=>không thỏa mãn yêu cầu chờ đợi hữu hạn
10/29/2013
25
Chương 5: Đồng bộ hóa
•
•
Dữ liệu chia sẻ (khởi tạo false): boolean lock;
boolean waiting[n];
Tiến trình Pi:
do {
waiting[i] = true;
key = true;
while (waiting[i] && key)
key = test_and_set(&lock);
waiting[i] = false;
/* critical section */
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;
/* remainder section */
} while (true);
=> Thỏa tất cả các yêu cầu cho vấn đề miền tương trục
10/29/2013
26
Chương 5: Đồng bộ hóa