linux SysV IPC实现

来源:互联网 发布:java md5加密 32位 编辑:程序博客网 时间:2024/06/05 15:57

IPC(Interprocess Communication)表示进程间通信机制;System V IPC机制主要有消息队列、共享内存、信号量,linux中实现了SysV IPC。

 

I.SysV IPC创建/获取
消息队列、共享内存、信号量的创建/获取API原型如下:
int msgget(key_t key, int msgflg);
int shmget(key_t key, size_t size, int shmflg);
int semget(key_t key, int nsems, int semflg);

以上API主要用来创建新的IPC或根据key值查找IPC,并返回IPC id;内核实现均调用ipcget:

ipc/util.c:734 /**735  * ipcget - Common sys_*get() code736  * @ns : namsepace737  * @ids : IPC identifier set738  * @ops : operations to be called on ipc object creation, permission checks739  *        and further checks740  * @params : the parameters needed by the previous operations.741  *742  * Common routine called by sys_msgget(), sys_semget() and sys_shmget().743  */744 int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,745                         struct ipc_ops *ops, struct ipc_params *params)746 {747         if (params->key == IPC_PRIVATE)748                 return ipcget_new(ns, ids, ops, params);749         else750                 return ipcget_public(ns, ids, ops, params);751 }


i.key
key类型为key_t,即int:

/usr/include/bits/types.h:101:#define__S32_TYPEint/usr/include/bits/typesizes.h:55:#define __KEY_T_TYPE__S32_TYPE/usr/include/bits/types.h:155:__STD_TYPE __KEY_T_TYPE __key_t;/* Type of an IPC key.  *//usr/include/sys/ipc.h:48:typedef __key_t key_t;
SysV IPC均用key值作为主键,系统级标识出IPC;同时每一个IPC都有一个id与之对应。
key和id都能标识出IPC,区别主要是,key由用户程序提供,以便用户程序标识IPC实现进程间通信;id由系统返回,能快速查找到IPC并使用。
key查找IPC过程:遍历IPC id基数树的叶子结点,找出key对应的IPC。实现为ipc_findkey:
170 /**171  *      ipc_findkey     -       find a key in an ipc identifier set     172  *      @ids: Identifier set173  *      @key: The key to find174  *      175  *      Requires ipc_ids.rw_mutex locked.176  *      Returns the LOCKED pointer to the ipc structure if found or NULL177  *      if not.178  *      If key is found ipc points to the owning ipc structure179  */180 181 static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)182 {183         struct kern_ipc_perm *ipc;184         int next_id;185         int total;186 187         for (total = 0, next_id = 0; total < ids->in_use; next_id++) {188                 ipc = idr_find(&ids->ipcs_idr, next_id);189 190                 if (ipc == NULL)191                         continue;192 193                 if (ipc->key != key) {194                         total++;195                         continue;196                 }197 198                 ipc_lock_by_ptr(ipc);199                 return ipc;200         }201 202         return NULL;203 }
KEY_PRIVATE是一个特殊的key值,不能作为系统范围标识IPC的key;主要用于创建进程私有的IPC(其它进程不能根据key值使用),而非系统级IPC(其它进程能够根据key值使用)。
KEY_PRIVATE值如下:
/usr/include/bits/ipc.h:39:#define IPC_PRIVATE((__key_t) 0)/* Private key.  */

ii.SysV IPC创建
在以下情况创建IPC:
1.key=IPC_PRIVATE
2.key!=IPC_PRIVATE,key值标识的IPC不存在,且flg中IPC_CREATE置位
当创建IPC时,用flg的低9位作为mode(用户、组、其它的读、写、执行权限)来创建IPC

key=IPC_PRIVATE时,调用ipcget_new创建新的IPC;
否则,使用ipcget_public查找key标识的IPC,如果存在校验权限,如果不存在创建IPC或报错。
实现如下:

288 /**289  *      ipcget_new      -       create a new ipc object290  *      @ns: namespace291  *      @ids: IPC identifer set292  *      @ops: the actual creation routine to call293  *      @params: its parameters294  *295  *      This routine is called by sys_msgget, sys_semget() and sys_shmget()296  *      when the key is IPC_PRIVATE.297  */298 static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,299                 struct ipc_ops *ops, struct ipc_params *params)300 {301         int err;302 retry:303         err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL);304 305         if (!err)306                 return -ENOMEM;307 308         down_write(&ids->rw_mutex);309         err = ops->getnew(ns, params);310         up_write(&ids->rw_mutex);311 312         if (err == -EAGAIN)313                 goto retry;314 315         return err;316 }

1.idr_pre_get分配idr缓存,供分配IPC id使用
2.具体的IPC函数,分配相应的IPC。newque、newseg、newary分别创建消息队列、共享内存、信号量

348 /**349  *      ipcget_public   -       get an ipc object or create a new one350  *      @ns: namespace351  *      @ids: IPC identifer set352  *      @ops: the actual creation routine to call353  *      @params: its parameters354  *355  *      This routine is called by sys_msgget, sys_semget() and sys_shmget()356  *      when the key is not IPC_PRIVATE.357  *      It adds a new entry if the key is not found and does some permission358  *      / security checkings if the key is found.359  *360  *      On success, the ipc id is returned.361  */362 static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,363                 struct ipc_ops *ops, struct ipc_params *params)364 {365         struct kern_ipc_perm *ipcp;366         int flg = params->flg;367         int err;368 retry:369         err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL);370 371         /*372          * Take the lock as a writer since we are potentially going to add373          * a new entry + read locks are not "upgradable"374          */375         down_write(&ids->rw_mutex);376         ipcp = ipc_findkey(ids, params->key);377         if (ipcp == NULL) {378                 /* key not used */379                 if (!(flg & IPC_CREAT))380                         err = -ENOENT;381                 else if (!err)382                         err = -ENOMEM;383                 else384                         err = ops->getnew(ns, params);385         } else {386                 /* ipc object has been locked by ipc_findkey() */387 388                 if (flg & IPC_CREAT && flg & IPC_EXCL)389                         err = -EEXIST;390                 else {391                         err = 0;392                         if (ops->more_checks)393                                 err = ops->more_checks(ipcp, params);394                         if (!err)395                                 /*396                                  * ipc_check_perms returns the IPC id on397                                  * success398                                  */399                                 err = ipc_check_perms(ipcp, ops, params);400                 }401                 ipc_unlock(ipcp);402         }403         up_write(&ids->rw_mutex);404 405         if (err == -EAGAIN)406                 goto retry;407 408         return err;409 }

1.idr_pre_get分配idr缓存,供分配IPC id使用
2.如果key对应的IPC不存在,分配IPC
3.如果key对应的IPC存在,做权限等校验

 

 

II.SysV IPC共性
SysV IPC有许多共同的属性,如key、id、mode等;IPC共性用kern_ipc_perm表示:

include/linux/ipc.h: 85 /* used by in-kernel data structures */ 86 struct kern_ipc_perm 87 { 88         spinlock_t      lock; 89         int             deleted; 90         int             id; 91         key_t           key; 92         uid_t           uid; 93         gid_t           gid; 94         uid_t           cuid; 95         gid_t           cgid; 96         mode_t          mode; 97         unsigned long   seq; 98         void            *security; 99 };

lock:IPC锁
deleted:删除标识
id:IPC id
key:IPC key
uid:IPC所属用户
gid:IPC所属用户组
cuid:IPC创建用户
cgid:IPC创建用户组
mode:用户、用户组、其它的读、写、执行权限
seq:序列号

 

i.seq
seq用于表示IPC id分配序列号;每次分配IPC id,seq会自增1,到seq_max时会从0重新开始。
1.seq初始化

ipc/util.h: 16 #define SEQ_MULTIPLIER  (IPCMNI)ipc/util.c:111 /**112  *      ipc_init_ids            -       initialise IPC identifiers113  *      @ids: Identifier set114  *115  *      Set up the sequence range to use for the ipc identifier range (limited116  *      below IPCMNI) then initialise the ids idr.117  */118 119 void ipc_init_ids(struct ipc_ids *ids)120 {121         init_rwsem(&ids->rw_mutex);122 123         ids->in_use = 0;124         ids->seq = 0;125         {126                 int seq_limit = INT_MAX/SEQ_MULTIPLIER;127                 if (seq_limit > USHORT_MAX)128                         ids->seq_max = USHORT_MAX;129                  else130                         ids->seq_max = seq_limit;131         }132 133         idr_init(&ids->ipcs_idr);134 }
2.seq递增
236 /**237  *      ipc_addid       -       add an IPC identifier238  *      @ids: IPC identifier set239  *      @new: new IPC permission set240  *      @size: limit for the number of used ids241  *242  *      Add an entry 'new' to the IPC ids idr. The permissions object is243  *      initialised and the first free entry is set up and the id assigned244  *      is returned. The 'new' entry is returned in a locked state on success.245  *      On failure the entry is not locked and a negative err-code is returned.246  *247  *      Called with ipc_ids.rw_mutex held as a writer.248  */249 250 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)251 {279 280         new->seq = ids->seq++;281         if(ids->seq > ids->seq_max)282                 ids->seq = 0;283 284         new->id = ipc_buildid(id, new->seq);285         return id;286 }

ii.id
IPC id是由基数树中空闲id与seq计算得到,实现如下:
ipc/util.h: 16 #define SEQ_MULTIPLIER  (IPCMNI)145 static inline int ipc_buildid(int id, int seq)146 {147         return SEQ_MULTIPLIER * seq + id;148 }include/linux/ipc.h: 83 #define IPCMNI 32768  /* <= MAX_INT limit for ipc arrays (including sysctl changes) */

如果没有IPC释放的情况时,从基数树中分配的id值会比上次分配id增加1,则IPC id会增大IPCMNI+1=32768+1=32769;情况如下:

[redhat@localhost linux-2.6.32.60]$ ipcs------ Shared Memory Segments --------key        shmid      owner      perms      bytes      nattch     status      0x00000000 98304      redhat     600        393216     2          dest         0x00000000 131073     redhat     600        393216     2          dest         0x00000000 163842     redhat     600        393216     2          dest         0x00000000 196611     redhat     600        393216     2          dest         0x00000000 229380     redhat     600        393216     2          dest         0x00000000 262149     redhat     600        393216     2          dest         0x00000000 622598     redhat     600        393216     2          dest         0x00000000 327687     redhat     600        393216     2          dest         0x00000000 360456     redhat     600        393216     2          dest         0x00000000 393225     redhat     600        393216     2          dest         0x00000000 425994     redhat     600        393216     2          dest         0x00000000 458763     redhat     600        393216     2          dest         0x00000000 491532     redhat     600        393216     2          dest  


iii.mode
mode是在创建IPC时,取自flg的低9位,表示用户、用户组、其它的读、写、执行权限;当IPC已经存在,获取IPC时会做权限校验,由ipcperms实现:

606 /**607  *      ipcperms        -       check IPC permissions608  *      @ipcp: IPC permission set609  *      @flag: desired permission set.610  *611  *      Check user, group, other permissions for access612  *      to ipc resources. return 0 if allowed613  */614 615 int ipcperms (struct kern_ipc_perm *ipcp, short flag)616 {       /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */617         uid_t euid = current_euid();618         int requested_mode, granted_mode;619 620         audit_ipc_obj(ipcp);621         requested_mode = (flag >> 6) | (flag >> 3) | flag;622         granted_mode = ipcp->mode;623         if (euid == ipcp->cuid ||624             euid == ipcp->uid)625                 granted_mode >>= 6;626         else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))627                 granted_mode >>= 3;628         /* is there some bit set in requested_mode but not in granted_mode? */629         if ((requested_mode & ~granted_mode & 0007) &&630             !capable(CAP_IPC_OWNER))631                 return -1;632 633         return security_ipc_permission(ipcp, flag);634 }

1.根据进程的身分是IPC的所有者/创建者、组内其它成员、或其它,来确定进程的授权granted_mode
2.计算请求访问IPC的权限requested_mode(读、写、执行);请求权限放在用户、用户组、或其它域中均可,所以做位或运算来计算requested_mode
3.检查请求的权限都有授权


原创粉丝点击