我将共享内存与 shmget
和 shmat
用于教育目的。
我正在尝试使内存块仅由其创建者可变,而所有其他进程只能读取。
但是读取器进程可以以某种方式写入而不会出现任何错误。
这是共享内存创建者的代码:
#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;
}
如您所见,读者可以编辑内存但不会发生错误,即使我在阅读器中以只读方式打开内存并在共享内存的创建者中使用只读标志创建它。
最佳答案
我没有在 linux 或 freebsd 手册页中看到任何 O_RDONLY
或 SHM_RDONLY
被记录为 shmat(2)
系统调用的标志。问题可能是误用或对其工作方式的误解。最后更多关于这个,因为在尝试之后我看到 SHM_RDONLY
是你应该用来控制只读附件的标志,而不是 O_RDONLY
(在这里没有用)
可能您必须在创建 shmget(2)
系统调用中指定权限位以禁用对其他用户进程的访问,以实现您想要的。有了权限,它就可以工作,否则你会遇到使用共享内存的系统的严重安全问题(例如 postgresql 数据库使用 sysvipc 共享内存段)
据我所知,最好的实现方式是以某个用户身份运行共享内存段的编写器,允许进程以不同用户身份读取它,调整权限位以允许它们读取但不能写入在共享内存段上。就像让所有进程都在同一个组 id 中,writer 进程作为创建共享内存段的用户,而其他进程只有读访问权限,没有其他用户 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);
循环中执行 busywait,而是使用 sleep(3600);
执行非消耗性 cpu 等待,这将使其休眠整整一个小时。
shm_creator.c
#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_RDONLY
源文件中使用 shm_client.c
作为标志,在运行时(作为相同或不同的用户)我得到以下信息:
usr1@host:~/shm$ shm_client
Segmentation fault (generated `core')
这是预期的行为,因为您试图写入不可写内存(它被附加为只读内存)
编辑 2
在线浏览 linux 手册页后,有一个对 SHM_RDONLY
的引用,允许将共享内存段附加为只读。否则不支持只写共享内存段。因为它没有在 freebsd 上记录,所以这个选项在那里也可用(常量包含在正确的包含文件中)并且在 freebsd 手册中发现了一些其他不精确(如使用 S_IROWN
, S_IWOWN
, S_IRGRP
, S_IWGRP
, S_IROTH
和S_IWOTH
标志来控制权限位,手册页的概要中不包含 #include <sys/stat.h>
)
结论
如果 SHM_RDONLY
在您的系统中可用,那么您可以使用它作为一种非抢占方式来禁止对您的共享内存进行写访问,但是如果您想要内核强制方式,您必须切换到用户权限位方式.
关于c - 共享内存忽略 Linux c 中的只读标志,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44878885/