通过
mmap(2)
,你很难在不对列表的最大大小施加一些限制的情况下完成它。原因是虽然容易为子进程分配一个大块内存,但在分叉之后扩展该块是不可行的。
你可以通过使用 XSI 共享内存接口来解决这个问题(请参阅
man 7 shm_overview
):使用
shm_open(3)
创建和访问共享内存块,你可以使用
ftruncate(2)
设置其大小。理论上,这将使你拥有动态增长和收缩的能力,但代价是更复杂的代码:与 mmap 内存不同,XSI 共享内存对象直到系统关闭或所有进程取消映射该对象并使用
shm_unlink(3)
删除它时才会从系统中删除。如果任何一个进程崩溃并非正常终止,你就有了一个清理问题。此外,你面临通知每个其他进程每次有人更改内存对象大小的问题。如果一个进程在插入中被通知怎么办?如果一个进程正在向不存在的内存位置插入节点,因为某个进程在此期间缩小了其大小怎么办?
你没有说出为什么要做这个,但在所有情况下,线程都是更理性和合理的选择,因为它们正好满足了这个目的:轻松共享资源。你不必设置共享内存段,而且你的列表大小不限于你分配的块大小。
无论如何,以下是一个使用
mmap(2)
创建共享内存段并有一堆子进程从该内存段中添加和删除元素的程序示例。前几个内存块用于一些管理工作,即指向列表头和空闲节点列表的指针以及用于同步访问的互斥量。我们需要保留一个空闲节点列表,以便工作进程在将新节点添加到真实列表时可以找到可用节点。每个工作进程(共有10个)向列表中插入10个新节点,其中存储了工作进程的ID。每次插入时,工作进程打印整个列表。插入所有这些节点后,工作进程从列表中删除它们并终止。
请注意,列表的最大大小为1024,因为我们为此分配了这么多。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LIST_SZ 1024
#define WORKERS 10
#define WORK_UNIT 10
struct list_node {
long value;
struct list_node *next;
};
static struct list_node **list_head;
static struct list_node **free_head;
static pthread_mutex_t *mutex;
void print_list(void) {
struct list_node *curr = *list_head;
while (curr != NULL) {
printf("%ld -> ", curr->value);
curr = curr->next;
}
printf("NULL\n");
}
void do_work(void) {
int i;
for (i = 0; i < WORK_UNIT; i++) {
pthread_mutex_lock(mutex);
struct list_node *n = *free_head;
if (n == NULL) {
pthread_mutex_unlock(mutex);
assert(0);
}
*free_head = (*free_head)->next;
n->value = (long) getpid();
n->next = *list_head;
*list_head = n;
print_list();
pthread_mutex_unlock(mutex);
}
for (i = 0; i < WORK_UNIT; i++) {
pthread_mutex_lock(mutex);
struct list_node *n = *list_head;
*list_head = (*list_head)->next;
n->next = *free_head;
*free_head = n;
pthread_mutex_unlock(mutex);
}
}
int main(void) {
void *ptr;
size_t region_sz = 0;
region_sz += sizeof(**list_head)*MAX_LIST_SZ;
region_sz += sizeof(list_head)+sizeof(free_head);
region_sz += sizeof(*mutex);
ptr = mmap(NULL, region_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
if (ptr == MAP_FAILED) {
perror("mmap(2) failed");
exit(EXIT_FAILURE);
}
mutex = ptr;
free_head = (struct list_node **) (((char *) ptr)+sizeof(*mutex));
list_head = free_head+1;
*free_head = (struct list_node *) (list_head+1);
*list_head = NULL;
int i;
struct list_node *curr;
for (i = 0, curr = *free_head; i < MAX_LIST_SZ-1; i++, curr++) {
curr->next = curr+1;
}
curr->next = NULL;
pthread_mutexattr_t mutex_attr;
if (pthread_mutexattr_init(&mutex_attr) < 0) {
perror("Failed to initialize mutex attributes");
exit(EXIT_FAILURE);
}
if (pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED) < 0) {
perror("Failed to change mutex attributes");
exit(EXIT_FAILURE);
}
if (pthread_mutex_init(mutex, &mutex_attr) < 0) {
perror("Failed to initialize mutex");
exit(EXIT_FAILURE);
}
for (i = 0; i < WORKERS; i++) {
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork(2) error");
exit(EXIT_FAILURE);
}
if (pid == 0) {
do_work();
return 0;
}
}
for (i = 0; i < WORKERS; i++) {
if (wait(NULL) < 0) {
perror("wait(2) error");
}
}
assert(*list_head == NULL);
return 0;
}
public static
变量怎么样? - mustangDCpublic static
? - Federico Ponzifork()
会复制子进程的地址空间,因此全局变量的更改将不会在进程之间可见。但你已经有一个工具可以在进程之间获取共享内存:mmap(...MAP_SHARED)
。只需将共享变量放入共享内存段中。请记住,您需要同步访问,否则行为将是未定义的。 - EOFfork()
之后在进程之间共享内存,可以使用shmop()
系列函数或shm_open()
。 - EOF