如何在 C 中使用 Linux 共享内存

我的一个项目出了点问题。

我一直试图找到一个很好的文档示例,在 fork()中使用共享内存,但是没有成功。

基本上,当用户启动程序时,我需要在共享内存中存储两个值: Current _ path 当前路径,它是 Char * File _ name,它也是 Char *

根据命令参数的不同,使用 fork()启动一个新进程,该进程需要读取和修改存储在共享内存中的 Current _ path 当前路径变量,而 File _ name变量是只读的。

有没有一个关于共享内存的好的教程和示例代码(如果可能的话) ,你可以告诉我?

407426 次浏览

有两种方法: shmgetmmap。我将讨论 mmap,因为它更加现代和灵活,但是如果你更愿意使用老式的工具,你可以看看 man shmget(或者这个教程)。

mmap()函数可用于分配具有高度可定制参数的内存缓冲区,以控制访问和权限,并在必要时用文件系统存储来支持它们。

下面的函数创建一个进程可以与其子进程共享的内存缓冲区:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>


void* create_shared_memory(size_t size) {
// Our memory buffer will be readable and writable:
int protection = PROT_READ | PROT_WRITE;


// The buffer will be shared (meaning other processes can access it), but
// anonymous (meaning third-party processes cannot obtain an address for it),
// so only this process and its children will be able to use it:
int visibility = MAP_SHARED | MAP_ANONYMOUS;


// The remaining parameters to `mmap()` are not important for this use case,
// but the manpage for `mmap` explains their purpose.
return mmap(NULL, size, protection, visibility, -1, 0);
}

下面是一个示例程序,它使用上面定义的函数来分配缓冲区。父进程将编写消息 fork,然后等待其子进程修改缓冲区。两个进程都可以读写共享内存。

#include <string.h>
#include <unistd.h>


int main() {
char parent_message[] = "hello";  // parent process will write this message
char child_message[] = "goodbye"; // child process will then write this one


void* shmem = create_shared_memory(128);


memcpy(shmem, parent_message, sizeof(parent_message));


int pid = fork();


if (pid == 0) {
printf("Child read: %s\n", shmem);
memcpy(shmem, child_message, sizeof(child_message));
printf("Child wrote: %s\n", shmem);


} else {
printf("Parent read: %s\n", shmem);
sleep(1);
printf("After 1s, parent read: %s\n", shmem);
}
}

这些内容包括用于使用共享内存

#include<sys/ipc.h>
#include<sys/shm.h>


int shmid;
int shmkey = 12222;//u can choose it as your choice


int main()
{
//now your main starting
shmid = shmget(shmkey,1024,IPC_CREAT);
// 1024 = your preferred size for share memory
// IPC_CREAT  its a flag to create shared memory


//now attach a memory to this share memory
char *shmpointer = shmat(shmid,NULL);


//do your work with the shared memory
//read -write will be done with the *shmppointer
//after your work is done deattach the pointer


shmdt(&shmpointer, NULL);

下面是一个共享内存的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>


#define SHM_SIZE 1024  /* make it a 1K shared memory segment */


int main(int argc, char *argv[])
{
key_t key;
int shmid;
char *data;
int mode;


if (argc > 2) {
fprintf(stderr, "usage: shmdemo [data_to_write]\n");
exit(1);
}


/* make the key: */
if ((key = ftok("hello.txt", 'R')) == -1) /*Here the file must exist */
{
perror("ftok");
exit(1);
}


/*  create the segment: */
if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1) {
perror("shmget");
exit(1);
}


/* attach to the segment to get a pointer to it: */
if ((data = shmat(shmid, NULL, 0)) == (void *)-1) {
perror("shmat");
exit(1);
}


/* read or modify the segment, based on the command line: */
if (argc == 2) {
printf("writing to segment: \"%s\"\n", argv[1]);
strncpy(data, argv[1], SHM_SIZE);
} else
printf("segment contains: \"%s\"\n", data);


/* detach from the segment: */
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}


return 0;
}

步骤:

  1. 使用 ftok 将路径名和项目标识符转换为 System V IPC 密钥

  2. 使用 shmget 分配共享内存段

  3. 使用 shmat 将 shmid 标识的共享内存段附加到调用进程的地址空间

  4. 对内存区域执行操作

  5. 使用 shmdt 分离

尝试这个代码示例,我测试了它,来源: http://www.makelinux.net/alp/035

#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>


int main ()
{
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
const int shared_segment_size = 0x6400;


/* Allocate a shared memory segment.  */
segment_id = shmget (IPC_PRIVATE, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
/* Attach the shared memory segment.  */
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("shared memory attached at address %p\n", shared_memory);
/* Determine the segment's size. */
shmctl (segment_id, IPC_STAT, &shmbuffer);
segment_size  =               shmbuffer.shm_segsz;
printf ("segment size: %d\n", segment_size);
/* Write a string to the shared memory segment.  */
sprintf (shared_memory, "Hello, world.");
/* Detach the shared memory segment.  */
shmdt (shared_memory);


/* Reattach the shared memory segment, at a different address.  */
shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0);
printf ("shared memory reattached at address %p\n", shared_memory);
/* Print out the string from shared memory.  */
printf ("%s\n", shared_memory);
/* Detach the shared memory segment.  */
shmdt (shared_memory);


/* Deallocate the shared memory segment.  */
shmctl (segment_id, IPC_RMID, 0);


return 0;
}

下面是一个 mmap 示例:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/*
* pvtmMmapAlloc - creates a memory mapped file area.
* The return value is a page-aligned memory value, or NULL if there is a failure.
* Here's the list of arguments:
* @mmapFileName - the name of the memory mapped file
* @size - the size of the memory mapped file (should be a multiple of the system page for best performance)
* @create - determines whether or not the area should be created.
*/
void* pvtmMmapAlloc (char * mmapFileName, size_t size, char create)
{
void * retv = NULL;
if (create)
{
mode_t origMask = umask(0);
int mmapFd = open(mmapFileName, O_CREAT|O_RDWR, 00666);
umask(origMask);
if (mmapFd < 0)
{
perror("open mmapFd failed");
return NULL;
}
if ((ftruncate(mmapFd, size) == 0))
{
int result = lseek(mmapFd, size - 1, SEEK_SET);
if (result == -1)
{
perror("lseek mmapFd failed");
close(mmapFd);
return NULL;
}


/* Something needs to be written at the end of the file to
* have the file actually have the new size.
* Just writing an empty string at the current file position will do.
* Note:
*  - The current position in the file is at the end of the stretched
*    file due to the call to lseek().
*  - The current position in the file is at the end of the stretched
*    file due to the call to lseek().
*  - An empty string is actually a single '\0' character, so a zero-byte
*    will be written at the last byte of the file.
*/
result = write(mmapFd, "", 1);
if (result != 1)
{
perror("write mmapFd failed");
close(mmapFd);
return NULL;
}
retv  =  mmap(NULL, size,
PROT_READ | PROT_WRITE, MAP_SHARED, mmapFd, 0);


if (retv == MAP_FAILED || retv == NULL)
{
perror("mmap");
close(mmapFd);
return NULL;
}
}
}
else
{
int mmapFd = open(mmapFileName, O_RDWR, 00666);
if (mmapFd < 0)
{
return NULL;
}
int result = lseek(mmapFd, 0, SEEK_END);
if (result == -1)
{
perror("lseek mmapFd failed");
close(mmapFd);
return NULL;
}
if (result == 0)
{
perror("The file has 0 bytes");
close(mmapFd);
return NULL;
}
retv  =  mmap(NULL, size,
PROT_READ | PROT_WRITE, MAP_SHARED, mmapFd, 0);


if (retv == MAP_FAILED || retv == NULL)
{
perror("mmap");
close(mmapFd);
return NULL;
}


close(mmapFd);


}
return retv;
}