linux进程间通信之共享内存

时间:2022-08-28 21:39:36

1:共享内存

共享内存允许两个或多个进程共享一给定的内存。因为数据不需要再进程间复制,可以直接读写内存,所以它是目前最快的一种进程间通信方式。内核专门为共享内存留出一块专有内存区域,在使用前只需要将该地址映射到进程的私有地址空间,就可以直接访问,一般需要结合互斥锁或信号量等同步机制来进行访问。

linux进程间通信之共享内存

上图示意了共享内存原理示意图, 在内核部分将讲述共享内存区的划分及实现。

2:主要函数

对共享内存操作主要分为两步,创建共享内存和映射共享内存

创建共享内存函数:
int shmget(key_t key, int size, int shmflg)
参数:key值就是共享内存的键值,多个进程可以通过它访问同一个共享内存,其中有个特殊值IPC_PRIVATE.它用于创建当前进程的私有共享内存
size: 共享内存大小
shmflg:同open()函数的权限位,也可以用八进制表示法。

共享内存映射函数:
char shmat(int shmid, const void shmaddr, int shmflg)
参数:shmid:要映射的共享内存区标识符。
shmaddr:将共享内存映射到指定地址(若为0, 则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
shmflg:共享内存权限。默认为0:共享内存可读写。SHM_RDONLY:共享内存只读。

当该进程操作完共享内存时,需要进程脱接该段:
int shmdt(const void *shmaddr)
参数: shmaddr:被映射的共享内存段地址。

3:实验

实验1:查看共享内存在每个进程的映射地址:

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


#define BUFFER_SIZE 2048

int main()
{
pid_t pid;
int shmid;
char *shm_addr;
char flag[] = "WROTE";
char *buff;

/*create share memory*/
if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
{
perror("Shmget");
exit(1);
} else {
printf("Create share memory: %d\n", shmid);
}


pid = fork();
if ( pid == -1)
{
perror("fork");
exit(1);
} else if (pid == 0)
{
sleep(1);

/*map share memory*/
if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
{
perror("Child: shmat");
exit(1);
}

printf("Child: Share memory addess :0x%x\n", (unsigned long)shm_addr);

/* delet memory map*/
if ((shmdt(shm_addr)) < 0)
{
perror("Shmdt");
exit(1);
} else {
printf("Child: Dettach share memory\n");
}


/*Delete share memory */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("Child: shmctl(IPC_RMID)\n");
exit(1);
} else {
printf("Delete share memory\n");
}


} else {
/* map share memory */
if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
{
perror("Parent: shmat");
exit(1);
}

printf("Father process share address 0x%x\n",(unsigned long)shm_addr);

/* Delete share memory map */
if ((shmdt(shm_addr)) < 0)
{
perror("Parent: shmdt");
exit(1);
} else {
printf("Parent: Deattach share memory\n");
}

waitpid(pid, NULL, 0);
printf("Finished \n");
}
exit(0);
}

运行结果:
root# ./a.out
Create share memory: 1933326
Father process share address 0xb76da000
Parent: Deattach share memory
Child: Share memory addess :0xb76da000
Child: Dettach share memory
Delete share memory
Finished

实验2:下面是参考《linux应用程序开发标准教程》中的一个例子,没有对共享内存加入同步机制,因为是同时只有一个进程对该内存进行写,直接上代码:

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


#define BUFFER_SIZE 2048

int main()
{
pid_t pid;
int shmid;
char *shm_addr;
char flag[] = "WROTE";
char *buff;

/*create share memory*/
if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
{
perror("Shmget");
exit(1);
} else {
printf("Create share memory: %d\n", shmid);
}


/* show share memory */
system("ipcs -m");

pid = fork();
if ( pid == -1)
{
perror("fork");
exit(1);
} else if (pid == 0)
{

/*map share memory*/
if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
{
perror("Child: shmat");
exit(1);
} else
{
printf("Child: Attach share memory: %p\n", shm_addr);
}
system("ipcs -m");


/* check memory head */
while(strncmp(shm_addr, flag, strlen(flag)))
{
printf("Child: Wait for enable data ...\n");
sleep(5);
}

/*Get data*/
strcpy(buff, shm_addr + strlen(flag));
printf("Child: Share memory :%s\n", buff);

/* delet memory map*/
if ((shmdt(shm_addr)) < 0)
{
perror("Shmdt");
exit(1);
} else {
printf("Child: Dettach share memory\n");
}

system("ipcs -m");

/*Delete share memory */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("Child: shmctl(IPC_RMID)\n");
exit(1);
} else {
printf("Delete share memory\n");
}

system("ipcs -m");

} else {
/* map share memory */
if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
{
perror("Parent: shmat");
exit(1);
} else {
printf("Parent :Attach shared memory :%p\n", shm_addr);
} sleep(1);
printf("n Input some string:\n");
fgets(buff, BUFFER_SIZE, stdin);
strncpy(shm_addr + strlen(flag), buff, strlen(buff));
strncpy(shm_addr, flag, strlen(flag));

/* Delete share memory map */
if ((shmdt(shm_addr)) < 0)
{
perror("Parent: shmdt");
exit(1);
} else {
printf("Parent: Deattach share memory\n");
}

system("ipcs -m");

waitpid(pid, NULL, 0);
printf("Finished \n");
}
exit(0);
}

运行结果
root@x:/repo/training/fork# ./a.out
Create share memory: 1867790

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 0

Parent :Attach shared memory :0xb7768000
Child: Attach share memory: 0xb7768000

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 2

Child: Wait for enable data …
asdn Input some string:
wds
Parent: Deattach share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 1

Child: Share memory :asdwds

Child: Dettach share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 0

Delete share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest

Finished

实验三:增加信号量与共享内存向结合使用实验用例:

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


#define BUFFER_SIZE 2048

union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};


int init_sem(int sem_id, int init_value)
{
union semun sem_union;

sem_union.val = init_value;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
{
perror("Initialize semaphore");
return -1;
}

return 0;
}


int del_sem(int sem_id)
{
union semun sem_union;

if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
{
perror("Delete semaphore");
return -1;
}

}
int sem_p(int sem_id)
{
struct sembuf sem_b;

sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;

if (semop(sem_id, &sem_b, 1) == -1)
{
perror("P operation fail");
return -1;
}

return 0;
}

int sem_v(int sem_id)
{
struct sembuf sem_b;

sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;

if (semop(sem_id, &sem_b, 1) == -1)
{
perror("V operation");
return -1;
}

return 0;
}

int ignore_signal(void)
{
signal(SIGINT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}

int main()
{
pid_t pid;
char *shm_addr;
char flag[] = "WROTE";
char *buff;
int shmid, semid;

ignore_signal();

semid = semget(ftok(".", 'a'), 1, 0666 | IPC_CREAT);
init_sem(semid, 1);

/*create share memory*/
if ((shmid = shmget(ftok(".", 'b'), BUFFER_SIZE, 0666 | IPC_CREAT)) < 0)
{
perror("Shmget");
exit(1);
} else {
printf("Create share memory: %d\n", shmid);
}



pid = fork();
if ( pid == -1)
{
perror("fork");
exit(1);
} else if (pid == 0)
{
sleep(1);
/*map share memory*/
if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
{
perror("Child: shmat");
exit(1);
} else
{
printf("Child: Attach share memory: %p\n", shm_addr);
}

do {
sem_p(semid);
if (strncmp(shm_addr, "quit", 4) == 0)
{
sem_v(semid);
break; }

printf("Child: get from father: %s\n", shm_addr);

memset(shm_addr, 0, BUFFER_SIZE);
sem_v(semid);
} while(1);


/* delet memory map*/
if ((shmdt(shm_addr)) < 0)
{
perror("Shmdt");
exit(1);
} else {
printf("Child: Dettach share memory\n");
}

sleep(5);
/*Delete share memory */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("Child: shmctl(IPC_RMID)\n");
exit(1);
} else { printf("Child: Dettach share memory\n");
}

sleep(5);
/*Delete share memory */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("Child: shmctl(IPC_RMID)\n");
exit(1);
} else {
printf("Delete share memory\n");
}


} else {
/* map share memory */
if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
{
perror("Parent: shmat");
exit(1);
} else {
printf("Parent :Attach shared memory :%p\n", shm_addr);
}
do {
sem_p(semid);
printf("n Input some string:\n");
if (fgets(shm_addr, BUFFER_SIZE, stdin) == NULL)
{
perror("fgets");
sem_v(semid);
break;
}

if (strncmp(shm_addr, "quit", 4) == 0)
{
sem_v(semid);
break;
}

sem_v(semid);
} while(1);

/* Delete share memory map */
if ((shmdt(shm_addr)) < 0)
{
perror("Parent: shmdt");
exit(1);
} else {
printf("Parent: Deattach share memory\n");
}

waitpid(pid, NULL, 0);
printf("Finished \n");
}
exit(0);

实验代码在:https://github.com/zhikunhuo/training.git