-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结构不能保证缓存对齐,因此除非您明确 synchronize chData,否则您仍然会受到数据竞争的影响。

这是一场比赛的草图:

  1. 系统调用想要读入 CPU 0 上的通道 2。
  2. CPU 0 获取所有包含通道 2 的缓存行,这意味着:
    • 通道 2 中的所有字节
    • 通道 1 末尾的几个字节
    • 通道 3 开头的几个字节
  3. 阅读照常进行。
  4. CPU 1 向通道 1 写入 500 个字节。
  5. 系统调用想要将 500 个字节读入 CPU 0 上的通道 1。
  6. CPU 0 从通道 1 中获取之前未获取的所有字节。
    • 通道 1 末尾的几个字节不会被重新获取

在这种情况下,这几个字节在 CPU 0 上是陈旧的,因为它们被 CPU 1 覆盖,而 CPU 0 不知道。

它不知道,因为没有内存屏障告诉它它的缓存可能是陈旧的。

现在,在许多情况下,系统调用会触发内存屏障,但不能保证。

您的用户空间程序很好,字符设备是与内核模块通信的规范方式,但您的内核模块必须正确同步。甚至您链接中的示例似乎也试图非常介绍性,并且在Device_Open++不使用原子的情况下执行类似操作。

于 2020-03-15T00:02:03.163 回答