1)功能:
共享内存是IPC通信的一种机制,并且是高效的进程间通信方式,它允许多个不相关的进程访问同一个逻辑内存,就如同malloc()函数向不同进程返回指向同一个物理内存区域的指针。
2)流程:
创建共享内存:shmget()
|
映射共享内存:shmat()
|
对映射的共享内存进行操作
|
解除映射:shmdt()
|
删除共享内存:shmctl()
概括说明:要使用共享内存,进程首先必须分配一块共享内存,随后要访问这块共享内存的每个进程都要将共享内存绑定到自己进程的地址空间中,然后就可以对这块共享内存进行操作了,当一个进程改变共享内存中的内容时,其他进程也会马上察觉到这个改变。当完成通信之后,所有的进程都要脱离共享内存,并后释放掉该共享内存。
3).注意:共享内存可以为多个进程提供数据传输,但是它并没有提供同步机制,就是说:如果一个进程对共享内存进程写操作,其他进程也可以对该共享内存进行读或写操作,这样会导致数据读写操作不正确,数据冲突。所以要增加同步机制,一般要使用信号量。
4).同步:是指多个任务(进程或线程)按照一定的约定顺序互相配合去完成一件事情。由于多个进程共享一段内存,因此也需要依靠某种同步机制,如:互斥锁或信号量等。
5).函数基础:
#include <sys/shm.h> //共享内存原函数位置
创建共享内存:
int shmget(key_t key, size_t size, int shmflg);
参数:
key:键值,用于对共享内存段命名
size:共享内存的容量,以字节为单位
shmflg:权限标志,和创建文件时使用的mode标志一样。通常使用:CREAT | EXCL(创建新的共享内存,如果已有指定键值的共享内存,创建失败,但是会直接打开这个已有的共享内存)
返回值:共享内存的标识(shmid),失败返回-1
映射共享内存:(创建共享内存段后,不能被进程访问,想要使用,需要将其连接到一个进程的地址空间)
void *shmat(int shm_id, const void *shmaddr, int shmflg);
参数:
shm_id:共享内存的标识
shm_addr:共享内存连接到当前进程中的地址位置。它通常是NULL,让系统自己去分配地址。
shmflg:权限标志,通常为:0 ,可以对共享内存进行读写操作
返回值:该共享内存在进程中的映射地址
解除映射:
int shmdt(const void *shmaddr);
参数:上面共享内存在进程中的虚拟内存地址
返回值:成功:0;错误:-1
删除共享内存:
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
参数:
shm_id:共享内存的标识
cmd:操作命令;IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构放到buf中
IPC_RMID:删除共享内存
返回值:成功:0,失败:-1
6).项目应用:
①先创建变量
key_t key_info;
//键值
int shmid, semid; //共享内存的id,信号量的id
struct shm_addr
{
char cgi_status;
struct env_info_clien_addr rt_status;
};//共享内存存放数据的结构体(自定义)
struct shm_addr *shm_buf;//定义结构体的指针
②获得键值
key_info = ftok ("/app", 'i');
③创建共享内存和信号量
shmid = shmget (key_info, 1024, IPC_CREAT | IPC_EXCL | 0666) //参数:键值,共享内存大小,权限
semid = semget (key_info, 1, IPC_CREAT | IPC_EXCL |0666);//参数:键值,信号量的数量,权限
④初始化信号量
init_sem (semid, 0, 1); //
⑤映射共享内存
shm_buf = (struct shm_addr *)shmat (shmid, NULL, 0);
⑥清空下数据结构体
bzero (shm_buf, sizeof (struct shm_addr));
⑦while(1)循环
先上锁
pthread_mutex_lock (&mutex_refresh);
再等待被唤醒,互斥锁与唤醒条件同时使用
pthread_cond_wait (&cond_refresh, &mutex_refresh);
增加共享内存的同步机制,使用信号量,获得信号
sem_p (semid, 0);
对处理数据加锁
pthread_mutex_lock (&mutex_global);
把已知数据放到共享内存映射的地址中,这个用结构体接收数据
shm_buf->rt_status = all_info_RT;
对临界资源解锁
pthread_mutex_unlock (&mutex_global);
释放信号,让CGI进程去获得
sem_v (semid, 0);
解锁
pthread_mutex_unlock (&mutex_refresh);
7).信号量的函数
信号量其实是一个计数器,用于计算当前的资源是否可用。
每个信号量在内核中都有一个无名的结构表示,它至少包括以下成员:
/*我们必须自己定义union semun (信号量联合体)*/
union semun {
int val; /* Value for SETVAL(信号量的初始值) */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
/*获取信号量的标识符
int semget(key_t key, int nsems, int flag)
参数:键值,信号量的数量,权限
/*控制信号量*/
int semctl(int semid, int semnum, int cmd, /*union semun*/)
参数:
semid:信号量的标识符
semnum:要操作的信号量在信号量集中的下标(0到nsems-1)
cmd:系统控制命令,常用的是:SETVAL,用来把信号量的值初始化为我们设定的值。这个值由第四个参数semun联合体中的val成员提供。
/*对信号量进行增减操作*/
int semop(int semid, struct sembuf semoparray[ ], size_t nops)
参数:
semid:信号量的标识
semoparray[ ]:指向信号量操作数组的指针。该指针有如下结构:
struct sembuf
{
unsigned short sem_num; //信号量的编号
short sem_op; //操作中改变的数值,+1表示释放资源,-1表示占用资源
short sem_flag; //通常设为SEM_UNDO,当系统在没有释放信号量的情况下退出进程,系统将自动释放进程占用的信号量
}
nops : 操作的信号数量
8).信号量的设计(sem.h)
①先定义信号量的联合体
union semun {
int val; /* Value for SETVAL(信号量的初始值) */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
②信号量的初始化
int init_sem(int semid, int num, int val)
{
union semun myun; //信号量联合体的变量myun
myun.val = val; //把形参val传给联合体的成员val
if(semctl(semid, num, SETVAL, myun) < 0) //利用semctl函数对信号量初始化
{
perror("semctl");
exit(1);
}
return 0;
}
③获得信号量
int sem_p(int semid, int num)
{
struct sembuf mybuf; //信号量的操作结构体
mybuf.sem_num = num; //信号量的编号
mybuf.sem_op = -1; //-1表示占用资源
mybuf.sem_flg = SEM_UNDO;//系统没有释放信号量自动退出进程。
if(semop(semid, &mybuf, 1) < 0) //对该信号量进行操作,操作由mybuf结构体提供,信号量的数量是1个
{
perror("semop");
exit(1);
}
return 0;
}
④释放信号量
int sem_v(int semid, int num)
{
struct sembuf mybuf;
mybuf.sem_num = num;
mybuf.sem_op = 1; //+1表示释放信号量
mybuf.sem_flg = SEM_UNDO;
if(semop(semid, &mybuf, 1) < 0)
{
perror("semop");
exit(1);
}
return 0;
}
参考:百度,华清教科书
作者:Aaron Liu