共享内存是一种进程间通信(IPC)的机制,允许不同的进程共享同一块内存区域。这样,多个进程可以同时访问和修改共享内存中的数据,从而达到数据共享的目的。
共享内存通常由一个进程创建,并通过特定的系统调用将其附加到其他进程的地址空间中。进程可以通过读写共享内存中的数据来进行通信和同步。由于共享内存的访问速度非常快,因此它是一种高效的进程间通信机制。
但是,由于多个进程可以同时访问和修改共享内存中的数据,因此需要特殊的同步机制来避免数据竞争和其他并发问题。常见的同步机制包括信号量、互斥量和条件变量等。
话不多说,直接上代码。
/* Filename: shm_write.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt; //计数器
int complete; //标志位
char buf[BUF_SIZE]; //长度为BUF_SIZE 的缓冲区
};
int fill_buffer(char * bufptr, int size);
int main(int argc, char *argv[]) {
int shmid, numtimes;
struct shmseg *shmp; //创建共享内存区域
char *bufptr;
int spaceavailable;
//shmget函数用于创建共享内存段
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT); //SHM_KEY 是一个用户定义的常量,它是一个键值,用于标识共享内存段。
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from buffer to shared memory */
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
for (numtimes = 0; numtimes < 5; numtimes++) {
shmp->cnt = fill_buffer(bufptr, spaceavailable);
shmp->complete = 0;
printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sleep(3);
}
printf("Writing Process: Wrote %d times\n", numtimes);
shmp->complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
return 0;
}
int fill_buffer(char * bufptr, int size) {
static char ch = 'A';
int filled_count;
//printf("size is %d\n", size);
memset(bufptr, ch, size - 1);
bufptr[size-1] = '\0';
if (ch > 122)
ch = 65;
if ( (ch >= 65) && (ch <= 122) ) {
if ( (ch >= 91) && (ch <= 96) ) {
ch = 65;
}
}
filled_count = strlen(bufptr);
//printf("buffer count is: %d\n", filled_count);
//printf("buffer filled is:%s\n", bufptr);
ch++;
return filled_count;
}
//更多请阅读:https://www.yiibai.com/ipc/shared_memory.html
这是一个使用共享内存实现进程间通信的程序。它创建了一个共享内存区域,然后向其中写入数据。下面是代码的大致流程:
shmseg
,其中包含了一个计数器、一个标志位以及一个长度为 BUF_SIZE
的缓冲区。shmget
函数来创建共享内存区域,如果创建失败则输出错误信息并退出程序。shmat
函数将进程附加到共享内存区域,获取到了一个指向共享内存区域的指针,如果失败则输出错误信息并退出程序。fill_buffer
的函数,该函数填充缓冲区,然后将缓冲区中的数据写入共享内存区域。循环执行 5 次,每次等待 3 秒钟。complete
标志位置为 1,然后通过调用 shmdt
函数将进程从共享内存区域分离,最后调用 shmctl
函数删除共享内存区域。"Writing Process: Complete"
。总之,这是一个向共享内存区域写入数据的程序。它创建了一个共享内存区域,然后将数据写入其中。该程序可以与另一个程序共同使用,以实现进程间通信。
shmget
函数用于创建或访问一个共享内存段。它的函数原型为:int shmget(key_t key, size_t size, int shmflg);
它接受三个参数:
(1)key
:用于标识共享内存段的键值,可以通过 ftok 函数生成。
(2)size
:指定共享内存段的大小(以字节为单位)。
(3)shmflg
:指定访问权限和其他标志。
shmget
函数返回共享内存段的标识符(也称为共享内存段的 ID)。如果共享内存段已经存在,则返回它的标识符;如果共享内存段不存在,则根据指定的 size 创建一个新的共享内存段,并返回它的标识符。
2. struct shmseg
结构体定义了共享内存段中存储的数据的格式。它包含三个成员:
(1)cnt
:表示缓冲区中存储的数据的长度。
(2)complete
:一个标志,用于表示数据是否已经全部写入共享内存段。
(3)buf
:一个字符数组,用于存储实际的数据。
在程序中,使用 shmget
函数创建了一个共享内存段,然后使用 struct shmseg
结构体来表示该共享内存段中存储的数据。程序使用共享内存段来实现两个进程之间的通信,其中一个进程用于写入数据,另一个进程用于读取数据。
因此,shmget
函数和 struct shmseg
结构体的作用不同,但它们都是实现共享内存通信的关键组成部分。
shmat
函数用于将进程与一个共享内存段关联起来,从而使得进程可以访问共享内存段中的数据。它的函数原型为:
void *shmat(int shmid, const void *shmaddr, int shmflg);
它接受三个参数:
(1)shmid
:共享内存段的标识符,它是由 shmget 函数返回的。
(2)shmaddr
:指定共享内存段将要被映射到进程的地址。通常传入 NULL,表示让系统自动选择一个合适的地址。
(3)shmflg
:指定映射方式和其他标志。
shmat
函数返回一个指向共享内存段第一个字节的指针,如果出现错误,则返回 (void *)-1
。
在这个程序中,shmat
函数用于将当前进程与共享内存段关联起来,从而使得进程可以访问共享内存段中的数据。具体来说,程序在调用 shmget
函数创建共享内存段之后,通过 shmat
函数将 struct shmseg
结构体映射到共享内存段上,这样就可以在进程中使用该结构体来访问共享内存段中的数据了。
fill_buffer
函数的作用是将指定的缓冲区填充为重复的字母序列,直到缓冲区填满为止。
具体来说,fill_buffer
函数的第一个参数 bufptr
是一个指向缓冲区的指针,第二个参数 size
是缓冲区的大小(以字节为单位)。函数首先将一个静态的字符变量 ch
赋值为 'A'
,然后使用 memset
函数将缓冲区填充为 size-1
个 ch
字符,最后将缓冲区的最后一个字符设置为 '\0'
,以确保缓冲区可以当作一个 C 语言风格的字符串使用。
如果 ch
的值超过了字母表的最后一个字符 'z'
,则将 ch
的值重置为字母表的第一个字符 'A'
。此外,fill_buffer
函数还会返回实际填充缓冲区的字符个数(即不包括最后一个 '\0'
字符)。
在该程序中,fill_buffer
函数用于向共享内存段中写入数据,即将缓冲区填充为字母序列,然后将数据写入到共享内存段中。在写入数据之前,程序将缓冲区的地址赋给指向共享内存段的指针 shmp->buf
,这样程序就可以通过 shmp->buf
指针来访问共享内存段中的缓冲区了。具体来说,程序在循环中反复调用 fill_buffer
函数,将缓冲区填充为不同的字母序列,然后将填充后的数据写入到共享内存段中。
shmdt()
函数是用于将共享内存从进程的地址空间中分离的函数。调用该函数后,进程将不能再访问共享内存中的数据。该函数的函数原型如下:
#include
int shmdt(const void *shmaddr);
其中,参数shmaddr
是指向共享内存段附加到进程地址空间的指针。
如果分离成功,shmdt()
函数返回0,否则返回-1,并设置errno
变量来指示错误原因。
在使用共享内存完成数据传输之后,需要使用shmdt()
函数将共享内存从进程的地址空间中分离。这是一个很重要的步骤,因为共享内存是由多个进程共享的资源,而不是属于某个进程的私有资源,如果不使用shmdt()
函数将共享内存分离,可能会导致其他进程无法访问该共享内存,造成资源泄漏等问题。
/* Filename: shm_read.c */
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt; //计数器
int complete; //标志位
char buf[BUF_SIZE]; //长度为BUF_SIZE 的缓冲区
};
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from shared memory to stdout*/
while (shmp->complete != 1) {
printf("segment contains : \n\"%s\"\n", shmp->buf);
if (shmp->cnt == -1) {
perror("read");
return 1;
}
printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
sleep(3);
}
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
printf("Reading Process: Complete\n");
return 0;
}
//更多请阅读:https://www.yiibai.com/ipc/shared_memory.html
这段代码实现了一个使用共享内存的读进程。这段代码的作用是从共享内存中读取数据并输出到标准输出流,主要用于进程间通信。以下是代码的详细解读:
stdio.h
、sys/ipc.h
、sys/shm.h
、sys/types.h
、string.h
、`errno.h、stdlib.h。BUF_SIZE
表示共享内存中的缓冲区大小为1024
,SHM_KEY
表示共享内存的键值为0x1234
。shmseg
,包括三个成员变量:cnt
表示缓冲区中实际数据的大小;complete
表示是否读取完毕;buf
表示共享内存中的缓冲区。main()
函数首先创建共享内存,如果创建失败则输出错误信息并返回1。shmat()
函数将共享内存附加到进程的地址空间,返回一个指向共享内存的指针,如果附加失败则输出错误信息并返回1。complete
成员变量被设置为1,表明读取完毕,此时读进程将共享内存从地址空间中分离,如果分离失败则输出错误信息并返回1。“Reading Process: Complete”
,并返回0表示正常退出。 发现问题: 在以上代码中,使用了一个简单的标志位complete
来指示进程是否已经完成对共享内存的读写操作。标志位的值为0表示正在写入,值为1表示写入已完成,读取进程可以根据该标志位的值来判断何时停止读取。如果使用信号量来控制进程对共享内存的访问,则可以更加精细地控制读写进程之间的同步,从而避免竞态条件和数据不一致等问题。
/* Filename: shm_write.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //添加了信号量的头文件
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg
{
int cnt; // 计数器
int complete; // 标志位
char buf[BUF_SIZE]; // 长度为BUF_SIZE 的缓冲区
};
int fill_buffer(char *bufptr, int size);
int main(int argc, char *argv[])
{
int shmid, numtimes;
struct shmseg *shmp; // 创建共享内存区域
char *bufptr;
int spaceavailable;
sem_t *sem_read, *sem_write;
sem_read = sem_open("/sem_read", O_CREAT, 0644, 0);
sem_write = sem_open("/sem_write", O_CREAT, 0644, 1);
// shmget函数用于创建共享内存段
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644 | IPC_CREAT); // SHM_KEY 是一个用户定义的常量,它是一个键值,用于标识共享内存段。
if (shmid == -1)
{
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *)-1)
{
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from buffer to shared memory */
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
for (numtimes = 0; numtimes < 5; numtimes++)
{
// 等待信号量,保证同步
sem_wait(sem_write);
shmp->cnt = fill_buffer(bufptr, spaceavailable);
shmp->complete = 0;
printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sleep(3);
// 释放信号量
sem_post(sem_read); // 发送读取信号量
}
printf("Writing Process: Wrote %d times\n", numtimes);
shmp->complete = 1;
if (shmdt(shmp) == -1)
{
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1)
{
perror("shmctl");
return 1;
}
// 关闭信号量
sem_close(sem_read);
sem_close(sem_write);
sem_unlink("/sem_read");
sem_unlink("/sem_write");
printf("Writing Process: Complete\n");
return 0;
}
int fill_buffer(char *bufptr, int size)
{
static char ch = 'A';
int filled_count;
// printf("size is %d\n", size);
memset(bufptr, ch, size - 1);
bufptr[size - 1] = '\0';
if (ch > 122)
ch = 65;
if ((ch >= 65) && (ch <= 122))
{
if ((ch >= 91) && (ch <= 96))
{
ch = 65;
}
}
filled_count = strlen(bufptr);
// printf("buffer count is: %d\n", filled_count);
// printf("buffer filled is:%s\n", bufptr);
ch++;
return filled_count;
}
// 更多请阅读:https://www.yiibai.com/ipc/shared_memory.html
/* Filename: shm_read.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //添加了信号量的头文件
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg
{
int cnt; // 计数器
int complete; // 标志位
char buf[BUF_SIZE]; // 长度为BUF_SIZE 的缓冲区
};
int main(int argc, char *argv[])
{
int shmid;
struct shmseg *shmp; // 创建共享内存区域
sem_t *sem_read, *sem_write;
sem_read = sem_open("/sem_read", O_CREAT, 0644, 0);
sem_write = sem_open("/sem_write", O_CREAT, 0644, 1);
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644 | IPC_CREAT);
if (shmid == -1)
{
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *)-1)
{
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from shared memory to stdout*/
while (shmp->complete != 1)
{
sem_wait(sem_read); // 等待读取信号量
printf("segment contains : \n\"%s\"\n", shmp->buf);
if (shmp->cnt == -1)
{
perror("read");
return 1;
}
printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
sem_post(sem_write); // 发送写入信号量
sleep(3);
}
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
if (shmdt(shmp) == -1)
{
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1)
{
perror("shmctl");
return 1;
}
sem_close(sem_read);
sem_close(sem_write);
sem_unlink("/sem_read");
sem_unlink("/sem_write");
printf("Reading Process: Complete\n");
return 0;
}
// 更多请阅读:https://www.yiibai.com/ipc/shared_memory.html
在这个例子中,可以使用两个二进制信号量(semaphore
)来同步和保护共享内存的读和写:
#include
#include
sem_t *sem_read, *sem_write;
sem_read = sem_open("/sem_read", O_CREAT, 0644, 0);
sem_write = sem_open("/sem_write", O_CREAT, 0644, 1);
以上创建了两个二进制信号量,一个用于读取进程sem_read
,一个用于写入进程sem_write
。 初始值分别为0和1,以确保写入进程可以立即开始写入。
sem_wait(sem_write); // 等待写入信号量
shmp->cnt = fill_buffer(bufptr, spaceavailable);
shmp->complete = 0;
printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sem_post(sem_read); // 发送读取信号量
while (shmp->complete != 1) {
sem_wait(sem_read); // 等待读取信号量
printf("segment contains : \n\"%s\"\n", shmp->buf);
if (shmp->cnt == -1) {
perror("read");
return 1;
}
printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
sem_post(sem_write); // 发送写入信号量
sleep(3);
}
这确保了在读取完成之前不会有其他进程写入新的数据。
sem_close(sem_read);
sem_close(sem_write);
sem_unlink("/sem_read");
sem_unlink("/sem_write");
致谢:本章学习参考连接(https://www.yiibai.com/ipc/shared_memory.html)