NỘI DUNG BÀI LÀM 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?. - Chương trình là tập hợp các câu lệnhd được viết bằng ngôn ngữ lập tình, chưa đ
Trang 1TRƯỜNG ĐẠI HỌC CÔNG NGHỆ THÔNG TIN
KHOA KỸ THUẬT MÁY TÍNH
MÔN HỆ ĐIỀU HÀNH BÀI TẬP THỰC HÀNH 5
GVHD: Nguyễn Xuân Duy Bách Sinh viên thực hiện: Bùi Anh Khôi - 23520759
Tp Hồ Chí Minh, 05/2025
Trang 2NHẬN XÉT CỦA GIÁO VIÊN HƯỚNG DẪN
……., ngày…… tháng……năm 2025
Người nhận xét
(Ký tên và ghi rõ họ tên)
Trang 4NỘI DUNG BÀI LÀM
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)?
- Chương trình là tập hợp các câu lệnhd được viết bằng ngôn ngữ lập tình, chưa được thực thi và nó tồn tại ở dạng tĩnh
- Tiến trình làm một thực thể động, là chương trình đang được thực thi Một chương trình có thể tạo ra nhiều tiến trình và mỗi tiến trình có không gian bộ nhớ và tài nguyên riêng
- Tiểu trình là đơn vị nhỏ hơn trong tiến trình, dùng để chia sẻ tài nguyên và thực thi song song trong cùng một tiến trình Tất cả các thread của một tiến trình
dùng chung không gian địa chỉ và tài nguyên
Sự tranh chấp xảy ra khi nào? Cho ví dụ
- Sự tranh chấp xảy ra khi nhiều tiến trình hoặc thread cùng truy cập và thay đổi một thời nguyên dùng chung mà không có cơ chế đồng bộ hợp lý
- Ví dụ:
o Có 2 thread A và B cùng tăng giá trị biến x = 0, khi đó cả hai cùng đọc x
= 0, rồi cùng cộng 1 và ghi x = 1, kết quả mong muốn cả người dùng là x
= 2, nhưng thực tế x = 1 từ đó ta thấy được sự tranh chấp của cả 2 biến A
o Tốn CPU: Ở giải pháp này tốn CPU rất nhiều vì kiểm tra liên tục
o Độ phức tạp: Giải pháp này dễ dàng lập trình nhưng không hiệu quả
o Phạm vi hoạt động: Dùng ở một số hệ điều hành cũ, hoặc phần cứng đơn giản
- Sleep & wake up
Trang 5o Cách hoạt động: tiến trình ngủ khi không thể tiếp tục và chỉ thức dậy khi
o Phạm vi hoạt động: Dùng ở hầu hết hệ điều hành hiện đại
- Ví dụ về một số hệ điều hành sử dụng 2 nhóm giải pháp trên:
o Busy waiting: một số hệ điều hành thời gian thực đơn giản, hoặc các hệ thóng nhúng không dùng interupt
o Sleep and wake up:
#define MSSV_4SOCUOI 759 // 4 số cuối của MSSV: 0759
#define MIN_ROUNDS 10 // Bắt đầu kiểm tra sau 10 vòng
int sells = 0; // Số sản phẩm đã bán
int products = 0; // Số sản phẩm đã tạo
int rounds = 0; // Số vòng đã chạy
sem_t sem, sem1; // Semaphore để đồng bộ luồng
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Khóa mutex để bảo vệ dữ liệu dùng chung
Trang 6// Hàm kiểm tra điều kiện an toàn sau MIN_ROUNDS vòng
void check_condition() {
pthread_mutex_lock(&mutex); // Khóa mutex khi truy cập biến toàn cục
rounds++; // Tăng số vòng
if (rounds >= MIN_ROUNDS) {
if (products < sells || products > sells + MSSV_4SOCUOI) {
printf("VI PHẠM: sells = %d, products = %d → DỪNG CHƯƠNG TRÌNH!\n", sells, products); exit(0); // Vi phạm điều kiện: kết thúc chương trình
sem_wait(&sem); // Đợi đến lượt
int rand_sells = (rand() % 5) + 1; // Ngẫu nhiên từ 1–5
sells += rand_sells;
printf("BÁN: +%d → sells = %d (max: %d)\n", rand_sells, sells, sells + MSSV_4SOCUOI);
check_condition(); // Kiểm tra điều kiện sau khi bán
sem_post(&sem1); // Cho phép luồng sản xuất chạy
}
return NULL;
}
// Tiến trình B: mô phỏng tạo sản phẩm
void* processB(void* arg) {
while (1) {
sem_wait(&sem1); // Đợi đến lượt
int rand_products = (rand() % 5) + 1; // Ngẫu nhiên từ 1–5
products += rand_products;
Trang 7printf("TẠO: +%d → products = %d\n", rand_products, products);
check_condition(); // Kiểm tra điều kiện sau khi tạo
sem_post(&sem); // Cho phép luồng bán tiếp tục
}
return NULL;
}
int main() {
srand(time(NULL)); // Khởi tạo seed cho random
// Khởi tạo semaphore
sem_init(&sem, 0, 1); // Cho phép A chạy đầu tiên
sem_init(&sem1, 0, 0); // B chờ
// Tạo 2 luồng
pthread_t tA, tB;
pthread_create(&tA, NULL, processA, NULL);
pthread_create(&tB, NULL, processB, NULL);
// Chờ 2 luồng (sẽ kết thúc nếu điều kiện vi phạm)
Trang 8o Khai báo và khởi tạo
▪ Nội dung code
#define MSSV_4SOCUOI 759
#define MIN_ROUNDS 10
int sells = 0, products = 0, rounds = 0;
sem_t sem, sem1;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
▪ MSSV_4SOCUOI: là giới hạn trên để kiểm tra điều kiện
▪ MIN_ROUNDS: Số vòng tối thiểu trước khi bắt đầu kiểm tra điều kiện
▪ Khai báo các biến toàn cục:
• sells: Tổng số sản phẩm đã bán
Trang 9• products: Tổng số sản phẩm sản xuất
• rounds: Số vòng giao tiếp giữa A và B diễn ra
▪ Semaphore: sem, sem1: đồng bộ luồng A và B
▪ Mutex: mutex: bảo vệ khi truy cập biến toàn cục trong môi trường đa luồng
▪ Tiến trình bán hàng chờ đến lượt chạy bằng sem_wait(&sem)
▪ Thực hiện random bán ngẫu nhiên 1 – 5 sản phẩm
▪ Gọi hàm check_condition() để kiểm tra điều kiện
▪ Gửi tín hiệu cho tiến trình tạo sản phẩm bằng sem_post(&sem1)
Trang 10▪ Tiến trình sản xuất chờ đến lượt chạy bằng sem_wait(&sem1)
▪ Thực hiệnm random sản xuất ngẫu nhiên 1 – 5 sản phẩm
▪ Gọi hàm check_condition() để kiểm tra điều kiện
▪ Gửi tiến hiệu cho tiến trình bán bằng sem_post(&sem)
o Hàm main()_Khởi tạo và chờ luồng
▪ Nội dung code int main() {
srand(time(NULL));
sem_init(&sem, 0, 1); // A chạy trước
sem_init(&sem1, 0, 0); // B chờ
pthread_t tA, tB;
pthread_create(&tA, NULL, processA, NULL);
pthread_create(&tB, NULL, processB, NULL);
▪ Dùng srand() để tạo số ngẫu nhiên khác nhau mỗi lần chạy chương trình
▪ Khởi tạo semaphort: A được phép chạy đầu tiên, B phải đợi
▪ Tạo 2 luồng A và B hoạt động song song vơi snhau
▪ pthread_join giúp chương trình chínhd đợi đến khi có lỗi thì kết thúc
▪ Dọn dẹp tài ngyên khi semaphort kết thúc
Trang 11int count = 0; // số phần tử hiện tại trong mảng
void* producer(void* arg) {
while (1) {
if (count < MAX) {
int num = rand() % 100;
a[count] = num; // thêm số vào cuối mảng
Trang 12}
int main() {
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
Producer: Added 83 → count = 1
Producer: Added 86 → count = 2
Consumer: Removed 86 → count = 1
Producer: Added 77 → count = 2
Consumer: Removed 77 → count = 1
Producer: Added 15 → count = 2
Producer: Added 93 → count = 3
Consumer: Removed 93 → count = 2
Producer: Added 35 → count = 3
Consumer: Removed 35 → count = 2
Producer: Added 86 → count = 3
Producer: Added 92 → count = 4
Consumer: Removed 92 → count = 3
Producer: Added 49 → count = 4
Consumer: Removed 49 → count = 3
Producer: Added 21 → count = 4
Producer: Added 62 → count = 5
Consumer: Removed 62 → count = 4
Producer: Added 27 → count = 5
Consumer: Removed 27 → count = 4
Producer: Added 90 → count = 5
Producer: Added 59 → count = 6
Consumer: Removed 59 → count = 5
Trang 13Producer: Added 63 → count = 6
Consumer: Removed 63 → count = 5
Producer: Added 26 → count = 6
Producer: Added 40 → count = 7
Consumer: Removed 40 → count = 6
Producer: Added 26 → count = 7
Consumer: Removed 26 → count = 6
Producer: Added 72 → count = 7
Producer: Added 36 → count = 8
Consumer: Removed 36 → count = 7
Producer: Added 11 → count = 8
Consumer: Removed 11 → count = 7
Producer: Added 68 → count = 8
Producer: Added 67 → count = 9
Consumer: Removed 67 → count = 8
Producer: Added 29 → count = 9
Consumer: Removed 29 → count = 8
Producer: Added 82 → count = 9
Producer: Added 30 → count = 10
Consumer: Removed 30 → count = 9
Producer: Added 62 → count = 10
Consumer: Removed 62 → count = 9
Producer: Added 23 → count = 10
Consumer: Removed 23 → count = 9
Producer: Added 67 → count = 10
Consumer: Removed 67 → count = 9
Producer: Added 35 → count = 10
Consumer: Removed 35 → count = 9
Producer: Added 29 → count = 10
Consumer: Removed 29 → count = 9
Producer: Added 2 → count = 10
Consumer: Removed 2 → count = 9
Producer: Added 22 → count = 10
Consumer: Removed 22 → count = 9
Producer: Added 58 → count = 10
Consumer: Removed 58 → count = 9
Producer: Added 69 → count = 10
Consumer: Removed 69 → count = 9
Producer: Added 67 → count = 10
Trang 14Consumer: Removed 67 → count = 9
Producer: Added 93 → count = 10
Consumer: Removed 93 → count = 9
Producer: Added 56 → count = 10
Consumer: Removed 56 → count = 9
Producer: Added 11 → count = 10
^C
bui@bui-VirtualBox:~/Downloads/LAB5$
- Ta tìm thấy các lỗi như sau:
o Chương trình in ra “Nothing in array a” trong khi producer đã thêm phần tử vì consumer đọc count trước khi producer cập nhật count do thiếu đồng bộ đọc-ghi biến chung
o Count tăng giảm không theo thứ tự chính xác
o Producer và Consumer cùng truy cập mảng không đồng bộ, cùng đọc và ghi mảng mà không khóa truy cập dẫn đến lỗi dữ liệu
- Chương trình đã thực hiện đồng bộ bằng semaphore
int count = 0; // số phần tử hiện tại trong buffer
int in = 0; // chỉ số chèn phần tử tiếp theo
int out = 0; // chỉ số lấy phần tử tiếp theo
sem_t emptySlots; // semaphore đếm số ô trống trong buffer
sem_t fullSlots; // semaphore đếm số ô đầy trong buffer
Trang 15pthread_mutex_t mutex; // mutex để bảo vệ truy cập buffer và biến count
void *producer(void *arg) {
int item;
while (1) {
item = rand() % 100;
sem_wait(&emptySlots); // chờ nếu buffer đầy
pthread_mutex_lock(&mutex); // khóa truy cập buffer
// Thêm phần tử vào buffer
sem_post(&fullSlots); // báo có thêm phần tử
sleep(rand() % 2); // giả lập thời gian sản xuất
sem_wait(&fullSlots); // chờ nếu buffer rỗng
pthread_mutex_lock(&mutex); // khóa truy cập buffer
Trang 16// Lấy phần tử ra khỏi buffer
sem_post(&emptySlots); // báo có thêm ô trống
sleep(rand() % 2); // giả lập thời gian tiêu thụ
}
return NULL;
}
int main() {
pthread_t prodThread, consThread;
// Khởi tạo semaphore và mutex
sem_init(&emptySlots, 0, BUFFER_SIZE); // ban đầu buffer có BUFFER_SIZE ô trống
sem_init(&fullSlots, 0, 0); // ban đầu buffer chưa có phần tử nào
pthread_mutex_init(&mutex, NULL);
// Tạo thread Producer và Consumer
pthread_create(&prodThread, NULL, producer, NULL);
pthread_create(&consThread, NULL, consumer, NULL);
// Chờ 2 thread chạy (thường không return)
pthread_join(prodThread, NULL);
Trang 17Producer: Added 83 → count = 1
Consumer: Removed 83 → count = 0
Producer: Added 15 → count = 1
Consumer: Removed 15 → count = 0
Producer: Added 86 → count = 1
Producer: Added 49 → count = 2
Consumer: Removed 86 → count = 1
Consumer: Removed 49 → count = 0
Producer: Added 90 → count = 1
Consumer: Removed 90 → count = 0
Producer: Added 63 → count = 1
Consumer: Removed 63 → count = 0
Producer: Added 72 → count = 1
Producer: Added 11 → count = 2
Consumer: Removed 72 → count = 1
Producer: Added 29 → count = 2
Producer: Added 30 → count = 3
Producer: Added 23 → count = 4
Consumer: Removed 11 → count = 3
Producer: Added 29 → count = 4
Producer: Added 22 → count = 5
Producer: Added 69 → count = 6
Consumer: Removed 29 → count = 5
Producer: Added 93 → count = 6
Consumer: Removed 30 → count = 5
Trang 18Consumer: Removed 23 → count = 4
Consumer: Removed 29 → count = 3
Producer: Added 73 → count = 4
Consumer: Removed 22 → count = 3
Consumer: Removed 69 → count = 2
Producer: Added 98 → count = 3
Producer: Added 15 → count = 4
Producer: Added 13 → count = 5
Producer: Added 91 → count = 6
Producer: Added 56 → count = 7
Producer: Added 62 → count = 8
Producer: Added 96 → count = 9
Consumer: Removed 93 → count = 8
Producer: Added 25 → count = 9
Producer: Added 27 → count = 10
Consumer: Removed 73 → count = 9
Producer: Added 46 → count = 10
Consumer: Removed 98 → count = 9
Producer: Added 57 → count = 10
Consumer: Removed 15 → count = 9
Producer: Added 95 → count = 10
Consumer: Removed 13 → count = 9
Consumer: Removed 91 → count = 8
^C
bui@bui-VirtualBox:~/Downloads/LAB5$
- So sánh giữa 2 kết quả chương trinh
Kiểm soát số phần tử buffer Có thể vượt giới hạn Được giới hạn bởi empty/ full
Độ chính xác biến count Có thể sai do race conditiion Luôn đúng vì có mutex bảo vệ Trật tự hoạt động Producer có thể thêm liên tục Luân phiên ổn định
Câu 3
- Code
#include <stdio.h>
#include <pthread.h>
Trang 19int x = 0;
void* processA(void* arg) {
for (int i = 0; i < 30; i++) {
void* processB(void* arg) {
for (int i = 0; i < 30; i++) {
pthread_create(&t1, NULL, processA, NULL);
pthread_create(&t2, NULL, processB, NULL);
Trang 24o Process A chạy trước và hoàn thành toàn bộ 30 vòng lặp
o Sau đó, process B mới bắt đầu chạy:
o Kết quả:
▪ A bắt đầu từ x = 0 và sau 30 bước, x = 10
▪ Nhưng B bắt đầu từ x = 13 rồi quay lại x = 11 => điều này chứng tỏ có xảy ra lỗi race condition
Chương trình hiện tại đang có lỗi racecondition và cần khắc phục bằng phương pháp đồng bộ sử dụng mutex
Trang 25for (int i = 0; i < 30; i++) {
void* processB(void* arg) {
for (int i = 0; i < 30; i++) {
pthread_create(&t1, NULL, processA, NULL);
pthread_create(&t2, NULL, processB, NULL);
Trang 31pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv_v_ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t cv_w_ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t cv_y_ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t cv_z_ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t cv_final_ready = PTHREAD_COND_INITIALIZER;
int v_ready = 0, w_ready = 0, y_ready = 0, z_ready = 0;
int y_final_ready = 0, z_final_ready = 0;
Trang 34while (!w_ready || !z_ready)
Trang 35pthread_create(&t[0], NULL, calc_w, NULL); // (a)
pthread_create(&t[1], NULL, calc_v, NULL); // (b)
pthread_create(&t[2], NULL, calc_y, NULL); // (c)
pthread_create(&t[3], NULL, calc_z, NULL); // (d)
pthread_create(&t[4], NULL, calc_y_final, NULL); // (e)
pthread_create(&t[5], NULL, calc_z_final, NULL); // (f)
pthread_create(&t[6], NULL, calc_ans, NULL); // (g)
for (int i = 0; i < 7; ++i)
Trang 36pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_v_done = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_w_y_done = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_yz_done = PTHREAD_COND_INITIALIZER;
// Các biến trạng thái báo hiệu các bước đã hoàn thành
int v_done = 0;
int w_y_done = 0;
int yz_done = 0;
▪ Biến x1 -> x6 là các dữ liệu đầu vào
▪ w, v, y, z, ans: các biến kết quả
▪ pthread_mutex_t mutex: Khóa để bảo bệ vùng truy cập
▪ ptrhead_cond_t cond_*: Điều kiện để báo hiệu giữa các thread
▪ v_done, w_y_done, yz_done: Các cờ trạng thái các bước tính toán đã hoàn thành
o Thread thực hiện w = x1 * x2 và báo hiệu
void *thread_w(void *arg) {
▪ Cập nhật w_y_done = 1 để báo các thread biết w đã xong
▪ Gọi pthread_cond_broadcast để đánh thứcc các thread đang chờ w
▪ Mở khóa mutex
o Thread thực hiện v = x3 * x4 và báo hiệu
void *thread_v(void *arg) {