日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

聊聊跨進程共享內存的內部工作原理

來源: 責編: 時間:2023-12-08 09:15:18 314觀看
導讀大家好,我是飛哥!在 Linux 系統的進程虛擬內存中,一個重要的特性就是不同進程的地址空間是隔離的。A 進程的地址 0x4000 和 B 進程的 0x4000 之間沒有任何關系。這樣確確實實是讓各個進程的運行時互相之間的影響降到了最

大家好,我是飛哥!xpk28資訊網——每日最新資訊28at.com

在 Linux 系統的進程虛擬內存中,一個重要的特性就是不同進程的地址空間是隔離的。A 進程的地址 0x4000 和 B 進程的 0x4000 之間沒有任何關系。這樣確確實實是讓各個進程的運行時互相之間的影響降到了最低。某個進程有 bug 也只能自己崩潰,不會影響其它進程的運行。xpk28資訊網——每日最新資訊28at.com

但是有時候我們想要跨進程傳遞一些數據。因為進程虛擬內存地址是隔離的。所以目前業界最常用的做法是讓進程之間通過 127.0.0.1 或者是 Unix Domain Socket 等本機網絡手段進行數據的傳輸。這個方案在傳輸的數據量較小的時候工作是很不錯的。xpk28資訊網——每日最新資訊28at.com

但如果進程間想共享的數據特別大,比如說幾個 GB,那如果使用網絡 IO 方案的話,就會涉及到大量的內存拷貝的開銷,導致比較低的程序性能。這是可以采用進程間共享內存的方法來在通信時避免內存拷貝。xpk28資訊網——每日最新資訊28at.com

那么問題來了,不同進程之間的虛擬地址是隔離的,共享內存又是如何突破這個限制的呢?我們今天就來深入地了解下共享內存的內部工作原理。xpk28資訊網——每日最新資訊28at.com

一、共享內存的使用方式

共享內存發送方進程的開發基本過程是調用 memfd_create 創建一個內存文件。然后通過 mmap 系統調用為這個內存文件申請一塊共享內存。然后這個內存文件就可以寫入數據了。最后把這個文件的句柄通過 Unix Domain Socket 的方式給接收方進程發送過去。xpk28資訊網——每日最新資訊28at.com

下面是發送方的核心代碼。xpk28資訊網——每日最新資訊28at.com

int main(int argc, char **argv) { // 創建內存文件 fd = memfd_create("Server memfd", ...); // 為內存文件申請 MAP_SHARED 類型的內存 shm = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 向共享內存中寫入數據 sprintf(shm, "這段內容是保存在共享內存里的,接收方和發送方都能根據自己的fd訪問到這塊內容"); // 把共享內存文件的句柄給接收方進程發送過去 struct msghdr msgh; *((int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh))) = fd; sendmsg(conn, &msgh, 0); ......}

共享內存接收方的工作過程是先用 Unix Domain Socket 連接上服務器,然后使用 recvmsg 就可以收到發送方發送過來的文件句柄。xpk28資訊網——每日最新資訊28at.com

int main(int argc, char **argv) { // 通過 Unix Domain Socket 連接發送方 connect(conn, (struct sockaddr *)&address, sizeof(struct sockaddr_un)); // 通過連接取出發送方發送過來的內存文件句柄 int size = recvmsg(conn, &msgh, 0); fd = *((int *) CMSG_DATA(cmsgh)); // 讀取共享文件中的內容 shm = mmap(NULL, shm_size, PROT_READ, MAP_PRIVATE, fd, 0); printf("共享內存中的文件內容是: %s/n", shm); ......}

這樣這兩個進程都各自有一個文件句柄,在底層上是指向同一個內存文件的。這樣就實現了發送方和接收方之間的內存文件共享了。xpk28資訊網——每日最新資訊28at.com

圖片圖片xpk28資訊網——每日最新資訊28at.com

但我們上面介紹的是開發基本過程。按照我們開發內功修煉公眾號的風格,這還不算完,我們是要把它最底層的原理真正的弄通透才算的。所以接下來我們再深入地分析 memfd_create、 mmap、以及 Unix Domain socket sendmsg 和 recvmsg 的底層工作原理,來看看它們是如何配合來實現跨進程共享內存的。xpk28資訊網——每日最新資訊28at.com

二、共享內存文件原理

在發送方發送文件之前,需要先通過 memfd_create 來創建一個內存文件,然后再使用 mmap 為其分配內存。xpk28資訊網——每日最新資訊28at.com

2.1 創建內存文件

其中 memfd_create 函數是一個系統調用。內核中它的主要邏輯有兩個,一是調用 get_unused_fd_flags 申請一個沒使用過的文件句柄,二是調用 shmem_file_setup 創建一個共享內存文件。xpk28資訊網——每日最新資訊28at.com

圖片圖片xpk28資訊網——每日最新資訊28at.com

我們來看 memfd_create 的源碼。xpk28資訊網——每日最新資訊28at.com

// file:mm/memfd.cSYSCALL_DEFINE2(memfd_create,  const char __user *, uname,  unsigned int, flags){ ... // 申請一個未使用過的文件句柄 fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0); // 創建一個共享內存的文件 file = shmem_file_setup(name, 0, VM_NORESERVE); fd_install(fd, file); return fd;}

其中在 shmem_file_setup 函數中又調用了 __shmem_file_setup。xpk28資訊網——每日最新資訊28at.com

// file:mm/shmem.cstatic struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, ...){ ... // 申請一個 inode inode = shmem_get_inode(mnt->mnt_sb, NULL, S_IFREG | S_IRWXUGO, 0,    flags); inode->i_flags |= i_flags; inode->i_size = size; ... // 創建一個文件 res = alloc_file_pseudo(inode, mnt, name, O_RDWR,    &shmem_file_operations); return res;}

我們都知道磁盤文件在內核的實現中是由 inode 和 struct file 對象一起組成的。其實共享內存文件也一樣,__shmem_file_setup 中就是先申請了一個 inode,然后再調用 alloc_file_pseudo 創建一個文件。值得注意的是,這個文件并非是磁盤上的文件,而只是在內存里的。xpk28資訊網——每日最新資訊28at.com

2.2 mmap申請內存

mmap 也是一個系統調用,注意我們在開篇處調用它的時候傳入的第三個 flag 參數是 MAP_SHARED。這表示的是要通過 mmap 申請一塊跨進程可共享的內存出來。mmap 的實現入口在 arch/x86/kernel/sys_x86_64.cxpk28資訊網——每日最新資訊28at.com

//file:arch/x86/kernel/sys_x86_64.cSYSCALL_DEFINE6(mmap, unsigned long, addr, ...){ return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);}

接下來的這個函數的調用鏈路如下xpk28資訊網——每日最新資訊28at.com

SYSCALL_DEFINE6(mmap-> ksys_mmap_pgoff---> vm_mmap_pgoff------> do_mmap_pgoff--------> do_mmap

在 do_mmap 函數中,對輸入的 MAP_SHARED 進行了處理。xpk28資訊網——每日最新資訊28at.com

//file:mm/mmap.cunsigned long do_mmap(struct file *file, unsigned long addr,   unsigned long len, unsigned long prot,   unsigned long flags, vm_flags_t vm_flags,   unsigned long pgoff, unsigned long *populate,   struct list_head *uf){ struct mm_struct * mm = current->mm; ... // 如果包含 MAP_SHARED,則對要申請的虛擬內存設置一個 VM_SHARED switch (flags & MAP_TYPE) {  case MAP_SHARED:  case MAP_SHARED_VALIDATE:   vm_flags |= VM_SHARED | VM_MAYSHARE;    ...  }  ...  addr = mmap_region(file, addr, len, vm_flags, pgoff, uf); ......}

如果 flag 包含了 MAP_SHARED,則對要申請的虛擬內存設置一個 VM_SHARED。該標記指明的是要申請一個可以跨進程共享的內存塊。接下來進入 mmap_region 中申請虛擬內存。xpk28資訊網——每日最新資訊28at.com

//file:mm/mmap.cunsigned long mmap_region(struct file *file, ...){ struct mm_struct *mm = current->mm; ...... // 申請虛擬內存vma vma = vm_area_alloc(mm); // vma初始化 vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = vm_flags; vma->vm_page_prot = vm_get_page_prot(vm_flags); vma->vm_pgoff = pgoff; ...... // 加入到進程的虛擬內存 vma 鏈表中來 vma_link(mm, vma, prev, rb_link, rb_parent);}

進程的虛擬內存地址空間在內核底層中就是由這樣一個個的 vma 來組成的。每一個 vma 都聲明的是進程虛擬地址中的某一段地址范圍已經分配出去了。在 mmap_region 函數中申請了 vma,并在內核中將其管理了起來。xpk28資訊網——每日最新資訊28at.com

這里注意我們在申請共享內存的時候,給 vma 是帶了 VM_SHARED 標記的。帶了這個標記的 vma和普通的虛擬內存不一樣。后面在發生缺頁中斷申請物理內存的時候,在不同的進程間是可以對應到同一塊物理內存的。所以可以實現進程間的共享。xpk28資訊網——每日最新資訊28at.com

圖片圖片xpk28資訊網——每日最新資訊28at.com

所以真正讓進程之間可以共享內存的是這個帶 VM_SHARED 的 vma。xpk28資訊網——每日最新資訊28at.com

三、發送方發送文件句柄

發送方在使用 memfd_create 創建出來內存文件,并用 mmap 為其申請可跨進程共享的內存后。接著就可以通過 Unix Domain Socket 中對應的 sendmsg 方法將這個共享內存文件的句柄發送出來。如下是發送的代碼示例。xpk28資訊網——每日最新資訊28at.com

static void send_fd(int conn, int fd) {    struct msghdr msgh;    struct iovec iov;    ...    // 把文件句柄放到消息中來    *((int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh))) = fd;    // 發送出去    sendmsg(conn, &msgh, 0);}

sendmsg 又是一個內核提供的系統調用,它位于 net/socket.c 文件中。xpk28資訊網——每日最新資訊28at.com

//file:net/socket.cSYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags){ return __sys_sendmsg(fd, msg, flags, true);}

該函數的調用路徑如下xpk28資訊網——每日最新資訊28at.com

SYSCALL_DEFINE3(sendmsg, ...)-> __sys_sendmsg---> ___sys_sendmsg-----> ____sys_sendmsg-------> sock_sendmsg---------> sock_sendmsg_nosec-----------> unix_stream_sendmsg

在 unix_stream_sendmsg 中執行了真正的發送。xpk28資訊網——每日最新資訊28at.com

//file:net/unix/af_unix.c static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, ...){ // 把文件描述符指向的文件信息復制到 scm_cookie 中 struct scm_cookie scm; scm_send(sock, msg, &scm, false); // 不斷構建數據包發送,直到發送完畢    while (sent < len) {     // 申請一塊緩存區     skb = sock_alloc_send_pskb(sk, size - data_len, data_len,        msg->msg_flags & MSG_DONTWAIT, &err,        get_order(UNIX_SKB_FRAGS_SZ));     // 拷貝數據到 skb     err = unix_scm_to_skb(&scm, skb, !fds_sent);     err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);          // 直接把 skb 放到對端的接收隊列中     skb_queue_tail(&other->sk_receive_queue, skb);    //發送完畢回調  other->sk_data_ready(other);  sent += size;     ...    }}

在 unix_stream_sendmsg 中申請了個 skb 緩存區,然后把要發送的文件句柄等數據都塞到里面,最后調用 skb_queue_tail 直接把 skb 放到 Unix Domain Socket 連接另一端的接收隊列中了。xpk28資訊網——每日最新資訊28at.com

這里注意文件句柄只有在當前進程內才是有意義的。如果直接發送 fd 出去,接收方是沒有辦法使用的。所以在 scm_send 函數中,重要的邏輯是把 fd 對應的 struct file 的指針給找了出來,放到待發送的數據里面了。只有 file 這種內核級的對象接收方才能使用。xpk28資訊網——每日最新資訊28at.com

scm_send-> __scm_send---> scm_fp_copy

在 scm_fp_copy 中根據 fd 把 file 給找了出來。它的指針會被放到發送數據中xpk28資訊網——每日最新資訊28at.com

//file:net/core/scm.cstatic int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp){ ... //把每一個要發送的 fd 對應的 file 給找出來 for (i=0; i< num; i++) {  int fd = fdp[i];  struct file *file;  if (fd < 0 || !(file = fget_raw(fd)))   return -EBADF;  *fpp++ = file;  fpl->count++; }}

四、接收方接收文件

接下來接收方就可以通過 recvmsg 來接收發送方發送過來的文件了。recvmsg 系統會調用到 unix_stream_read_generic 中,然后在這個函數中把 skb 給取出來。xpk28資訊網——每日最新資訊28at.com

圖片圖片xpk28資訊網——每日最新資訊28at.com

下面是接收函數核心 unix_stream_read_generic 的源碼。xpk28資訊網——每日最新資訊28at.com

//file:net/unix/af_unix.cstatic int unix_stream_read_generic(struct unix_stream_read_state *state,        bool freezable){ do {  // 拿出一個 skb  last = skb = skb_peek(&sk->sk_receive_queue);  ... } ... if (state->msg)  scm_recv(sock, state->msg, &scm, flags); return copied ? : err;}

在 skb 拿出來后,還需要調用 scm_recv 來把 skb 中包含的文件給找出來。在 scm_recv 中調用 scm_detach_fds。xpk28資訊網——每日最新資訊28at.com

//file:net/core/scm.cvoid scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm){ for (i = 0; i < fdmax; i++) {  err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags);  if (err < 0)   break; } ...}

在 scm->fp->fp[i] 中包含的是發送方發送過來的 struct file 指針。這樣文件就取出來了。當然 struct file 是個內核態的對象,用戶沒有辦法使用。所以還需要再為其在新的進程中申請一個文件句柄,然后返回。本文來自公眾號「開發內功修煉」。xpk28資訊網——每日最新資訊28at.com

//file:fs/file.cint __receive_fd(struct file *file, int __user *ufd, unsigned int o_flags){ //申請一個新的文件描述符 new_fd = get_unused_fd_flags(o_flags); ... //關聯文件 fd_install(new_fd, get_file(file)); return new_fd;}

五、總結

共享內存發送方進程的開發過程基本分 memfd_create 創建內存文件、mmap 申請共享內存、Unix Domain Socket 發送文件句柄三步。xpk28資訊網——每日最新資訊28at.com

  • 第一步,memfd_create 系統調用的主要邏輯有兩個,一是調用 get_unused_fd_flags 申請一個沒使用過的文件句柄,二是調用 shmem_file_setup 創建一個共享內存文件。
  • 第二步,mmap 系統調用在調用它的時候傳入的第三個 flag 參數是 MAP_SHARED,該參數是申請一塊跨進程可共享訪問的物理內存。
  • 第三步,接著通過 Unix Domain Socket 中對應的 sendmsg 方法將這個共享內存文件的句柄發送出去。在發送時,把文件句柄對應的 struct file 指針找到并放到要封裝的 skb 數據包中了。

接收方進程的主要實現原理是 recvmsg 系統調用。在這個系統調用中,內核會把發送方發送過來的 struct file 指針取出來,然后再在當前進程下為其申請一個新的文件句柄。這個文件句柄返回給用戶進程后,用戶進程就可以用它來和另外一個進程共享地訪問同一塊內存了。xpk28資訊網——每日最新資訊28at.com

總體來看,共享內存本質上共享的是內核對象 struct file,通過在不同的進程之間使用同一個 struct file 來實現的共享。當然也得需要在虛擬內存對象 vma 帶上 VM_SHARED 標記來支持。xpk28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-39523-0.html聊聊跨進程共享內存的內部工作原理

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 如何做好微服務容量規劃?

下一篇: 架構的低成本約束

標簽:
  • 熱門焦點
  • 對標蘋果的靈動島 華為帶來實況窗功能

    繼蘋果的靈動島之后,華為也在今天正式推出了“實況窗”功能。據今天鴻蒙OS 4.0的現場演示顯示,華為的實況窗可以更高效的展現出實時通知,比如鎖屏上就能看到外賣、打車、銀行
  • 影音體驗是真的強 簡單聊聊iQOO Pad

    大公司的好處就是產品線豐富,非常細分化的東西也能給你做出來,例如早先我們看到了新的vivo Pad2,之后我們又在iQOO Neo8 Pro的發布會上看到了iQOO的首款平板產品iQOO Pad。雖
  • 6月安卓手機好評榜:魅族20 Pro蟬聯冠軍

    性能榜和性價比榜之后,我們來看最后的安卓手機好評榜,數據來源安兔兔評測,收集時間2023年6月1日至6月30日,僅限國內市場。第一名:魅族20 Pro好評率:95%5月份的時候魅族20 Pro就是
  • 三言兩語說透設計模式的藝術-簡單工廠模式

    一、寫在前面工廠模式是最常見的一種創建型設計模式,通常說的工廠模式指的是工廠方法模式,是使用頻率最高的工廠模式。簡單工廠模式又稱為靜態工廠方法模式,不屬于GoF 23種設計
  • 如何正確使用:Has和:Nth-Last-Child

    我們可以用CSS檢查,以了解一組元素的數量是否小于或等于一個數字。例如,一個擁有三個或更多子項的grid。你可能會想,為什么需要這樣做呢?在某些情況下,一個組件或一個布局可能會
  • “又被陳思誠騙了”

    作者|張思齊 出品|眾面(ID:ZhongMian_ZM)如今的國產懸疑電影,成了陳思誠的天下。最近大爆電影《消失的她》票房突破30億斷層奪魁暑期檔,陳思誠再度風頭無兩。你可以說陳思誠的
  • 認真聊聊東方甄選:如何告別低垂的果實

    來源:山核桃作者:財經無忌爆火一年后,俞敏洪和他的東方甄選依舊是頗受外界關心的&ldquo;網紅&rdquo;。7月5日至9日,為期5天的東方甄選&ldquo;甘肅行&rdquo;首次在自有App內直播,
  • 聯想YOGA 16s 2022筆記本將要推出,屏幕支持觸控功能

    聯想此前宣布,將于11月2日19:30召開聯想秋季輕薄新品發布會,推出聯想 YOGA 16s 2022 筆記本等新品。官方稱,YOGA 16s 2022 筆記本將搭載 16 英寸屏幕,并且是一
  • 華為舉行春季智慧辦公新品發布會 首次推出電子墨水屏平板

    北京時間2月27日晚,華為在巴塞羅那舉行春季智慧辦公新品發布會,在海外市場推出之前已經在中國市場上市的筆記本、平板、激光打印機等辦公產品,并首次推出搭載
Top 主站蜘蛛池模板: 内丘县| 松桃| 中西区| 永新县| 喀喇沁旗| 玉门市| 临朐县| 唐海县| 灵丘县| 甘德县| 策勒县| 西宁市| 繁昌县| 六安市| 舟曲县| 揭阳市| 白城市| 定南县| 磐安县| 宣恩县| 临沂市| 白河县| 前郭尔| 大理市| 扶绥县| 新余市| 若尔盖县| 南充市| 浦东新区| 南涧| 辰溪县| 华坪县| 崇明县| 上杭县| 延安市| 比如县| 洛隆县| 砀山县| 东阳市| 禹城市| 平安县|