在尝试解决一些debugging问题时,我在代码中添加了一些printf
-s:
我用这个代码:
struct PipeShm { int init; sem_t sema; ... ... } struct PipeShm * sharedPipe = NULL;
FUNC2:
int func2() { if (!sharedPipe) { int myFd = shm_open ("/myregion", O_CREAT | O_TRUNC | O_RDWR, 0666); if (myFd == -1) error_out ("shm_open"); // allocate some memory in the region in the size of the struct int retAlloc = ftruncate (myFd, sizeof * sharedPipe); if (retAlloc < 0) // check if allocation failed error_out("ftruncate"); // map the region and shared in with all the processes sharedPipe = mmap (NULL, sizeof * sharedPipe,PROT_READ | PROT_WRITE,MAP_SHARED , myFd, 0); if (sharedPipe == MAP_FAILED) // check if the allocation failed error_out("mmap"); // put initial value int value = -10; // get the value of the semaphore sem_getvalue(&sharedPipe->semaphore, &value); if (sharedPipe->init != TRUE) // get in here only if init is NOT TRUE ! { if (!sem_init (&sharedPipe->semaphore, 1, 1)) // initialize the semaphore to 0 { sharedPipe->init = TRUE; sharedPipe->flag = FALSE; sharedPipe->ptr1 = NULL; sharedPipe->ptr2 = NULL; sharedPipe->status1 = -10; sharedPipe->status2 = -10; sharedPipe->semaphoreFlag = FALSE; sharedPipe->currentPipeIndex = 0; printf("\nI'm inside the critical section! my init is: %d\n" , sharedPipe->init); } else perror ("shm_pipe_init"); printf("\nI'm out the critical section! my init is: %d\n" , sharedPipe->init); } } return 1; // always successful }
有了这个主要:
int main() { int spd, pid, rb; char buff[4096]; fork(); func2(); return 0; }
得到这个:
shm_pipe_mkfifo:文件存在
I'm inside the critical section! my init is: 1 I'm out the critical section! my init is: 1 Output:hello world! I'm inside the critical section! my init is: 1 I'm out the critical section! my init is: 1
似乎共享内存不那么共享,为什么?
该段由于MAP_SHARED | MAP_ANONYMOUS
而在所有进程之间共享 MAP_SHARED | MAP_ANONYMOUS
,为什么两个进程after
值和before
都相同?
似乎每个进程都有自己的信号量,尽pipe它们之间是共享的,那么出了什么问题呢?
谢谢
由于您将MAP_ANONYMOUS
标志用于mmap
,因此myFd
参数将被忽略,并且会在每个进程中创建两个独立的共享内存块,这些块之间没有任何关系。
MAP_ANONYMOUS The mapping is not backed by any file; its contents are initial‐ ized to zero. The fd and offset arguments are ignored; however, some implementations require fd to be -1 if MAP_ANONYMOUS (or MAP_ANON) is specified, and portable applications should ensure this. The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4.
如果你摆脱了MAP_ANONYMOUS
那么你将只有一个共享的内存块,但是你有没有调用sem_init
的问题。 在使用NPTL的Linux上,它实际上可以工作,因为将sem_t清除为全0字节(这里的初始状态)等同于sem_init(&sema, anything, 0);
(NPTL忽略pshared标志),但这不能移植到其他系统。
根据Karoly对另一个答案的评论,在公开电话会议上也有O_TRUNC的竞赛状况。 如果在第一个线程已经开始修改信号量之后第二个线程调用open
,那么TRUNC将会破坏信号量状态。 可能最好的解决方案是将创建,打开和压缩共享内存的代码移动到另一个名为BEFORE调用fork的函数中。
编辑
要修复O_TRUNC问题,不能有多个进程使用O_TRUNC调用shm_open。 但是,如果你只是摆脱了O_TRUNC,那么你有启动问题,如果共享内存对象已经存在(从以前的程序运行),它可能不会处于可预测的状态。 在可能性上是分裂开始的func2:
main() { func1(); fork(); func2(); } func1() { int myFd = shm_open ("/myregion", O_CREAT | O_TRUNC | O_RDWR, 0666); if (myFd == -1) error_out ("shm_open"); // allocate some memory in the region in the size of the struct int retAlloc = ftruncate (myFd, sizeof *sharedPipe); if (retAlloc < 0) // check if allocation failed error_out("ftruncate"); // map the region and shared in with all the processes sharedPipe = mmap (NULL, sizeof *sharedPipe, PROT_READ|PROT_WRITE, MAP_SHARED, myFd, 0); if (sharedPipe == MAP_FAILED) // check if the allocation failed error_out("mmap"); } func2() { // put initial value int value = -10; // get the value of the semaphore sem_getvalue(&sharedPipe->semaphore, &value); :
或者你可以保持相同的代码(只是摆脱O_TRUNC),并在fork之前添加一个清理:
main() { shm_unlink("/myregion"); fork(); func2();
在所有情况下,如果您同时运行程序的多个副本,您仍然会遇到问题。
一些想法…
我认为这是POSIX信号量如何工作的一个有趣的误解。 我没有看到对sem_init
或sem_open
的调用。 你不应该能够跨越流程使用它们,而不会比你做得更明确。
我在Linux上实现mmap
并不是那么新鲜, MAP_ANONYMOUS
可能会对此产生影响,但通常写入映射区域并不是真正的瞬间。 linux.die上的manpage说 :
MAP_SHARED
分享这个映射。 映射的更新对映射此文件的其他进程可见,并被传送到底层文件。 在调用msync(2)或munmap()之前,文件可能不会实际更新。
原因是你的内存访问被困在一个页面错误中,此时内核将填充文件描述符中的内容,然后让你在RAM中写入内容,然后在稍后的某个时刻内核会刷新到文件描述符。