读核笔记(5) - 共享内存

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的数组,之后有扩充数组的需求的时候再重新分配内存,转移数据。这样效率会不会太低呢?

你可能感兴趣的:(读核笔记(5) - 共享内存)