如何在共享内存数据中完成同步 linux c
how synchronization is done in shared memory data linux c
我在面试中被问到一个问题,同步是如何在共享内存中完成的。我告诉Take a struct。因为你有一个标志和一个数据。测试标志并更改数据。
我从互联网上获取了以下程序,如下所示。谁能告诉我共享内存中是否有更好的同步方式
#define NOT_READY -1
#define FILLED 0
#define TAKEN 1
struct Memory {
int status;
int data[4];
};
假设服务器端和客户端在当前目录下。服务器使用 ftok() 生成密钥并将其用于请求共享内存。在共享内存被数据填充之前,状态设置为NOT_READY。共享内存填满后,服务器将状态设置为 FILLED。然后,服务器等待状态变为 TAKEN,这意味着客户端已获取数据。
以下是服务器程序。单击此处下载此服务器程序的副本 server.c。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(int argc, char *argv[])
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
if (argc != 5) {
printf("Use: %s #1 #2 #3 #4\n", argv[0]);
exit(1);
}
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), IPC_CREAT | 0666);
if (ShmID < 0) {
printf("*** shmget error (server) ***\n");
exit(1);
}
printf("Server has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (server) ***\n");
exit(1);
}
printf("Server has attached the shared memory...\n");
ShmPTR->status = NOT_READY;
ShmPTR->data[0] = atoi(argv[1]);
ShmPTR->data[1] = atoi(argv[2]);
ShmPTR->data[2] = atoi(argv[3]);
ShmPTR->data[3] = atoi(argv[4]);
printf("Server has filled %d %d %d %d to shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = FILLED;
printf("Please start the client in another window...\n");
while (ShmPTR->status != TAKEN)
sleep(1);
printf("Server has detected the completion of its child...\n");
shmdt((void *) ShmPTR);
printf("Server has detached its shared memory...\n");
shmctl(ShmID, IPC_RMID, NULL);
printf("Server has removed its shared memory...\n");
printf("Server exits...\n");
exit(0);
}
客户端部分与服务器端类似。它一直等到状态为 FILLED。然后,客户端检索数据并将状态设置为 TAKEN,通知服务器数据已被获取。下面是客户端程序。单击此处下载此服务器程序的副本 client.c。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(void)
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), 0666);
if (ShmID < 0) {
printf("*** shmget error (client) ***\n");
exit(1);
}
printf(" Client has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (client) ***\n");
exit(1);
}
printf(" Client has attached the shared memory...\n");
while (ShmPTR->status != FILLED)
;
printf(" Client found the data is ready...\n");
printf(" Client found %d %d %d %d in shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = TAKEN;
printf(" Client has informed server data have been taken...\n");
shmdt((void *) ShmPTR);
printf(" Client has detached its shared memory...\n");
printf(" Client exits...\n");
exit(0);
}
Can anyone tell if there is better way of synchronization in shared memory?
当然,是的。我会说你在忙等待 (while (ShmPTR->status != FILLED) ;
) 中浪费 CPU 个周期的方式已经是一个致命的错误。
请注意,POSIX 共享内存具有比旧 SysV 更明智的接口。有关详细信息,请参阅 man 7 shm_overview。
同步原语有两个不同的用途:
数据同步
为了保护数据免受并发修改,并确保每个 reader 获得一致的数据视图,可采用三种基本方法:
原子访问
原子访问需要硬件支持,通常仅支持本机字大小的单位(32 或 64 位)。
互斥锁和条件变量
互斥锁是互斥锁。这个想法是在检查或修改值之前获取互斥量。
条件变量基本上是线程或进程等待 "condition" 的无序队列。 POSIX pthreads 库包括用于自动释放互斥量和等待条件变量的工具。这使得等待数据集更改变得微不足道,如果每个修改器在每次修改后都在条件变量上发出信号或广播。
读写锁。
rwlock 是一种原语,它允许任意数量的并发 "read locks",但任何时候只能持有一个 "write lock"。这个想法是每个 reader 在检查数据之前获取一个读取锁,并且每个写入者在修改它之前获取一个写入锁。当检查数据的频率高于修改数据的频率时,这种方法效果最佳,并且不需要等待更改发生的机制。
进程同步
有些情况下线程和进程应该等待 (block) 直到某个事件发生。有两个最常用的原语用于此:
信号量
A POSIX semaphore 基本上是一个不透明的非负计数器,您可以将其初始化为任何值(零值或正值,在实现设置的限制内)。
sem_wait()
检查计数器。如果它非零,它会递减计数器并继续执行。如果计数器为零,它会阻塞,直到另一个 thread/process 在计数器上调用 sem_post()
。
sem_post()
递增计数器。它是您可以在 signal handler.
中使用的罕见同步原语之一
障碍
屏障是一种同步原语,它会阻塞直到屏障中阻塞了特定数量的线程或进程,然后立即将它们全部释放。
Linux 不实现 POSIX 障碍(pthread_barrier_init()
、pthread_barrier_wait()
、pthread_barrier_destroy()
),但您可以使用互斥锁轻松实现相同的效果,一个计数器(计算释放所有等待者所需的额外进程数)和一个条件变量。
有许多更好的方法来实现上述服务器-客户端对(其中共享内存包含标志和一些数据)。
为了数据完整性和变更管理,应该使用互斥锁和一两个条件变量。 (如果服务器可能随时更改数据,一个条件变量(changed
)就足够了;如果服务器必须等到客户端读取数据后再修改数据,则需要两个条件变量(changed
和observed
).)
这是一个示例结构,您可以用来描述共享内存段:
#ifndef SHARED_H
#define SHARED_H
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct shared_data {
/* Shared memory data */
};
struct shared {
pthread_mutex_t lock;
pthread_cond_t change; /* Condition variable for clients waiting on data changes */
pthread_cond_t observe; /* Condition variable for server waiting on data observations */
unsigned long changed; /* Number of times data has been changed */
unsigned long observed; /* Number of times current data has been observed */
struct shared_data data;
};
/* Return the size of 'struct shared', rounded up to a multiple of page size. */
static inline size_t shared_size_page_aligned(void)
{
size_t page, size;
page = (size_t)sysconf(_SC_PAGESIZE);
size = sizeof (struct shared) + page - 1;
return size - (size % page);
}
#endif /* SHARED_H */
changed
和 observed
字段是计数器,有助于避免任何检查时间到使用时间的竞争 windows。重要的是,在访问共享内存之前,线程会 pthread_mutex_lock(&(shared_memory->lock))
,以确保数据的一致视图。
如果 thread/process 检查数据,它应该做
shared_memory->observed++;
pthread_cond_broadcast(&(shared_memory->observe));
pthread_mutex_unlock(&(shared_memory->lock));
如果 thread/process 修改数据,它应该做
shared_memory->modified++;
shared_memory->observed = 0;
pthread_cond_broadcast(&(shared_memory->change));
pthread_mutex_unlock(&(shared_memory->lock));
在解锁互斥体时通知任何服务员并更新计数器。
我在面试中被问到一个问题,同步是如何在共享内存中完成的。我告诉Take a struct。因为你有一个标志和一个数据。测试标志并更改数据。 我从互联网上获取了以下程序,如下所示。谁能告诉我共享内存中是否有更好的同步方式
#define NOT_READY -1
#define FILLED 0
#define TAKEN 1
struct Memory {
int status;
int data[4];
};
假设服务器端和客户端在当前目录下。服务器使用 ftok() 生成密钥并将其用于请求共享内存。在共享内存被数据填充之前,状态设置为NOT_READY。共享内存填满后,服务器将状态设置为 FILLED。然后,服务器等待状态变为 TAKEN,这意味着客户端已获取数据。
以下是服务器程序。单击此处下载此服务器程序的副本 server.c。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(int argc, char *argv[])
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
if (argc != 5) {
printf("Use: %s #1 #2 #3 #4\n", argv[0]);
exit(1);
}
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), IPC_CREAT | 0666);
if (ShmID < 0) {
printf("*** shmget error (server) ***\n");
exit(1);
}
printf("Server has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (server) ***\n");
exit(1);
}
printf("Server has attached the shared memory...\n");
ShmPTR->status = NOT_READY;
ShmPTR->data[0] = atoi(argv[1]);
ShmPTR->data[1] = atoi(argv[2]);
ShmPTR->data[2] = atoi(argv[3]);
ShmPTR->data[3] = atoi(argv[4]);
printf("Server has filled %d %d %d %d to shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = FILLED;
printf("Please start the client in another window...\n");
while (ShmPTR->status != TAKEN)
sleep(1);
printf("Server has detected the completion of its child...\n");
shmdt((void *) ShmPTR);
printf("Server has detached its shared memory...\n");
shmctl(ShmID, IPC_RMID, NULL);
printf("Server has removed its shared memory...\n");
printf("Server exits...\n");
exit(0);
}
客户端部分与服务器端类似。它一直等到状态为 FILLED。然后,客户端检索数据并将状态设置为 TAKEN,通知服务器数据已被获取。下面是客户端程序。单击此处下载此服务器程序的副本 client.c。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(void)
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), 0666);
if (ShmID < 0) {
printf("*** shmget error (client) ***\n");
exit(1);
}
printf(" Client has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (client) ***\n");
exit(1);
}
printf(" Client has attached the shared memory...\n");
while (ShmPTR->status != FILLED)
;
printf(" Client found the data is ready...\n");
printf(" Client found %d %d %d %d in shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = TAKEN;
printf(" Client has informed server data have been taken...\n");
shmdt((void *) ShmPTR);
printf(" Client has detached its shared memory...\n");
printf(" Client exits...\n");
exit(0);
}
Can anyone tell if there is better way of synchronization in shared memory?
当然,是的。我会说你在忙等待 (while (ShmPTR->status != FILLED) ;
) 中浪费 CPU 个周期的方式已经是一个致命的错误。
请注意,POSIX 共享内存具有比旧 SysV 更明智的接口。有关详细信息,请参阅 man 7 shm_overview。
同步原语有两个不同的用途:
数据同步
为了保护数据免受并发修改,并确保每个 reader 获得一致的数据视图,可采用三种基本方法:
原子访问
原子访问需要硬件支持,通常仅支持本机字大小的单位(32 或 64 位)。
互斥锁和条件变量
互斥锁是互斥锁。这个想法是在检查或修改值之前获取互斥量。
条件变量基本上是线程或进程等待 "condition" 的无序队列。 POSIX pthreads 库包括用于自动释放互斥量和等待条件变量的工具。这使得等待数据集更改变得微不足道,如果每个修改器在每次修改后都在条件变量上发出信号或广播。
读写锁。
rwlock 是一种原语,它允许任意数量的并发 "read locks",但任何时候只能持有一个 "write lock"。这个想法是每个 reader 在检查数据之前获取一个读取锁,并且每个写入者在修改它之前获取一个写入锁。当检查数据的频率高于修改数据的频率时,这种方法效果最佳,并且不需要等待更改发生的机制。
进程同步
有些情况下线程和进程应该等待 (block) 直到某个事件发生。有两个最常用的原语用于此:
信号量
A POSIX semaphore 基本上是一个不透明的非负计数器,您可以将其初始化为任何值(零值或正值,在实现设置的限制内)。
sem_wait()
检查计数器。如果它非零,它会递减计数器并继续执行。如果计数器为零,它会阻塞,直到另一个 thread/process 在计数器上调用sem_post()
。sem_post()
递增计数器。它是您可以在 signal handler.
中使用的罕见同步原语之一
障碍
屏障是一种同步原语,它会阻塞直到屏障中阻塞了特定数量的线程或进程,然后立即将它们全部释放。
Linux 不实现 POSIX 障碍(
pthread_barrier_init()
、pthread_barrier_wait()
、pthread_barrier_destroy()
),但您可以使用互斥锁轻松实现相同的效果,一个计数器(计算释放所有等待者所需的额外进程数)和一个条件变量。
有许多更好的方法来实现上述服务器-客户端对(其中共享内存包含标志和一些数据)。
为了数据完整性和变更管理,应该使用互斥锁和一两个条件变量。 (如果服务器可能随时更改数据,一个条件变量(changed
)就足够了;如果服务器必须等到客户端读取数据后再修改数据,则需要两个条件变量(changed
和observed
).)
这是一个示例结构,您可以用来描述共享内存段:
#ifndef SHARED_H
#define SHARED_H
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct shared_data {
/* Shared memory data */
};
struct shared {
pthread_mutex_t lock;
pthread_cond_t change; /* Condition variable for clients waiting on data changes */
pthread_cond_t observe; /* Condition variable for server waiting on data observations */
unsigned long changed; /* Number of times data has been changed */
unsigned long observed; /* Number of times current data has been observed */
struct shared_data data;
};
/* Return the size of 'struct shared', rounded up to a multiple of page size. */
static inline size_t shared_size_page_aligned(void)
{
size_t page, size;
page = (size_t)sysconf(_SC_PAGESIZE);
size = sizeof (struct shared) + page - 1;
return size - (size % page);
}
#endif /* SHARED_H */
changed
和 observed
字段是计数器,有助于避免任何检查时间到使用时间的竞争 windows。重要的是,在访问共享内存之前,线程会 pthread_mutex_lock(&(shared_memory->lock))
,以确保数据的一致视图。
如果 thread/process 检查数据,它应该做
shared_memory->observed++;
pthread_cond_broadcast(&(shared_memory->observe));
pthread_mutex_unlock(&(shared_memory->lock));
如果 thread/process 修改数据,它应该做
shared_memory->modified++;
shared_memory->observed = 0;
pthread_cond_broadcast(&(shared_memory->change));
pthread_mutex_unlock(&(shared_memory->lock));
在解锁互斥体时通知任何服务员并更新计数器。