2010-11-18 25 views
6

Tôi là một lập trình viên nhúng cố gắng mô phỏng một trình lên lịch thời gian thực trong môi trường Win32 sử dụng Visual Studio 2010 và MingW (hai môi trường xây dựng riêng biệt). Tôi rất xanh trên môi trường lập kế hoạch Win32 và đã đánh một bức tường gạch với những gì tôi đang cố gắng làm. Tôi không cố gắng để đạt được hành vi thời gian thực - chỉ để có được các nhiệm vụ mô phỏng để chạy theo thứ tự và trình tự giống như chúng sẽ thực hiện trên phần cứng đích thực sự.Bắt buộc lập lịch trình chuỗi Win32 thành một chuỗi được xác định dựa trên mức ưu tiên

Trình lập lịch biểu thời gian thực được mô phỏng có mục tiêu đơn giản - luôn thực thi tác vụ ưu tiên cao nhất (chuỗi) có thể chạy. Ngay sau khi một nhiệm vụ có thể chạy - nó phải làm nhiệm vụ trước đang chạy nếu nó có mức ưu tiên cao hơn nhiệm vụ hiện đang chạy. Một nhiệm vụ có thể chạy do một sự kiện bên ngoài mà nó đang chờ đợi, hoặc một thời gian ra/chặn thời gian/thời gian ngủ hết hạn - với một ngắt đánh dấu tạo ra cơ sở thời gian.

Ngoài hành vi ưu tiên này, nhiệm vụ có thể mang lại hoặc tình nguyện từ bỏ phần thời gian của nó vì đang thực hiện chức năng ngủ hoặc chờ.

Tôi đang mô phỏng điều này bằng cách tạo chuỗi Win32 ưu tiên thấp cho mỗi tác vụ được tạo bởi trình lên lịch thời gian thực được mô phỏng (luồng có hiệu quả chuyển ngữ cảnh sẽ thực hiện trên mục tiêu được nhúng thực), ưu tiên trung bình Chuỗi Win32 như một trình xử lý ngắt giả (xử lý các ngắt đánh dấu mô phỏng và các yêu cầu về năng suất được báo hiệu bằng cách sử dụng đối tượng sự kiện Win32) và chuỗi Win32 ưu tiên cao hơn để mô phỏng thiết bị ngoại vi tạo ra các ngắt đánh dấu. Khi trình xử lý ngắt giả thiết lập rằng một lệnh chuyển đổi nhiệm vụ sẽ xảy ra, nó đình chỉ chuỗi đang thực thi bằng cách sử dụng SuspendThread() và tiếp tục luồng thực thi tác vụ mới được chọn bằng cách sử dụng ResumeThread(). Trong số nhiều tác vụ và các chuỗi Win32 liên quan của chúng có thể được tạo ra, chỉ có một luồng quản lý tác vụ sẽ thoát khỏi trạng thái bị treo bất kỳ lúc nào. Điều quan trọng là một sợi treo bị đình chỉ ngay lập tức rằng SuspendThread() được gọi, và rằng chuỗi xử lý ngắt giả thực thi ngay sau khi sự kiện nói rằng ngắt đang chờ được báo hiệu - nhưng đây không phải là hành vi tôi đang thấy. Một vấn đề ví dụ mà tôi đã có một công việc xung quanh cho: Khi một nhiệm vụ/thread mang lại sự kiện lợi nhuận được chốt trong một biến và chuỗi xử lý ngắt được báo hiệu là có một ngắt giả (năng suất) cần Chế biến. Bây giờ trong một hệ thống thời gian thực như tôi đang sử dụng để lập trình tôi mong đợi các thread xử lý ngắt để thực hiện ngay lập tức rằng nó được báo hiệu bởi vì nó có một ưu tiên cao hơn so với các sợi tín hiệu nó. Những gì tôi thấy trong môi trường Win32 là luồng báo hiệu luồng ưu tiên cao hơn tiếp tục một thời gian trước khi bị đình chỉ - hoặc vì phải mất một thời gian trước khi chuỗi ưu tiên cao hơn được báo hiệu bắt đầu thực hiện hoặc vì phải mất một thời gian nhiệm vụ thực sự ngừng chạy - Tôi không chắc chắn. Trong mọi trường hợp, điều này có thể dễ dàng chính xác bằng cách tạo khối chuỗi Win32 có hiệu lực trên semaphore sau khi báo hiệu chuỗi xử lý ngắt Win32 và có xử lý ngắt chuỗi Win32 bỏ chặn luồng khi nó kết thúc chức năng của nó (bắt tay). Sử dụng hiệu quả đồng bộ hóa luồng để buộc mô hình lên lịch cho những gì tôi cần. Tôi đang sử dụng SignalObjectAndWait() cho mục đích này.

Sử dụng kỹ thuật này mô phỏng hoạt động hoàn hảo khi bộ lập lịch thời gian thực được mô phỏng hoạt động ở chế độ hợp tác - nhưng không (khi cần) trong chế độ ưu tiên.Vấn đề với chuyển đổi nhiệm vụ preemptive là tôi đoán như nhau, nhiệm vụ tiếp tục thực hiện một thời gian sau khi nó đã được yêu cầu đình chỉ trước khi nó thực sự ngừng chạy để hệ thống không thể được đảm bảo để được ở trạng thái nhất quán khi chuỗi chạy lệnh treo. Trong trường hợp preemptive mặc dù, bởi vì nhiệm vụ không biết khi nào nó sẽ xảy ra, cùng một kỹ thuật của việc sử dụng một semaphore để ngăn chặn các Win32 thead tiếp tục cho đến khi nó tiếp tục tiếp theo không thể được sử dụng.

Có ai đó đã thực hiện điều này ở xa bài đăng này - xin lỗi vì độ dài của nó!

Câu hỏi của tôi sau đó là:

  • Làm thế nào tôi có thể buộc Win32 (XP) lên lịch bắt đầu và chấm dứt nhiệm vụ ngay lập tức rằng ngưng và tiếp tục chức năng chủ đề được gọi là - hay - làm thế nào tôi có thể buộc một ưu tiên cao hơn Win32 thread để bắt đầu thực hiện ngay lập tức mà nó có thể làm như vậy (đối tượng nó bị chặn trên được báo hiệu). Hiệu quả buộc Win32 lên lịch lại các tiến trình đang chạy của nó.

  • Có cách nào không đồng bộ dừng tác vụ chờ một sự kiện khi nó không nằm trong đường dẫn thực thi tuần tự nhiệm vụ/chủ đề không.

  • Trình mô phỏng hoạt động tốt trong môi trường Linux nơi tín hiệu POSIX được sử dụng để ngắt luồng có hiệu quả - có tương đương trong Win32 không?

Cảm ơn bất kỳ ai đã dành thời gian đọc bài đăng dài này và đặc biệt cảm ơn trước bất kỳ ai có thể nắm lấy tay kỹ sư thời gian thực của tôi qua mê cung Win32 này.

Trả lời

5

Nếu bạn cần lập lịch biểu của riêng mình, bạn có thể xem xét sử dụng fibers thay vì chuỗi. Các sợi giống như các luồng, trong đó chúng là các khối riêng biệt của mã thực thi, tuy nhiên các sợi có thể được lên lịch trong mã người dùng trong khi các luồng chỉ được hệ điều hành lên lịch. Một sợi đơn có thể lưu trữ và quản lý việc lập lịch trình của nhiều sợi, và các sợi thậm chí có thể lên lịch cho nhau.

+0

Nhưng các sợi sẽ không chạy đồng thời trên nhiều lõi. –

+0

Cảm ơn lời khuyên.Tôi sẽ tìm kiếm một số tài liệu tham khảo trên sợi ngày mai. – Richard

+0

@Zan: nhiều sợi có thể chạy trong một chuỗi duy nhất và bạn có thể chạy nhiều luồng, một chuỗi cho mỗi lõi. –

1

Thứ nhất, bạn sử dụng giá trị ưu tiên nào cho chuỗi của mình?

Nếu bạn đặt chủ đề ưu tiên cao thành THREAD_PRIORITY_TIME_CRITICAL, nó sẽ chạy khá nhanh --- chỉ những chuỗi được liên kết với quy trình thời gian thực mới có mức độ ưu tiên cao hơn.

Thứ hai, làm thế nào để bạn biết rằng việc tạm ngưng và tiếp tục không xảy ra ngay lập tức? Bạn có chắc đây là vấn đề không?

Bạn không thể buộc một chủ đề phải đợi thứ gì đó từ bên ngoài mà không cần treo chuỗi để chờ mã chờ; nếu SuspendThread không hoạt động cho bạn thì điều này sẽ không giúp ích gì.

Gần nhất với tín hiệu có lẽ là QueueUserAPC, sẽ lập lịch cuộc gọi lại để chạy vào lần tiếp theo luồng vào trạng thái chờ "có thể cảnh báo", ví dụ: bằng cách gọi số SleepEx hoặc WaitForSingleObjectEx hoặc tương tự.

1

@Anthony W - cảm ơn lời khuyên. Tôi đã chạy các chủ đề Win32 mô phỏng các nhiệm vụ thời gian thực tại THREAD_PRIORITY_ABOVE_NORMAL, và các chủ đề chạy trình xử lý ngắt giả và trình tạo ngắt đánh dấu tại THREAD_PRIORITY_HIGHEST. Các chủ đề đã bị treo tôi đã thay đổi thành THREAD_PRIORITY_IDLE trong trường hợp có bất kỳ sự khác biệt nào. Tôi vừa thử đề xuất của bạn về việc sử dụng THREAD_PRIORITY_TIME_CRITICAL nhưng tiếc là nó không tạo ra bất kỳ sự khác biệt nào.

Liên quan đến câu hỏi của bạn, tôi chắc chắn rằng việc tạm ngưng và tiếp tục không xảy ra ngay lập tức là vấn đề - tôi không có. Đó là dự đoán tốt nhất của tôi trong một môi trường mà tôi không quen thuộc. Suy nghĩ của tôi về sự thất bại của việc đình chỉ và tiếp tục làm việc ngay lập tức bắt nguồn từ quan sát của tôi khi một nhiệm vụ mang lại. Nếu tôi thực hiện cuộc gọi để sinh lợi (tín hiệu [sử dụng sự kiện Win32] một chuỗi Win32 ưu tiên cao hơn để chuyển sang tác vụ thời gian thực tiếp theo) Tôi có thể đặt điểm ngắt sau khi sinh lời và bị trúng trước điểm ngắt trong ưu tiên cao hơn chủ đề. Không rõ liệu sự chậm trễ trong báo hiệu sự kiện và nhiệm vụ ưu tiên cao hơn đang chạy hay trì hoãn tạm dừng luồng và luồng thực sự ngừng chạy đã gây ra điều này - nhưng hành vi này chắc chắn đã được quan sát. Điều này đã được cố định bằng cách sử dụng một cái bắt tay semaphore, nhưng điều đó không thể được thực hiện cho các preemptions gây ra bởi tick ngắt.

Tôi biết mô phỏng không hoạt động như tôi mong đợi bởi vì một tập hợp các kiểm tra kiểm tra trình tự lập kế hoạch của các tác vụ thời gian thực là không thành công. Nó luôn luôn có thể lên lịch có một vấn đề, hoặc thử nghiệm có một vấn đề, nhưng thử nghiệm sẽ chạy trong nhiều tuần mà không thất bại trên một mục tiêu thời gian thực thực vì vậy tôi nghiêng để nghĩ rằng thử nghiệm và lên lịch là ok. Một sự khác biệt lớn là trên mục tiêu thời gian thực tần số đánh dấu là 1 ms, trong khi trên mục tiêu mô phỏng Win32 nó là 15ms với khá nhiều biến thể ngay cả sau đó.

@Remy - Tôi đã thực hiện khá nhiều đọc về sợi ngày nay, và kết luận của tôi là để mô phỏng lịch trình trong chế độ hợp tác, chúng sẽ hoàn hảo. Tuy nhiên, theo như tôi thấy, chúng chỉ có thể được sắp xếp bởi các sợi tự gọi hàm SwitchToFiber(). Một sợi có thể được thực hiện để chặn trên bộ đếm thời gian hoặc ngủ để nó chạy định kỳ, có hiệu quả trước khi các sợi đang chạy vào thời điểm đó không? Từ những gì tôi đã đọc câu trả lời là không vì chặn một sợi sẽ chặn tất cả các sợi chạy trong luồng. Nếu nó có thể được thực hiện để làm việc, có thể thực hiện định kỳ sợi sau đó gọi chức năng SwitchToFiber() để chọn sợi tiếp theo để chạy trước khi một lần nữa ngủ trong một thời gian cố định? Một lần nữa tôi nghĩ rằng câu trả lời là không vì một khi nó chuyển sang một sợi khác, nó sẽ không còn thực thi nữa và vì vậy sẽ không thực sự gọi hàm Sleep() cho đến khi lần sau sợi thực thi chuyển về nó. Xin vui lòng sửa logic của tôi ở đây nếu tôi đã có ý tưởng sai về cách thức hoạt động của sợi. Tôi nghĩ rằng nó có thể hoạt động nếu chức năng định kỳ có thể vẫn còn trong chủ đề riêng của nó, tách biệt với chuỗi đã thực hiện các sợi - nhưng (một lần nữa từ những gì tôi đã đọc) Tôi không nghĩ rằng một chủ đề có thể ảnh hưởng đến việc thực hiện của sợi chạy trong một sợi khác nhau. Một lần nữa tôi sẽ biết ơn nếu bạn có thể sửa chữa kết luận của tôi ở đây nếu họ là sai.

1

[EDIT] - đơn giản hơn hack bên dưới - có vẻ như việc đảm bảo tất cả các luồng chạy trên cùng một lõi CPU cũng khắc phục được sự cố: o) Sau tất cả. Vấn đề duy nhất sau đó là CPU chạy ở gần 100% và tôi không chắc liệu nhiệt có gây hại cho nó hay không. [/ EDIT]

Ahaa! Tôi nghĩ rằng tôi có một công việc xung quanh cho điều này - nhưng xấu xí của nó. Sự xấu xí được giữ trong lớp cổng mặc dù.

Điều tôi làm bây giờ là lưu trữ ID luồng mỗi lần tạo chuỗi để chạy tác vụ (chuỗi Win32 được tạo cho mỗi tác vụ thời gian thực được tạo). Sau đó tôi đã thêm hàm dưới đây - được gọi là sử dụng macro theo dõi. Các macro theo dõi có thể được định nghĩa để làm bất cứ điều gì bạn muốn, và đã được chứng minh rất hữu ích trong trường hợp này. Các ý kiến ​​trong đoạn code dưới đây giải thích. Mô phỏng không hoàn hảo, và tất cả điều này đúng là lập lịch trình chuỗi khi nó đã lệch khỏi lập lịch thời gian thực trong khi tôi muốn nó không bị sai ở vị trí đầu tiên, nhưng vị trí của macro theo dõi làm cho mã chứa giải pháp này vượt qua tất cả các thử nghiệm:

void vPortCheckCorrectThreadIsRunning(void) 
{ 
xThreadState *pxThreadState; 

    /* When switching threads, Windows does not always seem to run the selected 
    thread immediately. This function can be called to check if the thread 
    that is currently running is the thread that is responsible for executing 
    the task selected by the real time scheduler. The demo project for the Win32 
    port calls this function from the trace macros which are seeded throughout 
    the real time kernel code at points where something significant occurs. 
    Adding this functionality allows all the standard tests to pass, but users 
    should still be aware that extra calls to this function could be required 
    if their application requires absolute fixes and predictable sequencing (as 
    the port tests do). This is still a simulation - not the real thing! */ 
    if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) 
    { 
     /* Obtain the real time task to Win32 mapping state information. */ 
     pxThreadState = (xThreadState *) *((unsigned long *) pxCurrentTCB); 

     if(GetCurrentThreadId() != pxThreadState->ulThreadId) 
     { 
      SwitchToThread(); 
     } 
    } 
} 
Các vấn đề liên quan