Linux ipc_alloc_permm ipc权限结构体分配与refcnt

发布时间:2026/6/13 7:48:13

Linux ipc_alloc_permm ipc权限结构体分配与refcnt Linux ipc_alloc_permm ipc权限结构体分配与refcntipc_alloc_permm是ipc/util.c中的关键辅助函数负责为System V IPC对象分配包含struct kern_ipc_perm权限结构体的内存块。该函数被semget、msgget、shmget三个路径的通用分配器ipc_addid调用返回已清零的权限结构体指针。函数签名cstruct kern_ipc_perm *ipc_alloc_permm(struct ipc_namespace *ns,size_t total_size, int id_shift){struct kern_ipc_perm *perm;int i;perm kvmalloc(total_size, GFP_KERNEL_ACCOUNT);if (!perm)return NULL;memset(perm, 0, total_size);/* 初始化公用部分 */spin_lock_init(perm-lock);perm-deleted false;perm-id_shift id_shift;perm-ns ns;refcount_set(perm-refcount, 1);/* 初始化key哈希节点 */INIT_LIST_NODE(perm-key_list);return perm;}total_size参数由各IPC对象决定- 信号量sizeof(struct sem_array) nsems * sizeof(struct sem)- 消息队列sizeof(struct msg_queue)- 共享内存sizeof(struct shmid_kernel)ipc_alloc_permm使用kvmalloc分配允许在需要时通过vmalloc后备避免物理连续内存压力下的分配失败。GFP_KERNEL_ACCOUNT表示该内存计入当前cgroup的内存账户。struct kern_ipc_perm是所有IPC对象权限结构体的基类cstruct kern_ipc_perm {spinlock_t lock; /* 保护IPC对象的自旋锁 */bool deleted; /* 标记是否已被删除 */int id_shift; /* ID生成移位 */struct ipc_namespace *ns; /* 所属命名空间 */refcount_t refcount; /* 引用计数 */struct list_head key_list; /* 挂入rhashtable的key链 *//* 以下字段用于权限校验 */key_t key; /* IPC key */kuid_t uid; /* 所有者uid */kgid_t gid; /* 所有者gid */kuid_t cuid; /* 创建者uid */kgid_t cgid; /* 创建者gid */umode_t mode; /* 访问权限模式 */unsigned short seq; /* 序列号 */unsigned short size; /* 用于信号量时存储sem_nsems */};refcount_t是内核引入的原子引用计数器替换了早期atomic_t。refcount_t提供的溢出保护可以检测refcount_inc/dec的不匹配使用cvoid ipc_rcu_getref(struct kern_ipc_perm *perm){refcount_inc(perm-refcount);}void ipc_rcu_putref(struct kern_ipc_perm *perm,void (*free_func)(struct rcu_head *head)){if (refcount_dec_and_test(perm-refcount)) {call_rcu(perm-rcu, free_func);}}ipc_rcu_putref在引用计数降为零时通过call_rcu将释放操作推迟到RCU宽限期之后。free_func函数释放整个IPC对象内存例如sem_rcu_free、msg_rcu_free、shm_rcu_free。这种设计确保了读者在RCU读锁保护下安全访问IPC对象后对象不会在释放途中被并发读取cstatic void sem_rcu_free(struct rcu_head *head){struct kern_ipc_perm *p container_of(head,struct kern_ipc_perm, rcu);kvfree(p);}refcount的递增发生在以下场景1. ipc_addid中分配成功后设为1初始引用2. ipc_rcu_getref在ipc_lock和ipc_obtain_object_idr时被调用持有锁期间增加引用防止释放3. shmat增加shm_file引用但不直接增加kern_ipc_perm引用通过file结构间接管理4. semop和msgrcv/msgsnd在操作期间通过ipc_lock获取引用ipc_addid是分配新IPC对象的公共入口在ipc_alloc_permm之后将结构体加入idr和key哈希表cint ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new,int max_id){int idx;int id;idr_preload(GFP_KERNEL);/* 在idr中分配ID */idx idr_alloc_cyclic(ids-ipcs_idr, new, 0, max_id,GFP_NOWAIT);idr_preload_end();if (idx 0)return idx;/* 计算外界可见的IPC ID */id (idx SEQ_MULTIPLIER * new-seq);new-id id;/* 将key插入哈希表 */if (new-key ! IPC_PRIVATE)rhltable_insert(ids-key_ht, new-key_list,ipc_kht_params);ids-in_use;new-deleted false;return id;}ID计算使用序列号seq和索引idx组合避免重用同一索引时发生ID冲突ipc_id (idx seq * SEQ_MULTIPLIER)SEQ_MULTIPLIER为IPCMNI32768。这种设计确保IPC ID不因连续创建/销毁而迅速回绕。ipc_findkey利用rhashtable在O(1)时间内查找key对应的IPC对象。如果多个IPC对象共享相同key在IPC_PRIVATE之外不允许key_list链表会保存多个条目。rhashtable的查找进入RCU域因此refcount的递增必须保证对象不被并行删除。

相关新闻