Posted on 2008-02-21 14:10
ZelluX 阅读(641)
评论(0) 编辑 收藏 所属分类:
Linux 、
System
ipc/shm.c:
sys_shmat 连接共享内存
/**/
/*
* Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
*/
asmlinkage
long
sys_shmat (
int
shmid,
char
*
shmaddr,
int
shmflg,
ulong
*
raddr)
{
struct
shmid_kernel
*
shp;
unsigned
long
addr;
unsigned
long
size;
struct
file
*
file;
int
err;
unsigned
long
flags;
unsigned
long
prot;
unsigned
long
o_flags;
int
acc_mode;
void
*
user_addr;
if
(shmid
<
0
)
return
-
EINVAL;
if
((addr
=
(
ulong
)shmaddr))
{
//
如果shmaddr不为零,则为固定地址
if
(addr
&
(SHMLBA
-
1
))
{
//
检查是否是SHMLBA的整数倍
if
(shmflg
&
SHM_RND)
//
如果设置了SHM_RND,则自动调整,否则返回错误信息
addr
&=
~
(SHMLBA
-
1
);
/**/
/*
round down
*/
else
return
-
EINVAL;
}
flags
=
MAP_SHARED
|
MAP_FIXED;
}
else
{
if
((shmflg
&
SHM_REMAP))
return
-
EINVAL;
flags
=
MAP_SHARED;
}
if
(shmflg
&
SHM_RDONLY)
{
//
权限设置
prot
=
PROT_READ;
o_flags
=
O_RDONLY;
acc_mode
=
S_IRUGO;
}
else
{
prot
=
PROT_READ
|
PROT_WRITE;
o_flags
=
O_RDWR;
acc_mode
=
S_IRUGO
|
S_IWUGO;
}
/**/
/*
* We cannot rely on the fs check since SYSV IPC does have an
* additional creator id
*/
shp
=
shm_lock(shmid);
if
(shp
==
NULL)
return
-
EINVAL;
err
=
shm_checkid(shp,shmid);
//
检查共享内存是否存在
if
(err)
{
shm_unlock(shmid);
return
err;
}
if
(ipcperms(
&
shp
->
shm_perm, acc_mode))
{
//
检查访问权限
shm_unlock(shmid);
return
-
EACCES;
}
file
=
shp
->
shm_file;
size
=
file
->
f_dentry
->
d_inode
->
i_size;
shp
->
shm_nattch
++
;
shm_unlock(shmid);
down_write(
&
current
->
mm
->
mmap_sem);
if
(addr
&&
!
(shmflg
&
SHM_REMAP))
{
user_addr
=
ERR_PTR(
-
EINVAL);
if
(find_vma_intersection(current
->
mm, addr, addr
+
size))
goto
invalid;
/**/
/*
* If shm segment goes below stack, make sure there is some
* space left for the stack to grow (at least 4 pages).
*/
if
(addr
<
current
->
mm
->
start_stack
&&
addr
>
current
->
mm
->
start_stack
-
size
-
PAGE_SIZE
*
5
)
goto
invalid;
}
//
映射内存
user_addr
=
(
void
*
) do_mmap (file, addr, size, prot, flags,
0
);
invalid:
up_write(
&
current
->
mm
->
mmap_sem);
down (
&
shm_ids.sem);
if
(
!
(shp
=
shm_lock(shmid)))
BUG();
shp
->
shm_nattch
--
;
//
这里又减一了,真正的映射数增加是在do_mmap中完成的
if
(shp
->
shm_nattch
==
0
&&
shp
->
shm_flags
&
SHM_DEST)
shm_destroy (shp);
shm_unlock(shmid);
up (
&
shm_ids.sem);
*
raddr
=
(unsigned
long
) user_addr;
err
=
0
;
if
(IS_ERR(user_addr))
err
=
PTR_ERR(user_addr);
return
err;
}
shmctl这函数功能乱得很,一个switch一堆case,代码分析略
ipc/utils.h:IPC子系统对共享内存的管理是通过shm_ids{}来实现的。
struct ipc_ids {
int size;
int in_use;
int max_id; // 最大索引号
unsigned short seq; // 当前的顺序标号
unsigned short seq_max; // 顺序编号上界
struct semaphore sem; // 信号量
spinlock_t ary; // 自旋锁
struct ipc_id* entries; // 标识符数组
};
struct ipc_id {
struct kern_ipc_perm* p;
};shm_ids的初始化:
调用关系链:start_kernel() -> ipc_init() -> shm_init() -> ipc_init_ids(&shm_ids, 1)
void __init ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
sema_init(&ids->sem,1);
if(size > IPCMNI)
size = IPCMNI;
ids->size = size;
ids->in_use = 0;
ids->max_id = -1;
ids->seq = 0;
{
int seq_limit = INT_MAX/SEQ_MULTIPLIER;
if(seq_limit > USHRT_MAX)
ids->seq_max = USHRT_MAX;
else
ids->seq_max = seq_limit;
}
ids->entries = ipc_alloc(sizeof(struct ipc_id)*size);
if(ids->entries == NULL) {
printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
ids->size = 0;
}
ids->ary = SPIN_LOCK_UNLOCKED;
for(i=0;i<ids->size;i++)
ids->entries[i].p = NULL;
}看ipc/util.c中的代码似乎一开始创建了一个只能容纳一个ipc_id的数组,之后有扩充数组的需求的时候再重新分配内存,转移数据。这样效率会不会太低呢?