在 C 中 child 和 parent 之间共享链表
Share linked list between child and parent in C
我有一个链表。我有许多 child 个进程处理这个链表,添加和删除元素。 child人如何共享?
我试过这段使用内存映射的代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
typedef struct Struttura
{
int prova;
struct Struttura* next;
} Struttura;
Struttura* glob;
int main()
{
int fd = open("/dev/zero", O_RDWR, 0);
glob = mmap(NULL, sizeof(Struttura), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
int pid = fork();
if(pid == 0)
{
printf("Glob value: %d\n", glob->prova);
glob->next = mmap(NULL, sizeof(Struttura), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
glob->next->prova = 3;
printf("The next value: %d\n\n", glob->next->prova);
return (0);
}
else
{
wait(NULL);
printf("Glob value: %d\n", glob->prova);
printf("The next value: %d\n\n", glob->next->prova); //Segmentation fault core dumped
}
return (0);
}
如何做到这一点?请不要介意同步和未检查的 return 值,因为这只是一个尝试:)
使用 mmap(2)
,如果不对列表的最大大小施加一些限制,则无法轻松做到这一点。原因是分配一大块内存与子进程共享很容易,但是在 分叉后 增长那块内存是不可行的。
您可以通过使用 XSI 共享内存接口来解决这个问题(参见 man 7 shm_overview
):您可以使用 shm_open(3)
来创建和访问共享内存块,并且您可以设置它的大小ftruncate(2)
。这在理论上会给你动态增长和收缩的能力,但是以更复杂的代码为代价:与映射内存不同,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;
/* Space for the nodes */
region_sz += sizeof(**list_head)*MAX_LIST_SZ;
/* Space for house-keeping pointers */
region_sz += sizeof(list_head)+sizeof(free_head);
/* Space for the mutex */
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);
}
/* Set up everything */
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;
/* Initialize free list */
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;
}
不要使用制表符进行缩进,因为每个 editor/wordprocessor 都有不同的制表符 widths/tab 停止位
mmap() returns 可以分配给任何指针的 void*,所以
1) do not cast the return value.
2) always check the returned value for MAP_FAILED which indicates the operation failed
调用 fork() 后的 pid 值被错误使用
1) when pid < 0 then an error occurred
2) when pid == 0 then child executing
3) when pid > 0 then parent executing
使用 fork() 时,始终检查所有三个条件
发布的代码错误地使用了 pid 值
建议阅读每个使用的系统函数的手册页
我有一个链表。我有许多 child 个进程处理这个链表,添加和删除元素。 child人如何共享?
我试过这段使用内存映射的代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
typedef struct Struttura
{
int prova;
struct Struttura* next;
} Struttura;
Struttura* glob;
int main()
{
int fd = open("/dev/zero", O_RDWR, 0);
glob = mmap(NULL, sizeof(Struttura), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
int pid = fork();
if(pid == 0)
{
printf("Glob value: %d\n", glob->prova);
glob->next = mmap(NULL, sizeof(Struttura), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
glob->next->prova = 3;
printf("The next value: %d\n\n", glob->next->prova);
return (0);
}
else
{
wait(NULL);
printf("Glob value: %d\n", glob->prova);
printf("The next value: %d\n\n", glob->next->prova); //Segmentation fault core dumped
}
return (0);
}
如何做到这一点?请不要介意同步和未检查的 return 值,因为这只是一个尝试:)
使用 mmap(2)
,如果不对列表的最大大小施加一些限制,则无法轻松做到这一点。原因是分配一大块内存与子进程共享很容易,但是在 分叉后 增长那块内存是不可行的。
您可以通过使用 XSI 共享内存接口来解决这个问题(参见 man 7 shm_overview
):您可以使用 shm_open(3)
来创建和访问共享内存块,并且您可以设置它的大小ftruncate(2)
。这在理论上会给你动态增长和收缩的能力,但是以更复杂的代码为代价:与映射内存不同,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;
/* Space for the nodes */
region_sz += sizeof(**list_head)*MAX_LIST_SZ;
/* Space for house-keeping pointers */
region_sz += sizeof(list_head)+sizeof(free_head);
/* Space for the mutex */
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);
}
/* Set up everything */
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;
/* Initialize free list */
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;
}
不要使用制表符进行缩进,因为每个 editor/wordprocessor 都有不同的制表符 widths/tab 停止位
mmap() returns 可以分配给任何指针的 void*,所以
1) do not cast the return value.
2) always check the returned value for MAP_FAILED which indicates the operation failed
调用 fork() 后的 pid 值被错误使用
1) when pid < 0 then an error occurred
2) when pid == 0 then child executing
3) when pid > 0 then parent executing
使用 fork() 时,始终检查所有三个条件
发布的代码错误地使用了 pid 值
建议阅读每个使用的系统函数的手册页