目录
四、标准IPC
标准IPC是一类遵循指定标准的IPC(进程间通信)的统称,其实就是其中所有的IPC都具有一部分相同的标准
标准IPC包含 共享内存 消息队列 信号量集
标准IPC的通用规范(上: 用户 下: 内核), 如下图所示:
( 1 )所有的标准IPC都有一个内部ID作为唯一标识
( 2 )获取内部ID, 需要借助外部的 key,类型为key_t
( 3 )获取key的方法有三种:
1、使用宏IPC_PRIVATE作为key,但这种方式外部无法获取,因此基本不用
2、在代码中自定义所有的key
3、使用ftok()函数获取一个key
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数:
pathname - 路径(必须有效 / or .)
proj_id - 工程ID(非0的8位数据,通常给一个字符)
成功返回key,失败返回-1
( 4 )通过刚刚获取的key来获取ID, 常用的函数都是叫 xxxget(),比如:shmget() msgget()
( 5 )标准IPC一定提供一个叫 xxxctl()的函数,这个函数至少包括以下功能
功能通过一个cmd的参数来控制
查询 ------- IPC_STAT
删除 ------- IPC_RMID
修改 ------- IPC_SET
( 6 )标准IPC共用命令
ipcs - 查询当前系统的标准IPC结构
ipcrm - 删除当前系统的标准IPC结构(通过ID删除)
选项:
-a 所有IPC结构
-m 共享内存
-q 消息队列
-s 信号量集
nattch 挂载进程数量
4.1 共享内存
4.1.1 概念和原理
共享内存是其中一个标准IPC, 其大概的标准如前面所讲的
它的原理就是系统取出一块物理内存作为媒介,让系统内核( MMU)负责管理, 它允许所有的进程对这块物理内存进行映射, 这样不同的进程就可以访问同一片物理内存,从而实现数据的交互
由于内存访问速度极快,所以共享内存是效率最高的IPC
但是,多个进程同时对一块物理内存读写容易造成数据混乱,需要额外的保护机制(利用信号量集)
4.1.2 使用
1)获取key,通过ftok函数
2)通过shmget() 获取/创建 共享内存
参数:
key - 上一步获取的key
size - 共享内存的大小(创建时有效)
shmflg - 创建标识和权限(创建时有效)
IPC_CREAT | 0666
成功返回共享内存的ID,失败返回-1
3)通过shmat() 映射/挂接 共享内存
参数:
shmid - 共享内存ID
shmaddr - 映射的目标地址(给NULL由系统选择)
shmflg - 映射标识(通常给0,给SHM_RDONLY表示只读)
成功返回映射目标地址,失败返回 (void *)-1
4) 使用共享内存
5) 使用完通过 shmdt() 解除映射/脱接 共享内存
6) 如果不再使用,使用shmctl来删除共享内存 (删除共享内存只是给共享内存加一个删除标记, 等挂载数为0时 才删除)
参数:
shmid - 共享内存ID
cmd - 命令
IPC_RMID:删除
IPC_SET: 修改
IPC_STAT: 获取
buf - 设置/获取 时 传入/传出 共享内存的信息 , 下面是它的数据类型 ,其中可以修改的是uid、gid和mode
shmctl函数 成功返回0,失败返回-
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* 大小 */
time_t shm_atime; /* 最后挂载(映射)时间*/
time_t shm_dtime; /* 最后卸载(解除映射)时间 */
time_t shm_ctime; /* 最后修改时间*/
pid_t shm_cpid; /* 创建者PID */
pid_t shm_lpid; /* 最后一个挂载/卸载PID */
shmatt_t shm_nattch; /* 当前挂载的进程数*/
...
};
struct ipc_perm {
key_t __key; /* Key*/
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* 权限 */
unsigned short __seq; /* Sequence number */
};
代码如下、这个进程创建共享内存后、并映射它到自己的进程空间、再使用它、最后解除映射:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
//1.获取key
key_t key = ftok(".",'x');
if(key==-1){
perror("ftok");
exit(-1);
}
//2.创建共享内存
int shmid = shmget(key,10,IPC_CREAT|0666);
if(shmid==-1){
perror("shmget");
exit(-1);
}
//3.映射共享内存
void *p = shmat(shmid,0,0);
if(p==(void *)-1){
perror("shmat");
exit(-1);
}
//4.使用共享内存
*(int *)p = 100;
//5.解除映射
shmdt(p);
return 0;
}
与它一起的进行通信的是下面这个代码、这个进程获取的key要保持一致才能获得同样的共享内存ID、用这个ID进行映射,然后使用它、最后要删除共享内存:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
//1.获取key
key_t key = ftok(".",'x');
if(key==-1){
perror("ftok");
exit(-1);
}
//2.获取共享内存
int shmid = shmget(key,0,0);
if(shmid==-1){
perror("shmget");
exit(-1);
}
//3.映射共享内存
void *p = shmat(shmid,0,0);
if(p==(void *)-1){
perror("shmat");
exit(-1);
}
//4.使用共享内存
printf("data = %d\n",*(int *)p);
//5.解除映射
shmdt(p);
//6.不再使用删除共享内存
shmctl(shmid,IPC_RMID,0);
return 0;
}
但是, 这是不规范的!
如果对于同一个资源, 一个进程对它写, 同时另一个进程又对它读
这会乱 !!!
因此产生了进程的互斥和同步(我博客中的信号量集有讲)