2021年10月6日水曜日

tar xz指令用法

壓縮: 

tar Jcvf <filename>.tar.xz <path>/<to>/<folder> 

  or

tar -c -I 'xz -9 -T0' -f <filename>.tar.xz  <path>/<to>/<folder>

-9: 跟 xz 說要用最大壓縮

-T0:跟 xz 說要用 CPU 最大線程數量的執行緒

 

解壓: 

tar Jxvf <filename>.tar.xz

2021年7月11日日曜日

製作在 Linux 使用的 shared library 並使用 shared memory

年代久遠的東西
好久沒做都快忘光了
以前學校學的東西都忘的差不多了
這麼多年來專案做了不少個
但也只有在每次專案剛開始的時候才會用到這些東西
時間一久就什麼都忘光了= ="

TL;DR

這篇是在記錄
在 Linux 中 POSIX shared memory 跟 dynamic shared library 合併使用的方式

軟硬體設備

其實這個不重要
重要的是你的 Linux 系統本體所放的 glibc
不要因為閹割太多而導致 ld.so 的功能有缺失或是整個不見
基本上這樣的 code 在 x86 或是 arm 都沒有問題
如果 ld.so 不存在的話基本上這篇不適合可以直接左轉上一頁!!

預期的目的

這次預期的目標是
在一個 shared library 中製作一個 shared memory
然後讓另外兩個獨立的執行檔可以共同存取它

在 Linux 中 IPC 其實有不少可以用
這次選用 POSIX 的方式也沒有什麼特別的原因
單純就是懶惰...(;‘∀’)

用到的檔案

 

 

 

 

 

 

 

 

 

這次總共 5 個檔案
foo.c / foo.h 用來製作 shared library 本人
test_dll.c 用來製作第一個執行檔
test_dll_2.c 用來製作第二個執行檔
makefile 用來編譯

Shared library

foo.h
 


 

 

 

這個檔案裡的東西實際上只是給人看的而已
這樣跟其他人在 co-work 的時候至少別人有東西可以參考
不過編譯跟執行實際上是不需要的

foo.c
 


 

 

 

 

 

 


 

 

 

 

因為這次的功能設計上來說
只會在開機的時候執行第一次之後就再也不會釋放
所以就偷懶直接把 init 放到了 constructor 裡面
然後把 deinit 放到了 destructor 裡面
實際上如果沒有特別的需求的話
下面這兩行並不需要
直接放到正常的流程裡面執行就好

static void __attribute__ ((constructor)) init_lib(void);
static void __attribute__ ((destructor)) deinit_lib(void);

上面這兩行的目的在於
當這個檔案被執行到的時候會先直接執行 constructor 的部分把 init() 做完
當這個檔案被生命週期到盡頭的時候會執行 destructor 的部分做 deinit()
不過 constructor / destructor 的部分不在這篇的範圍裡面所以跳過(;^ω^)

另外因為這次的 sample code 沒有什麼功能
所以實際上 foo.h 沒有也沒差
不過為了以後功能越加越多
所以還是先把它保留下來了

這個檔案是這篇的主角之一
Shared memory 實際上就是由他建立
然後讓其他執行檔案可以使用
主要是下面這三行

// create shared memory
int fd = shm_open("/posix_shared_memory", O_CREAT | O_RDWR | O_EXCL, 0600);

// resize
ftruncate(fd, ALLOCATE_SHARED_MEMORY_SIZE);

// mount
shared_memory_address = mmap(NULL, ALLOCATE_SHARED_MEMORY_SIZE, PORT_READ | PORT_WRITE, MAP_SHARED, fd, 0);
  1. 先配置 shared memory
  2. 把這個記憶體空間擴展成指定大小
  3. 掛載取得 address

圖中的 if (fd >= 0)
是因為當兩個執行檔呼叫的時候為了避免重複產生而做的基本檢查

fflush()msync()
這純粹只是我們的系統會一直卡 buffer
所以硬是給他加上去的而已 = ="

然後只要是遇到非同步讀寫都會遇到搶資源的問題
所以還是需要做點保護
至少保證 write after read 或 read after write
所以在 init() 的最後弄了一個 semaphore 用來卡存取狀態
這個鎖之後會給另外兩個獨立的執行檔使用
同樣偷懶的直接用 POSIX 的 semaphore

sem_t " semaphore_mutex = NULL;
semaphore_mutex = sem_open("/posix_semaphore", O_RDWR | O_CREAT, 0600, 1);

另外有個奇妙的地方
就是shm_open()sem_open()根據 man 所提供的說明
name 應該都要是 “/” 開頭的字串
不過實際上試了一下就算不加 “/”
他還是會掛到相對應的位置
理論上應該都可以在 /dev/shm底下找到他們

deinit()中放入munmap()
實際上 munmap() 應該在存取完之後就直接放掉
不過這邊為了測試所以把它留到了 deinit()的時候才執行

test_dll.c
 


 

 

 

 

 

 

 

 

 

#include <dlfcn.h>/* for dl* function */

這個檔案用到的
dlopen()dlsym()dlclose()
都是這裡面的東西

然後 load library

// libfoo.so 是用前面的 foo.c 產生出來的
// 理論上它在哪邊都沒有問題
// 只要找的到他本人就可以
void * handle = dlopen("./libfoo.so", RTLD_LAZY);

這和一般的 shared library 不同
一般的 shared library 在程式開始的時候就會去檢查 shared library 在不在了
而使用 dlopen()載入 function 的方式
會讓 libfoo.so 只有在執行到這行的時候才會被載入

在存取 shared memory 之前
先卡一個 semaphore

// 開一個 semaphore 用來卡讀寫狀態
sem_t * sem_mutex = sem_open("/posix_semaphore", O_RDWR | O_CREAT, 0600, 1);

// 等待進入 critical section
sem_wait(sem_mutex);

個人習慣在 mmap() 之前就先上鎖了
雖然這樣會讓 critical section 變得很長
但是為了確保 mmap() 在動作時不會在中途有資料沒有被更新
導致資料不同步的問題
所以個人習慣會把鎖加在 mmap() 之前
實際上 cached/uncached 的同步還有很多東西要做
不過那不是這篇的重點所以也是先跳過(;^ω^)

然後 shm_open()ftruncate()mmap()
這裡的步驟跟前面的 foo.c 是一樣的就先略過了

接下來就是對 mmap() 返回的位置做存取了
操作完這塊空間之後就可以用 munmap()
把這塊空間放掉了

通常卡到 cached/uncached 的時候
都會在 munmap() 之前多做 invalidate 跟 flush 的動作
這樣才有辦法保證資料有確實寫入 uncached 的空間中

釋放完空間之後
就要趕快把 semaphore 給打開
不然鎖卡太久可能會造成其他問題

// 解鎖離開 critical section
sem_post(sem_mutex);

最後 shared library 確認用完之後
就可以把它給卸載了

// 釋放 shared library
dlclose(handle);

這裡沒有用到 sem_unlink()
是因為我們的系統預設沒有需要用到這裡的地方
所以就直接忽略掉沒使用了…OTL

test_dll_2.c


 

 

 

 

 

 

 

 

 


 

這個檔案跟 test_dll.c 大部分是一樣的
這個只是用來當作第二個獨立執行檔用的而已

makefile
 


 

 

 

 

 

 

 


編譯 libfoo.so 的時候要 -fPIC
不然程式載入不同系統時位置會出問題
然後加 -shared 給 LD
編譯執行檔的時候要加 -lrt -lpthread 這樣才能用 shared library
然後加 -ldl 這樣 dl* function 才能有作用

執行

./test &
./test2

執行完之後可以在 /dev/shm底下
找到 posix_shared_memorysem.posix_semaphore
原因是因為程式執行完
沒有執行 shm_unlink()sem_unlink()
所以最後這兩個物件都沒有被消除
不過這兩個物件在重開機之後就會不見了
所以以我們的系統來說其實用不太到

其他

我們 target 的系統竟然沒有 /dev/shm 這個路徑
所以一開始的時候一直出現 bus error
不過後來手動建立這個路徑之後就沒問題了