Một số khái niệm khi làm việc với RTOS

Các khái niệm cần chú ý khi làm việc với RTOS

1. Mutex

Mutex là gì?

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ụ:

2. Semaphore

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:

Semaphore

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.

3. Mailbox (or Message queue)

Mailbox là gì?

Mailbox 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.

Mailbox

Ví dụ sử dụng mailbox trong RTX:

Định nghĩa một mailbox gồm 20 messages

Khởi tạo mailbox

Kiểm tra mailbox còn chỗ trống để cho tiếp message mới vào không?

Cho thêm message mới vào mailbox

Lấy message ra để xử lý. Sau đó size của mailbox sẽ giảm đi 1.

4. Deadlock

Đâ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).

5. Round-Robin (Hay Round – Robin Scheduling)

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 trên 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 dưới, 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.

RRS

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

 

Tham khảo daothainam’s blog

Leave a Reply

2 Comments on "Một số khái niệm khi làm việc với RTOS"

Notify of
avatar
Sort by:   newest | oldest | most voted
Tuat
Guest

Cám ơn anh nhiều lắm, bài viết rất bổ ích

wpDiscuz