Lon*_*ner 3 c volatile shared-memory compiler-optimization strncmp
这是一个foo.c将数据写入共享内存的程序.
#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;
}
Run Code Online (Sandbox Code Playgroud)
这是另一个bar.c从同一共享内存中读取数据的程序.
#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;
}
Run Code Online (Sandbox Code Playgroud)
我首先在一个终端运行编写程序.
touch ftok && gcc foo.c -o foo && ./foo
Run Code Online (Sandbox Code Playgroud)
当编写器程序仍在运行时,我在另一个终端中运行读取器程序.
gcc -O1 bar.c -o bar && ./bar
Run Code Online (Sandbox Code Playgroud)
读者程序进入无限循环.看起来优化器已经优化了以下代码
while (strncmp((char *) mem, "exit", 4) != 0)
;
Run Code Online (Sandbox Code Playgroud)
至
while (1)
;
Run Code Online (Sandbox Code Playgroud)
因为它在循环中看不到任何东西,mem它可以在读取一次后修改数据.
但我宣布mem的volatile正是这个原因; 防止编译器优化它.
volatile char *mem;
Run Code Online (Sandbox Code Playgroud)
为什么编译器仍然优化读取mem?
顺便说一下,我找到了一个有效的解决方案.有效的解决方案是修改
while (strncmp((char *) mem, "exit", 4) != 0)
;
Run Code Online (Sandbox Code Playgroud)
至
while (mem[0] != 'e' || mem[1] != 'x' || mem[2] != 'i' || mem[3] != 't')
;
Run Code Online (Sandbox Code Playgroud)
为什么编译器会优化掉,strncmp((char *) mem, "exit", 4) != 0但mem[0] != 'e' || mem[1] != 'x' || mem[2] != 'i' || mem[3] != 't'即使char *mem声明volatile在两种情况下都没有优化掉?
6.7.3类型限定符
6 [...]如果尝试通过使用具有非volatile限定类型的左值来引用使用volatile限定类型定义的对象,则行为未定义.133)
133)这适用于那些行为就好像用限定类型定义的对象,即使它们实际上从未被定义为程序中的对象(例如内存映射输入/输出地址的对象).
这正是您在代码中观察到的内容.编译器基本上是在"行为未定义"的狂野自由下优化代码.
换句话说,不可能strncmp直接正确地应用于易失性数据.
你可以做的是实现你自己的比较,不放弃volatile限定符(这是你已经完成的),或者使用一些易失性感知方法将易失性数据复制到非易失性存储,它们适用strncmp于后者.