目录
Linux进程通信(IPC)。共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成
以下为shm.h源码
/* The following System V style IPC functions implement a shared memory
facility. The definition is found in XPG4.2. */
/* Shared memory control operation. */
extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) __THROW;
/* Get shared memory segment. */
extern int shmget (key_t __key, size_t __size, int __shmflg) __THROW;
/* Attach shared memory segment. */
extern void *shmat (int __shmid, const void *__shmaddr, int __shmflg)
__THROW;
/* Detach shared memory segment. */
extern int shmdt (const void *__shmaddr) __THROW;
__END_DECLS
#endif /* sys/shm.h */
1.shmget()
函数原型:int semget(key_t key, int nsems, int semflg);
函数作用:得到一个共享内存标识符或创建一个共享内存对象 ,也就是该即可创建一个共享内存,也可以获取一个共享内存,key是开发者定义的一个整数(标识符),如果该标识符在内存池已经存在,则是获取该段内存的地址,否则是开辟内存空间,创建共享内存。
- key 0(IPC_PRIVATE):会建立新共享内存对象
大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值
- size 大于0的整数:新建的共享内存大小,以字节为单位
0:只获取共享内存时指定为0
- shmflg 0:取共享内存标识符,若不存在则函数会报错
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
IPC_CREAT|IPC_EXCL: 如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
2. semctl()函数
函数原型:int semctl(int semid,int semnum,int cmd, /union semun arg/);
函数作用:系统调用semctl用来执行在信号量集上的控制操作。
其中比较重要的是cmd命令:
·IPC_STAT 读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
·IPC_SET 设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
·IPC_RMID 将信号量集从内存中删除。
·GETALL 用于读取信号量集中的所有信号量的值。
·GETNCNT 返回正在等待资源的进程数目。
·GETPID 返回最后一个执行semop操作的进程的PID。
·GETVAL 返回信号量集中的一个单个的信号量的值。
·GETZCNT 返回正在等待完全空闲的资源的进程数目。
·SETALL 设置信号量集中的所有的信号量的值。
·SETVAL 设置信号量集中的一个单独的信号量的值。
3.semop()
函数原型 int semop(int semid, struct sembuf *sops, unsigned nsops);
函数作用 共享内存进行并发访问时使用,用于对信号量进行操作来实现进程之间的互斥和同步。在 Linux 下,PV 操作通过调用semop函数来实现。该函数定义在头文件 sys/sem.h中操作一个或一组信号。
这个函数我们会发现第二个参数传入的是一个结构体指针,sembuf 结构体里面的变量需要大家简单了解,在下面将会给出
1.1 三个参数的含义:
- 1.1.1 semid:信号集的识别码,一般在调用该函数前通过semget()函数获取。
- 1.1.2 sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下:
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
/*
1.sem_num 信号量集中要操作的信号量的索引,对于单个信号量集合来说,一般将其设置为0
2.sem_op : 为正值用于释放所控制的资源
为 0 如果没有设置IPC_NOWAIT,则调用该操作的进程或者线程将暂时睡眠,直到信号量的值为0;否则,进程或者线程不会睡眠,函数返回错误EAGAIN。
在次,可以简单理解为:
-1(P操作):申请资源,即使资源不可用也会阻塞等待。
1(V操作):释放资源。
为负值通常用于获取所控制资源的使用权,其绝对值大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;
3.sem_flg:信号操作标志,可能的选择有两种
IPC_NOWAIT :对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
可以简单理解为
0:正常操作(无特殊标志)。
IPC_NOWAIT:非阻塞操作,即如果资源不可用,立即返回而不是阻塞等待。
*/
- 1.1.3 nsops:信号操作结构的数量,恒大于或等于1.
4. shmat()函数
函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg)
函数作用:把共享内存区对象映射到调用进程的地址空间,一般在创建共享内存之后
函数返回值:成功:附加好的共享内存地址,失败:-1,错误原因存于errno中
参数说明:
shmid 共享内存标识符 也就是key,由是shmget()函数的返回值
shmaddr 指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
shmflg SHM_RDONLY:为只读模式,其他为读写模式
5. 代码实例
#include <iostream>
#include <thread>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define SHARED_MEMORY_KEY 12345
#define SHARED_MEMORY_SIZE 1024
#define SEMAPHORE_KEY 54321
void producer() {
// 创建共享内存
int shmid = shmget(SHARED_MEMORY_KEY, SHARED_MEMORY_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
std::cerr << "Failed to create shared memory" << std::endl;
return;
}
// 连接共享内存
char* shared_memory = (char*)shmat(shmid, nullptr, 0);
if (shared_memory == (char*)-1) {
std::cerr << "Failed to attach shared memory" << std::endl;
return;
}
// 获取信号量
int semid = semget(SEMAPHORE_KEY, 1, IPC_CREAT | 0666);
if (semid == -1) {
std::cerr << "Failed to create semaphore" << std::endl;
return;
}
// 对信号量进行P操作,获取互斥锁
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
semop(semid, &sem_op, 1);
// 推送数据到共享内存
for (int i = 0; i < 10; i++) {
std::string data = "Data " + std::to_string(i);
snprintf(shared_memory, SHARED_MEMORY_SIZE, "%s", data.c_str());
sleep(1); // 每隔1秒推送一次数据
}
// 对信号量进行V操作,释放互斥锁
sem_op.sem_op = 1;
semop(semid, &sem_op, 1);
// 分离共享内存
shmdt(shared_memory);
// 删除共享内存
shmctl(shmid, IPC_RMID, nullptr);
// 删除信号量
semctl(semid, 0, IPC_RMID);
}
void consumer() {
// 获取共享内存
int shmid = shmget(SHARED_MEMORY_KEY, SHARED_MEMORY_SIZE, 0666);
if (shmid == -1) {
std::cerr << "Failed to get shared memory" << std::endl;
return;
}
// 连接共享内存
char* shared_memory = (char*)shmat(shmid, nullptr, 0);
if (shared_memory == (char*)-1) {
std::cerr << "Failed to attach shared memory" << std::endl;
return;
}
// 获取信号量
int semid = semget(SEMAPHORE_KEY, 1, IPC_CREAT | 0666);
if (semid == -1) {
std::cerr << "Failed to create semaphore" << std::endl;
return;
}
// 对信号量进行P操作,获取互斥锁
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
semop(semid, &sem_op, 1);
// 从共享内存中取出数据并打印
for (int i = 0; i < 10; i++) {
std::cout << "Received data: " << shared_memory << std::endl;
sleep(1); // 每隔1秒取出一次数据
}
// 对信号量进行V操作,释放互斥锁
sem_op.sem_op = 1;
semop(semid, &sem_op, 1);
// 分离共享内存
shmdt(shared_memory);
}
int main() {
// 创建一个线程用于推送数据到共享内存
std::thread producerThread(producer);
// 创建一个线程用于从共享内存取出数据并打印
std::thread consumerThread(consumer);
// 等待两个线程执行完毕
producerThread.join();
consumerThread.join();
return 0;
}