我正在使用shmget
和shmat
共享内存用于教育目的。
我试图让一个内存块只能由它的创build者可变,所有其他进程只能读取。
但读者进程可以以某种方式写入没有任何错误。
这是我对共享内存创build者的代码:
#include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> int main(){ int shmid = shmget((key_t)56666, 1, IPC_CREAT | O_RDONLY); if (shmid ==-1) { perror("Err0:"); exit(EXIT_FAILURE); } void* shmaddr = shmat(shmid, (void *)0,0); if (shmaddr == (void *)-1) { perror("Err:"); exit(EXIT_FAILURE); } *(char*)shmaddr = 'a'; putchar(*(char*)shmaddr); while(1); return 0; }
这是我的读者代码:
#include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> int main(){ int shmid = shmget((key_t)56666, 4, O_RDONLY); if (shmid ==-1) { perror("Err0:"); exit(EXIT_FAILURE); } void* shmaddr = shmat(shmid, (void *)0,0); if (shmaddr == (void *)-1) { perror("Err:"); exit(EXIT_FAILURE); } *(char*)shmaddr = 'b'; putchar(*(char*)shmaddr); return 0; }
正如你所看到的,读者可以编辑内存,但是即使我只打开读取器中的内存,并且在共享内存的创build者中用只读标志创build内存,也不会出现错误。
我还没有看到在linux或freebsd手册页SHM_RDONLY
任何O_RDONLY
或SHM_RDONLY
记录为shmat(2)
系统调用的标志。 这个问题可能是误用或者误解了它的工作原理。 更多关于这个在最后,因为在尝试后,我看到SHM_RDONLY
是你应该用来控制只读附件的标志,而不是O_RDONLY
(在这里没有用)
可能你必须在创建shmget(2)
系统调用中指定权限位来禁止其他用户进程的访问,以实现你想要的。 有了权限,它可以正常工作,或者对使用共享内存的系统有严重的安全问题(例如postgresql数据库使用sysvipc共享内存段)
据我所知,最好的实现方式是以某个用户的身份运行共享内存段的作者,并允许进程读取它们作为不同的用户,调整权限位以允许他们读取但不写入共享内存段。 就像将所有进程放在同一组ID中一样,编写器进程作为创建共享内存段的用户,而其他进程只具有读取权限,而对其他用户ID没有权限,这对于任何应用程序来说都是足够的。
shmget((key_t)56666, 1, IPC_CREAT | 0640);
并以其他不同的用户在同一组ID中运行其他进程。
在freebsd机器上测试你的代码后(对不起,没有linux可用,但是ipc调用是SysV AT&T unix调用,所以一切都应该是兼容的),创建过程在shmat(2)
$ shm_creator Err:: Permission denied
很可能是因为你没有给予共享内存创建的权限,甚至对所有者(我试图想象你不是在你的机器上开发root
,是吗?))
ipcs(1)
显示:
usr1@host ~$ ipcs -m Shared Memory: T ID KEY MODE OWNER GROUP m 65537 56666 ----------- usr1 usr1
并且您看到共享内存段没有活动的权限位,但已创建。 我已经修改了你的程序,而不是在一会儿忙着等待while(1);
循环,做一个非消费CPU等待sleep(3600);
那会让它睡一个小时。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <unistd.h> int main(){ int shmid = shmget((key_t)56666, 1, IPC_CREAT | 0640 ); if (shmid ==-1) { perror("Err0:"); exit(EXIT_FAILURE); } void* shmaddr = shmat(shmid, (void *)0,0); if (shmaddr == (void *)-1) { perror("Err:"); exit(EXIT_FAILURE); } *(char*)shmaddr = 'a'; putchar(*(char*)shmaddr); puts(""); sleep(3600); return 0; }
我以用户usr1
运行:
usr1@host:~/shm$ shm_creator & [2] 76950 a
然后我切换到另一个用户usr2
,并运行:
$ su usr2 Password: [usr2@host /home/usr1/shm]$ shm_client & [1] 76963 [usr2@host /home/usr1/shm]$ Err:: Permission denied
当你标记它,它发生在shmat(2)
系统调用。 但是,如果我运行它为usr1
我得到:
usr1@host:~/shm$ shm_client b
如果在shm_client.c
源文件中使用SHM_RDONLY
作为标志,在运行时(作为相同或不同的用户),我得到以下内容:
usr1@host:~/shm$ shm_client Segmentation fault (generated `core')
这是预期的行为,因为你试图写不可写的内存(它被附加为只读内存)
在在线浏览linux手册页之后,有一个SHM_RDONLY
的引用允许以只读方式连接共享内存段。 不提供对只写共享内存段的支持,否则。 由于freebsd上没有记录这个选项,这个选项在那里也是可用的(常量包含在合适的包含文件中),在freebsd手册中还有一些其他的S_IROWN
(如使用S_IROWN
, S_IWOWN
, S_IRGRP
, S_IWGRP
, S_IROTH
和S_IWOTH
标志来控制权限位,并且在手册页的概要中不包含#include <sys/stat.h>
)
如果SHM_RDONLY
在您的系统中可用,那么您可以将其作为非抢先的方式来禁止对共享内存的写入访问,但是如果您希望内核强制执行,则必须切换到用户权限位方式。