ラベル Linux の投稿を表示しています。 すべての投稿を表示
ラベル Linux の投稿を表示しています。 すべての投稿を表示

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
不過後來手動建立這個路徑之後就沒問題了

2019年4月1日月曜日

[Linux] 用 git 把 Linux Kernel 的 source code 抓下來的方法

抓 kernel source 最簡單的方法當然就是直接到官網 → kernel.org

然後把 source code 以 tarball 的方式抓下來






















不過這次要講的不是這個方法

這次要講的是用 git 把它從遠端 repo 給抓回來的方法

在 kernel 的 repo 中有多到不行的東西




















不過我們這次只是要抓穩定版的 kernel source

所以直接往下找到 stable 的地方





















這個 repo 就是我們要的東西了

所以把它給抓回來

向下面這樣

git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git








抓完就會在資料夾裡面看到完整的 kernel source code 了















是說測試完發現

git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git

好像會自動導向

git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git


所以不管是哪個好像都可以的樣子

大概就是這樣了




2018年11月9日金曜日

[Ubuntu] 在 Ubuntu 18.04 上開發 kernel module


在做 Embedded Linux 開發的大部分時間中

通常很多東西都是平台相依

所以只要沒有板子就沒辦法做任何的測試

但當然也有些東西是不需要板子就可以做測試

所以這篇就是要說

怎麼在 Ubuntu 上進行 kernel module 開發

然後寫好之後進行驗證

當開發平台跟執行平台都是同一個的時候

這意味著這個動作完全不會用到 cross compiler

也就是說如果要驗證交叉編譯的東西的話

基本上這個方法不適用

不過如果只是想做個 kernel module 開發的話

基本上是沒什麼問題才對

所以下面就開始說明大概要做哪些事


首先需要把開發所使用的 Ubuntu 平台變成有編譯 kernel module 的能力

所以需要安裝一些東西


安裝開發工具

sudo apt install build-essential kernel-package libncurses5-dev libelf-dev gcc g++

基本上工具有這些就足夠了

但是還需要安裝 kernel 開發用的 library source

所以還需要再做點事情



安裝 linux-headers


在安裝之前需要先查看一下現在使用的平台的 kernel 版本

apt-cache search linux-headers-$(uname -r)










看起來大概會像上面那張圖一樣

是一個 看起來像是 linux-headers-X.Y.Z-V-M 的格式

通常格式的最後一串英文字表示平台的種類

至於為什麼要這個

是因為接下來的開發會在這個平台上執行跟測試

所以必須先查看這個平台的資訊

不然等等做出來的檔案會沒辦法執行

也就是說如果要在其他平台上執行

至少要先確認要執行的平台的環境才是

檢查完之後接著就是安裝 linux-headers 了

sudo apt install linux-headers-$(uname -r)-generic

這邊我偷懶了

因為直接代入 $(uname -r)

所以其實前面那個版本號查不出來也沒關係


到這邊基本上就完成所以的 kernel module 開發的前置作業了




接下來就可以開始寫 code 了


製作 kernel module


首先隨便找個路徑用來做 kernel module

然後在資料夾下面產生一個 Makefile 和 .c source file

看起來會像下面這張圖這樣



















然後看看 Makefile 裡面長怎麼樣















然後再來看看 source




















基本上這只是個 sample 而已

所以就不要太計較了...OTL

然後就可以編譯它了












沒意外的話

編譯完應該會長的類似上面那張圖一樣

然後就可以看看它產出了什麼東東














正常來說

如果有編譯成功的話

會在路徑下找到一個 .ko 檔

這個就是一個可以搬的走的 kernel module 了



既然寫好了

當然就是把它掛載起來執行看看啦


掛載 kernel module


這邊因為是製作成 loadable 的模組

所以基本上只能使用 insmod 來掛載

因為在 系統的 lib module 中 並不存在這個 module

所以 modprobe 是沒有辦法使用的 ...

然後掛載起來看起來會像下面這樣









敲個密碼

然後什麼事都沒有發生就結束了

所以那剛剛那個 printk 的訊息印到哪去了?



查看 kernel 訊息


這時候就要請出另外一個叫做 dmesg 的指令了













至於這張圖裡面為什麼會有那麼多奇怪的內容

是因為在寫這篇之前我正在幫我的 module debug...

而這篇的範例就是用它改出來的

所以這點小事就不要太計較了XD


最後如果這個 module 不是設計來常駐的話

記得把它卸載

卸載 kernel module


看起來像下面這張圖一樣










一樣什麼訊息都沒顯示

這時候再看一次 dmesg












這時候就會看到離開的訊息了

話說回來

實際上根據不同的 kernel message 等級

其實 dmesg 是會有顏色的



















這樣看起來就清楚多了


然後這篇大概就是這樣了
それでは(・ω・)ノシ


2018年11月8日木曜日

[Ubuntu] 在 Ubuntu 18.04 上安裝多個 gcc/g++ 版本並切換


前一陣子在做開發的時候遇到了一些小問題

就是有時候某些專案所指定的 gcc/g++ 必須使用特定的版本

但不幸的是這些專案都指定必須要使用 host gcc/g++ 的設定

所以就必須要讓多個 gcc/g++ 的版本共存在 host 端

但即便是已經存在了多個版本的時候還是會需要手動切換不同的版本

所以就有這篇了

先檢查看看有沒有 gcc/g++

因為 gcc/g++ 兩個使用的是完全一樣的動作

所以下面偷懶 只用 gcc 舉例

g++ 的部份就把 gcc 換成 g++ 就行了

查看當前 gcc 的版本

gcc -v

如果這樣沒有出現 gcc 的版本訊息的話

表示系統裡不存在 gcc

這時候需要先安裝 gcc/g++

安裝的指令

sudo apt-get install -y gcc-4.8 g++-4.8 gcc-4.9 g++-4.9 gcc-5 g++-5 gcc-6 g++-6 gcc-7 g++-7

這邊安裝的 gcc 版本可以依照自己喜歡的選擇


如果這個時候發現想要的版本抓不下來的時候

也許還有其他選擇

改從其他地方抓

所以這個時候要先增加一個 repository

新增 repo

sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update


如果這時候你的 gcc/g++ 版本很多很亂

也可以選擇先把它們全部移除

從 update-alternatives 移除所有的 gcc/g++

sudo update-alternatives --remove-all gcc
sudo update-alternatives --remove-all g++

這樣會從 update-alternatives 中移除所有的 gcc/g++ 選項

對 update-alternatives 安裝想要的 gcc/g++

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 20
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 30
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 40
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 50

sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 20
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 30
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 40
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 50

上面這些版本必須是前面剛剛已經先從 apt install 裝完的各個版本

每個指令最後的那個數字表示預設的優先度

數字越大就越高


設定 預設 gcc/g++ 的版本

最後在安裝完之後就是對 update-alternatives 進行設定

sudo update-alternatives --set cc /usr/bin/gcc
sudo update-alternatives --set c++ /usr/bin/g++

之後要切換版本的時候就只要執行 config 就可以了

切換 預設 gcc/g++ 的版本

sudo update-alternatives --config gcc
sudo update-alternatives --config g++


大概是這樣

2018年11月1日木曜日

[Linux] PCIe Endpoint Driver with Character Device Driver


好一陣子沒寫東西了
來紀錄一下最近做的東西

最近從 Windows driver 轉做 Linux driver
不知道是不是找資料的方式不對
還是 Linux 更新太快了
一直都找不到比較新的資料

Linux kernel 都已經更新到 4.19 了
結果大部分的資料都還在講 2.6.x
雖然絕大部分都可以向下相容
但總是有些東西沒辦法用

所以這篇用來紀錄一下
前陣子用 kernel 4.x 做 PCIe EP device driver 開發的東西
說是紀錄
但也就只是放個 pcie 的 device driver 的樣板上來而已
本來偷懶想直接全部複製貼上
不過因為有很多東西不能公開
所以只好貼通用的部份上來了

是說完整的內容太長了
所以還是分段貼好了...OTL

理論上下面這串程式直接做成一個.c檔
建成一個 kernel module 應該就可以用了
但不保證我手殘有很大的機會敲錯就是了...w



#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/init.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/phy/phy.h>

#include <linux/irqreturn.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/dma-mapping.h>
#include <linux/wait.h>
#include <linux/completion.h>


#ifndef PCI_EP_ENGINE_DRIVER_VERSION_NUMBER
#define PCI_EP_ENGINE_DRIVER_VERSION_NUMBER "1.0"
#endif

#ifndef PCI_EP_ENGINE_NAME
#define PCI_EP_ENGINE_NAME "pcie_ep_device"
#endif

#ifndef PCI_EP_ENGINE_VENDOR_ID
/* 0x11ab for Marvell Technology Group Ltd.*/
/* ref. http://pci-ids.ucw.cz/read/PC */
#define PCI_EP_ENGINE_VENDOR_ID 0x11ab 
#endif

#ifndef PCI_EP_ENGINE_DEVICE_ID
/* 0x0023 for Marvell 88pa6220 */
#define PCI_EP_ENGINE_DEVICE_ID 0x0023
#endif

#ifndef PCI_EP_ENGINE_CLASS_ID
/* 0x08: Generic system peripheral */
/* 0xXX80: System peripheral */
/* 0xXXXX00: Program interfaces */
#define PCI_EP_ENGINE_CLASS_ID 0x088000
#endifux

#ifndef PCI_EP_ENGINE_CLASS_MASK
/* i am not sure what this is ... */
#define PCI_EP_ENGINE_CLASS_MASK 0xffffff00
#endif

// 0: for static; 1: for dynamic
#define PCI_EP_ENGINE_DYNAMIC_ALLOC_CHR_NUM 1

#ifndef PCI_EP_ENGINE_ALLOC_CHR_COUNT
#define PCI_EP_ENGINE_ALLOC_CHR_COUNT 1
#endif

#ifndef PCI_EP_ENGINE_CHAR_MAJOR
#define PCI_EP_ENGINE_CHAR_MAJOR 0
#endif

#ifndef PCI_EP_ENGINE_CHAR_MINOR
#define PCI_EP_ENGINE_CHAR_MINOR 0
#endif

#define PCI_EP_ENGINE_FUNCTION_IN_PRINT \
    printk(KERN_ALERT "%s(%d): in\n", __func__, __LINE__)

#define PCI_EP_ENGINE_FUNCTION_OUT_PRINT \
    printk(KERN_ALERT "%s(%d): out\n", __func__, __LINE__)

#define PCI_EP_ENGINE_PRINT_FUNCTION_CALLER \
    printk(KERN_ALERT "Caller is: %pS\n", __builtin_return_address(0))

static struct class * g_p_pci_ep_engine_class;
static struct device * g_p_pci_ep_engine_device;

static struct pci_device_id pci_ep_engine_ids[] = {
//{PCI_EP_ENGINE_VENDOR_ID, PCI_EP_ENGINE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* how to determine the RC or EP with same VID & DID ? */
{ PCI_DEVICE_CLASS(PCI_EP_ENGINE_CLASS_ID, PCI_EP_ENGINE_CLASS_MASK) }, /* determine the EP which is a System peripheral */
{0,}
};
MODULE_DEVICE_TABLE(pci, pci_ep_engine_ids);

struct pci_ep_engine
{
    struct pci_device * pci_dev;
    struct cdev chr_dev;
    dev_t devno;
    unsigned long bar0_physical;
    unsigned long bar0_virtual;
    unsigned long bar0_length; 
} ep_engine_device;

static int pci_ep_engine_probe(struct pci_dev * pdev, const struct pci_device_id * id);
static void pci_ep_engine_remove(struct pci_dev * pdev);
static irqreturn_t pci_ep_engine_interrupt(int irq, void * pdev);

static int pci_ep_engine_probe(struct pci_dev * pdev, const struct pci_device_id * id)
{
    int i;
    int result;
    PCI_EP_ENGINE_FUNCTION_IN_PRINT; 
    pci_set_drvdata(pdev, ep_engine_device.pci_dev);

    if (pci_enable_device(pdev)) {
        PCI_EP_ENGINE_DEBUG_ERR("failed' pci_enable_device\n");
        result = -EIO;
        goto end;
    }

    pci_set_master(pdev);
    ep_engine_device.pci_dev = pdev;

    if (unlikely(pci_request_regions(pdev, PCI_EP_ENGINE_NAME))) {
        printk(KERN_ALERT "%s(%d): failed pci_request_regions\n", __func__, __LINE__);
        result = -EIO;
        goto enable_device_err;
    }

    // get bar 0 physical & virtual address
    ep_engine_device.bar0_physical = pci_resource_start(pdev, 0);
    if (ep_engine_device.bar0_physical < 0) {
        printk(KERN_ALERT "%s(%d): failed: pci_resource_start\n", __func__, __LINE__);
        result = -EIO;
        goto request_regions_err;
    } else {
        printk(KERN_ALERT "%s(%d): got pci resource: bar 0 physical address(0x%08x)\n", __func__, __LINE__, ep_engine_device.bar0_physical);
    }

    // get bar 0 virtual address
    ep_engine_device.bar0_length = pci_resource_len(pdev, 0);
    if (ep_engine_device.bar0_length != 0) {
        ep_engine_device.bar0_virtual = (unsigned long)ioremap(ep_engine_device.bar0_physical, ep_engine_device.bar0_length);
        printk(KERN_ALERT "%s(%d): bar 0 physical remap to virt: 0x%08x\n", __func__, __LINE__, ep_engine_device.bar0_virtual);
        printk(KERN_ALERT "%s(%d): bar 0 length: %lu\n", __func__, __LINE__, ep_engine_device.bar0_length);
    } else {
        printk(KERN_ALERT "%s(%d): failed: pci_resource_len get zero!!\n", __func__, __LINE__);
    }

    // enable MSI
    result = pci_enable_msi(pdev);
    if (unlikely(result)) {
        printk(KERN_ALERT "%s(%d): failed: pci_enable_msi\n", __func__, __LINE__);
        goto free_bar0;
    } else {
        printk(KERN_ALERT "%s(%d): enable msi succeeded.\n", __func__, __LINE__);
    }

    result = request_irq(pdev->irq, pci_ep_engine_interrupt, 0, PCI_EP_ENGINE_NAME, ep_engine_device.pci_dev);
    if (unlikely(result)) {
        printk(KERN_ALERT "%s(%d): failed: request_irq\n", __func__, __LINE__);
        goto enable_msi_error;
    } else {
        printk(KERN_ALERT "%s(%d): request_irq succeeded.\n", __func__, __LINE__);
    }

    goto end;
  
enable_msi_error:
    pci_disable_msi(pdev);
free_bar0:
    iounmap((void*)ep_engine_device.bar0_virtual);
request_regions_err:
    pci_release_regions(pdev);
enable_device_err:
    pci_disable_device(pdev);
end:
    PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
    return result;   
}

static void pci_ep_engine_remove(struct pci_dev * pdev)
{
    PCI_EP_ENGINE_FUNCTION_IN_PRINT;

    free_irq(pdev->irq, ep_engine_device.pci_dev);
    pci_disable_msi(pdev);

    iounmap((void*)ep_engine_device.bar0_virtual);
    pci_release_regions(pdev);
    pci_disable_device(pdev);

    PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
}

static irqreturn_t pci_ep_engine_interrupt(int irq, void * dev)
{
    PCI_EP_ENGINE_FUNCTION_IN_PRINT;
  
    // do something at here 

    PCI_VIRTIO_ENGINE_FUNCTION_OUT_PRINT;
    return IRQ_HANDLED;
}

static struct pci_driver pci_ep_engine_driver = {
    .name = PCI_EP_ENGINE_NAME,
    .id_table = pci_ep_engine_ids,
    .probe = pci_ep_engine_probe,
    .remove = pci_ep_engine_remove,
};

static int pci_ep_engine_open(struct inode * inode, struct file * file)
{
 PCI_EP_ENGINE_FUNCTION_IN_PRINT;
 PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
 return 0;
}

int pci_ep_engine_close(struct inode * inode, struct file * file)
{
 PCI_EP_ENGINE_FUNCTION_IN_PRINT;
 PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
 return 0;
}

long pci_ep_engine_unlocked_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
 PCI_EP_ENGINE_FUNCTION_IN_PRINT;
 PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
 return 0;
}

static ssize_t pci_ep_engine_read(struct file * file, const char __user * buf, size_t count, loff_t * ppos)
{
 PCI_EP_ENGINE_FUNCTION_IN_PRINT;
 PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
 return 0;
}

static ssize_t pci_ep_engine_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos)
{
 PCI_EP_ENGINE_FUNCTION_IN_PRINT;
 PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
 return 0;
}

static struct file_operations pci_ep_engine_fops = {
    .owner = THIS_MODULE,
    .open = pci_ep_engine_open,
    .release = pci_ep_engine_close,
    .unlocked_ioctl = pci_ep_engine_unlocked_ioctl,
    .read = pci_ep_engine_read,
    .write = pci_ep_engine_write,
};

static int pci_ep_engine_driver_init(void)
{
    int ret;
    PCI_EP_ENGINE_FUNCTION_IN_PRINT;
    PCI_Ep_ENGINE_PRINT_FUNCTION_CALLER;
    
    ret = pci_register_driver(&pci_ep_engine_driver);
    if (ret < 0) {
        printk("failed: pci_register_driver\n");
        return ret;
    }

#if    PCI_EP_ENGINE_DYNAMIC_ALLOC_CHR_NUM
    ret = alloc_chrdev_region(&ep_engine_device.devno, 0, PCI_EP_ENGINE_ALLOC_CHR_COUNT, PCI_EP_ENGINE_NAME);        
#else
    ep_engine_device.devno = MKDEV(PCI_EP_ENGINE_CHAR_MAJOR, PCI_EP_ENGINE_CHAR_MINOR);
    ret = register_chrdev_region(ep_engine_device.devno, PCI_EP_ENGINE_CHAR_MINOR, PCI_EP_ENGINE_ALLOC_CHR_COUNT, PCI_EP_ENGINE_NAME);
#endif
    if (ret < 0) {
        printk("failed: register_chrdev_region\n");
        return ret;
    }

    cdev_init(&ep_engine_device.chr_dev, &pci_ep_engine_fops);
    ret = cdev_add(&ep_engine_device.chr_dev, ep_engine_device.devno, PCI_EP_ENGINE_ALLOC_CHR_COUNT);
    if (ret < 0) {
        printk("failed: cdev_add\n");
        return ret;
    }

    g_p_pci_ep_engine_class = class_create(THIS_MODULE, PCI_EP_ENGINE_NAME);
    g_p_pci_ep_engine_device = device_create(g_p_pci_ep_engine_class, NULL, ep_engine_device.devno, NULL , PCI_EP_ENGINE_NAME);

    PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
    return 0;
}

static void pci_ep_engine_driver_exit(void)
{
    PCI_EP_ENGINE_FUNCTION_IN_PRINT;

    device_destroy(g_p_pci_ep_engine_class, ep_engine_device.devno);
    class_destroy(g_p_pci_ep_engine_class);

    cdev_del(&(ep_engine_device.chr_dev));
    unregister_chrdev_region(ep_engine_device.devno, PCI_EP_ENGINE_ALLOC_CHR_COUNT);
    pci_unregister_driver(&pci_ep_engine_driver);

    PCI_EP_ENGINE_FUNCTION_OUT_PRINT;
}

module_init(pci_ep_engine_driver_init);
module_exit(pci_ep_engine_driver_exit);

MODULE_AUTHOR("yuutan");
MODULE_LICENSE("GPL");
MODULE_VERSION(PCI_EP_ENGINE_DRIVER_VERSION_NUMBER);
MODULE_DESCRIPTION("ep engine over PCIe: device driver with character device driver");



2018年6月19日火曜日

[OpenCV] Cross Compiling OpenCV with non-free modules for Linux ARM


最近 Ubuntu 用的比較習慣,所以這次還是使用 Ubuntu 來做 cross compiling。

這次一樣又很偷懶直接拿一台現有的機器來試試看

為了方便起見,這次用 ubuntu 18.04 x64 的系統

直接抓 opencv master branch 下來做。

至於為什麼要做 master branch ...

好像沒有為什麼,就是偷懶不想花時間去找其他版本來用XD

需要的東西

1. Virtualbox 任何可以安裝 Ubuntu 18.04 x64系統的版本都好
2. Ubuntu 18.04 x64 系統映像檔

然後用預設的方式安裝系統

在裝好後乾淨的 Ubuntu 系統上什麼都不要做

連更新什麼的都不要...O(>﹏<)O

雖然其實做了也不影響就是ww

然後直接在 Ubuntu 上打開終端機(alt + F2 → gnome-terminal, ctrl + alt + T)

在 terminal 裡照著下面的指令一行一行的敲就好...

沒有意外的話最後就會出現結果了XD

安裝必要的套件

$ sudo apt-get install git repo bison build-essential zip curl cmake
$ sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi

建立工作路徑

$ mkdir -p <work_directory>/opencv_build
$ cd <work_directory>/opencv_build

把 OpenCV master 分支的 source 抓下來

$ git clone https://github.com/Itseez/opencv.git
$ git clone https://github.com/opencv/opencv_contrib.git

除了抓 opencv 的 source 之外

non-free 模組是另外的放的

所以才要在把 opencv_contrib 抓下來

建置 OpenCV master 分支

在建置之前先建立一下輸出路徑
$ mkdir -p build
$ cd build

如果不需要 non-free 模組就執行下面這行
$ cmake -DSOFTFP=ON  -DCMAKE_TOOLCHAIN_FILE=../opencv/platforms/linux/arm-gnueabi.toolchain.cmake ../opencv

如果需要 non-free 模組就執行下面這行
$ cmake -DSOFTFP=ON  -DCMAKE_TOOLCHAIN_FILE=../opencv/platforms/linux/arm-gnueabi.toolchain.cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules ../opencv

最後就是建置啦
$ make
$ make install


基本上到這邊只要 make 跑完沒有錯誤就全部結束了...

然後建置完的輸出檔案會再 install 資料夾下

如果資料夾下有東西的話

應該就是有正常結束了

=====


相關來源:

Building / Cross Compiling OpenCV for Linux ARM
http://www.ridgesolutions.ie/index.php/2013/05/24/building-cross-compiling-opencv-for-linux-arm/

Repository for OpenCV's extra modules
https://github.com/opencv/opencv_contrib

2016年4月22日金曜日

[OpenMPI] 用 VirtualBox 5.0 基於 Ubuntu Server x64 14.04.4 建立 cluster

因為實機測試不走對外網路,要測試變得很麻煩,所以乾脆直接在家裡的電腦建模擬環境。
雖然說比不上實際 cluster 的效能,但至少簡單的測試還是可以做。


要準備的東西

1. ubuntu server x64 14.04.4 的 iso 檔
2. VirtualBox 5.0 以上的版本
3. 要能對外的網路

建置步驟

1. 建立一台當作 Master 的虛擬機
2. 在 Master 虛擬機裡安裝 VirtualBox Guest Additions
3. 建立一台或多台 Slaver 的虛擬機
4. 在每台 Slaver 虛擬機裡分別安裝 VirtualBox Guest Additions
5. 建立虛擬網路
6. 安裝 OpenMPI 和 SSH

建立一台當作 Master 的虛擬機

建立至少一台的 Master 和一台的 Slaver。
也就是說,至少會有兩台 VM 存在。
這次的筆記就以最低限度的兩台來實作。

1. 建立一台當作 Master 的虛擬機


↑ 按 [New] 建立一台虛擬機


↑ 名字隨便取就好


↑ 記憶體依照需求配置,這邊使用最小建議需求的 768 MB


↑ 選第二個,弄顆新的虛擬硬碟出來


↑ 為了方便就用 VDI 形式就好


↑ 用動態的大小可以避免一次占用掉太多的實體硬碟空間


↑ 中間右邊的按鈕可以選擇 vdi 映像檔存放的路徑,至於大小就看需求給就好。


↑ 建好之後第一次開機會遇到要插入光碟的選項,選擇 ubuntu-14.04.4-server-amd64.iso

2. 安裝 ubuntu server x64 14.04.4


↑ 一開始就是選擇語言,這邊選自己習慣的就好,我是選 [English]


↑ 選第一個 [Install Ubuntu Server] 開始安裝


↑ 又要在選一次語言,我還是選 [English]


↑ 接著要選國家,台灣的選項在 [other]→[Asia]→[Taiwan]


↑ 選擇鍵盤配置,按<Yes>會自動偵測,我是按<No>


↑ 因為我的鍵盤配置並不是美規的所以必須要另外選擇


↑ 然後就是一直等待安裝了


↑ 這裡要輸入 Hostname,這邊的名字很重要,因為 cluster 的每個 node 就是靠這個在判斷的


↑ 輸入使用者的名字,這邊隨興就好


↑ 輸入帳戶的名字


↑ 建立使用者密碼


↑ 再次輸入,確認使用者密碼


↑ 因為我密碼打了 [1234],所以被問是否要使用這個弱密碼,然後按 <Yes>


↑ 這邊不要加密選 <No>


↑ 接著又是一直等待


↑ 確認時區是否正確


↑ 選擇 partition 形式,這邊選第二個 [Guided - use entire disk and set up LVM]


↑ 選擇磁區,也就只有這麼一個可以選


↑ 確認變更硬碟設定選 <Yes>


↑ 輸入硬碟大小,這邊預設就全部的容量


↑ 確認變更設定


↑ 因為沒有要用 proxy 所以這邊空白就好


↑ 通常伺服器應該不會隨便更新,所以這邊選 [No automatic updates]


↑ 選則伺服器的種類,因為只是單純的 cluster ,所以這邊選 OpenSSH server


↑ 安裝 GRUB 開機選單


↑ 完成安裝,按 <Continue> 重開機

在 Master 虛擬機裡安裝 VirtualBox Guest Additions

接著開機完之後就是安裝 VirtualBox Guest Additions

這邊安裝 VirtualBox Guest Additions 的目的是為了和 Host 共用 shared 資料夾

或是調整螢幕解析度和其他 Host-Client 之間的溝通等功能

所以這裡的步驟不是必需的

如果沒有要用 VirtualBox 的功能和 Host 做溝通的話

這裡就可以省略不做

1. 在安裝之前先更新開發套件


↑  更新套件清單並將套件升級

sudo aptitude update && sudo aptitude -y upgrade


2. 更新並升級完成


↑ 更新完成畫面

3. 安裝 VirtualBox Guest Additions 套件所需的開發工具


↑ 套件指令

sudo aptitude -y install make gcc xserver-xorg xserver-xorg-core build-essential module-assistant linux-headers-`uname -r` dkms


↑ 開發套件安裝完成畫面

4. 插入 VirtualBox Guest Additions iso 檔


↑ 從 [Device]->[Insert Guest Addition CD Image]

5. 開始安裝


 掛載映像檔

sudo mount /dev/cdrom /media/cdrom



↑ 切換路徑到 /media/cdrom


cd /media/cdrom



↑ 切換路徑


↑ 執行 VirtualBox Guest Additions 建置及安裝


sudo ./VBoxLinuxAdditions.run



↑ 在 [Building the VirtualBox Guest Additions kernel modules] 這邊會等待一段時間


↑ 編譯和安裝完成

紅色框框裡面的那段文字一定要出現 [...done.]

這裡是最重要的,如果沒有出現就代表失敗了。


↑ 確認安裝成功

lsmod | grep vboxguest



↑ 有出現紅色的 [vboxguest] 這行就表示有完成


↑ 安裝完成後關機


sudo poweroff


為了要掛載 shared folder 所以這邊先暫時關機

6. 掛載 shared folder

回到 VirtualBox Manager


↑ 對這台虛擬機器點選 [設定]


↑ 從左邊點選 [Shared folders] 並按下圖中紅色箭頭指的圖示


↑ 選擇要位於 Host 端要被掛載的資料夾,並將 [Auto-mount]打勾


↑ 新增完成後會像這張圖一樣多出一行共用資料夾,按下 [OK] 離開

將虛擬機開機並登入


↑ 重開機後登入完成畫面


↑ 重新確認 virtualbox guest additions 有沒有被執行


lsmod | grep vboxguest


像上圖這樣表示沒有在執行

在沒有執行的情況下,是無法掛載資料夾的

所以只好再來一次了


↑ 找到 VirtualBox Guest Additions 的安裝路徑

因為剛剛已經插入過 VirtualBox Guest Additions 的映像檔了

所以這邊不用再插入一次,直接進行掛載就可以了


sudo mount /dev/cdrom /media/cdrom
cd /media/cdrom
ll #兩個小寫的 L 用來列出當前路徑下的東西


如果遇到

[sudo] password for <username>: 

就直接輸入使用者密碼按 [Enter]


↑ 再次執行完成的結果


sudo ./VBoxLinuxAdditions.run



↑ 確認 vboxguest 有被執行


lsmod | grep vboxguest



↑ 建立一個資料夾用來掛載 shared folder,並變更權限


sudo mkdir /mnt/<shared folder name>
sudo chmod 777 /mnt/<shared folder name>


<shared folder name> 的名字可以依個人喜好設定


↑  確認資料夾內容


ls -ld /mnt/<shared folder name>



↑ 列出資料夾資訊的結果

接著要掛載資料夾


↑ 掛載資料夾的時候要使用到 Host 的 shared folder 名字


sudo mount -t vboxsf <host shared folder name> /mnt/<shared folder name>


<host shared folder name> 是掛載資料夾時設定的名稱

<shared folder name> 是剛剛取好名字的資料夾名稱


↑ 掛載完成


↑ 切換路徑到掛載的資料夾下,並列出資料夾的內容物


cd /mnt/<shared folder name>
ll

如果掛載成功就可以看到 guest 端出現和 host 端資料夾一樣的內容物

7. 將掛載進來的資料複製出來

這個步驟並不是必要的

不過因為這種掛載方式是暫時的

也就是說只要一旦重開機,就要在重新掛載一次

為了省麻煩,乾脆把資料給複製到 guest 端裡面


↑ 執行複製資料夾指令


cp -avr /mnt/<shared folder name> ~/<any name>


要複製到哪個路徑基本上隨個人喜好設定即可

這邊為了作業方便,所以直接複製到家目錄底下

<shared folder name> 就是剛剛一直出現的那個名字
<any name> 基本上也是自行決定即可


↑ 複製完資料夾的結果


↑ 切換到家目錄底下看看資料夾是否有被成功建立


cd
ll



↑ 切換到 <shared folder name> 路徑下


↑ 列出資料夾內的內容物


ll #列出內容


確認是否資料夾內容物都有完整的複製進去

建立一台或多台 Slaver 的虛擬機

這裡基本上和建立 Master 虛擬機的部分一樣。

只有在取 hostname 的時候要有區別而已。

其實可以直接用 clone 的方式就好。

只是這樣複製出來的機器還需要從 guest 端裡面去改 hostname。

反正不趕時間,所以就慢慢再裝一台。

這裡只記錄跟建立 Master 不一樣的地方。


↑ 幫虛擬機取個跟 Master 不一樣的名字


↑ 找個空間存放虛擬機 Slaver 的映像檔


↑ 幫 Slaver 取個名字

這個這個 Hostname 不能被重複,在 MPI 的網路中,這個名字是用來做識別用的

也就是說,如果有多台 slaver 的話,可能就要取個什麼 slaver1, slaver2, slaver3...之類的

這邊因為只用一台 slaver,所以就不做編號了



↑ user name 這邊為了 MPI 通訊方便,所以取跟 Master 一樣的名稱


↑ 同樣的 username

既然是 cluster,每個節點在通訊的時候自然需要使用者權限,

為了方便操作,這邊用同樣的使用者名稱確保 MPI 通訊時可以穿過每個節點。



↑ 安裝完,並登入的畫面

接下來的安裝步驟跟 Master 就完全一樣了。

所以這邊就略過不記錄了XD

在每台 Slaver 虛擬機裡分別安裝 VirtualBox Guest Additions

這裡同樣和在 Master 虛擬機裡安裝 VirtualBox Guest Additions 的步驟一樣。

完全照抄就好。

因為我懶得截圖再寫一次一樣的步驟。

所以這邊就拉回去看上面的 「在 Master 虛擬機裡安裝 VirtualBox Guest Additions」。

然後這段就這樣沒了XD

建立虛擬網路

既然是 cluster,也就是說這幾台虛擬機都要可以互相透過網路溝通。

所以這邊幫每台虛擬機都增加一張虛擬網卡用來當作互聯的內網。

原本的那張虛擬網卡就繼續留著當作對外網路。

VirtualBox 提供的虛擬網路有很多種介面。

這次使用一個 NAT 做對外網路,和一個 Host-Only Adapter 做對內網路

所以一台虛擬機就會有兩張虛擬網卡。

這裡回到 VirtualBox Manager

一台一台的設定網卡。

1. 建立 Master 虛擬機的網卡


↑ 找到要設定網卡的 Master 虛擬機然後按設定

↑ 在紅色箭頭找到 [network]


↑ 找到紅色箭頭的第二個標籤


↑ 啟用這張網卡,並找到 [Host-Only Adapter] 的選項


↑ 設定好之後按 [OK] 離開

2. 建立 Slaver 的虛擬網卡


↑ 找到 Slaver 虛擬機按右鍵選 [setting] 進入設定


↑ 一樣找到紅色箭頭的 [Network]


↑ 找到 [Adapter 2] 的標籤,啟用它,並選到 [Host-Only Adapter]


↑ 完成後,按 [OK] 離開

3. 建立群組


↑ 找到 Master 虛擬機,按右鍵選 [Group]

這時候會 Master 虛擬機會跑進新的群組裡面。


↑ 新增的群組

這時候找到 Slaver 虛擬機,按住滑鼠左鍵,

用拖拉的方式把它加進剛剛建立的群組裡面。


↑ 把 Slaver 虛擬機拖進群組裡

加入群組之後的結果

就會跟下面的那張圖看起來很類似

一次出現多台虛擬機的訊息

如果有多台 slaver 的話

全部都要加進群組裡面


↑ 群組狀態

接著進入虛擬機裡面進行網卡設定

這邊使用群組開機


↑ 在群組的標籤按右鍵找到開機選項

接著你的畫面可能會像下面這樣

很多台被啟動的虛擬機


↑ 多台一起開機

分別登入兩台主機


↑ 兩台主機登入完的畫面

4. 建立 cluster 虛擬網路

首先要編輯 host 清單


↑ 對兩台分別下指令編輯 hosts 文件

Master, Slaver
sudo vi /etc/hosts


有可能會需要輸入使用者密碼,有需要就輸入。


↑ 個別的 hosts 文件內容

然後把文件分別改成下面的圖的樣子。


↑ 寫入所有的 hostname

如果不會 vi 操作的話,就自己 google 好了。

不然這樣這篇會太長XD

然後存檔離開

基本上使用 192.168.*.* 當作內網 IP

這時候 Master 的 IP 準備要使用 192.168.56.105

而 Slaver 的 IP 準備要使用 192.168.56.106

如果有多台 Slaver 的話就一直往下增加就好了。

基本上是每台都要寫,所以越多台,這個檔案就會越長。


↑ 分別查看網卡訊息

Master, Slaver
ifconfig -a


正常來說,剛剛在 VirtualBox Manager 設定的第二張網卡

因為都還沒有做設定,所以應該只會跟上圖一樣不會有其他 IP 的訊息。

這時候就要對每台的第二張網卡 eht1 寫入 IP。


↑ 寫入 IP 訊息

Master
sudo ifconfig eth1 <static ip> netmask 255.255.255.0


Slaver
sudo ifconfig eth1 <static ip> netmask 255.255.255.0


<static ip> 是剛剛在 hosts 文件裡面設定的 IP

也就是說根據每台的 Hostname 分別決定要寫入的 IP

這時候在分別查看兩台的 eth1 網卡訊息


↑ eth1 網卡訊息

Master, Slaver
ifconfig -a


這時候應該可以看到 eth1 網卡有剛剛寫進去的 IP 訊息

這邊要注意的是,這個方法是暫時性的,

也就是說,如果關機再重來的話,這個步驟就要再重做一次。


↑ ping 任何一台的位置

ping 任何一台測試看看網路有沒有通

因為這邊只有兩台,所以就變成互相 ping 對方了

Master, Slaver
ping <any ip>



↑ ping 測試的結果

從上圖可以看到這樣內部網路應該是有通了


↑ 測試對外網路

找個外部網路試試看,確認外部網路沒有掛掉。

這邊找的外部網路直接用 google 來測試。

Master, Slaver
ping www.google.com



↑ ping www.google.com 的結果

從結果來看,外部網路應該也還活著。

外部網路要活著的原因是因為等等要裝 OpenMPI 和相關套件

所以需要用到外部網路

不然正常的話,cluster 其實不需要對外網路。

安裝 OpenMPI 和 SSH

接著安裝 OpenMPI

這時候同樣對著每台虛擬機下指令安裝 OpenMPI


↑ 安裝 OpenMPI 指令

Master, Slaver
sudo aptitude -y openmpi-bin libopenmpi-dev



↑ 安裝結果

這邊兩台結果不一樣是因為

其中一台之前已經裝過了...OTL

只是打這篇筆記的時候再打開來下指令

所以就會變成這樣

不然正常都沒有安裝的話

應該要出現跟右邊一樣的結果才對XD

接下來 Master 和 Slaver 分別要安裝不一樣的東西

Master 要安裝 nfs server 套件

Slaver 要安裝 SSH 和 nfs 套件


↑ 分別安裝 nfs 和 SSH

Master
sudo aptitude -y install nfs-kernel-server


Slaver
sudo aptitude -y install nfs-common ssh 



↑ 分別完成安裝的結果

==========

到這邊基本上就全部結束了

測試?

因為實在太麻煩了,所以這篇筆記基本上就到這裡

測試等有空的時候再來寫下一篇好了

光是這篇筆記打到這就已經花了快20個小時了...(~_~;)


相關來源

Ubuntu 14.04.01中安装Virtualbox guest addition
http://kiwenlau.blogspot.tw/2015/05/ubuntu-140401virtualbox-guest-addition.html

Ubuntu on VirtualBox で ホストとのファイル共有ができなくなった
http://xawa99.blogspot.tw/2013/05/Ubuntsu-Vbox-FailToMountSharedFolder.html

PGIコンパイラによる MPICH2 の使い方(Linux)
https://www.softek.co.jp/SPG/Pgi/TIPS/public/general/mpich2_use.html

OpenMPIライブラリ | スーパーコンピュータシステムの使い方

Ubuntu日本語フォーラム / LAN内でsshに接続できません(port 22: Connection timed out)
https://forums.ubuntulinux.jp/viewtopic.php?id=10673

UbuntuへのVirtualBox Guest Additionsのインストール
http://d.hatena.ne.jp/akamushi97/20140131/1391157868

Ubuntu 14.04 : Install VirtualBox Guest Additions & also create videos
http://sharadchhetri.com/2014/10/07/ubuntu-14-04-install-virtualbox-guest-additions-also-create-videos/

How to install Virtualbox guest additions on Ubuntu 14.04
http://www.binarytides.com/vbox-guest-additions-ubuntu-14-04/

Install a Cluster of Ubuntu Servers on Oracle VM VirtualBox
http://www.ixtendo.com/install-ubuntu-server-on-oracle-vm-virtualbox/

【 Virtual Box 】共有フォルダを介して、ホストOS(Windows 7) と ゲストOS(Debian/ MathLibre)でファイルやりとりするための設定
http://qiita.com/HirofumiYashima/items/6044cfc64cfa3e84f97c

Show the List of Installed Packages on Ubuntu or Debian
http://www.howtogeek.com/howto/linux/show-the-list-of-installed-packages-on-ubuntu-or-debian/

Ubuntu Linux: Start / Stop / Restart OpenSSH ( SSH ) Server
http://www.cyberciti.biz/faq/howto-start-stop-ssh-server/

MPI (Message Passing Interface Standard)
http://www.hitachi.co.jp/Prod/comp/hpc/foruser/sr8000/tebiki/mpi_exec.html

MPICH2のインストール
http://www.nslabs.jp/mpi-setup.rhtml

Sshでログインできないときに疑うべき点
http://dev.satake7.net/posts/292

Vagrantを使って2つの仮想マシン上にPacemaker + Heartbeatによる2ノードクラスタを作る
http://l-w-i.net/t/pacemaker/setup_002.txt

計算機クラスタ構築メモ
http://d.hatena.ne.jp/Tayama/20110803/1312375197

Ceph : クラスターの設定
http://www.server-world.info/query?os=Ubuntu_14.04&p=ceph

並列プログラミングのための、 linux クラスタ環境の構築
http://maijou2501.hateblo.jp/entry/20120523/1337783387

CUDA対応ビデオカード搭載の複数ホスト間でGPGPU ( その4: CUDA-aware 対応のため OpenMPI のコンパイル )
http://maijou2501.hateblo.jp/entry/2015/05/17/230059