Bài trước: Chapter 3: Interrupt management (quản lý ngắt).
Qua 3 chương 1,2,3 thì mình cũng gần như đã nắm được cơ bản về hoạt động của FreeRTOS, thấy nó cũng gần giống như 1 OS thực chạy trên PC như Ubuntu, Windown... tất nhiên là cái này đơn giản hơn nhiều rồi.
Và có 1 tin cực vui là bây giờ mình đã có thể built thành công 1 chương trình FreeRTOS thực tế nhỏ nhỏ rồi :D dù nó chạy chưa được chuẩn cho lắm nhưng mà cũng là 1 thành công sau mấy tháng cố gắng nghiên cứu. hehe
Tiếp theo là chương 4 về quản lý tài nguyên, đây là 1 chương cũng khá quan trọng, những kiến thức của chương này sẽ đảm bảo cho 1 chương trình không chỉ chạy được mà phải chạy đúng và ổn định theo thời gian.
Trong chương này chúng ta sẽ trả lời các câu hỏi sau:
+ 1 critial section là gì? (kiểu như là 1 khu vực được bảo vệ ấy)
+ Loại trừ lẫn nhau là gì?
+ Trạng thái suspend (lơ lửng) là như thế nào?
+ Mutex là gì và dùng thế nào?
Rồi, bắt đầu nào!
Đầu tiên có 1 việc mà ta hay làm là thường sử dụng Lcd hoặc Uart để hiển thị dữ liệu hoặc Debug chương trình, nhưng có 1 điều "tai hại" hay xảy ra là: khi ta dùng task1 xuất ra 1 dòng dữ liệu ví dụ là "hello cô em. "
nhưng khi nhiệm vụ này chưa được hoàn thành thì 1 task2 chiếm quyền và yêu cầu xuất ra dòng "Bụi đời chợ bé." thế là ta sẽ thu được kết quả là "hello cBui đời chợ lớn." :)) nói chung là loạn xì ngầu lên. Vậy nên FreeRTOS cung cấp cho ta cơ chế để điều đó không xảy ra là: Critical Sections và Suspending.
1. Basic Critical Sections là gì?
Đây là vùng chương trình mà Task khác không được phép tác động vào khi mà vùng này chưa thực hiện xong. Nó nằm ở trong cặp:
taskENTER_CRITICAL();
{
// Chương trình cần bảo vệ nằm ở đây
}
taskEXIT_CRITICAL();
Khi mà hàm taskENTER_CRITICAL(); được gọi thì tất cả các ngắt sẽ bị Disable. Tất nhiên là ngoại trừ các ngắt có mức ưu tiên lớn hơn configMAX_SYSCALL_INTERRUPT_PRIORITY.
Khuyến cáo là đoạn Code cần được bảo vệ này nên càng ngắn càng tốt.
Kết quả thử nghiệm: dùng hàm này để bảo vệ đoạn xuất dữ liệu ra uart: xuất ký tự rất tốt nhưng chỉ chạy được 1 lúc thì chip bị đơ??? chưa rõ nguyên nhân, để xem xét tiếp.
2. Suspending (or Locking) the Scheduler (Hic. chẳng biết dịch răng cho hay thôi cứ để rứa vậy)
Suspending cũng tạo nên 1 vùng bảo vệ nhưng khác cái trên ở chỗ là cái này chỉ bảo vệ khỏi sự truy nhập của task khác còn khi ngắt thì vẫn được phép xảy ra.
Hình như 2 kiểu này nó không được dùng nhiều thì phải, trong các Example cũng không thấy dùng? thôi cứ để đây, tìm hiểu sau đã :))
3. Mutexes ( tương tự như là Binary Semaphore ) Example015
MUTEX là viết tắt của "MUTual EXclusion" nghĩa là loại trừ lẫn nhau.
Mutex thì chẳng khác Binary Semaphore là mấy chỉ khác là:
Binary Semaphore: dùng để đồng bộ sự kiện và không cần trả về.
Mutex: dùng để đồng bộ dữ liệu sử dụng và bắt buộc phải trả về sau khi dùng.
Hoạt động của Mutex được mô tả ở biểu đồ bên dưới:
Hàm khởi tạo MUTEX:
xSemaphoreHandle xSemaphoreCreateMutex( void );
Giá trị trả về của hàm: NULL nếu khởi tạo thất bại; non-NULL nếu thành công.
Thường thì phải kiểm tra điều kiện này rồi mới chạy những bước tiếp theo. Dùng lệnh If chẳng hạn.
Sử dụng thì dùng 2 hàm "Take" và "Give" của semaphore. nhớ là cho thời gian đợi cực đại ở hàm Take => Block để chờ nhận được.
ví dụ:
xSemaphoreTake ( xMutex, portMAX_DELAY );
{
// Code cần bảo vệ;
}
xSemaphoreGive( xMutex );
Thử nghiệm trên chương trình vào Avr32 đã thành công mỹ mãn ^^
4. Đảo ngược giá trị ưu tiên.
Đây là 1 trường hợp không mong muốn, 1 cái Task có ưu tiên cao lại không được chạy trong khi 1 cái Task có mức ưu tiên thấp hơn được chạy dù rằng nó chẳng giữ cái Mutex nào cả, đơn giản vì nó chiếm được quyền từ 1 cái Task có mức ưu tiên thấp hơn nữa nhưng lại đang giữa cái Mutex mà cái Task có mức ưu tiên cao nhất cần (Hic. dài dòng rắm rối quá) sơ đồ sau:
5. Kế thừa giá trị ưu tiên.
Cái ni còn hay hơn nựa!!!
Để hạn chế những hậu quả do sự đảo ngược thứ tự ưu tiên như trên gây ra, MUTEX đưa ra 1 cơ chế là cái Task mà đang giữ Mutex thì sẽ mang giá trị ưu tiên của cái Task đang chờ Mutex có mức ưu tiên cao nhất. (Cái này binary Semaphore không có đâu). Đó là kế thừa :-p
Ah tất nhiên sau khi dùng xong Mutex thì sẽ trở về giá trị ưu tiên vốn có của nó.
6. Deadlock (Cái ôm chết chóc. haha)
Đây cũng là 1 kịch bản xấu khi mà dùng Mutex. nó là lúc mà 2 Task đang giữ 2 Mutex của nhau mà đều bị blocking cả. (đọc thêm ở trang 113 nhé).
Giải pháp: thì cũng không có giải pháp gì cả, chỉ là chú ý về phân thời gian và các Mutex yêu cầu thôi.
7. Gatekeeper Task (dịch tạm là người gác cổng vậy).
Cái này được đưa ra để giải quyết các vấn đề của Mutex như đảo ngược giá trị ưu tiên và Deadlock.
Cái này thực ra là 1 hàng đợi và thuộc quyền sở hữu duy nhất của 1 tài nguyên.
Khi mà 1 Task muốn chiếm 1 tài nguyên nào đó, nó không chiếm trực tiếp mà chỉ gửi 1 yêu cầu vào hàng đợi Gatekeeper rồi đợi đến lượt được xử lý. Vậy thôi! y như kiếu là lấy phiếu thứ tự mua vé tàu về quê luôn.
Lưu ý là với các Task bình thường thì gửi yêu cầu vào cuối hàng đợi, còn với các Task ngắt thì gửi yêu cầu luôn vào đâu hàng đợi để được xử lý nhanh nhất.
Xem Example 16 nhé.
Rồi! xong chương này coi như đã ngon lành rồi đó. Bạn nào thử biên dịch 1 code đơn giản rồi chạy thử đi.
Còn mình thì đã chạy được rồi. hê hê
Bài tiếp: Chap 5: Memory Management (Quản lý bộ nhớ)
Qua 3 chương 1,2,3 thì mình cũng gần như đã nắm được cơ bản về hoạt động của FreeRTOS, thấy nó cũng gần giống như 1 OS thực chạy trên PC như Ubuntu, Windown... tất nhiên là cái này đơn giản hơn nhiều rồi.
Và có 1 tin cực vui là bây giờ mình đã có thể built thành công 1 chương trình FreeRTOS thực tế nhỏ nhỏ rồi :D dù nó chạy chưa được chuẩn cho lắm nhưng mà cũng là 1 thành công sau mấy tháng cố gắng nghiên cứu. hehe
Tiếp theo là chương 4 về quản lý tài nguyên, đây là 1 chương cũng khá quan trọng, những kiến thức của chương này sẽ đảm bảo cho 1 chương trình không chỉ chạy được mà phải chạy đúng và ổn định theo thời gian.
Trong chương này chúng ta sẽ trả lời các câu hỏi sau:
+ 1 critial section là gì? (kiểu như là 1 khu vực được bảo vệ ấy)
+ Loại trừ lẫn nhau là gì?
+ Trạng thái suspend (lơ lửng) là như thế nào?
+ Mutex là gì và dùng thế nào?
Rồi, bắt đầu nào!
Đầu tiên có 1 việc mà ta hay làm là thường sử dụng Lcd hoặc Uart để hiển thị dữ liệu hoặc Debug chương trình, nhưng có 1 điều "tai hại" hay xảy ra là: khi ta dùng task1 xuất ra 1 dòng dữ liệu ví dụ là "hello cô em. "
nhưng khi nhiệm vụ này chưa được hoàn thành thì 1 task2 chiếm quyền và yêu cầu xuất ra dòng "Bụi đời chợ bé." thế là ta sẽ thu được kết quả là "hello cBui đời chợ lớn." :)) nói chung là loạn xì ngầu lên. Vậy nên FreeRTOS cung cấp cho ta cơ chế để điều đó không xảy ra là: Critical Sections và Suspending.
1. Basic Critical Sections là gì?
Đây là vùng chương trình mà Task khác không được phép tác động vào khi mà vùng này chưa thực hiện xong. Nó nằm ở trong cặp:
taskENTER_CRITICAL();
{
// Chương trình cần bảo vệ nằm ở đây
}
taskEXIT_CRITICAL();
Khi mà hàm taskENTER_CRITICAL(); được gọi thì tất cả các ngắt sẽ bị Disable. Tất nhiên là ngoại trừ các ngắt có mức ưu tiên lớn hơn configMAX_SYSCALL_INTERRUPT_PRIORITY.
Khuyến cáo là đoạn Code cần được bảo vệ này nên càng ngắn càng tốt.
Kết quả thử nghiệm: dùng hàm này để bảo vệ đoạn xuất dữ liệu ra uart: xuất ký tự rất tốt nhưng chỉ chạy được 1 lúc thì chip bị đơ??? chưa rõ nguyên nhân, để xem xét tiếp.
2. Suspending (or Locking) the Scheduler (Hic. chẳng biết dịch răng cho hay thôi cứ để rứa vậy)
Suspending cũng tạo nên 1 vùng bảo vệ nhưng khác cái trên ở chỗ là cái này chỉ bảo vệ khỏi sự truy nhập của task khác còn khi ngắt thì vẫn được phép xảy ra.
Hình như 2 kiểu này nó không được dùng nhiều thì phải, trong các Example cũng không thấy dùng? thôi cứ để đây, tìm hiểu sau đã :))
3. Mutexes ( tương tự như là Binary Semaphore ) Example015
MUTEX là viết tắt của "MUTual EXclusion" nghĩa là loại trừ lẫn nhau.
Mutex thì chẳng khác Binary Semaphore là mấy chỉ khác là:
Binary Semaphore: dùng để đồng bộ sự kiện và không cần trả về.
Mutex: dùng để đồng bộ dữ liệu sử dụng và bắt buộc phải trả về sau khi dùng.
Hoạt động của Mutex được mô tả ở biểu đồ bên dưới:
xSemaphoreHandle xSemaphoreCreateMutex( void );
Giá trị trả về của hàm: NULL nếu khởi tạo thất bại; non-NULL nếu thành công.
Thường thì phải kiểm tra điều kiện này rồi mới chạy những bước tiếp theo. Dùng lệnh If chẳng hạn.
Sử dụng thì dùng 2 hàm "Take" và "Give" của semaphore. nhớ là cho thời gian đợi cực đại ở hàm Take => Block để chờ nhận được.
ví dụ:
xSemaphoreTake ( xMutex, portMAX_DELAY );
{
// Code cần bảo vệ;
}
xSemaphoreGive( xMutex );
Thử nghiệm trên chương trình vào Avr32 đã thành công mỹ mãn ^^
4. Đảo ngược giá trị ưu tiên.
Đây là 1 trường hợp không mong muốn, 1 cái Task có ưu tiên cao lại không được chạy trong khi 1 cái Task có mức ưu tiên thấp hơn được chạy dù rằng nó chẳng giữ cái Mutex nào cả, đơn giản vì nó chiếm được quyền từ 1 cái Task có mức ưu tiên thấp hơn nữa nhưng lại đang giữa cái Mutex mà cái Task có mức ưu tiên cao nhất cần (Hic. dài dòng rắm rối quá) sơ đồ sau:
Khi code nhớ đừng để cái này xảy ra!
5. Kế thừa giá trị ưu tiên.
Cái ni còn hay hơn nựa!!!
Để hạn chế những hậu quả do sự đảo ngược thứ tự ưu tiên như trên gây ra, MUTEX đưa ra 1 cơ chế là cái Task mà đang giữ Mutex thì sẽ mang giá trị ưu tiên của cái Task đang chờ Mutex có mức ưu tiên cao nhất. (Cái này binary Semaphore không có đâu). Đó là kế thừa :-p
Ah tất nhiên sau khi dùng xong Mutex thì sẽ trở về giá trị ưu tiên vốn có của nó.
6. Deadlock (Cái ôm chết chóc. haha)
Đây cũng là 1 kịch bản xấu khi mà dùng Mutex. nó là lúc mà 2 Task đang giữ 2 Mutex của nhau mà đều bị blocking cả. (đọc thêm ở trang 113 nhé).
Giải pháp: thì cũng không có giải pháp gì cả, chỉ là chú ý về phân thời gian và các Mutex yêu cầu thôi.
7. Gatekeeper Task (dịch tạm là người gác cổng vậy).
Cái này được đưa ra để giải quyết các vấn đề của Mutex như đảo ngược giá trị ưu tiên và Deadlock.
Cái này thực ra là 1 hàng đợi và thuộc quyền sở hữu duy nhất của 1 tài nguyên.
Khi mà 1 Task muốn chiếm 1 tài nguyên nào đó, nó không chiếm trực tiếp mà chỉ gửi 1 yêu cầu vào hàng đợi Gatekeeper rồi đợi đến lượt được xử lý. Vậy thôi! y như kiếu là lấy phiếu thứ tự mua vé tàu về quê luôn.
Lưu ý là với các Task bình thường thì gửi yêu cầu vào cuối hàng đợi, còn với các Task ngắt thì gửi yêu cầu luôn vào đâu hàng đợi để được xử lý nhanh nhất.
Xem Example 16 nhé.
Rồi! xong chương này coi như đã ngon lành rồi đó. Bạn nào thử biên dịch 1 code đơn giản rồi chạy thử đi.
Còn mình thì đã chạy được rồi. hê hê
Bài tiếp: Chap 5: Memory Management (Quản lý bộ nhớ)
Bạn ơi, bạn có thể cho thông tin liên lạc của bạn để mình hỏi một số thông tin về RTOS được không ạ. Mình đang tìm hiểu về nó. Số đt của mình 0987079356. Mình tên Hải nhé!
Trả lờiXóaBạn có thể comment trước tiếp vào đây cũng được, không thì GG+ hay mail cho mình như trên thông tin ấy.
Xóacòn đây là fb của mình: https://www.facebook.com/nghiaquy.set
Có gì biết thì mình sẽ giúp bạn.
Nhận xét này đã bị tác giả xóa.
Xóacó fb mới ko bạn ơi, fb kia ko vào được :D
Trả lờiXóa