文章目录

  • 1. 简介
  • 2. 源码分析
    • 2.1 copy_ipcs()
    • 2.2 ipcget()
    • 2.3 ipc_check_perms()
    • 2.4 相关系统调用
  • 参考文档:

1. 简介

进程间通讯的机制称为 IPC(Inter-Process Communication)。Linux 下有多种 IPC 机制:管道(PIPE)、命名管道(FIFO)、信号(Signal)、消息队列(Message queues)、信号量(Semaphore)、共享内存(Share Memory)、内存映射(Memory Map)、套接字(Socket)。

其中的三种消息队列(Message queues)、信号量(Semaphore)、共享内存(Share Memory)被称为 XSI IPC,他们源自于 UNIX System V IPC。

Linux 的 IPC Namespace 主要就是针对 XSI IPC 的,和其他 IPC 机制无关。

我们用简单操作来熟悉一下 IPC Namespace 的概念:

1、查看普通进程的 IPC namespace :

pwl@ubuntu:~$ sudo ls -l /proc/$$/ns
[sudo] password for pwl:
total 0
lrwxrwxrwx 1 root root 0 3月  14 10:53 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 net -> 'net:[4026531993]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 3月  14 10:53 uts -> 'uts:[4026531838]'
pwl@ubuntu:~$ ipcs------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    ------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 262144     pwl        600        67108864   2          dest
0x00000000 360449     pwl        600        524288     2          dest         ------ Semaphore Arrays --------
key        semid      owner      perms      nsems     pwl@ubuntu:~$

2、创建新的 IPC namespace 并查看:

pwl@ubuntu:~$ sudo unshare --ipc
[sudo] password for pwl:
root@ubuntu:~#
root@ubuntu:~# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 3月  14 10:55 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 ipc -> 'ipc:[4026532643]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 net -> 'net:[4026531993]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 3月  14 10:55 uts -> 'uts:[4026531838]'
root@ubuntu:~# ipcs------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    ------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      ------ Semaphore Arrays --------
key        semid      owner      perms      nsems     root@ubuntu:~#

2. 源码分析

2.1 copy_ipcs()

在进程创建或者 unshare()/setns() 系统调用时,如果设置了 CLONE_NEWIPC 标志会调用 copy_ipcs() 创建一个新的 IPC namespace。其中的核心是创建一个新的 struct ipc_namespace 结构,相当于创建了一个新的 XSI IPC 域:

copy_ipcs() → create_ipc_ns():static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,struct ipc_namespace *old_ns)
{struct ipc_namespace *ns;struct ucounts *ucounts;int err;err = -ENOSPC;ucounts = inc_ipc_namespaces(user_ns);if (!ucounts)goto fail;err = -ENOMEM;/* (1) 分配新的ipc_namespace数据结构 */ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);if (ns == NULL)goto fail_dec;err = ns_alloc_inum(&ns->ns);if (err)goto fail_free;ns->ns.ops = &ipcns_operations;refcount_set(&ns->count, 1);ns->user_ns = get_user_ns(user_ns);ns->ucounts = ucounts;/* (2) 初始化信号量sem对应的idr池和一些参数 */err = sem_init_ns(ns);if (err)goto fail_put;/* (3) 初始化消息msg对应的idr池和一些参数 */err = msg_init_ns(ns);if (err)goto fail_destroy_sem;/* (4) 初始化共享内存shm对应的idr池和一些参数 */err = shm_init_ns(ns);if (err)goto fail_destroy_msg;/* (5) 初始化消息队列mq对应的一些参数 */err = mq_init_ns(ns);if (err)goto fail_destroy_shm;return ns;
}

2.2 ipcget()

三种 XSI IPC 的实现机制也是非常类似的,IPC namespace 提供了对应的3个 idr 池 ns->ids[3] ,每新建一个 XSI IPC 对象,会从对应的 idr 池中分配一个 key

以信号量 sem 为例,来分析一下这个过程:

SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
{struct ipc_namespace *ns;/* (1) 构造 ipc_ops 参数 */static const struct ipc_ops sem_ops = {.getnew = newary,.associate = sem_security,.more_checks = sem_more_checks,};struct ipc_params sem_params;/* (2) 构造 ipc namespace 参数 */ns = current->nsproxy->ipc_ns;if (nsems < 0 || nsems > ns->sc_semmsl)return -EINVAL;/* (3) 构造 ipc_params 参数 */sem_params.key = key;sem_params.flg = semflg;sem_params.u.nsems = nsems;/* (4) 根据key查询已有的ipcp,或者创建新的ipcp */return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
}↓ipcget() → ipcget_public()↓static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,const struct ipc_ops *ops, struct ipc_params *params)
{struct kern_ipc_perm *ipcp;int flg = params->flg;int err;/** Take the lock as a writer since we are potentially going to add* a new entry + read locks are not "upgradable"*/down_write(&ids->rwsem);/* (4.1) 根据key在信号量 idr 池 `ns->ids[IPC_SEM_IDS]`中查找对应的ipcp */ipcp = ipc_findkey(ids, params->key);if (ipcp == NULL) {/* key not used */if (!(flg & IPC_CREAT))err = -ENOENT;else/* (4.2) 如果key对应的ipcp不存在,且设置了IPC_CREAT标志则根据key创建一个新的ipcp*/err = ops->getnew(ns, params);} else {/* ipc object has been locked by ipc_findkey() */if (flg & IPC_CREAT && flg & IPC_EXCL)err = -EEXIST;else {err = 0;/* (4.3) 如果key对应的ipcp存在,则针对参数进行第一步的检查 */if (ops->more_checks)err = ops->more_checks(ipcp, params);if (!err)/** ipc_check_perms returns the IPC id on* success*//* (4.4) ipcp存在,针对参数做第二步的权限检查 */err = ipc_check_perms(ns, ipcp, ops, params);}ipc_unlock(ipcp);}up_write(&ids->rwsem);return err;
}↓ops->getnew() → newary()↓static int newary(struct ipc_namespace *ns, struct ipc_params *params)
{int retval;struct sem_array *sma;key_t key = params->key;int nsems = params->u.nsems;int semflg = params->flg;int i;if (!nsems)return -EINVAL;if (ns->used_sems + nsems > ns->sc_semmns)return -ENOSPC;/* (4.2.1) 分配nsems个信号量的数据结构sem_array[] */sma = sem_alloc(nsems);if (!sma)return -ENOMEM;/* (4.2.2) 初始化其中的ipcp成员,保存UGO模式,保存对应的key */sma->sem_perm.mode = (semflg & S_IRWXUGO);sma->sem_perm.key = key;sma->sem_perm.security = NULL;retval = security_sem_alloc(sma);if (retval) {kvfree(sma);return retval;}for (i = 0; i < nsems; i++) {INIT_LIST_HEAD(&sma->sems[i].pending_alter);INIT_LIST_HEAD(&sma->sems[i].pending_const);spin_lock_init(&sma->sems[i].lock);}sma->complex_count = 0;sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS;INIT_LIST_HEAD(&sma->pending_alter);INIT_LIST_HEAD(&sma->pending_const);INIT_LIST_HEAD(&sma->list_id);sma->sem_nsems = nsems;sma->sem_ctime = ktime_get_real_seconds();/* ipc_addid() locks sma upon success. *//* (4.2.3) 赋值 ipcp 中的 uid、gid  并将新分配的ipcp加入信号量 idr 池 `ns->ids[IPC_SEM_IDS]`中*/retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);if (retval < 0) {call_rcu(&sma->sem_perm.rcu, sem_rcu_free);return retval;}ns->used_sems += nsems;sem_unlock(sma, -1);rcu_read_unlock();return sma->sem_perm.id;
}↓int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
{kuid_t euid;kgid_t egid;int id, err;if (limit > IPCMNI)limit = IPCMNI;if (!ids->tables_initialized || ids->in_use >= limit)return -ENOSPC;idr_preload(GFP_KERNEL);refcount_set(&new->refcount, 1);spin_lock_init(&new->lock);new->deleted = false;rcu_read_lock();spin_lock(&new->lock);/* (4.2.3.1) 获取当前进程的uid/gid,赋值给 ipcp 的相关成员,类似文件的 `chown uid:gid ` 操作*/current_euid_egid(&euid, &egid);new->cuid = new->uid = euid;new->gid = new->cgid = egid;id = ipc_idr_alloc(ids, new);idr_preload_end();/* (4.2.3.2) 加入信号量 idr 池 `ns->ids[IPC_SEM_IDS]`中 */if (id >= 0 && new->key != IPC_PRIVATE) {err = rhashtable_insert_fast(&ids->key_ht, &new->khtnode,ipc_kht_params);if (err < 0) {idr_remove(&ids->ipcs_idr, id);id = err;}}if (id < 0) {spin_unlock(&new->lock);rcu_read_unlock();return id;}ids->in_use++;if (id > ids->max_id)ids->max_id = id;new->id = ipc_buildid(id, ids, new);return id;
}

2.3 ipc_check_perms()

从上一节可以看到 key 被包含在 ipcp 即 struct kern_ipc_perm 结构中:

struct kern_ipc_perm {spinlock_t lock;bool       deleted;int     id;key_t        key;kuid_t      uid;kgid_t      gid;kuid_t      cuid;kgid_t     cgid;umode_t        mode;unsigned long  seq;void        *security;struct rhash_head khtnode;struct rcu_head rcu;refcount_t refcount;
} ____cacheline_aligned_in_smp __randomize_layout;

在 sem,shm,mq 对象中都有这个成员的存在:

struct sem_array {struct kern_ipc_perm   sem_perm;   /* permissions .. see ipc.h */...
}struct shmid_kernel /* private to the kernel */
{   struct kern_ipc_perm    shm_perm;...
}    struct msg_queue {struct kern_ipc_perm q_perm;...
}

struct kern_ipc_perm->key 成员保存了key值;->mode 成员保存了 UGO 操作权限,类似文件的 chmod 777 属性;->uid/gid/cuid/cgid 成员保存了属主 uid/gid,类似文件的 chown uid:gid 操作。

在 ipc_check_perms() 函数中,会对被操作的 sem,shm,mq 对象,进行 UGO 权限检查:

ipc_check_perms() → ipcperms()↓int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
{kuid_t euid = current_euid();int requested_mode, granted_mode;audit_ipc_obj(ipcp);/* (1) 或 请求的 UGO 操作 */requested_mode = (flag >> 6) | (flag >> 3) | flag;/* (2) 获取 ipcp 的 UGO 规则 */granted_mode = ipcp->mode;/* (2.1) 如果当前进程 和 ipcp 对象 uid 相等,则取用 U 规则 */if (uid_eq(euid, ipcp->cuid) ||uid_eq(euid, ipcp->uid))granted_mode >>= 6;/* (2.2) 如果当前进程 和 ipcp 对象 gid 相等,则取用 G 规则 */else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))granted_mode >>= 3;/* (2.3) 如果当前进程 和 ipcp 对象 uid gid 都不相等,则取用 O 规则 *//* is there some bit set in requested_mode but not in granted_mode? *//* (3) 判断请求的操作是否适配对应的规则 */if ((requested_mode & ~granted_mode & 0007) &&!ns_capable(ns->user_ns, CAP_IPC_OWNER))return -1;return security_ipc_permission(ipcp, flag);
}

2.4 相关系统调用

sem,shm,mq 对象通用部分的解析如上所示,除此以外 XSI IPC 还有其他的操作系统调用,这里就不一一解析:

Module Syscall Descript
sem semget() 创建信号量
- semctl() 初始化信号量
- semop() 信号量的PV操作
msg msgget() 创建消息队列
- msgctl() 获取和设置消息队列的属性
- msgsnd() 将消息写入到消息队列
- msgrcv() 从消息队列读取消息
shm shmget() 创建共享内存对象
- shmctl() 共享内存管理
- shmat() 把共享内存区对象映射到调用进程的地址空间
- shmdt() 断开共享内存连接

参考文档:

1.ipc_namespace
2.Linux内核命名空间之(2) ipc namespace
3.linux进程间通信(IPC)机制总结
4.POSIX:XSI Interprocess Communication

Linux ns 5. IPC Namespace 详解相关推荐

  1. Linux ns 3. Mnt Namespace 详解

    1. 文件系统层次化 对 Linux 系统来说一切皆文件,Linux 使用树形的层次化结构来管理所有的文件对象. 完整的Linux文件系统,是由多种设备.多种文件系统组成的一个混合的树形结构.我们首先 ...

  2. linux服务器怎么查看cpu配置信息,linux服务器cpu信息查看详解

    在linux系统中,提供了/proc目录下文件,显示系统的软硬件信息.如果想了解系统中CPU的提供商和相关配置信息,则可以查/proc/cpuinfo.但是此文件输出项较多,不易理解.例如我们想获取, ...

  3. 在Linux中ipcs命令,linux中ipcs命令使用详解

    linux中ipcs命令使用详解 用途 报告进程间通信设施状态. 语法 代码如下: ipcs [-mqs] [-abcopt] [-C core] [-N namelist] -m 输出有关共享内存( ...

  4. 【linux】Valgrind工具集详解(八):Memcheck命令行参数详解

    [linux]Valgrind工具集详解(五):命令行详解中不够全,在此专门针对Memcheck工具中的命令行参数做一次详细的解释. Memcheck命令行选项 –leak-check=<no| ...

  5. linux ps(process status) 命令详解

    linux ps(process status) 命令详解 功能说明:报告程序状况. 语 法:ps [-aAcdefHjlmNVwy][acefghLnrsSTuvxX][-C <指令名称> ...

  6. Linux操作系统上lsof命令详解

    Linux操作系统上lsof命令详解 2011-10-08 18:31:31 http://xjsunjie.blog.51cto.com/999372/682865 标签:Linux lsof命令 ...

  7. linux slocate(secure locate) 命令详解

    linux slocate(secure locate) 命令详解 功能说明:查找文件或目录. 语 法:slocate [-u][--help][--version][-d <目录>][查 ...

  8. (传送门)linux命令总结dd命令详解

    linux命令总结dd命令详解 https://www.cnblogs.com/ginvip/p/6370836.html 懒癌末期,不想花时间拷贝内容+排版,而且,原文排版就已经很棒了,我在这里只是 ...

  9. Linux上的free命令详解

    Linux上的free命令详解 转自: http://www.cnblogs.com/coldplayerest/archive/2010/02/20/1669949.html 解释一下Linux上f ...

最新文章

  1. Python爬虫入门教程 54-100 博客园等博客网站自动评论器
  2. 即刻—你的私人消息定制
  3. 【干货】产品经理必读:app开发版本迭代的节奏该如何把握?
  4. 炒菜机器人的弊端_机器人炒菜真不是你想的那样!
  5. 说明exit()函数作用的程序
  6. 履带式机器人与轮式机器人的异同分析
  7. 微信小程序开发资料汇总
  8. 奇怪的加拿大:一方面大力禁烟,另一方面放松大麻
  9. Android入门项目(校园软件)
  10. fluidsim win7版本_FluidSIM 5|FluidSIM(液压气动仿真软件)下载v5.0中文免费版 附安装教程 - 欧普软件下载...
  11. rtl8211 smi读取_RTL8211E应用(二)之信号输入、输出接口
  12. python里的平方_python中的平方
  13. 在家如何访问公司电脑文件和服务器的共享文件夹
  14. orc识别 语音识别 云真机 内网穿透快速调研
  15. matlab dwt函数应用,MATLAB中关于DCT,DFT和DWT的相关函数
  16. iOS开发-汤姆猫Tom(序列帧动画)附详细注释
  17. 为什么要用Handler
  18. Excel 2010 SQL应用117 分组统计之GROUP BY 与First
  19. veek-soc-iii_所以您想使用招聘人员第III部分-警告
  20. 银河麒麟连不上网怎么办

热门文章

  1. 火山视窗读写ini文件,写配置项与读配置项使用
  2. 《计算机系统基础》——计算机系统导论
  3. 基本的html文档组成三要素是哪些,三大构成 复习资料
  4. 育儿-《让孩子远离焦虑》书中的精髓:家长如何帮助孩子解决他们自身的焦虑问题。
  5. 计算机原理与应用 第二章——ARM处理器
  6. 人工智能究竟离我们有多远?
  7. MFC--利用Haru库生成PDF文件
  8. RSA加密学习的简单例子
  9. RSA大数运算实现(1024位n)(3)
  10. 赤道子午线弧长反演大地纬度