پاسخ : سیستم عامل بی درنگ RTX برای ARM7/9 و Cortex-M
سلام سلام سلام !!
شرمنده خیلی دیر شد :mrgreen:
این پست به تعیین رویدادها و اینکه چطوری تسک ها می تونن اجرای هم رو کنترل کنند، اختصاص داره. (preemptive)
طبق معمول با مثال توضیح میدم. فرض کنید تسکی داریم که برای اجرا شدنش باید چندتا تسک دیگه کامل یا تا حدودی اجرا شده باشن. مثلا به داده های نیاز داره که توسط چندتا تسک دیگه تامین میشه. یا اینکه زمینه اجرای این تسک رو، چندتا تسک دیگه آماده کنن. به دلیل اینکه ممکنه شرایط مختلفی پیش بیاد ما نمی تونیم دقیق مشخص کنیم که اون تسک ها کی به مرحله ای میرسن که زمینه برای اجرای تسک مورد نظر آماده بشه.
هر تسک یک پرچم (flag) 16 بیتی برای تعیین رویدادها داره. هر بیت یک رویداد. فرض کنید task3 به مرحله ای از اجرا که رسید که باید منتظر 2 تا رویداد بمونه بعد ادامه بده. این تسک با اشاره به 2 تا از بیت های پرچمش به سیستم عامل اعلام میکنه که منتظره این دو پرچم فعال بشن و بعد ادامه بده. این 2 تا رویداد توسط دو تسک دیگه به نام های task1 و task2 تامین میشن. پس تو هر کدوم از تسک های 1 و 2 باید در قسمت مورد نظر این دو پرچم فعال شده و رویداد خودشون رو اعلام کنن. به محض اینکه هر دو پرجم فعال شدن، task3 روند اجرای خودش رو ادامه میده.
#include <AT91SAM7X256.h>
#include <rtl.h>
OS_TID tsk_ID1, tsk_ID2, tsk_ID3;
__task void task_init(void);
__task void task1(void);
__task void task2(void);
__task void task3(void);
int main()
{
os_sys_init(task_init);
while(1){}
return 0;
}
__task void task_init(void)
{
//initialization ports for led
*AT91C_PIOB_PER = (1 << 1);
*AT91C_PIOB_OER = (1 << 1);
*AT91C_PIOB_OWER = (1 << 1);
tsk_ID1 = os_tsk_create(task1, 1); //priority:1
tsk_ID2 = os_tsk_create(task2, 1); //priority:1
tsk_ID3 = os_tsk_create(task3, 2); //priority:2
os_tsk_delete_self(); //necessary
}
__task void task1(void)
{
int i = 0;
while(1){
for (i = 0; i < 0x5ffff; i++); //process
os_evt_set(0x1, tsk_ID3);
}
}
__task void task2(void)
{
int i = 0;
while(1){
for (i = 0; i < 0x6ffff; i++); //process
os_evt_set(0x2, tsk_ID3);
}
}
__task void task3(void)
{
while(1){
os_evt_wait_and(0x3, 0xffff); //wait for bit 1 and 2 forever
*AT91C_PIOB_ODSR ^= (1 << 1); //Blink
}
}
task3 در ابتدای اجرای خودش توسط تابع os_evt_wait_and اعلام میکنه که منتظر بیت شماره 0 و 1 پرچم میمونه. پارامتر اول الگوی بیت های مورد نظر تابع رو مشخص میکنه (حداکثر 16 بیت). 0x3 یعنی دو بیت اول. پارامتر دوم timeout رو مشخص میکنه. یعنی اندازه زمانی که تسک منتظر اتفاق افتادن این رویدادها باشه. بعد از گذشت این زمان، بدون توجه به رویدادها تسک ادامه پیدا میکنه. این پارامتر هم 16 بیتی به غیر از 0xffff. مقدار 0xffff یعنی بی نهایت (تابع اونقدر منتظر میمونه تا رویدادها صورت بگیرن). خب بعد از اجرای این تابع، تسک به حالت معلق درمیاد. task1 و task2 ادامه داده میشن تا هر کدوم به دستور os_evt_set برسن. اینجا همونجایی که توسط این تسک ها اعلام رویداد میشه. پارامتر دوم این تابع شماره ID تسکی که می خوایم بیت های پرچمش رو یک کنیم. پارامتر اول بیت های مورد نظر برای فعال شدن رو مشخص میکنه. همونطور می بینید task1 بیت شماره 0 و task2 بیت شماره 1 از پرچم task3 رو فعال میکنه. بعد از فعال شدن هر دو بیت، task3 دوباره فعال میشه، تابع wait تسک3 همه بیت های پرچم رو صفر میکنه (برای دفعه بعد) و از تابع خارج میشه تا تسک ادامه پیدا کنه.
تابع wait رویداد یک مقدار برگشتی هم داره (برای وقتی که timeout غیر از 0xffff باشه) اگه مقدار برگشتی OS_R_EVT باشه یعنی با فعال شدن پرچم ها (رویداد) از تابع گذشته شده و اگه OS_R_TMO باشه یعنی زمان timeout تموم و از تابع خارج شده. با توجه به این مقدار برگشتی میشه تصمیم گیری مناسب کرد.
تابع مشابه دیگه ای به نام os_evt_wait_or وجود داره که فقط منتظر یکی از بیت های مشخص شده پرچم میمونه. یعنی لازم نیست همه بیت های مشخص شده برای پرچم یک بشن تا از تابع گذشته بشه. فقط یکی از بیت ها کافیه. برای اینکه بفهمیم کدوم یکی از بیت ها (کدوم رویداد) باعث فعال شدن تسک شده، از تابع os_evt_get استفاده میکنیم. خروجی این تابع مقدار 16 بیتی که بیت رویداد مورد نظر رو یک کرده.
یه نکته مهم اینکه تو توابع وقفه irq (نه fiq) از تابع isr_evt_set برای فعال کردن پرچم ها استفاده میشه (نه تابع os_evt_set)
می خوام نقش این توابع تعیین رویداد رو بیشتر درک کنید
فکر کتید اگه این توابع نبودن باید چیکار میکردین؟؟
...
...
میشه چندتا متغیر سراسری تعریف کرد. مقدارشون صفر بشه. تو تابعی که قراره wait بخوره، این متغیر ها رو تو حلقه چک میکنیم. تا موقعی که صفر هستن از حلقه گذشته نشه. مقدار این متغیرها هم تو تسک هایی که رویداد رو تولید میکنن یک میشه...
خب بنظرتون اشکالش چیه؟
اگه task3 که می خواد منتظر رویداد باشه، اولویتش از تسک های دیگه بیشتر باشه (مثل مثال بالا)، تا موقعی که داره دستوراتش (حلقه چک کردن متغیر) اجرا میشه، اصلا اجازه اجرا شدن تسک های دیگه رو نمیده!! (یعنی برنامه همین جا قفل میشه)
اگه این تسک با تسک های دیگه هم اولویت باشه، اجازه اجرا به اونا هم داده میشه. اما خب زمان پردازش به task3 هم داده میشه. یعنی پردازش بیخود. تلف کردن وقت پردازنده. در حالی که اگه از تابع تعیین رویداد سیستم عامل استفاده کنیم، دیگه سراغ task3 نمیاد(تسک به حالت معلق درمیاد) تا وقتی که رویدادها صورت بگیرن.
امیدوارم تونسته باشم مطلب رو برسونم. تاپیک مهمی بود. اهمیتش رو در عمل خواهید دید...
سلام سلام سلام !!
شرمنده خیلی دیر شد :mrgreen:
این پست به تعیین رویدادها و اینکه چطوری تسک ها می تونن اجرای هم رو کنترل کنند، اختصاص داره. (preemptive)
طبق معمول با مثال توضیح میدم. فرض کنید تسکی داریم که برای اجرا شدنش باید چندتا تسک دیگه کامل یا تا حدودی اجرا شده باشن. مثلا به داده های نیاز داره که توسط چندتا تسک دیگه تامین میشه. یا اینکه زمینه اجرای این تسک رو، چندتا تسک دیگه آماده کنن. به دلیل اینکه ممکنه شرایط مختلفی پیش بیاد ما نمی تونیم دقیق مشخص کنیم که اون تسک ها کی به مرحله ای میرسن که زمینه برای اجرای تسک مورد نظر آماده بشه.
هر تسک یک پرچم (flag) 16 بیتی برای تعیین رویدادها داره. هر بیت یک رویداد. فرض کنید task3 به مرحله ای از اجرا که رسید که باید منتظر 2 تا رویداد بمونه بعد ادامه بده. این تسک با اشاره به 2 تا از بیت های پرچمش به سیستم عامل اعلام میکنه که منتظره این دو پرچم فعال بشن و بعد ادامه بده. این 2 تا رویداد توسط دو تسک دیگه به نام های task1 و task2 تامین میشن. پس تو هر کدوم از تسک های 1 و 2 باید در قسمت مورد نظر این دو پرچم فعال شده و رویداد خودشون رو اعلام کنن. به محض اینکه هر دو پرجم فعال شدن، task3 روند اجرای خودش رو ادامه میده.
#include <AT91SAM7X256.h>
#include <rtl.h>
OS_TID tsk_ID1, tsk_ID2, tsk_ID3;
__task void task_init(void);
__task void task1(void);
__task void task2(void);
__task void task3(void);
int main()
{
os_sys_init(task_init);
while(1){}
return 0;
}
__task void task_init(void)
{
//initialization ports for led
*AT91C_PIOB_PER = (1 << 1);
*AT91C_PIOB_OER = (1 << 1);
*AT91C_PIOB_OWER = (1 << 1);
tsk_ID1 = os_tsk_create(task1, 1); //priority:1
tsk_ID2 = os_tsk_create(task2, 1); //priority:1
tsk_ID3 = os_tsk_create(task3, 2); //priority:2
os_tsk_delete_self(); //necessary
}
__task void task1(void)
{
int i = 0;
while(1){
for (i = 0; i < 0x5ffff; i++); //process
os_evt_set(0x1, tsk_ID3);
}
}
__task void task2(void)
{
int i = 0;
while(1){
for (i = 0; i < 0x6ffff; i++); //process
os_evt_set(0x2, tsk_ID3);
}
}
__task void task3(void)
{
while(1){
os_evt_wait_and(0x3, 0xffff); //wait for bit 1 and 2 forever
*AT91C_PIOB_ODSR ^= (1 << 1); //Blink
}
}
task3 در ابتدای اجرای خودش توسط تابع os_evt_wait_and اعلام میکنه که منتظر بیت شماره 0 و 1 پرچم میمونه. پارامتر اول الگوی بیت های مورد نظر تابع رو مشخص میکنه (حداکثر 16 بیت). 0x3 یعنی دو بیت اول. پارامتر دوم timeout رو مشخص میکنه. یعنی اندازه زمانی که تسک منتظر اتفاق افتادن این رویدادها باشه. بعد از گذشت این زمان، بدون توجه به رویدادها تسک ادامه پیدا میکنه. این پارامتر هم 16 بیتی به غیر از 0xffff. مقدار 0xffff یعنی بی نهایت (تابع اونقدر منتظر میمونه تا رویدادها صورت بگیرن). خب بعد از اجرای این تابع، تسک به حالت معلق درمیاد. task1 و task2 ادامه داده میشن تا هر کدوم به دستور os_evt_set برسن. اینجا همونجایی که توسط این تسک ها اعلام رویداد میشه. پارامتر دوم این تابع شماره ID تسکی که می خوایم بیت های پرچمش رو یک کنیم. پارامتر اول بیت های مورد نظر برای فعال شدن رو مشخص میکنه. همونطور می بینید task1 بیت شماره 0 و task2 بیت شماره 1 از پرچم task3 رو فعال میکنه. بعد از فعال شدن هر دو بیت، task3 دوباره فعال میشه، تابع wait تسک3 همه بیت های پرچم رو صفر میکنه (برای دفعه بعد) و از تابع خارج میشه تا تسک ادامه پیدا کنه.
تابع wait رویداد یک مقدار برگشتی هم داره (برای وقتی که timeout غیر از 0xffff باشه) اگه مقدار برگشتی OS_R_EVT باشه یعنی با فعال شدن پرچم ها (رویداد) از تابع گذشته شده و اگه OS_R_TMO باشه یعنی زمان timeout تموم و از تابع خارج شده. با توجه به این مقدار برگشتی میشه تصمیم گیری مناسب کرد.
تابع مشابه دیگه ای به نام os_evt_wait_or وجود داره که فقط منتظر یکی از بیت های مشخص شده پرچم میمونه. یعنی لازم نیست همه بیت های مشخص شده برای پرچم یک بشن تا از تابع گذشته بشه. فقط یکی از بیت ها کافیه. برای اینکه بفهمیم کدوم یکی از بیت ها (کدوم رویداد) باعث فعال شدن تسک شده، از تابع os_evt_get استفاده میکنیم. خروجی این تابع مقدار 16 بیتی که بیت رویداد مورد نظر رو یک کرده.
یه نکته مهم اینکه تو توابع وقفه irq (نه fiq) از تابع isr_evt_set برای فعال کردن پرچم ها استفاده میشه (نه تابع os_evt_set)
می خوام نقش این توابع تعیین رویداد رو بیشتر درک کنید
فکر کتید اگه این توابع نبودن باید چیکار میکردین؟؟
...
...
میشه چندتا متغیر سراسری تعریف کرد. مقدارشون صفر بشه. تو تابعی که قراره wait بخوره، این متغیر ها رو تو حلقه چک میکنیم. تا موقعی که صفر هستن از حلقه گذشته نشه. مقدار این متغیرها هم تو تسک هایی که رویداد رو تولید میکنن یک میشه...
خب بنظرتون اشکالش چیه؟
اگه task3 که می خواد منتظر رویداد باشه، اولویتش از تسک های دیگه بیشتر باشه (مثل مثال بالا)، تا موقعی که داره دستوراتش (حلقه چک کردن متغیر) اجرا میشه، اصلا اجازه اجرا شدن تسک های دیگه رو نمیده!! (یعنی برنامه همین جا قفل میشه)
اگه این تسک با تسک های دیگه هم اولویت باشه، اجازه اجرا به اونا هم داده میشه. اما خب زمان پردازش به task3 هم داده میشه. یعنی پردازش بیخود. تلف کردن وقت پردازنده. در حالی که اگه از تابع تعیین رویداد سیستم عامل استفاده کنیم، دیگه سراغ task3 نمیاد(تسک به حالت معلق درمیاد) تا وقتی که رویدادها صورت بگیرن.
امیدوارم تونسته باشم مطلب رو برسونم. تاپیک مهمی بود. اهمیتش رو در عمل خواهید دید...
دیدگاه