Mutex là gì?
Mutual exclusion:
Trên một hệ điều hành thường có nhiều chương trình (hay tác vụ - task) chạy đồng thời. Mutex giúp ngăn chặn việc hai task cùng truy cập vào 1 tài nguyên (memory, register gọi chung là critical section) của hệ thống trong cùng một thời điểm.
Ví dụ, ta có 1 cái FIFO, task 1 đang read FIFO, task 2 lại write FIFO, nếu task 1 chưa lấy xong phần dữ liệu cần thiết mà task 2 lại write đè vào đúng vị trí task 1 đang read, nó sẽ làm sai lệch dữ liệu mà task 1 đang đọc.
Lưu ý: Cơ chế mutex được dùng trong cả thiết kế hardware và software.
Dùng trong chương trình như thế nào?
Hiểu đơn giản, mutex giống như cái khóa tủ dùng chung khi vào hiệu sách hay siêu thị. Để mở được tủ thì phải có khóa. Dùng xong tủ thì phải trả lại chìa khóa lại cho thằng khác dùng
ví dụ 1:
void task1()
{
//Đợi lấy chìa khóa để mở tủ
osMutexWait();
//Cất mủ, cặp, túi sách...
@to do
//Trả chìa khóa khi xong việc
osMutexRelease();
}
Mutual exclusion:
Trên một hệ điều hành thường có nhiều chương trình (hay tác vụ - task) chạy đồng thời. Mutex giúp ngăn chặn việc hai task cùng truy cập vào 1 tài nguyên (memory, register gọi chung là critical section) của hệ thống trong cùng một thời điểm.
Ví dụ, ta có 1 cái FIFO, task 1 đang read FIFO, task 2 lại write FIFO, nếu task 1 chưa lấy xong phần dữ liệu cần thiết mà task 2 lại write đè vào đúng vị trí task 1 đang read, nó sẽ làm sai lệch dữ liệu mà task 1 đang đọc.
Lưu ý: Cơ chế mutex được dùng trong cả thiết kế hardware và software.
Dùng trong chương trình như thế nào?
Hiểu đơn giản, mutex giống như cái khóa tủ dùng chung khi vào hiệu sách hay siêu thị. Để mở được tủ thì phải có khóa. Dùng xong tủ thì phải trả lại chìa khóa lại cho thằng khác dùng
ví dụ 1:
void task1()
{
//Đợi lấy chìa khóa để mở tủ
osMutexWait();
//Cất mủ, cặp, túi sách...
@to do
//Trả chìa khóa khi xong việc
osMutexRelease();
}
Semaphore là gì?
Semaphore là 1 cái advance mutex. Dùng để quản lý và bảo vệ tài nguyên dùng chung(share resource)
Mutex được gọi là 1 cái binary semaphore.
Nguyên tắc hoạt động của semaphore như hình dưới:
Nó giống như 1 cái hàng đợi (queue). Các thread/task khác nhau khi có yêu cầu sử dụng tài nguyên dùng chung sẽ bị tống vào hàng đợi này. Khi nhận được semaphore token thì thread nào được tống vào queue trước thì sử dụng tài nguyên trước. Sau đó nó lại release ra cho thread khác dùng.
Dùng như thế nào?
Cách dùng tương tự giống mutex
Semaphore là 1 cái advance mutex. Dùng để quản lý và bảo vệ tài nguyên dùng chung(share resource)
Mutex được gọi là 1 cái binary semaphore.
Nguyên tắc hoạt động của semaphore như hình dưới:
Nó giống như 1 cái hàng đợi (queue). Các thread/task khác nhau khi có yêu cầu sử dụng tài nguyên dùng chung sẽ bị tống vào hàng đợi này. Khi nhận được semaphore token thì thread nào được tống vào queue trước thì sử dụng tài nguyên trước. Sau đó nó lại release ra cho thread khác dùng.
Dùng như thế nào?
Cách dùng tương tự giống mutex
Sự khác nhau giữa Semaphores và mutexes
Semaphore hỗ trợ multi process. Như trên ví dụ 1:với mutex thằng nào(task/process) lock tủ thì chính thằng đó phải ra unlock tủ. Thằng semaphore cho phép thằng khác unlock cái tủ đó nếu cần thiết
Mutex hỗ trợ cái priority inversion (nôm na là mỗi khi gọi hàm os_wait_mutex (đợi chìa khóa) - hàm này làm tăng độ ưu tiên của task chứa nó, độ ưu tiên của task này sẽ bằng độ ưu tiên của task đang chạy, giúp tránh tình trạng deadlock của hệ thống). Xem thêm
Khi một task đang giữ mutex. Hệ điều hành cho phép xóa cái mutex đó khi cần thiết. Semaphore không hỗ trợ cơ chế này.
Semaphores vs. mutexes
A mutex is essentially the same thing as a binary semaphore and sometimes uses the same basic implementation. The differences between them are:
- Mutexes have a concept of an owner, which is the process that locked the mutex. Only the process that locked the mutex can unlock it. In contrast, a semaphore has no concept of an owner. Any process can unlock a semaphore.
- Unlike semaphores, mutexes provide priority inversion safety. Since the mutex knows its current owner, it is possible to promote the priority of the owner whenever a higher-priority task starts waiting on the mutex.
- Mutexes also provide deletion safety, where the process holding the mutex cannot be accidentally deleted. Semaphores do not provide this.
Mailbox là gì?
Nó là 1 cái "hôp thư" :D. Một mailbox chứa nhiều lá thư (messages).
Thực ra nó là một cái FIFO viết cho RTOS.
Nó thường dùng để trung chuyển dữ liệu giữa các task.
Ví dụ: Cách sử dụng mailbox trong RTX
Định nghĩa một mailbox gồm 20 messages
4. DeadlockNó là 1 cái "hôp thư" :D. Một mailbox chứa nhiều lá thư (messages).
Thực ra nó là một cái FIFO viết cho RTOS.
Nó thường dùng để trung chuyển dữ liệu giữa các task.
Ví dụ: Cách sử dụng mailbox trong RTX
Định nghĩa một mailbox gồm 20 messages
os_mbx_declare (mailbox1, 20);Khởi tạo mailbox
os_mbx_init (&mailbox1, sizeof(mailbox1));Kiểm tra mailbox còn chỗ trống để cho tiếp message mới vào không?
if (os_mbx_check (&mailbox1) == 0) { printf("Mailbox is full.\n"); }Cho thêm message mới vào mailbox
os_mbx_send (&mailbox1, msg, 0xFFFF);Lấy message ra để xử lý. Sau đó size của mailbox sẽ giảm đi 1.
void *msg; .. if (os_mbx_wait (&mailbox1, &msg, 10) == OS_R_TMO) { printf ("Wait message timeout!\n"); } else { /* process message here */ free (msg); }
Đây là khái niệm mô tả 1 trong những trạng thái treo của hệ thống. Nó xảy ra khi hai (hoặc nhiều) luồng đứng đợi luồng kia chạy xong rồi mới hoạt động tiếp. Giống như là 2 thằng nhân viên cùng làm việc trong 1 phòng và cùng nói một câu:
"Mày làm xong việc của mày thì tao mới làm tiếp được"
Kết quả là: công việc mãi không được hoàn thành đến khi thằng sếp về và cho cả 2 thằng out of jobs (reset hệ thống :D).
Một số từ mới:
Concurrent:
"Mày làm xong việc của mày thì tao mới làm tiếp được"
Kết quả là: công việc mãi không được hoàn thành đến khi thằng sếp về và cho cả 2 thằng out of jobs (reset hệ thống :D).
5. Round-Robin (Hay Round - Robin Scheduling)
Nó là gì?
Là một thuật toán giúp quản lý các task (process hoặc job...vvv) chạy "đồng thời" hay là chạy song song thì đúng hơn.
Chương trình sẽ phân chia ra các "time slice"(hoặc time slot) giống như chia 1 cái bánh hình tròn. Về cơ bản, cái bánh cứ quay tròn, đến lượt thằng task nào thì thằng ấy gặm (sử dụng CPU và cái tài nguyên khác) hết phần của mình thì thôi.
Thường thì, các miếng bánh được cắt bằng nhau cho tất cả các task, và các task cũng được thực hiện một cách tuần tự. Vấn đề nảy sinh là?
- Thời gian thực hiện task lớn hơn time-slice?
Task đó chạy hết time-slice, nó sẽ hold-on (suspended, hay bị CPU seize). Đến vòng lặp sau, thì nó lại tiếp tục chạy tiếp...cứ như thế đến khi xong task thì thôi
Ví dụ: Time slot là 100 ms, Task A chạy mất 270 ms
Vòng 1 nó(Task A) được cấp phát 100 ms, nó chạy 100 ms đầu của 270 ms
Vòng 2 nó lại được cấp phát 100ms, nó chạy tiếp 100 ms của task
Vòng 3 nó cũng được cấp 100 ms để làm việc, nó chỉ sử dụng 70 ms là task done, sau đó CPU sẽ "ngồi chơi" trong khoảng 30 ms cho hết time-slot, sau đó nó lại chuyển sang time-slot của task tiếp theo
- Thời gian thực hiện task nhỏ time-slice?
Điều này đơn giản, ta làm xong việc sớm thì ta được ngồi chơi..
Trong thực tế có vài biến thể của RRS,
- Trong bàn ăn, sẽ có thằng đói ít, thằng đói nhiều, mức độ muốn ăn ngay của bọn nó luôn luôn khác nhau . Ta gọi cái "mức độ muốn ăn ngay" này là priority của task.
Để xử lý vấn đề này, sau khi task 1 hoàn thành, RRS sẽ quyết định task nào được chạy tiếp theo phụ thuộc vào priority của task, thằng nào có priority cao hơn thì thằng đó được chạy trước. Nếu có hơn 2 task có cùng priority, thì thằng nào có TaskId thấp hơn thì được gọi trước. (Ngoài ra còn 1 số cách phân xử nữa..tùy thuộc OS)
- Để giải quyết vấn đề "ngồi chơi"
khi task xong sớm?
Người ta tạo ra các RRS có time-slot không đều nhau. RRS sẽ call thằng task nào ready (hoặc có priority cao hơn) thực hiện ngay sau khi một task done
Ví dụ về RRS(Hình 2)
- Hình 1 là cách lập lịch cho các task dựa hoàn toàn vào priority của task. Trong bất kỳ 1 thời điểm của hệ thống, task nào raise priority lên cao nhất thì task đó được chạy.
- Hình 2, mặc dù priority cao hơn running-task nhưng, task đó vẫn phải đợi running task complete thì mới được thực hiện.
Dùng như thế nào?
- Với trường hợp sử dụng RTOS, sẽ có một hằng số trong file nào đó mô tả giá trị của time_slot
Với RTX thì thông số về Round-Robin sẽ đặt trong file RTX_config.c
Là một thuật toán giúp quản lý các task (process hoặc job...vvv) chạy "đồng thời" hay là chạy song song thì đúng hơn.
Chương trình sẽ phân chia ra các "time slice"(hoặc time slot) giống như chia 1 cái bánh hình tròn. Về cơ bản, cái bánh cứ quay tròn, đến lượt thằng task nào thì thằng ấy gặm (sử dụng CPU và cái tài nguyên khác) hết phần của mình thì thôi.
Thường thì, các miếng bánh được cắt bằng nhau cho tất cả các task, và các task cũng được thực hiện một cách tuần tự. Vấn đề nảy sinh là?
- Thời gian thực hiện task lớn hơn time-slice?
Task đó chạy hết time-slice, nó sẽ hold-on (suspended, hay bị CPU seize). Đến vòng lặp sau, thì nó lại tiếp tục chạy tiếp...cứ như thế đến khi xong task thì thôi
Ví dụ: Time slot là 100 ms, Task A chạy mất 270 ms
Vòng 1 nó(Task A) được cấp phát 100 ms, nó chạy 100 ms đầu của 270 ms
Vòng 2 nó lại được cấp phát 100ms, nó chạy tiếp 100 ms của task
Vòng 3 nó cũng được cấp 100 ms để làm việc, nó chỉ sử dụng 70 ms là task done, sau đó CPU sẽ "ngồi chơi" trong khoảng 30 ms cho hết time-slot, sau đó nó lại chuyển sang time-slot của task tiếp theo
- Thời gian thực hiện task nhỏ time-slice?
Điều này đơn giản, ta làm xong việc sớm thì ta được ngồi chơi..
Trong thực tế có vài biến thể của RRS,
- Trong bàn ăn, sẽ có thằng đói ít, thằng đói nhiều, mức độ muốn ăn ngay của bọn nó luôn luôn khác nhau . Ta gọi cái "mức độ muốn ăn ngay" này là priority của task.
Để xử lý vấn đề này, sau khi task 1 hoàn thành, RRS sẽ quyết định task nào được chạy tiếp theo phụ thuộc vào priority của task, thằng nào có priority cao hơn thì thằng đó được chạy trước. Nếu có hơn 2 task có cùng priority, thì thằng nào có TaskId thấp hơn thì được gọi trước. (Ngoài ra còn 1 số cách phân xử nữa..tùy thuộc OS)
- Để giải quyết vấn đề "ngồi chơi"
Người ta tạo ra các RRS có time-slot không đều nhau. RRS sẽ call thằng task nào ready (hoặc có priority cao hơn) thực hiện ngay sau khi một task done
Ví dụ về RRS(Hình 2)
- Hình 1 là cách lập lịch cho các task dựa hoàn toàn vào priority của task. Trong bất kỳ 1 thời điểm của hệ thống, task nào raise priority lên cao nhất thì task đó được chạy.
- Hình 2, mặc dù priority cao hơn running-task nhưng, task đó vẫn phải đợi running task complete thì mới được thực hiện.
Dùng như thế nào?
- Với trường hợp sử dụng RTOS, sẽ có một hằng số trong file nào đó mô tả giá trị của time_slot
Với RTX thì thông số về Round-Robin sẽ đặt trong file RTX_config.c
#define OS_ROBIN 1 // allow use Round Robin or not
#define OS_ROBINTOUT 5 //It is specified in number of system timer ticks, chính là giá trị của time-slice
- Non-OS: ...
Một số từ mới:
Concurrent:
Rất dễ hiểu. Cảm ơn anh nhiều :))
ReplyDeletegood !
ReplyDelete