本文首發地址:https://blog.frytea.com/archives/501/
在軟件中,術語共享內存指可被多個進程存取的內存,一個進程是一段程序的單個運行實例。在這種情況下,共享內存被用作進程間的通訊。——WikiPedia
在Linux系統中,有多種C語言支持的共享內存使用方法,包括以下幾種:
- 基於傳統
SYS V
的共享內存; - 基於
POSIX mmap
文件映射實現共享內存; - 通過
memfd_create()
和fd
跨進程共享實現共享內存; - 多媒體、圖形領域廣泛使用的基於
dma-buf
的共享內存。
CRIU
是用於Linux
操作系統的軟件工具。使用此工具,可以凍結正在運行的應用程序,並將其作爲文件集合檢查點到持久性存儲中。然後,人們可以使用這些文件從凍結點還原並運行應用程序。但不是所有程序都支持通過CRIU進行熱遷移,例如使用了SYS V
的C程序就不可以使用 CRIU 進行 進程熱遷移。
這篇文章討論如何使用CRIU遷移使用了共享內存的程序,主要討論其中的前兩種共享內存方法,最終介紹一種支持熱遷移的C程序共享內存使用方法。
共享簡單實現
System V
,曾經也被稱爲 AT&T System V,是Unix操作系統衆多版本中的一支, SYS V
共享內存歷史悠久、年代久遠、API怪異,對應內核代碼 linux/ipc/shm.c
,使用命令 ipcs
看到的就是這種內存; Posix
表示可移植操作系統接口(Portable Operating System Interface ,縮寫爲 POSIX ),POSIX標準定義了操作系統應該爲應用程序提供的接口標準,是IEEE爲要在各種 UNIX
操作系統上運行的軟件而定義的一系列API標準的總稱,其正式稱呼爲IEEE 1003,而國際標準名稱爲ISO/IEC 9945。
下面列舉了兩種共享內存的C程序使用方法。
(1)Sys V 共享內存
ipc_share_mem_write
,共享內存寫入示例程序。
/*************************************************************************
> File Name : ipc_share_mem_write.c
> Author : TL Song
> EMail : [email protected]
> Created Time : Wed 30 Dec 2020 03:17:00 PM CST
************************************************************************/
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
key_t key = ftok("/dev/shm/myshm2",0);
int shm_id = shmget(key, 0x400000, IPC_CREAT | 0666);
char *p = (char *)shmat(shm_id, NULL, 0);
char cIndex = 'A';
int i = 0;
for(i=0;i<100;i++){
memset(p, cIndex++, 0x400000);
printf("Write '%c' to mem '%p'\\n", *p, p);
sleep(1);
}
shmdt(p);
return 0;
}
ipc_share_mem_read
,共享讀取示例程序。
/*************************************************************************
> File Name : ipc_share_mem_read.c
> Author : TL Song
> EMail : [email protected]
> Created Time : Wed 30 Dec 2020 03:22:20 PM CST
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
int main(int argc, char ** argv)
{
key_t key = ftok("/dev/shm/myshm2", 0);
int shm_id = shmget(key, 0x400000, 0666);
char *p = (char *)shmat(shm_id, NULL, 0);
int i = 0;
for(i = 0 ;i < 100; i++)
{
printf("Read '%c' in mem '%p'\\n", *p, p);
sleep(1);
}
shmdt(p);
return 0;
}
直接編譯運行即可:
$ gcc ipc_share_mem_write.c -o ipc_share_mem_write
$ gcc ipc_share_mem_read.c -o ipc_share_mem_read
$ touch /dev/shm/myshm2
# 在兩個終端分別執行程序即可。
可以使用 free
命令查看執行前後共享內存空間的變化情況:
[root@criu_go ~]# free
total used free shared buff/cache available
Mem: 3880732 3193512 421660 9216 265560 461624
Swap: 4063228 0 4063228
[root@criu_go ~]# free
total used free shared buff/cache available
Mem: 3880732 3193744 417308 13312 269680 457292
(2)POSIX 共享內存
ipc_share_mem_posix_write
,共享內存寫入示例程序。
/*************************************************************************
> File Name : ipc_share_mem_posix_write.c
> Author : TL Song
> EMail : [email protected]
> Created Time : Wed 30 Dec 2020 03:17:00 PM CST
************************************************************************/
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
int fd = shm_open("posixsm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, 0x400000);
char *p = mmap(NULL, 0x400000, PROT_READ| PROT_WRITE, MAP_SHARED, fd, 0);
char cIndex = 'A';
int i = 0;
for(i=0;i<100;i++){
memset(p, cIndex++, 0x400000);
printf("Write '%c' to mem '%p'\\n", *p, p);
sleep(1);
}
munmap(p, 0x400000);
return 0;
}
ipc_share_mem_posix_read
,共享讀取示例程序。
/*************************************************************************
> File Name : ipc_share_mem_posix_read.c
> Author : TL Song
> EMail : [email protected]
> Created Time : Wed 30 Dec 2020 03:22:20 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char ** argv)
{
int fd = shm_open("posixsm", O_RDONLY, 0666);
ftruncate(fd, 0x400000);
char *p = mmap(NULL, 0x400000, PROT_READ, MAP_SHARED, fd, 0);
int i = 0;
for(i = 0 ;i < 100; i++)
{
printf("Read '%c' in mem '%p'\\n", *p, p);
sleep(1);
}
munmap(p, 0x400000);
return 0;
}
直接編譯運行即可:
$ gcc ipc_share_mem_posix_write.c -o ipc_share_mem_posix_write -lrt
$ gcc ipc_share_mem_posix_read.c -o ipc_share_mem_posix_read -lrt
# 在兩個終端分別執行程序即可。
之後可以在 /dev/shm/
、 /run/shm
下面看到一個文件。
進程熱遷移
上文簡單提到了 criu
工具,本文的目標即遷移使用了共享內存的C程序,實測使用了 Sys V
共享內存的C程序無法遷移,報錯如下:
Task 4526 with SysVIPC shmem map @7fdff5956000 doesn't live in IPC ns
使用POSIX mmap
文件映射實現共享內存的C程序可以使用 criu 實現進程熱遷移,只需遷移共享內存文件及相關程序和文件即可實現本機和跨主機間的進程遷移,前提是內核、criu版本保持一致。遷移方法很簡單,至於criu的安裝,使用以下命令安裝即可。
$ yum install criu -y
$ criu check
Looks good.
CLI 進行進程遷移
# 獲取進程 PID
$ ps -ef | grep ipc
root 15748 15340 0 10:56 pts/1 00:00:00 ./checkpoint_demo
root 15751 15479 0 10:56 pts/2 00:00:00 grep --color=auto checkpoint_demo
# 對進程打快照
$ criu dump -D ./migrate_imgs/ -j -t 15748
# 略去拷貝快照文件夾的步驟,自行拷貝至目標機器任意位置即可
# 拷貝共享內存文件至目標位置
$ scp /dev/shm/posixsm [email protected]:/dev/shm/posixsm
# 拷貝程序文件至目標位置,注意遷移前後的程序和依賴文件的路徑必須保持一致
$ scp PracticeDev/clang/ipc_test/ipc_share_mem_posix_write [email protected]:/root/PracticeDev/clang/ipc_test
# 恢復進程狀態
$ criu restore -D ./migrate_imgs/ -j
使用該方法可以將使用了共享內存的C程序凍結,之後恢復進程狀態,Posix
共享內存的API略有不同,但使用方法類似,至於更進一步的探索,還需繼續努力。
參考文獻
- 宋寶華:世上最好的共享內存(Linux共享內存最透徹的一篇):https://blog.csdn.net/21cnbao/article/details/103470878
- Linux下的幾種IPC方式及其C語言實現:https://www.cnblogs.com/acm-icpcer/p/8933628.html
- Linux Namespace : IPC:https://www.cnblogs.com/sparkdev/p/9400673.html
- IPC之Posix共享內存詳解:https://blog.csdn.net/daiyudong2020/article/details/50500651
- UNIX System V - WikiPedia:https://zh.wikipedia.org/wiki/UNIX_System_V
- 共享內存 - WikiPedia:https://zh.wikipedia.org/wiki/共享內存
- What software is supported - criu wiki:https://criu.org/What_software_is_supported
- Shared memory - criu wiki:https://criu.org/index.php?title=Shared_memory