2010-12-28 8 views
1

私は物理メモリを直接読み書きするための完全なアクセス権を持つ組み込みプラットフォームで作業しています。ターミナルI/O(特定のメモリ領域でターミナルリード/ wrtieをエミ​​ュレートする方法)

私はリアルタイム処理のアプリケーションをLinuxと同時に実行しているが、完全にLinuxから分離している非対称処理にも取り組んでいます。

RT appからLinuxコンソールにメッセージを表示したいのですが(LinuxからRT appにコマンドを送信することもできます)。私の現在の解決策は、RTアプリからシリアルポートにすべてを出力することです。そして、Linuxではシリアルポート入力を読みます。

これは動作しますが、RTアプリケーションとLinuxが同じ物理マシンにあるため、これは不要です。シリアルポートがどのように動作するかを考え直すと、メモリバッファがあり、アプリケーションはそのバッファを読み書きできます。したがって、ターミナルディスプレイを特定のメモリ領域(0x10000)に接続することが可能かどうか、RTアプリケーションが何らかのメッセージを0x10000に「印刷」すると、Linux端末はメッセージを表示しますか?

答えて

1

「メールボックス」技術を使用して、一連の仮想シリアルポートを構築することができます。簡単な作業接続の半分について説明します。おそらく、これらのいずれかが各方向に向かうようにして、コマンドを送信して応答を得ることができます。

カーネルの初期化中にメモリチャンクを予約します。これはしばしばページなので4kとしましょうが、256バイトまたは16バイトも機能します。他の方向のために秒を予約してください。

チャネルの「ライター」が何かを言いたいときは、まず最初の32ビットワードがゼロであるかどうかをチェックします。そうであれば、5番目のバイトから開始して、最大4k-4 = 4092バイトまでのメッセージテキストまたはバイナリデータを書き込みます。次に、最初のワードを書き込んだバイト数と等しく設定します。

受信側は、受信しているチャネルの最初のワードのバイト数を監視します。 0以外のバイト数を見ると、メモリからそのバイト数が読み込まれます。次に、バイトカウントをゼロに設定して、新しいメッセージがその都度都合のよいときに書き込まれることを作者に知らせる。

これは実際に実際のメモリにアクセスしたり、同じキャッシュを使用したり、バイト数を書き込むためのアトミックな書き込み操作(32ビットアトミック書き込みがない場合とにかく多くの16ビットカウントを使用するか、バッファを小さくして8ビットカウントを使用してください)。ライターはゼロになるとゼロ以外の値にしか設定できないので、リーダは0以外の値をゼロに設定することができます。

これはもちろん単純なメカニズムであり、いずれかの面が他方の面でブロックされる可能性があります。しかし、それを考慮に入れてメッセージを発信するコンポーネントを設計することができます。飛行中に複数のメッセージを表示する方法や、優先度やエラー報告チャネルを追加して並行して追加する方法もあります。

あなたがジャンプしてコードする前に、いくつかのウェブ検索を行います。私はすでにこのような何らかのメカニズムがあること、あるいはあなたのRTコンポーネントとLinuxコンポーネントを接続するために利用できる何か他のものがあることを確信しています。しかし、自分でやることを学ぶことは面白いかもしれません。機能を提供するOSがない小型の組み込みシステムでこの種の問題に遭遇する場合、必要です。

+0

これは良いアイデアです(わかりやすい)。私はこの方向で調査します! – Patrick

1

linuxでIPCを実行するにはいくつかの方法がありますが、通常はファイル記述子が使用されます。私の意見では、あなたの最善の策はあなたがやっていることをやり続けることです、あなたが言ったようにおそらく過剰ですが、あなた自身の共有メモリソリューションを実装しようとすると、間違いなく過大です。

EDIT:

コメントで述べたように、あなたはリアルタイムプロセスを実行しているという事実は、物事をオフに投げる、とネイティブIPCは、おそらくあなたの最善の策ではありません。 Here私はちょうどあなたが探している答えを提供するようだとグーグル記事。

すべてを読んでいない場合は、必要な通信の種類に応じて、使用する必要がある同時実行プリミティブとしてFIFOまたは共有メモリのいずれかを示します。個人的な経験から、同期についてはあまり心配する必要がないため、FIFOは長期的には頭痛を軽減します。

端末でプログラムを監視する場合は、fifo/sharedメモリから読み込み、stdoutにメッセージを送信する小さなプログラムを作成する必要があります。

+1

RTプロセスはLinuxカーネルの監督下では実行されないため、通常のIPCメソッドや同様のカーネルサービスはおそらく適用されません(カーネルはリアルタイムスケジューラによって最も優先度の低いタスクとして実行される傾向があります) –

+0

@Chrisこれは大きなポイントです。リアルタイムシステムは自分の利益のためにはあまりにも複雑すぎる:(私の答えを編集して補償する。 – num1

+0

リンクをありがとう、それは私が探している答えのように見えます!私はそれを読んで最初の投稿と解決策を組み合わせます。 – Patrick

0

私は、共有メモリFIFOシステムをプロセス間で通信するのに成功しました(これはあなたと同じシナリオではありません)。キーは、1つのスレッドのみがプロデューサになり、1つのスレッドはコンシューマになることができるということです。また、適切なメモリバリアを使用してキャッシングが適切に処理されることをChris Stratton氏が指摘していることを確認する必要があります。 Linuxはメモリ障壁のためのかなり単純なAPIを持っているので、私はあなたのリアルタイムアプリがそれに利用できるかもしれないか分からない。私は、メモリの障壁が必要な場所を特定しようとしました。

以下は、共有FIFOのテストされていない(完全に最適化されていない)実装です。 RTアプリケーションはFIFOに文字を書き込むことができ、LinuxのアプリやドライバはFIFOから文字を読み取ることができます。理想的には、データが準備ができていることをLinux側に通知するメカニズムがあります(RT側がポーズすると割り込みを発生させる可能性のある未使用のGPIOかもしれません)。さもなければ、Linux側はfifoのデータをポーリングすることができますが、それは通常の理由から理想的ではないでしょう。

struct fifo { 
    char volatile* buf; 
    int buf_len; 
    int volatile head; // index to first char in the fifo 
    int volatile tail; // index to next empty slot in fifo 
         // if (head == tail) then fifo is empty 
         // if (tail < head) the fifo has 'wrapped' 
}; 

void fifo_init(struct fifo* pFifo, char* buf, int buf_len) 
{ 
    pFifo->buf = buf; 
    pFifo->buf_len = buf_len; 
    pFifo->head = 0; 
    pFifo->tail = 0; 
} 

int fifo_is_full(struct fifo* pFifo) 
{ 
    int head; 
    int tail; 

    // a read barrier may be required here 
    head = pFifo->head; 
    tail = pFifo->tail; 

    // fifo is full if ading another char would cause 
    // tail == head 
    ++tail; 
    if (tail == pFifo->buf_len) { 
     tail = 0; 
    } 

    return (tail == head); 
} 


int fifo_is_empty( struct fifo* pFifo) 
{ 
    int head; 
    int tail; 

    // a read barrier may be required here 
    head = pFifo->head; 
    tail = pFifo->tail; 

    return head == tail; 
} 


// this function is the only one that modifies 
// the pFifo->tail index. It can only be used 
// by a single writer thread. 
int fifo_putchar(struct fifo* pFifo, char c) 
{ 
    int tail = pFifo->tail; 

    if (fifo_is_full(pFifo)) return 0; 

    pFifo->buf[tail] = c; 
    ++tail; 
    if (tail == pFifo->buf_len) { 
     tail = 0; 
    } 

    //note: the newly placed character isn't actually 'in' the fifo 
    // as far as the reader thread is concerned until the following 
    // statement is run  
    pFifo->tail = tail; 

    // a write barrier may need to be placed here depending on 
    // the system. Microsoft compilers place a barrier by virtue of 
    // the volatile keyword, on a Linux system a `wmb()` may be needed 
    // other systems will have other requirements 
    return 1; 
} 


// this function is the only one that modified the 
// pFifo->head index. It can only be used by a single 
// reader thread. 
int fifo_getchar(struct fifo* pFifo, char* pC) 
{ 
    char c; 
    int head = pFifo->head; 

    if (fifo_is_empty(pFifo)) return 0; 

    // a read barrier may be required here depending on the system 
    c = pFifo->buf[head]; 

    ++head; 
    if (head == pFifo->buf_len) { 
     head = 0; 
    } 

    // as far as the write thread is concerned, the char 
    // hasn't been removed until this statement is executed 
    pFifo->head = head; 

    // a write barrier might be required 

    *pC = c; 
    return 1; 
} 

インデックスを更新するときは、プラットフォームの「アトミック」APIを使用する方が適切な場合があります。

行うことができますいくつかの最適化:

  • FIFOサイズが2の累乗に制限されている場合、ラッピングはPUT /取得の機能を変更することができ
  • 適切にインデックスをマスキングすることによって処理することができますまたは追加のget/put関数を追加して文字列またはデータバイトの配列を受け入れ、文字列/配列をFIFOバッファに効率的にコピーすることができます。これにキーが設定

は、読者は作家がいる限りheadインデックスはデータが読み出された後まで更新されていないとして、それが上書きされることを心配することなく、FIFO内のデータを読み取ることができるということです。同様に、書き込み側については、データがバッファに書き込まれるまで、tailインデックスが更新されない限り、バッファの '空き'部分に書き込むことができます。本当に複雑なのは、適切な項目にvolatileとマークされ、適切なメモリバリアが呼び出されていることを確認することだけです。

関連する問題