IP【前两节我们讲的都是基于文件的通信方式(匿名管道和命名管道),今天我们将System V标准的进程间通信方式
在OS层面专门为进程间通信设计了一个方案,谁设计,xdm当然是计算机科学家和程序员(顶尖)。那么设计这个方案要不要给用户用,当然要给用户用,不然设计这个方案有什么意义。但是操作系统不相信任何用户,给用户提供功能的时候,采用系统调用!
System V进程间通信,一定会存在专门同来通信的接口(System call)
进程间通信的本质是让不同的进程看到同一块资源。
System V的通信方式:
1.共享内存(Shared Memory)
2.消息队列(Message Queues)
3.信号量(Semaphore)
接下里xdm我们看一下共享内存实现的图解:
xdm看到没,现在进程A和进程B 中都有一块共享内存,可能在各个进程中的进程地址不同,但是通过页表可以拿到同一块物理内存,那么我们操作进程中的共享内存是不是就可以相当于操作物理内存了,是不是就可以通信了!!!!!!!!
xdm我们可以理解为一下几个步骤:
1.通过某种调用,在内存中创建一份内存空间
2.通过某种调用,让进程“挂接”到这份新开辟的内存空间上
3.去关联(去挂接)
4.释放共享内存
xdm上面我们讲了共享内存的原理,接下来我们准备搞代码了。
准备工作:
1.操作系统存不存在多个进程,同时使用不同的共享内存进行通信呢?共享内存在系统中可能存不存在多份呢?有的话操作系统要不要管理这些共享内存呢?
? ? ? ? 答:如果不是我自己提的这个问题,我在心里肯定是问候提问者...。当然要操作系统来管理这些共享内存,不然不是乱套了吗?
怎么管理,记住六字真言:先描述,在组织!!!!!!!!
2.你怎么保证,两个或者多个进程,看到的是同一个共享内存呢?
? ? ? ? ? ?答:先让进程看到同一个ID,共享内存一定要有一个唯一的标识符(ID),方便让不同的进程看到同一块共享内存资源。
? ? ? ? ? ? ? ? 那么这个ID在哪里?一定在描述共享内存的数据结构体中。
这个ID是用户自己设定的!!
认识接口
创建共享内存:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
1,第一个参数key_t key是通过某种算法确定的一个key值,通信的进程要有相同的key,这个key 可以通过 ftok()得到
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
pathname 路径
proj_id 可以随便起
2,第二个参数是要设定的共享内存的大小,一半是4KB的整数倍,因为内存是以4KB为一页
3,第三个参数有一下几种选择
IPC_CREAT? //如果不存在就创建,如果存在就返回以这个key创建的共享内存的ID
IPC_EXCL? ?//如果是和IPC_CREAT 一起使用的,如果存在就返回错误,不存在则成功
SHM_HUGETLB //一般不用
SHM_HUGE_2MB? // 一般不用
关联共享内存
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
1,第一个参数为创建共享内存的ID
2,第二个参数为让物理内存映射到进程地址空间的什么位置,一般我们填NULL.
3,第三个参数一般我们填0
xmd这个方法你们可以理解为malloc,你们就当成malloc使用就行了。
去关联共享内存
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
这个方法是让进程共享空间和物理内存的共享内存失去联系
参数就当成free()用,上面我们不是申请了内存吗,下面就释放
就相当于
? ? ? ? char * men = (char * ) malloc(64);
? ? ? ? free(men)
一样使用。
释放共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
1,第一个参数是共享内存Id
2,第二个参数一般我们使用IPC_RMID------删除共享内存ID;
3,第三个参数我们不关心
好了xmd们我们现在就来搞代码了,定义一个sever.c client.c
让sever.c创建共享一个内存
common.h
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "./"
#define PROJ_ID 99
#define SIZE 4096
?server.c
#include "common.h"
int main()
{
key_t key = ftok(PATHNAME,PROJ_ID); //生成key
if( key < 0)
{
perror("ftok()");
exit(1);
}
int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0644); //创建共享内存,通过key
if(shmid < 0 )
{
perror("shmget()");
exit(1);
}
printf("get share memory is sucesskey->%u :shmid->%d\n",key, shmid);
sleep(10);
shmctl(shmid,IPC_RMID,NULL); //释放共享内存
printf("lose share memory : %d\n",shmid);
sleep(10);
}
makefile
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "./"
#define PROJ_ID 99
#define SIZE 4096
XDM我们接下来看实验效果,我们make生成server可执行程序,我们另一个终端一直执行脚本命令查看共享内存资源如下图;
?运行:
得到共享内存
?释放共享内存?
在运行:
?XDM 你们有没有惊奇的看见共享内存的ID依次增大的,你们难道就没联想到什么数据结构吗?当然是数组,这个我会在后面详细讲解。这个ID其实就是数据下标。
?接下来,本节既然是讲进程间通信,那么我们就让进程通信起来。
?
XDM我们让client端向共享内存里写,server从共享内存中读,但是我们一运行,server就开始读了,并不会等client写了在读,可见共享内存的通信方式是异步而管道的通信方式是同步的。
XDM我们可以看到两个进程关联了共享内存,那么它的连接数就是2,它这个终端对齐可能有些问题,XDM要注意呀!!
XDM看一下通信的代码吧,
server.c
int main()
{
key_t key = ftok(PATHNAME,PROJ_ID);
if( key < 0)
{
perror("ftok()");
exit(1);
}
int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0644);
if(shmid < 0 )
{
perror("shmget()");
exit(1);
}
printf("get share memory is sucesskey->%u :shmid->%d\n",key, shmid);
//sleep(10);
//关联共享内存
char * mem = (char*)shmat(shmid,NULL,0);
//这里就是我们的通信逻辑了
while(1)
{
printf("%s\n",mem);
}
//去关联共享内存
shmdt(mem);
shmctl(shmid,IPC_RMID,NULL);
printf("lose share memory : %d\n",shmid);
sleep(10);
}
client.c
int main()
{
key_t key = ftok(PATHNAME,PROJ_ID);
if( key < 0)
{
perror("ftok()");
exit(1);
}
int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0644);
if(shmid < 0 )
{
perror("shmget()");
exit(1);
}
printf("get share memory is sucesskey->%u :shmid->%d\n",key, shmid);
//sleep(10);
//关联共享内存
char * mem = (char*)shmat(shmid,NULL,0);
//这里就是我们的通信逻辑了
while(1)
{
printf("%s\n",mem);
}
//去关联共享内存
shmdt(mem);
shmctl(shmid,IPC_RMID,NULL);
printf("lose share memory : %d\n",shmid);
sleep(10);
}
XDM讲到这里你们还要说进程间通信-----共享内存难的话,那只能重新做人了。。。
终结一下:就4步
1,创建共享内存ID
2,通过共享内存ID关联共享内存
3,去关联共享内存
4.释放共享内存
这些步骤接口记住就行,就下来我们讲的才是重点,是能够直接影响对怎个框架的了解。
我们通过命令man 2 shmctl看到如下代码
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Creation time/time of last
modification via shmctl() */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
struct ipc_perm {
key_t key; 调用shmget()时给出的关键字
uid_t uid; /*共享内存所有者的有效用户ID */
gid_t gid; /* 共享内存所有者所属组的有效组ID*/
uid_t cuid; /* 共享内存创建 者的有效用户ID*/
gid_t cgid; /* 共享内存创建者所属组的有效组ID*/
unsigned short mode; /* Permissions + SHM_DEST和SHM_LOCKED标志*/
unsignedshort seq; /* 序列号*/
};
XDM们看到没,人家都直接说明了这是共享内存的数据结构
我们通过命令 man 2 msgctl看到如下代码
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
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; /* Permissions */
unsigned short __seq; /* Sequence number */
};
XDM们看到没,人家都直接说明了这是消息队列的数据结构
?我们通过命令man 2 semctl看到如下代码
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
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; /* Permissions */
unsigned short __seq; /* Sequence number */
};
XDM看到没,所有的System V进程间通信的数据结构中,第一个成员是啥?
是struct ipc_perm,上面我不是设置了一个悬念吗?为什么进程间通信的msqid shmid semid是递增的,我说过它在内核中是用数组组织起来的,那又有人说,内核怎么组织呢,我们不是拿到一个表示唯一的资源(共享内存,消息队列,信号量),操作系统怎么将它们组织起来呢,很简单,我们不直接存储它的结构体指针,我们用数组将它的结构体中的第一个成员,用指针数组的方式保存起来,当我们要使用这些资源的时候,就拿出这个指针数组的成员,进行强转,类似(semid_ds*)&ipc_perm 这样就可以操作semid_ds中成员了,这样类似于c++中切片。
那么我们接下来看一看linux内核中进程间通信的源代码。
?ipc目录下,就是linux 内核进程间通信的源代码。包括共享内存(shm.c),消息队列(msg.c),信号量(sem.c).
#include <linux/errno.h>
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/ipc.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/malloc.h>
extern int ipcperms (struct ipc_perm *ipcp, short semflg);
extern unsigned int get_swap_page(void);
static int findkey (key_t key);
static int newseg (key_t key, int shmflg, int size);
static int shm_map (struct shm_desc *shmd, int remap);
static void killseg (int id);
static int shm_tot = 0; /* total number of shared memory pages */
static int shm_rss = 0; /* number of shared memory pages that are in memory */
static int shm_swp = 0; /* number of shared memory pages that are in swap */
static int max_shmid = 0; /* every used id is <= max_shmid */
static struct wait_queue *shm_lock = NULL;
static struct shmid_ds *shm_segs[SHMMNI];
static unsigned short shm_seq = 0; /* incremented, for recognizing stale ids */
/* some statistics */
static ulong swap_attempts = 0;
static ulong swap_successes = 0;
static ulong used_segs = 0;
xdm我现在就是不说你们也能猜出个大概了吧。 系统调用的接口就是对这些接口的封装
|