即使使用volatile关键字,编译器为何仍会由于strncmp()而优化掉共享内存的读取? [英] Why does the compiler optimize away shared memory reads due to strncmp() even if volatile keyword is used?

查看:112
本文介绍了即使使用volatile关键字,编译器为何仍会由于strncmp()而优化掉共享内存的读取?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个程序foo.c,它将数据写入共享内存.

Here is a program foo.c that writes data to shared memory.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    key_t key;
    int shmid;
    char *mem;

    if ((key = ftok("ftok", 0)) == -1) {
        perror("ftok");
        return 1;
    }

    if ((shmid = shmget(key, 100, 0600 | IPC_CREAT)) == -1) {
        perror("shmget");
        return 1;
    }

    printf("key: 0x%x; shmid: %d\n", key, shmid);

    if ((mem = shmat(shmid, NULL, 0)) == (void *) -1) {
        perror("shmat");
        return 1;
    }

    sprintf(mem, "hello");
    sleep(10);
    sprintf(mem, "exit");

    return 1;
}

这是另一个程序bar.c,它从同一共享内存中读取数据.

Here is another program bar.c that reads data from the same shared memory.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    key_t key;
    int shmid;
    volatile char *mem;

    if ((key = ftok("ftok", 0)) == -1) {
        perror("ftok");
        return 1;
    }

    if ((shmid = shmget(key, sizeof (int), 0400 | IPC_CREAT)) == -1) {
        perror("shmget");
        return 1;
    }

    printf("key: 0x%x; shmid: %d\n", key, shmid);

    if ((mem = shmat(shmid, NULL, 0)) == (void *) -1) {
        perror("shmat");
        return 1;
    }

    printf("looping ...\n");
    while (strncmp((char *) mem, "exit", 4) != 0)
        ;

    printf("exiting ...\n");

    return 0;
}

我首先在一个终端中运行writer程序.

I run the writer program first in one terminal.

touch ftok && gcc foo.c -o foo && ./foo

虽然编写程序仍在运行,但我在另一个终端上运行了读取程序.

While the writer program is still running, I run the reader program in another terminal.

gcc -O1 bar.c -o bar && ./bar

阅读器程序进入无限循环.看来优化器已经优化了以下代码

The reader program goes into an infinite loop. It looks like the optimizer has optimized the following code

    while (strncmp((char *) mem, "exit", 4) != 0)
        ;

    while (1)
        ;

因为它在循环中看不到任何东西可以读取mem后修改mem上的数据.

because it sees nothing in the loop that could modify the data at mem after it has been read once.

但是正是出于这个原因,我将mem声明为volatile.以防止编译器对其进行优化.

But I declared mem as volatile precisely for this reason; to prevent the compiler from optimizing it away.

volatile char *mem;

为什么编译器仍然优化mem的读取?

Why does the compiler still optimize away the reads for mem?

顺便说一句,我找到了一个可行的解决方案.有效的解决方案是修改

By the way, I have found a solution that works. The solution that works is to modify

    while (strncmp((char *) mem, "exit", 4) != 0)
        ;

    while (mem[0] != 'e' || mem[1] != 'x' || mem[2] != 'i' || mem[3] != 't')
        ;

为什么在这两种情况下即使char *mem被声明为volatile,编译器也优化了strncmp((char *) mem, "exit", 4) != 0却没有优化mem[0] != 'e' || mem[1] != 'x' || mem[2] != 'i' || mem[3] != 't'?

Why is it that the compiler optimizes away strncmp((char *) mem, "exit", 4) != 0 but does not optimize away mem[0] != 'e' || mem[1] != 'x' || mem[2] != 'i' || mem[3] != 't' even though char *mem is declared to be volatile in both cases?

推荐答案

通过编写(char *)mem,您告诉strncmp函数它实际上不是易失性缓冲区.实际上,strncmp和其他C库函数并非旨在用于易失性缓冲区.

By writing (char *)mem you are telling the strncmp function that it it is actually not a volatile buffer. And indeed, strncmp and the other C library functions are not designed to work on volatile buffers.

实际上,您确实需要修改代码以在易失性缓冲区上不使用C库函数.您的选择包括:

You do in fact need to modify your code to not use C library functions on volatile buffers. Your options include:

  • 编写与易失性缓冲区一起使用的C库函数的替代方法.
  • 使用适当的内存屏障.

您已经选择了第一个选项;但请考虑一下,如果另一个进程修改了您的四个读取之间的内存会发生什么情况.为避免此类问题,您需要使用第二个选项,即进程间内存屏障-在这种情况下,缓冲区不再需要为volatile,您可以返回使用C库函数. (编译器必须假定障碍检查可能会更改缓冲区).

You've gone with the first option; but think about what would happen if the other process modified the memory in between your four reads. To avoid this sort of problem you'd need to use the second option, an inter-process memory barrier -- in which case the buffer no longer needs to be volatile and you can go back to using the C library functions. (The compiler must assume that the barrier check might change the buffer).

这篇关于即使使用volatile关键字,编译器为何仍会由于strncmp()而优化掉共享内存的读取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆