本文首發地址: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 : songtianlun@frytea.com
> 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 : songtianlun@frytea.com
> 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 : songtianlun@frytea.com
> 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 : songtianlun@frytea.com
> 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 root@192.168.7.48:/dev/shm/posixsm
# 拷貝程式檔案至目標位置,注意遷移前后的程式和依賴檔案的路徑必須保持一致
$ scp PracticeDev/clang/ipc_test/ipc_share_mem_posix_write root@192.168.7.48:/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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/243246.html
標籤:其他
