tea教室02 === [tea教室01問題討論](https://hackmd.io/s/HkNcPw5Il) [hackfoldr record](http://hackfoldr.org/1ViN2u8YerUjYvCcItW1dtjrkxHafOXotkczGsWyIKMM/https%253A%252F%252Fhackmd.io%252Fs%252FHkNcPw5Il%2523) [source code](https://github.com/NTHU-TEA/tea_02) ### outline * vim使用,Basic linux bash command * From Source to Binary :Makefile dive deeper * some example : socket --- * **vim使用,Basic linux bash command** * From Source to Binary :Makefile dive deeper * some example: socket ---- ### vim使用,Basic linux bash command vim模式 | 描述 | | ------ | ----------- | | insert mode | 用key `i` 進入打程式 | | normal mode | 剛進入的地方 | | commandline mode | 執行某些動作 | https://www.openvim.com/ 到行尾shift+4 到開頭 0 ---- 一開始我們在normal mode ![](https://i.imgur.com/VJhE2Am.jpg =320x240) ---- 按`i`進入insert mode ![](https://i.imgur.com/oEaUkWp.jpg =320x240) ---- 第一個程式都是hello world搂 ![](https://i.imgur.com/2Nn7aGm.jpg =320x240) ---- 打完按`esc`離開insert mode ![](https://i.imgur.com/KREeohW.jpg =320x240) ---- `:`代表進入command mode ![](https://i.imgur.com/VLduRG3.jpg =320x240) ---- `:w` 寫入 ![](https://i.imgur.com/B8UxMME.jpg =320x240) ---- `:wq` 寫入在離開 ![](https://i.imgur.com/Ta1ivUz.jpg =320x240) ---- copy paste ctrl + shift +c/v copy: `yy` + `p` (y3y) delete: `dd`(d3d) ---- 多行註解 multi comment normal mode `gg` 回到開頭 `ctrl`+`v` `shift`+`i` // ![](https://i.imgur.com/OUU48wf.png ) uncomment undo: `u` find(like ctrl f): / + `n` ---- my vimrc ![](https://i.imgur.com/NRjCYRz.png) ```vim= set background=dark set nu set ic syntax on set ignorecase smartcase set undofile set undodir=~/.vim/undodir filetype plugin indent on set tabstop=4 set softtabstop=4 set expandtab set cursorline set cindent set number set pastetoggle=<F12> ``` ---- **byobu** `F2` 新增terminal `F3`,`F4` switch between terminal `ctrl`+`F6` 刪除terminal `shift`+`F2`新增水平terminal `ctrl`+`F2`新增垂直terminal `shift`+方向鍵 切換 ---- **byobu** `F2` add terminal `F3`,`F4` switch between terminal `ctrl`+`F6` delete terminal `shift`+`F2`add new horizotal terminal `ctrl`+`F2`add new vertical terminal `shift`+arrow keys switch ---- **縮排vim** When you what to copy paste someone's code on web site usually you got this ![](https://i.imgur.com/CncWASl.jpg =320x240) ---- 按先按`F12`再`i` ![](https://i.imgur.com/AMvGBiW.jpg =320x240) `ctrl`+`shift`+`v` ![](https://i.imgur.com/9j2rhuO.jpg =320x240) 再按`F12` 離開paste mode 再:wq :set paste :set nopaste [ref](http://twed2k.org/viewthread.php?tid=336366#.WHyJ62dq2lM) [ATOM edittor](https://atom.io/) sudo dpkg -i atom-64.deb ---- **Basic bash command** Bash background process to foreground `fg` , (use socket to demo) `ctrl` + `c` to terminate process ---- ![vim cheat sheet](http://blog.vgod.tw.s3.amazonaws.com/wp-content/uploads/2009/12/vim-cheat-sheet-full.png) [vgod給程式設計師的Vim入門圖解說明](http://blog.vgod.tw/2009/12/08/vim-cheat-sheet-for-programmers/?variant=zh-tw) --- * vim使用,Basic linux bash command * **From Source to Binary :Makefile dive deeper** * some example : socket --- ### From source to Binary:Makefile dive deeper 回憶.c變身成執行檔過程 ![](https://i.imgur.com/vlizNGN.png) from [from source to binary](http://www.slideshare.net/jserv/from-source-to-binary-how-gnu-toolchain-works) ---- Preprocessing 展開header, define, 去除註解 ![](https://i.imgur.com/pbxkIBL.jpg) ---- 編譯產生組合語言(注意-c是小寫c) ![](https://i.imgur.com/8ZppAVT.jpg) try: `objdump -d <executable>` ---- 組譯生成物件檔01011 main裡面函式的實做可能在其它.cpp中 ![](https://i.imgur.com/U3i9WP4.jpg) ---- **動手做hands on** [original source](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/original) 算兩點距離 `cd ~/tea02/make-example/original` 編譯產生geom.o `gcc -o geom geom.c -l m` ![](https://i.imgur.com/1PUyMUf.jpg) `./geom` ---- 假使今天有另一個檔案tip.c, 也要用到get_double()函式呢? ---- 輕鬆~ 把get_double()整個貼上面就好啦 ---- great, 那今天是100個檔案都要用到get_double( )? ![](https://i.imgur.com/8GGLNZf.png =300x300 ) ---- 或者把get_double()寫成另外的gd.c, 當其它cpp需要使用只要`#include "gd.c"`請看[version1](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver1) ![](https://i.imgur.com/M6Alof2.jpg) ![](https://i.imgur.com/FXgsUDY.jpg) ---- 不過就算這樣,你還是得貼100次 `#include "gd.c"` ![](https://i.imgur.com/iCSqBay.jpg) 另外你每次都得重新編get_double函式, 即使我們不會去修改它。還記得嗎 preprocessing只是把標頭檔展開 ---- 或者我們事先把gd.c編譯成gd.o就是所謂的函式庫(library) 需要用到get_double()的.c再編譯後再去跟gd.o鏈接(linking) 請看[ver2-prototype](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver2-prototype), 不過geom.c不認得get_double()是什麼啊 於是在最上面要宣告函式原型(Function prototype) 這就是為什麼需要函式原型,因為你的實作在其它地方,但是跟編譯器講函式其實是存在的喔 ![](https://i.imgur.com/MSBcv37.jpg) ---- [ver3-empty](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver3-empty) 那如果我們唬爛編譯器, 宣告了function prototype卻沒有函式實作呢? gd.c裡面是空的, 你編了一個空的gd.c ---- gd.c還是能編出gd.o 用objdump反組譯gd.o 其實只加了一些overhead ![](https://i.imgur.com/Bqyvuy7.jpg) 接著終端機執行`gcc gd.o geom.o -o geom` ---- 沒法編執行檔 linker無法鏈接get_double的實做 **undefined reference to** ![](https://i.imgur.com/pwNXo4U.jpg) 所以當你往後編譯看到undefined reference to大概就是少了那個library的實作 通常是函式庫路徑指錯了 ---- recap what we have done ![](https://i.imgur.com/rPeWNHj.jpg) ---- 如果現在tip.c也要用get_double( ) 我們也可以 ![](https://i.imgur.com/hHyDTlL.jpg) ---- 到目前為止,採用的方法是把function獨立成gd.c檔,並且在需要使用的geom.c, tip.c上面宣告function prototype `double get_double( char *prompt, double min, double max);//function prototype` ---- 如果今天有更多function要用呢? 就要貼很多次function prototype搂? ---- 於是有了header標頭檔的存在,幫忙打包function prototype ,比如gd.h header標頭檔裡面沒有程式碼實作 只把function prototype, 變數的宣告放進去 請看[ver4-header](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver4-header) ---- dependency tree ![](https://i.imgur.com/sfq2TPW.jpg) ---- [ver5-makefile](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver5-Makefile) formating MakeFile recipe `# -*- MakeFile -*- ` make all Makefile撰寫 ``` Make= # -*- MakeFile -*- #target: dependencies # action all: geom tip geom: geom.o gd.o gcc geom.o gd.o -o geom -l m geom.o: geom.c gcc -c geom.c gd.o: gd.c gcc -c gd.c ``` 今天我們只要生成執行檔geom(目標target) `# -*- MakeFile -*-` 是把整份文件的格式訂成Makefile格式(有特定的縮排,不能按4次空白鍵 要用`tab`) ``` #target: dependencies # action ``` #是註解 目標: 編目標需要的原料(相依檔案們) 動作(ex: gcc geom.o gd.o -o geom -l m) ```Make= # -*- MakeFile -*- #target: dependencies # action geom: geom.o gd.o gcc geom.o gd.o -o geom -l m geom.o: geom.c gcc -c geom.c gd.o: gd.c gcc -c gd.c ``` GnuMake 會去找第一個目標 `geom` 然後她發現 喔 編geom需要 geom.o ,gd.o 接著又繼續找 要有geom.o 需要 geom.c 要有gd.o 需要 gd.c 直到拆解成原始碼 才不用繼續找下去 ---- 有多個執行檔要編要打 all all在這邊不是target 因為它不是一個真的的檔案 只是動作 (make clean, make run也是) 大家可以查查 .PHONY 那不是一個target 代表那不是要編譯出來的target,只是個動作 all: executable1 executable2 `make all` ---- Make不會recompile Make 只會重新編譯更新時間比較新的(有被修改的檔案),怎麼做到的呢? make會從從dependency tree 往回找(下面要時間比較近) 不會重新編所有的東西 geom.o的時間戳記要比gd.c後面(不然會重編) ex: geom.c修改 再make一次觀察輸出 ---- **增加header到Makefile** 當我們新增了功能比如函式add.c , gd.h裡面就要多加function prototype, 我們就需要在Makefile裡面dependency加上gd.h (如果沒加呢?) 範例[ver6-makeheader](https://github.com/NTHU-TEA/tea_02/tree/master/make-example/ver6-addHeader) ---- 好比我們修改gh.h, 比如在gd.h新增亂碼註解之後make ![](https://i.imgur.com/5eSSCWW.jpg) 還是相安無事並沒有重新編,但這不對 我們明明修改了gd.h 儘管它只是註釋, 如果新增了一個function prototype呢? ![](https://i.imgur.com/4r9pJA9.jpg) ---- 在Makefile增加gd.h 再make ![](https://i.imgur.com/tSWGVax.jpg) ---- 重新編譯 ![](https://i.imgur.com/UrxDnE3.jpg) 發現有東西被編出來了 ---- 其實make會去看檔案被修改的時間, geom.c + gd.h 先編出geom.o, geom再和gd.o,產生執行檔geom 因此dependency tree越下面的檔案時間要比較舊,最上面是最新的。 因此當gd.h被修改, make發現gd.h時間居然比geom.o還新於是geom.o需要被重新產生 ![](https://i.imgur.com/PJpxkn9.jpg) ---- ![](https://i.imgur.com/eIR11oV.jpg) ---- 我們發現只有跟修改gd.h無關的gd.c不會被重新編譯 ![](https://i.imgur.com/BdXvvJw.jpg) ---- 所以說make只會編有被修改過的檔案, 不會全部重編,這對龐大專案減少編譯時間有一定的幫助。 ---- 什麼時候Makefile裡要加.h呢? 基本上只要你的.c裡面有 #include "header" 通常 Makefile dependency裡面就要記得加 OOO.h 不加入#inclde <stdio.h>原因(因為基本上不會去改printf,scanf的內容) ---- 所以一開始的 gcc -o geom geom.c -l m真相大白 geom.c上面#include<math.h> 其實它就是放著 pow.o, sqrt.o 這些數學運算的header裡面放著他們的function prototype ---- 各位好奇可以在terminal打locate math.h ![](https://i.imgur.com/Nv5AkoO.jpg) 它在/usr/include底下所以用#include<>尖括號 `vim /usr/include/math.h` 啊不過裡面又是一堆.h跟macro歡迎大家跟我講pow或sqrt的function prototype被宣告在哪裡XD ![](https://i.imgur.com/vko8Mox.jpg) ---- 至於include什麼.h檔要-lm什麼 可以問男人 比如: `man sqrt` ![](https://i.imgur.com/oPONsr5.jpg) ---- 那math.h什麼時候放的? 一開始空空的ubuntu16.04作業系統我打了 ``` sudo apt-get update sudo apt-get install vim sudo apt-get install build-essential sudo apt-get install libc6-dev sudo apt-get install manpages sudo apt-get install manpages-posix  sudo apt-get install manpages-posix-dev  sudo apt-get install manpages-dev ``` `libc6-dev` http://packages.ubuntu.com/xenial/libc6-dev 裡面就把pow.o,sqrt.o實作obj放到你的系統資料夾 /usr/lib 或其實它是.a -l m 相當於代表一整大包比如 pow.o, sqrt.o library .a是一堆.o的集合 .a靜態函式庫 .so共享函式庫 靜態函式庫每次被呼叫會產生一個副本 共享函式庫只有一個副本 ---- 於是lapacke的編譯就真相大白了 ``` shell= g++ -o tea_svd svd_demo.cpp -I"/home/osboxes/lapack-3.6.1/LAPACKE/include" -L"/home/osboxes/lapack-3.6.1" -llapacke -llapack -lblas -lcblas -lgfortran ``` `-I`:指定 #include header的路徑 `-L`指定.o(library)們的路徑, 這些.o是一些.f被用fortran compilier編成.o的 `-l`:小寫, 剛剛有了路徑 那要找的library名稱是哪一個 我們需要lapacke lapack blas cblas gfortran 而這些.o檔就從我們下載的資料夾或apt-get install liblas之類的來 ---- BY 宇賢補充 **為什麼不用-l c就能讓linker連結到stdio.h裡面的printf?** -l m 相當於代表一整大包比如 pow.o, sqrt.o 那用到printf() scanf() 有#include<stdio.h> 編譯的時候怎麼不用加`-l c`呢? `gcc -o geom geom.c -l m -l c`? Ans: 預設 [ref](http://www.programmer-club.com.tw/ShowSameTitleN/c/33367.html) ---- 增加make clean 不要每次重新編譯都make clean一次 專案太大要編很久 ps.如果command前面不是打tab而是打4個空白鍵space (Makefile格式) ---- ![](https://i.imgur.com/u6rRdlF.jpg) ![](https://i.imgur.com/nwlZTJZ.jpg) ---- `gd.h.gch: GCC precompiled header (version 014) for C` `make run` 執行程式碼 其實你可以自訂動作 比如`make habonbon` facebooc write in c https://github.com/jserv/facebooc .PHONY 那不是一個target 代表那不是要編譯出來的target,只是個動作 ---- ### some example : [socket](https://github.com/NTHU-TEA/tea_02/tree/master/socket-c) 練習:把socket client跟server包成 Makefile編譯 ![](https://i.imgur.com/P6AFwx6.jpg) #include <header> c standard library | 描述 | 使用時機 | | ------ | ----------- |-------------| | <assert.h> | | | | <float.h> | | | |<math.h> | | | | <stdarg.h> | | | | <stdlib.h> | | | | <ctype.h> | | | | <limits.h> | | | | <setjmp.h> | | | | <stddef.h> | | | | <string.h> | | | | <errno.h> | | | | <local.h> | | | | <signal.h> | | | | <stdio.h> | | | | <time.h> | | | **materials to read:** *about gnu make* [gnu make](http://itrs.tw/itrs-tutorials/make_tutorial.pdf)--Scott [from source to binary](http://www.slideshare.net/jserv/from-source-to-binary-how-gnu-toolchain-works)-jserv [ELF](https://read01.com/zeG4M7.html) [靜態動態函式庫](http://sp1.wikidot.com/gnulinker) *about C* the C programming language https://hackmd.io/s/H1KTTZP8l http://hackfoldr.org/dykc/ https://neal.hackpad.com/ep/pad/static/mSZzBXYoUED https://embedded2015.hackpad.com/Summer2015--PwYbXEAP7ei bitwise operator [兩數值變換](http://adrianhuang.blogspot.tw/2011/08/c.html) [overflow大力光股價](http://finance.technews.tw/2017/01/10/largan-stock-trouble/) va_list http://www.cnblogs.com/dong008259/archive/2012/01/04/2312451.html *about socket* https://read01.com/8z4oP3.html http://blog.linux.org.tw/~jserv/archives/2006_06.html [function pointer](http://140.116.72.245/~q36991217/allenli/?p=153) server socket http://wiki.csie.ncku.edu.tw/embedded/2016q1h3 *about parallel programming* [Pthread](https://read01.com/5Qo4LP.html) [SIMD](https://hackmd.io/s/BJOTYoHge) [concurrent](http://opass.logdown.com/posts/784600-concurrency-series-1-the-road-to-understand-concurrency) * linker http://docs.oracle.com/cd/E19253-01/817-1984/ * git http://learngitbranching.js.org/ make 和bash https://yodalee.blogspot.tw/2017/04/makefile-header.html [makefile](https://hackmd.io/AzCsDMDYCMIWgJwBZSTkgJgg7I82M4BmARnAEMTgMJzIkg==?both#) ###### tags: `tutorial` `tea` `tea02`