系统调用是否是线程安全的吗?

问题描述 投票:-1回答:1

我已经编写了一个内核模块,它是一个字符设备驱动程序,类似于this 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);

是否有数据竞赛或类似的事情发生?

c++ c linux multithreading chardev
1个回答
0
投票

您的channelData结构不能保证与缓存对齐,因此,除非您明确同步chData,否则您仍然会遭受数据争夺。

这里是比赛的草图:

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

在这种情况下,这几个字节在CPU 0上已过时,因为它们被CPU 1覆盖,而CPU 0却不知道。

它不知道,因为没有memory barrier告诉它它的缓存可能已过时。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.