由共享內存引發的思考

1.共享內存段被映射進進程空間之後,存在於進程空間的什麼位置?共享內存段最大限制是多少?

存在於進程數據段,最大限制是0x2000000Byte

將一塊內存映射到兩個或者多個進程地址空間。通過指針訪問該共享內存區。一般通過mmap將文件映射到進程地址共享區。


Linux對共享內存的實現,在2.6採用了內存映射技術。對於內存共享,主要集中在三個內核函數,他們是do_shmat,sys_shmat和sys_shmdt。其中,sys_shmat調用了do_shmat最終實現了共享內存的attach。sys_shmdt實現了共享內存的detach和destroy。下面我主要對這三個函數的源碼進行分析。在分析之前,首先介紹共享內存實現原理。

 

原理:

    我們知道,LINUX的進程擁有自己獨立的地址空間,這點和vxworks是不同的。系統中多個進程共享同一內存段,可以通過系統提供的共享內存機制進行。當進程向系統提出創建或附着共享內存的申請的時候,內核會爲每一個新的共享內存段提供一個shmid_kernel的數據結構來維護共享段和文件系統之間的關係(也可以理解成共享內存段和文件系統之間建立關係的橋樑)。下面就是這個數據結構:

這個數據結構定義在shm.h頭文件中

struct shmid_kernel /* private to the kernel */

{  

    struct kern_ipc_perm    shm_perm; // 訪問權限的信息

    struct file *       shm_file; // 指向虛擬文件系統的指針

    unsigned long       shm_nattch; // 有多少個進程attach上了這個共享內存段

    unsigned long       shm_segsz; // 共享內存段大小

    // 以下是一些訪問時間的相關信息

    time_t          shm_atim;

    time_t          shm_dtim;

    time_t          shm_ctim;

   

    pid_t           shm_cprid;

    pid_t           shm_lprid;

    struct user_struct  *mlock_user;

};

該數據結構中最重要的部分就是shm_file這個字段。它指向了共享內存對應的文件。在該結構中有一個字段,f_mapping,它指向了該內存段使用的頁面(物理內存)。同時,結構中,也包含一個字段,f_path,用於指向文件系統中的文件(dentry->inode),這樣就建立了物理內存和文件系統的橋樑。當進程需要創建或者attach共享內存的時候,在用戶態,會先向虛擬內存系統申請各自的vma_struct,並將其插入到各自任務的紅黑樹中,該結構中有一個成員vm_file,它指向的就是struct file(shm_file)。這樣虛擬內存、共享內存(文件系統)和物理內存就建立了連接。

 

接着我們來看看主要的函數:

do_shmat:

1.         這個函數首先根據shared memory的id,在內核中查找相應的shmid_struct。

       shp = shm_lock_check(ns, shmid);

       if (IS_ERR(shp)) {

              err = PTR_ERR(shp);

              goto out;

       }

 

2.         對訪問者進行訪問權限檢查

       if (ipcperms(&shp->shm_perm, acc_mode))

              goto out_unlock;

3.         獲取共享內存對應的文件的信息。

獲取文件表項

       path.dentry = dget(shp->shm_file->f_path.dentry);

       獲取掛接點

       path.mnt    = shp->shm_file->f_path.mnt;

       共享內存引用計數自增

       shp->shm_nattch++;

       通過i節點獲取文件大小

       size = i_size_read(path.dentry->d_inode);

 

4.         分配相應的數據結構,並進行初始化。

       err = -ENOMEM;

       從slab分配其中分配shm_file_data數據結構

       sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);

       if (!sfd)

              goto out_put_dentry;

       分配一個struct file的數據結構

       file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);

       if (!file)

              goto out_free;

       初始化file。

       file->private_data = sfd;

       file->f_mapping = shp->shm_file->f_mapping;

       sfd->id = shp->shm_perm.id;

       sfd->ns = get_ipc_ns(ns);

       sfd->file = shp->shm_file;

       sfd->vm_ops = NULL;

 

5.         最後進行內存映射,完成attach操作。

       user_addr = do_mmap (file, addr, size, prot, flags, 0);

 

sys_shmat:

他是系統調用函數,他調用了do_shmat。

 

sys_shmdt:

首先查找相應的vma,如果找到執行ummap操作。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章