-1

私はカーネルモジュールを書きました。これは、この Linkのようなキャラクターデバイスドライバーです。ドライバーには、次のような内部構造があります。

struct {
    str[500];
}channelData;

static channelData chData[4];

したがって、このドライバーを使用するマルチスレッドアプリケーションがあります。現在、このドライバーを次のように使用しています。

typedef struct
{
        int channelId;
        int len;
        char arg[100];
} driverArgs;

class DevDrv{
    static void STR_READ(int channelId, char *data);
    static void STR_SEND(int channelId, char *data,int len);
};

void DevDrv::STR_READ(int channelId, char *data)
{
    driverArgs arg= {-1,0, {0}};
    arg.channelId = channelId;
    ioctl(mfilehandler,IOCTL_STR_READ,&arg);
    memcpy(data,arg.arg,arg.len)

}
void DevDrv::STR_SEND(int channelId, char *data,int len)
{
    driverArgs arg= {-1,0, {0}};
    arg.channelId = channelId;
    arg.len=len;
    memcpy(arg.arg,data,len);
    ioctl(mfilehandler,IOCTL_STR_SEND,&arg);
}

したがって、問題は、私のアプリケーションの 4 つのスレッドがこれらの関数を呼び出して、次のように独自の ChannelId を読み書きするかどうかです。ドライバーから読み書きする場合:

thread1:
   DevDrv::STR_READ(0,&localdst);
thread2:
   DevDrv::STR_READ(1,&localdst);
thread3:
   DevDrv::STR_READ(2,&localdst);
thread4:
   DevDrv::STR_READ(3,&localdst);

データ競合などはありますか?

4

1 に答える 1

1

構造がキャッシュアラインchannelDataされていることが保証されていないため、明示的に同期しない限り、chDataデータ競合が発生する可能性があります。

レースのスケッチは次のとおりです。

  1. システム コールは、CPU 0 のチャネル 2 を読み込もうとしています。
  2. CPU 0 は、チャネル 2 を含むすべてのキャッシュ ラインをフェッチします。つまり、次のことを意味します。
    • チャネル 2 のすべてのバイト
    • チャネル 1 の最後から数バイト
    • チャネル 3 の先頭から数バイト
  3. 読書はいつも通り。
  4. CPU 1 は 500 バイトをチャネル 1 に書き込みます。
  5. システム コールは、CPU 0 のチャネル 1 に 500 バイトを読み込もうとしています。
  6. CPU 0 は、以前にフェッチされなかったチャネル 1 からすべてのバイトをフェッチします。
    • チャネル 1 の最後からの数バイトは再取得されません

このシナリオでは、これらの数バイトは CPU 1 によって上書きされ、CPU 0 が認識していないため、CPU 0 では古くなります。

キャッシュが古い可能性があることを知らせるメモリバリアがなかったため、認識されませんでした。

現在、多くの場合、システム コールがメモリ バリアをトリガーしますが、保証はされていません。

ユーザー空間プログラムは問題なく、キャラクター デバイスはカーネル モジュールと通信する標準的な方法ですが、カーネル モジュールは適切に同期する必要があります。リンクの例でさえ、非常に入門的なものにしようとしているようDevice_Open++で、アトミックを使用せずに同様のことを行っています。

于 2020-03-15T00:02:03.163 に答える