私は、共有メモリ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
とマークされ、適切なメモリバリアが呼び出されていることを確認することだけです。
これは良いアイデアです(わかりやすい)。私はこの方向で調査します! – Patrick