本系列主要根據內核代碼,分析 消息隊列 共享內存 信號量 三種 進程間通信的方法。
內核 目錄下有個ipc文件夾, 實現代碼在這個裏面
linux/ipc$ ls
built-in.a ipc_sysctl.c modules.order mqueue.c msgutil.c sem.c syscall.c util.h
compat.c Makefile mq_sysctl.c msg.c namespace.c shm.c util.c
ipc_sysctl.c
static struct ctl_table ipc_kern_table[] = {
{
.procname = "shmmax", # 共享內存最多的條數
.data = &init_ipc_ns.shm_ctlmax,
.maxlen = sizeof(init_ipc_ns.shm_ctlmax),
.mode = 0644,
.proc_handler = proc_ipc_doulongvec_minmax,
},
device_initcall(ipc_sysctl_init);
這個文件主要是 系統啓動的時候調用 device_initcall 這篇文章介紹的不錯 device_initcall
register_sysctl_table(ipc_root_table); 這裏會去註冊ipc_kern_table
系統啓動的時候會註冊ipc 的配置, 初始化一些ipc的權限,權限,ipc最多的條數,超過的時候的處理函數。
syscall.c
/*定義系統調用 ipc 六個參數*/
SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
unsigned long, third, void __user *, ptr, long, fifth)
{
int version, ret;
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff; /* 解析出系統調用的類型*/
switch (call) {
case SEMOP:
return ksys_semtimedop(first, (struct sembuf __user *)ptr,
second, NULL);
case SEMTIMEDOP:
if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME))
return ksys_semtimedop(first, ptr, second,
(const struct __kernel_timespec __user *)fifth);
else if (IS_ENABLED(CONFIG_COMPAT_32BIT_TIME))
return compat_ksys_semtimedop(first, ptr, second,
(const struct old_timespec32 __user *)fifth);
else
return -ENOSYS;
case SEMGET:
return ksys_semget(first, second, third);
case SEMCTL: {
unsigned long arg;
if (!ptr)
return -EINVAL;
if (get_user(arg, (unsigned long __user *) ptr))
return -EFAULT;
return ksys_semctl(first, second, third, arg);
}
case MSGSND:
return ksys_msgsnd(first, (struct msgbuf __user *) ptr,
second, third);
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
if (!ptr)
return -EINVAL;
if (copy_from_user(&tmp,
(struct ipc_kludge __user *) ptr,
sizeof(tmp)))
return -EFAULT;
return ksys_msgrcv(first, tmp.msgp, second,
tmp.msgtyp, third);
}
default:
return ksys_msgrcv(first,
(struct msgbuf __user *) ptr,
second, fifth, third);
}
/**/
case MSGGET:
return ksys_msgget((key_t) first, second);
case MSGCTL:
return ksys_msgctl(first, second,
(struct msqid_ds __user *)ptr);
case SHMAT:
switch (version) {
default: {
unsigned long raddr;
ret = do_shmat(first, (char __user *)ptr,
second, &raddr, SHMLBA);
if (ret)
return ret;
return put_user(raddr, (unsigned long __user *) third);
}
case 1:
/*
* This was the entry point for kernel-originating calls
* from iBCS2 in 2.2 days.
*/
return -EINVAL;
}
case SHMDT:
return ksys_shmdt((char __user *)ptr);
case SHMGET:
return ksys_shmget(first, second, third);
case SHMCTL:
return ksys_shmctl(first, second,
(struct shmid_ds __user *) ptr);
default:
return -ENOSYS;
}
}
msg.c
long ksys_msgget(key_t key, int msgflg)
{
struct ipc_namespace *ns;
static const struct ipc_ops msg_ops = {
.getnew = newque,
.associate = security_msg_queue_associate,
};
struct ipc_params msg_params;
ns = current->nsproxy->ipc_ns; /* 獲取當前的 namespace */
msg_params.key = key;
msg_params.flg = msgflg;
return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
}
ipcget ------> ipcget_public ---->
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"
*/
ipcp = ipc_findkey(ids, params->key); /*查看ipc id 是否已經被使用*/
if (ipcp == NULL) {
/* key not used */
if (!(flg & IPC_CREAT))
err = -ENOENT;
else
err = ops->getnew(ns, params); /* 創建一個新的msg id*/
/**
* newque - Create a new msg queue
* @ns: namespace
* @params: ptr to the structure that contains the key and msgflg
*
* Called with msg_ids.rwsem held (writer)
*/
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
struct msg_queue *msq;
int retval;
key_t key = params->key;
int msgflg = params->flg;
msq = kvmalloc(sizeof(*msq), GFP_KERNEL);
if (unlikely(!msq))
return -ENOMEM;
msq->q_perm.mode = msgflg & S_IRWXUGO;
msq->q_perm.key = key;
msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(&msq->q_perm);
if (retval) {
kvfree(msq);
return retval;
}
msq->q_stime = msq->q_rtime = 0;
msq->q_ctime = ktime_get_real_seconds();
msq->q_cbytes = msq->q_qnum = 0;
msq->q_qbytes = ns->msg_ctlmnb;
msq->q_lspid = msq->q_lrpid = NULL;
INIT_LIST_HEAD(&msq->q_messages);
INIT_LIST_HEAD(&msq->q_receivers);
INIT_LIST_HEAD(&msq->q_senders);
/* ipc_addid() locks msq upon success. */
retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
if (retval < 0) {
ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
return retval;
}
ipc_unlock_object(&msq->q_perm);
rcu_read_unlock();
return msq->q_perm.id;
}