Thực hành Contiki OS - Concurrency

22 Tháng Tư 2021

Đa tác vụ có thể tăng khả năng mở rộng của các ứng dụng Contiki-NG của bạn. Bài này chúng ta sẽ khám phá cách làm việc với concurrency (đồng thời) trong các ứng dụng Contiki-NG. Nội dung chính bài viết này gồm tìm hiểu về concurrency, threading và task scheduling

Thực hành Contiki OS - Concurrency

Introduction to Concurrency

Concurrency là khả năng thực hiện nhiều hơn một tác vụ cùng một lúc. Giả sử bạn có một WSN với nhiều thiết bị cảm biến. Bạn muốn đọc giá trị của tất cả các cảm biến cùng một lúc.

Chúng ta có thể xem minh họa một concurrency bên dưới. Một quy trình có thể xử lý nhiều tác vụ với các mô hình vấn đề khác nhau. Chúng ta có thể đạt được sự đồng thời bằng cách áp dụng một hàng đợi FIFO hoặc LIFO. Một giải pháp khác là áp dụng việc lập trình không đồng trong code

Concurrency Approach in Contiki-NG

Contiki-NG cung cấp các tính năng về conccurrency. Chúng ta có thể sử dụng một số cách tiếp cận để phát triển tính đồng thời dựa trên ứng dụng. Có bốn phương pháp mà chúng ta có thể triển khai concurrency trong các ứng dụng Contiki-NG, như sau:

  • Processes
  • Timers
  • Threading
  • Task scheduling

Introducing Contiki-NG Processes

Các ứng dụng Contiki-NG có thể chạy trên một quy trình thực thi ở chế độ cooperative hoặc preemptive. Chế độ cooperative là thực thi thông thường trong vi điều khiển. Quá trình có chế độ ưu tiên chạy với sự gián đoạn do I / O hoặc bộ hẹn giờ (timer). Nói chung, chúng ta có thể tạo một tiến trình trong Contiki-NG bằng cách gọi PROCESS (). Chúng ta có thể định nghĩa một process như sau:

PROCESS(name, process_name)
  • name: là một biến của quá trình
  • process_name là tên của process đại diện bằng chuổi.

Sau đó, chúng ta thực hiện quy trình này bằng PROCESS_THREAD. Chúng ta có thể khai báo điều này như sau:

PROCESS_THREAD(name, ev, data)
{
 PROCESS_BEGIN();
 // do semothing
 PROCESS_END();
}
  • PROCESS_BEGIN(): khai báo sư bặt đầu của một protothread
  • PROCESS_END(): Khai báo sự kết thúc của một protothread
  • PROCESS_EXIT(): Thoát khỏi process
  • PROCESS_WAIT_EVENT(): Chờ đợi 1 sự kiện nào đó (ngắt ngoài, timers,...)
  • PROCESS_WAIT_EVENT_UNTIL(): Chờ đợi một sự kiện, nhưng với các điều kiện
  • PROCESS_YIELD (): Chờ sự kiện bất kỳ; tương đương với PROCESS_WAIT_EVENT ()
  • PROCESS_WAIT_UNTIL (): Chờ một điều kiện nhất định; có thể không đến từ lại vi điều khiển
  • PROCESS_PAUSE (): Tạm thời dừng vi điều khiển

Với mục đích demo, chúng ta sẽ tạo một ứng dụng Contiki-NG đơn giản. Tạo một thư mục có tên là demo-process. Sau đó, tạo hai tệp, demo-process.c và Makefile. Bước đầu tiên là viết một chương trình cho demo-process.c. 

#include "contiki.h"
#include <stdio.h>
PROCESS(myprocess1, "process 1");
PROCESS(myprocess2, "process 2");
PROCESS(myprocess3, "process 3");
AUTOSTART_PROCESSES(&myprocess1,&myprocess2,&myprocess3);

Bạn có thể thấy rằng chúng tôi đã tạo ra ba process. Chúng ta chuyển các biến process này cho AUTOSTART_PROCESSES (). Chúng ta triển khai code cho các process của mình

PROCESS_THREAD(myprocess1, ev, data)
{
 PROCESS_BEGIN();
 printf("This message from process 1\n");
 PROCESS_END();
}
PROCESS_THREAD(myprocess2, ev, data)
{
 PROCESS_BEGIN();
 printf("This message from process 2\n");
 PROCESS_END();
}
PROCESS_THREAD(myprocess3, ev, data)
{
 PROCESS_BEGIN();
 printf("This message from process 3\n");
 PROCESS_END();
}

Lưu chương trình này. Chúng ta tiếp tục Makefile. Chúng ta khai báo chương trình của mình và đường dẫn của thư mục gốc Contiki-NG thông qua CONTIKI:

CONTIKI_PROJECT = demo-process
all: $(CONTIKI_PROJECT)
CONTIKI = /home/user/Documents/book/contiki
include $(CONTIKI)/Makefile.include

Working with Timers

Chúng ta có thể thực hiện một số hoạt động bằng cách sử dụng các timer. Contiki-NG có một clock và một số mô-đun timer, chẳng hạn như timer, stimer, ctimer, etimer, và rtimer. Tất cả các thư viện này có thể được tìm thấy trong thư mục <contiki> / os / sys

Clock Library

Thư viện Clock Library có thể được sử dụng để thực hiện các hoạt động chung với thời gian. Nó được khai báo trong clock.h từ thư mục <contiki> / os / sys. Bạn có thể xem nội dung của tệp clock.h tại đây:

clock_time_t clock_time(); // Get the system time.
unsigned long clock_seconds(); // Get the system time in seconds.
void clock_delay(unsigned int delay); // Delay the CPU.
void clock_wait(int delay); // Delay the CPU for a number of clock ticks.
void clock_init(void); // Initialize the clock module.
CLOCK_SECOND; // The number of ticks per second

Để sử dụng thư viện này, hãy gọi các hàm này trực tiếp từ chương trình. Ví dụ: bạn có thể truy cập thời gian của clock bằng cách gọi hàm clock_time():

clock_time_t t = clock_time();
printf("Timer start: %lu \n", t);

Timer Library

Thư viện timer cung cấp các chức năng để cài đặt, đặt lại và khởi động lại timer và để kiểm tra xem timer đã expired chưa. Thư viện này được tìm thấy trong tệp timer.h và định nghĩa một số function như sau:

void timer_set(struct timer *t, clock_time_t interval); // Start the timer.
void timer_reset(struct timer *t); // Restart the timer from the previous expiration time.
void timer_restart(struct timer *t); // Restart the timer from current time.
int timer_expired(struct timer *t); // Check if the timer has expired.
clock_time_t timer_remaining(struct timer *t); // Get the time until the timer expires.

Một ứng dụng phải tự kiểm tra xem bộ hẹn giờ của nó đã hết hạn chưa. Để sử dụng thư viện Timer, chúng ta gọi timer_set (). Sau đó, chúng ta có thể xác minh bộ hẹn giờ đã hết hạn bằng cách gọi hàm timer_expired ():

timer_set(&timer_timer, 3 * CLOCK_SECOND);
if(timer_expired(&timer_timer)){
 t = clock_time();
 printf("timer expired: %lu \n", t);
}

Stimer Library

Thư viện Stimer tương tự như thư viện timer, nhưng sử dụng các giá trị thời gian tính bằng giây. Sau đây là danh sách các hàm Stimer được xác định trong tệp stimer.h.

void stimer_set(struct stimer *t, unsigned long interval); // Start the timer.
void stimer_reset(struct stimer *t); // Restart the stimer from the previous expiration time.
void stimer_restart(struct stimer *t); // Restart the stimer from current time.
int stimer_expired(struct stimer *t); // Check if the stimer has expired.
unsigned long stimer_remaining(struct stimer *t); // Get the 
time until the timer expires.

Etimer Library

Thư viện Etimer là một thư viện hẹn giờ sự kiện tạo ra một sự kiện. Chúng tôi có thể xác minh sự kiện này bằng PROCESS_WAIT_EVENT_UNTIL (). Bạn có thể xem khai báo bộ hẹn giờ sự kiện trong tệp etimer.h:

void etimer_set(struct etimer *t, clock_time_t interval); // Start the timer.
void etimer_reset(struct etimer *t); // Restart the timer from the previous expiration time.
void etimer_restart(struct etimer *t); // Restart the timer from current time.
void etimer_stop(struct etimer *t); // Stop the timer.
int etimer_expired(struct etimer *t); // Check if the timer has expired.
int etimer_pending(); // Check if there are any non-expired event timers.
clock_time_t etimer_next_expiration_time(); // Get the next event timer expiration time.
void etimer_request_poll(); // Inform the etimer library that the system clock has changed

CTimer Library

Thư viện CTimer cung cấp một hàm gọi lại sẽ được gọi khi bộ hẹn giờ hết hạn (timer expired)

void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), 
void *ptr); // Start the timer.
void ctimer_reset(struct ctimer *t); // Restart the timer from the previous expiration time.
void ctimer_restart(struct ctimer *t); // Restart the timer

Rtimer Library

Thư viện Rtimer cung cấp lập lịch và thực thi các tác vụ thời gian thực. Chúng ta có thể xác định thời gian thực thi cụ thể khi chúng ta đặt rtimer bằng cách sử dụng hàm rtimer_set ()

RTIMER_CLOCK_LT(a, b); // This should give TRUE if 'a' is less than 'b', otherwise false.
RTIMER_ARCH_SECOND; // The number of ticks per second.
void rtimer_arch_init(void); // Initialize the rtimer architecture code.
rtimer_clock_t rtimer_arch_now(); // Get the current time.
int rtimer_arch_schedule(rtimer_clock_t wakeup_time); // Schedule a call to rtimer_run_next().

Threading

Contiki-NG OS loại bỏ đa luồng khỏi source code. Bạn có thể đọc về nó tại đây. Điều này có thể cũng được hỗ trợ trong phiên bản tiếp theo. Nếu bạn muốn làm việc với đa luồng, bạn có thể sử dụng Contiki thông thường. Contiki cung cấp phân luồng để chúng ta có thể thực hiện nhiều tác vụ thông qua mt_thread trong tệp mt.h. Tệp này được tìm thấy trong mã nguồn Contiki trong thư mục <contiki_root> / core / sys /. Bạn có thể thấy một số chức năng trong tệp mt.h như sau:

void mt_init(void) : Initializes the library.
void mt_remove(void) : Uninstalls the library.
void mt_start(struct mt_thread *thread, void (* function)(void *), 
void *data) : Starts a thread.
void mt_exit(void) : Exits a thread.
void mt_exec(struct mt_thread *thread) : Execute as thread.
void mt_yield(void) : Release control voluntarily.
void mt_stop(struct mt_thread *thread) : Stops a thread.

Bạn có thể thấy các trạng thái của thread cho một ứng dụng Contiki trong hình

Mỗi thread chạy trên một process. Mỗi thread có thể giao tiếp bằng cách chia sẻ tài nguyên hoặc trao đổi dữ liệu. Hình dưới cho thấy ba thead đang chạy trên một process

Task Scheduling

Task scheduling có nghĩa là tất cả các tác vụ được lên lịch để thực hiện. Trong ứng dụng Contiki-NG, task scheduling thu thập tất cả các tác vụ trong process, timers và threading

Tổng Kết

Chúng ta đã học cách triển khai với concurrency trong Contiki-NG OS. Thực hiện một số tác vụ trong ứng dụng Contiki-NG. Chúng tôi đã khám phá process, timers, threading và task scheduling. Chúc bạn đọc vui vẻ

Bình luận