You are on page 1of 17

ĐẠI HỌC QUỐC GIA TP.

HỒ CHÍ MINH
TRƯỜNG ĐẠI HỌC CÔNG NGHỆ THÔNG TIN
o

Tài liệu hướng dẫn thực hành:

HỆ ĐIỀU HÀNH

Tác giả: ThS. Phan Đình Duy


ThS. Nguyễn Thanh Thiện
KS. Trần Đại Dương
KS. Trần Hoàng Lộc

LƯU HÀNH NỘI BỘ


MỤC LỤC

BÀI 5. ĐỒNG BỘ HÓA TIẾN TRÌNH, TIỂU TRÌNH ................. 1

5.1 Mục tiêu.............................................................................................................1

5.2 Nội dung thực hành ..........................................................................................1

5.3 Sinh viên chuẩn bị ............................................................................................1

5.4 Hướng dẫn thực hành ....................................................................................12

5.5 Bài tập ôn tập ..................................................................................................13


NỘI QUY THỰC HÀNH

1. Sinh viên tham dự đầy đủ các buổi thực hành theo quy định
của giảng viên hướng dẫn (GVHD) (6 buổi với lớp thực
hành cách tuần hoặc 10 buổi với lớp thực hành liên tục).
2. Sinh viên phải chuẩn bị các nội dung trong phần “Sinh viên
viên chuẩn bị” trước khi đến lớp. GVHD sẽ kiểm tra bài
chuẩn bị của sinh viên trong 15 phút đầu của buổi học (nếu
không có bài chuẩn bị thì sinh viên bị tính vắng buổi thực
hành đó).
3. Sinh viên làm các bài tập ôn tập để được cộng điểm thực
hành, bài tập ôn tập sẽ được GVHD kiểm tra khi sinh viên
có yêu cầu trong buổi học liền sau bài thực hành đó. Điểm
cộng tối đa không quá 2 điểm cho mỗi bài thực hành.
Bài 5. ĐỒNG BỘ HÓA TIẾN TRÌNH, TIỂU

TRÌNH

5.1 Mục tiêu


Giới thiệu đến sinh viên 2 thư viện Semaphore và thư viện
Mutex dùng để thực hiện việc đồng bộ hóa tiến trình, tiểu
trình.

Sinh viên thực hiện và hiểu được tầm quan trọng của việc
đồng bộ hóa tiến trình, tiểu trình.

5.2 Nội dung thực hành


Viết chương trình áp dụng các kỹ thuật đồng bộ sử dụng
semaphore và mutex.

5.3 Sinh viên chuẩn bị

Để thực hiện bài thực hành này, sinh viên phải đảm bảo những
điều sau:

Đã cài đặt C compiler cho hệ điều hành Linux.

Biết cách viết, build và chạy một chương trình trên hệ


điều hành Linux.
5.3.1 Semaphore

Trong hệ điều hành, semaphore được biết đến là 1 biến được


sử dụng để điều khiển sự truy xuất vào các tài nguyên chung của
1
tiểu trình trong xử lý song song hoặc các môi trường đa người
dùng. Nói cách khác, khi có hai hay nhiều tiểu trình cùng muốn sử
dụng một tài nguyên nào đó, để đảm bảo sự tranh chấp được diễn
ra “công bằng”, người ta sử dụng semaphore để điều khiển xem
tiến trình nào được tiến vào vùng tranh chấp và sử dụng tài
nguyên, khi tiến trình đó thoát khỏi vùng tranh chấp thì các tiến
trình nào sẽ được vào tiếp theo.
Semaphore được xem như một danh sách các đơn vị còn trống
của một tài nguyên trong máy tính. Có 2 thao tác cơ bản trên
semaphore là yêu cầu tài nguyên và giải phóng tài nguyên. Nếu
cần thiết, semaphore còn có thể làm cờ để đợi cho đến khi tài
nguyên được một tiểu trình khác giải phóng.
5.3.1.1 Các hàm cơ bản khi sử dụng semaphore

Chức
Tên hàm Ghi chú Ví dụ
năng
Sử dụng #include Khai báo thêm thư viện gcc -o
thư viện <semaphore.h> pthread và rt khi biên dịch. filename
semapho filename.c -
-re lpthread -
lrt
Định sem_t sem_name; sem_t sem;
nghĩa 1
semapho
re có tên

2

sem_na
me
Khởi tạo int sem_init (sem_t *sem_name: con trỏ chỉ đến sem_t sem;
1 biến *sem_name, int địa chỉ của biến semaphore sem_init
semapho pshared, unsigned (được khai báo như trên). (&sem, 0,
-re int value); pshared: 10);
- Nếu được đặt là 0: biến
semaphore sẽ được chia sẻ
giữa các tiểu trình của cùng
1 tiến trình (và cần đặt ở nơi
mà tất cả các tiểu trình đều
có thể truy xuất được như
biến toàn cục hoặc biến
động).
- Nếu được đặt khác 0: biến
semaphore sẽ được chia sẻ
giữa những tiến trình với
nhau và cần được đặt ở vùng
nhớ được chia sẻ (shared
memory).
value: giá trị khởi tạo cho
semaphore là số không âm.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
Đợi 1 int sem_wait(sem_t - Nếu giá trị của semaphore sem_wait(

3
semapho *sem); = 0: tiến trình bị block cho &sem);
-re đến khi giá trị của
semaphore > 0 (để có thể trừ
đi 1). Lưu ý: giá trị của
semaphore không là số âm
(xem khai báo ở trên)
- Nếu giá trị của semaphore
> 0: giá trị của semaphore
trừ đi 1 và return, tiến trình
tiếp tục chạy.
Giá trị trả về:
- Là 0 nếu thành công.
- Là -1 nếu thất bại, giá trị
của semaphore không thay
đổi.
Mở int sem_post(sem_t Một trong các tiến trình/tiểu sem_post(
khóa 1 *sem); trình bị block bởi sem_wait &sem);
semapho sẽ được mở và sẵn sàng để
-re thực thi.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
Lấy giá int Lấy giá trị của semaphore và sem_getval
trị của 1 sem_getvalue(sem_t gán vào biến được xác định ue(&sem,
semapho *sem, int *valp); tại địa chỉ valp. &value);
-re Giá trị trả về: Biến value
- Là 0 nếu thành công lúc này có

4
- Là -1 nếu thất bại giá trị là
giá trị của
semaphore.
Hủy 1 int Hủy đi 1 biến semaphore. sem_destro
biến sem_destroy(sem_t Lưu ý: nếu đã quyết định y(&sem);
semapho *sem) hủy biến semaphore thì cần
-re chắc chắn là không còn tiến
trình/tiểu trình nào truy xuất
vào biến semaphore đó nữa.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
5.3.1.2. Ví dụ về semaphore
Ví dụ có 2 process được thực thi song song như sau:
PROCESS A PROCESS B
processA processB
{ {
while (true) while (true)
sells++; products++;
} }
Process A mô tả số lượng hàng bán được: sells
Process B mô tả số lượng sản phẩm được làm ra: products
Biết rằng ban đầu chúng ta chưa có hàng và cũng chưa bán
được gì: sells = products = 0

5
Do khả năng tạo ra hàng hóa và khả năng bán hàng là không
đồng đều, có lúc bán đắt thì sẽ sells tăng nhanh, lúc bán ế thì sells
tăng chậm lại. Lúc công nhân làm việc hiệu quả thì sẽ tạo ra
products nhanh, ngược lại lúc công nhân mệt thì sẽ làm ra
products chậm lại. Tuy nhiên, dù bán đắt hay ế, làm nhanh hay
chậm thì vẫn phải đảm bảo một điều là phải “có hàng thì mới bán
được”, nói cách khác ta phải đảm bảo: products >= sells.
Vậy yêu cầu đặt ra là sử dụng semaphore để đồng bộ 2 tiến
trình: A (bán hàng) và B (tạo ra hàng) theo điều kiện trên?
Phân tích bài toán trên ta thấy như sau:

PROCESS A muốn “bán hàng” thì phải kiểm tra xem liệu
có hàng để bán hay không?

PROCESS B khi “tạo ra hàng” xong sẽ thông báo là hàng


đã có để bán!
Từ các ý trên ta nhận thấy ta có thể sử dụng 1 semaphore làm
điều kiện để kiểm tra việc bán hàng của A và B như sau:
sem_t sem; // Định nghĩa biến sem
sem_init (&sem, 0, 0); // Biến sem có giá trị ban đầu pshared = 0
và value = 0
PROCESS A PROCESS B
processA processB
{ {

6
while (true){ while (true){
sem_wait(&sem); products++;
sells++; sem_post(&sem);
} }
} }
Với 2 PROCESS A và PROCESS B, ta có 2 trường hợp như
sau:
PROCESS A nhanh hơn PROCESS B nhanh hơn
PROCESS B (bán nhanh hơn PROCESS A (làm nhanh hơn
làm) bán)
Mỗi khi PROCESS A muốn Sau khi PROCESS B tăng biến
tăng biến sells (bán hàng), nó sẽ products (làm ra hàng mới), nó
gặp hàm sem_wait(&sem) sẽ gọi hàm sem_post(&sem) để
trước, hàm này sẽ kiểm tra xem tăng giá trị của sem lên 1, lúc
giá trị của sem liệu có lớn hơn 0 này PROCESS A nếu như đang
(có hàng không). bị block do hàm sem_wait
+ Nếu sem.value = 0: trước đó sẽ được mở ra và sẵn
PROCESS A bị block không sàng để “bán hàng”.
bán nữa. PROCESS B chạy được 1 đoạn
+ Nếu sem.value > 0: thời gian sẽ phải nhường lại cho
PROCESS A được phép tăng PROCESS A, lúc này
sells (được phép bán hàng) và PROCESS A sẽ trừ giá trị của
giảm sem.value đi 1. sem đi 1 thông qua hàm

7
PROCESS A sau khi chạy được sem_wait, rồi sau đó mới tăng
1 đoạn thời gian sẽ được dừng giá trị của sells.
và chuyển cho PROCESS B
chạy (do quy tắc lập lịch của hệ
điều hành), lúc này PROCESS
B sẽ tăng products (làm ra
hàng) đồng thời tăng giá trị của
sem và sau đó khi tới phiên của
PROCESS A, nó sẽ có thể tăng
giá trị của sells (bán hàng).

5.3.2 Mutex

Mutex là một trường hợp đơn giản của semaphore: 0 <=


sem.value <= 1
Thông thường, mutex được sử dụng như sau:

8
Các hàm cơ bản khi sử dụng Mutex
Để có thể sử dụng mutex, ta cần phải include thư viện
pthread.h.
Sau khi include thư viện trên, ta có thể sử dụng mutex thông
qua các hàm:
Chức
Tên hàm Ghi chú Ví dụ
năng
Khai pthread_mutex_t Thông thường mutex được pthread_mutex_t
báo 1 mutex_name khai như một biến toàn cục mutex;
mutex
có tên

mutex_
9
name

Khởi int *mutex: con trỏ chỉ đến địa pthread_mutex_t


tạo 1 pthread_mutex_i chỉ của mutex (được khai mutex;
mutex nit báo như trên). pthread_mutex_i
(pthread_mutex *attr: con trỏ chỉ đến địa chỉ nit(&mutex,
_t *mutex, const nơi mà chứa các thuộc tính NULL);
pthread_mutexat cần khởi tạo ban đầu cho
tr_t *attr); mutex. Nếu ở đây để là
NULL thì mutex sẽ được
khởi tạo với giá trị mặc định.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
Khóa 1 int Khóa mutex được tham pthread_mutex_l
mutex pthread_mutex_l chiếu bởi con trỏ *mutex lại. ock(&mutex)
ock(pthread_mu Nếu như mutex này đã bị
tex_t *mutex); khóa bởi 1 thread khác trước
đó thì thread đang gọi hàm
khóa sẽ bị khóa lại cho đến
khi mutex được mở ra.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
Mở int Mở khóa mutex được tham pthread_mutex_
khóa 1 pthread_mutex_ chiếu bởi con trỏ *mutex. unlock(&mutex)

10
mutex unlock(pthread_ Sau khi mở khóa, các thread
mutex_t *mutex) khác sẽ được quyền tranh
chấp quyền khóa mutex.
Giá trị trả về:
- Là 0 nếu thành công
- Là -1 nếu thất bại
Hủy 1 int Hủy mutex được tham chiếu pthread_mutex_
mutex pthread_mutex_ bởi con trỏ *mutex. destroy(&mutex)
destroy(pthread
_mutex_t
*mutex)

5.3.3 Câu hỏi chuẩn bị

Sinh viên chuẩn bị câu trả lời cho những câu hỏi sau trước khi
bắt đầu phần thực hành:

Phân biệt các khái niệm chương trình (program), tiến trình
(process) và tiểu trình (thread)?

Sự tranh chấp xảy ra khi nào? Cho ví dụ.

Phân biệt sự khác nhau giữa 2 nhóm giải pháp: “busy


waiting” và “sleep & wake up”. Liệt kê một số hệ điều
hành sử dụng 2 nhóm giải pháp trên.

11
5.4 Hướng dẫn thực hành

1. Hiện thực hóa mô hình trong ví dụ 5.3.1.2, tuy nhiên thay


bằng điều kiện sau: sells <= products <= sells + [2 số cuối
của MSSV + 10]
2. Cho một mảng a được khai báo như một mảng số nguyên
có thể chứa n phần tử, a được khai báo như một biến toàn
cục. Viết chương trình bao gồm 2 thread chạy song song:

Một thread làm nhiệm vụ sinh ra một số nguyên ngẫu


nhiên sau đó bỏ vào a. Sau đó đếm và xuất ra số phần tử
của a có được ngay sau khi thêm vào.

Thread còn lại lấy ra một phần tử trong a (phần tử bất kỳ,
phụ thuộc vào người lập trình). Sau đó đếm và xuất ra số
phần tử của a có được ngay sau khi lấy ra, nếu không có
phần tử nào trong a thì xuất ra màn hình “Nothing in array
a”.
Chạy thử và tìm ra lỗi khi chạy chương trình trên khi chưa
được đồng bộ. Thực hiện đồng bộ hóa với semaphore.
3. Cho 2 process A và B chạy song song như sau:
int x = 0;
PROCESS A PROCESS B
processA() processB()
{ {

12
while(1){ while(1){
x = x + 1; x = x + 1;
if (x == 20) if (x == 20)
x = 0; x = 0;
print(x); print(x);
} }
} }
Hiện thực mô hình trên C trong hệ điều hành Linux và
nhận xét kết quả.
4. Đồng bộ với mutex để sửa lỗi bất hợp lý trong kết quả của
mô hình Bài 3.

5.5 Bài tập ôn tập

1. Biến ans được tính từ các biến x1, x2, x3, x4, x5, x6 như sau:
w = x1 * x2; (a)
v = x3 * x4; (b)
y = v * x5; (c)
z = v * x6; (d)
y = w * y; (e)
z = w * z; (f)
ans = y + z; (g)
Giả sử các lệnh từ (a) → (g) nằm trên các thread chạy song
song với nhau. Hãy lập trình mô phỏng và đồng bộ trên C
trong hệ điều hành Linux theo thứ tự sau:
13
(c), (d) chỉ được thực hiện sau khi v được tính

(e) chỉ được thực hiện sau khi w và y được tính

(g) chỉ được thực hiện sau khi y và z được tính

14

You might also like