發表文章

目前顯示的是有「Linux」標籤的文章

GCC Built-in Functions

最近上課的時候鬧了一個大笑話(而且那門課還是組合語言不是程式設計...)。我寫了下面這隻程式: int main() { printf( "Hello\n" ); return 0; } 很明顯,這程式沒有 include 任何 header file,理論上應該是要包含 stdio.h。在編譯的過程中,compiler 吐出了下面的 警告信息 ( 不是錯誤信息唷 ): test.c: In function ‘main’: test.c:3:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]      printf( "Hello\n" );      ^ test.c:3:5: warning: incompatible implicit declaration of built-in function ‘printf’ test.c:3:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’ 但是程式會動,還是可以印出 Hello 的字樣。為什麽?我這時候信誓旦旦的跟大家說,因為 printf 在 libc.so 裡面有,因此就算編譯的時候找不到,gcc 在連結 libc.so 的時候還是會看的到 printf ,所以這時候還是可以執行的。為了證明這件事情,我用 nm 去看一下編出來的 test.o 0000000000000000 T main                  U puts 等一下, where is my printf ?? ...在學生面前要保持鎮定,大概 printf 在系統裡被改成 puts ... 然後再做實驗給同學看,這次換成利用 libm.so 的 pow 函式。 int main() { printf( "2^3 = %f\n", pow( 2.0,3.0 ) ); return 0; } 然後說,這...

libpcap 使用範例

最近因為工作上的需求,所以透過 libpcap 寫了一個小程式。程式紀錄在下面。應該不難懂,所以就不多做說明。程式主要參考 sniffex ,不過主要針對  DHCP 的封包進行攔截以及顯示。 #include #include #include #include #include #include #include #include #define SIZE_ETHERNET (14) #define SIZE_UDP (8) #define ETHER_ADDR_LEN (6) const char *DHCP_MSG_TYPE_STR[9] = { "None", "DHCP Discover", "DHCP Offer", "DHCP Request", "DHCP Decline", "DHCP ACK", "DHCP NACK", "DHCP Release", "DHCP Inform" }; struct sniff_ethernet { uint8_t ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ uint8_t ether_shost[ETHER_ADDR_LEN]; /* source host address */ uint16_t ether_type; /* IP? ARP? RARP? etc */ }; struct sniff_ip { uint8_t ip_vhl; /* version << 4 | header length >> 2 */ uint8_t ip_tos; /* type of service */ uint16_t ip_len; /...

Write FD set 的用途

一段時間,我其實都不知道 select 和 epoll 這一類 function 裏面的 writefds 到底有什麼用。read fd set很容易理解,那就是有東西要讀,或者白話一點,收到封包了。那 write fd set 到底要幹嘛呢?直到今天遇到了一個情境我才使用了它 ... 在寫 TCP socket 程式的時候,會使用到 connect 這個函式。一般來說, connect 這個函式是 blocking 的,也就是會一直到事情做完才會返回。但如果將 socket 設定成 non-blocking 模式呢?這時候 connect 會立刻返回並將 error no 設定為 EINPROGRESS 。下面是"男人"的解釋: EINPROGRESS               The  socket  is  nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by  selecting  the  socket  for  writing.   After select(2) indicates writability , use getsockopt(2) to read  the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully  ( SO_ERROR is zero ) or unsuccessfully ( SO_ERROR is one of the usual  error codes listed here, explaining the reason for the failure ). 好了,這時候我們可以看到 select 可以用來判斷 connect 是否...

當 send 被 Block 的時候

今天在工作上遇到一個問題。有兩個 process ( process A 和 process B ) 透過 unix domain socket 彼此在傳遞資料,結果發現 process A 在送資料到 process B  的時候,程式居然卡在 send 這支系統函式。send 是會卡住的函式嗎? 答案是: 會 !來看看「男人」怎麼說: ssize_t send(int sockfd, const void *buf, size_t len, int flags ); When the message does not fit into the send  buffer  of  the  socket,  send()  normally blocks , unless the socket has been placed in nonblocking I/O mode .  In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case.   The  select(2)  call may be used to determine when it is possible to send more data. 意思是當呼叫 send 的時候,send 會先將資料放到 kernel 的 buffer,如果 buffer 已經放不進去了,而且 send 又處於 BLOCK 的模式時,這時候 send 就會卡住。而為什麼 buffer 會滿呢?因為 process B 處理封包的時間過久,進而造成 kernel buffer 被 process A 源源不絕的封包給塞滿了,然後 process A 的 send 就被 block 了。解決這個問題的方法可以將 socket 設成 non-blocking mode ,或是在傳送封包的時候把 flag 設成 MSG_DONTWAIT 。而這也就是為什麼 epoll, select 這類的函式需要去偵測 fd 是否已經是可寫狀態的原因。不過更根本要處理的問題是,為什麼 process...

當 console 壞掉的時候 ...

這是今天在工作上看到的一個小技巧,所以把它記錄下來。在程式開發的過程中很容易會使用 print 的方式來追蹤並解決問題,以 C 來說最常見的就是 printf,如果要吐到 console 上的話就開啟 /dev/console 並使用 fprintf 。 今天聽到的問題是:「版子的 console 壞掉了怎麼辦?」 最不花腦筋的答案:「換張版子吧~」 廢話,如果是這樣也不用紀錄了。我今天看到的作法紀錄在下面: $ killall klogd $ mv /dev/console /dev/console_bak $ ln -sf /dev/pts/0 /dev/console 這作法真是太 cool 了,連程式都不用改唷,當然前題是你可以透過 terminal 連進去就是了。第1行的目的是少印一些資訊。 那 /dev/pts/0 是什麼東西呢?pts 是 pseudo terminal slave 的縮寫。細節可以 google 得到,這邊只講最簡單的,當有 terminal 連到 linux 主機時,主機會分派一個 pts 給他。舉例來說,我在 linux 啟動兩個 gnome-terminal,再透過 who 這個指令,可以看到下面的訊息: $ who neokent  tty7         2016-03-03 15:05 (:0) neokent  pts/2        2016-03-03 15:06 (:0) neokent  pts/5        2016-03-03 17:08 (:0) 其中 tty7 是我的桌面,pts/2 和 pts/5 是我的兩個 terminal。這時候如果在 pts/2 的 terminal 上執行 echo Hello > /dev/pts/5 可以在 pts/5 上看到 Hello 唷。

TCP 和 UDP 共用同一個 Port?

這幾天在做網路實驗,利用 iperf3 來觀測流量的時候,發現一個神奇的地方,那就是 iperf3 在設定 server 端的時候,不用指定是 tcp 還是 udp,這跟 iperf 第2版以前是不一樣的。 iperf2: SYNOPSIS       iperf -s [ options ]       iperf -u -s [ options ] GENERAL OPTIONS       -u, --udp            use UDP rather than TCP iperf3: SYNOPSIS        iperf3 -s [ options ]        iperf3 -c server [ options ] CLIENT SPECIFIC OPTIONS       -u, --udp            use UDP rather than TCP 這代表 TCP 和 UDP 的 Socket 是可以同時使用同一個 Port 的。其實我一直以為是不行的(這就叫作孤陋寡聞 ...)。為了實驗起見,我自己寫了一個小小的 Echo Server,在指定的 Port 上同時監聽 TCP 和 UDP,來看看是否能夠運作(Client的程式就不附在上面了)。   #include <stdio.h> // for printf() and fprintf() #include <sys/socket.h> // for socket(), bind(), and connect() #include <arp...

popen 程式範例

最近公司有個案子在做 WiFi 管理系統,透過集中式的控制器(AC,Access Controller)來管理轄下的 AP(Access Points,CAPWAP 的用語是 WTP)。嗯,有些 sense 的人應該就會知道說:「啊,你在做 CAPWAP 」,Bingo!不過這篇沒打算要介紹 CAPWAP,下次有機會來講講 CAPWAP 的故事(不是技術,因為技術的部份我覺得很無聊,所以來講講故事和我的一些心得)。在做的時候遇到一個問題,廠商提供的平台,但卻沒提供人來進行系統整合。簡單來說,就是版子往我桌上一扔,就說:「給我在上面做這個功能」,然後時間短的不像話。額外抱怨一下,他選的 AC 平台比 raspberry pi 的效能還差,居然還敢跟我開口要一堆功能 ... 。理論上來說,AC 會把 WTP 的相關資訊帶到各 WTP,然後這時候 WTP 的 capwap daemon 會對核心層的 driver 進行設定。 一般說來,這種設定的方式是透過 ioctl 的溝通管道,最常見的範例程式就是 ifconfig,透過上下層定義好的 SIOCSIFFLAGS(Socket I/O Control Set Interface Flags)來進行溝通,所以一般來說這是屬於系統平台人員所要處理的問題。可是對於那種把版子跟一堆文件丟在我桌上,又不給我足夠時間的傢伙,我才懶得幫他查 WiFi driver 上需要的 SIOCSFFLAGS 勒。但案子又要進行,那要怎麼辦呢?所以我決定採取最單純的作法:直接呼叫系統的 WiFi 控制指令,如 iwpriv、wlanconfig 等,透過這些指令直接呼叫來取得並設定相關參數。缺點很明顯,就是效能會比較差,但一來是差不了多少(在意效能就給我拿比較像話的版子來!),二來是我相信對方提供的這些指令應該是測試過了,代表系統應該已經整合完畢了(我覺得是我一廂情願,但起碼對方無法還口,要合作也拿點誠意來吧)。 下一個問題是,要怎麼執行系統指令?最直覺的作法,用 system 的函式,問題是我要怎麼撈回傳的資料呢?因此這篇文章就來紀錄另外一支系統函式:popen 的用法。先來看看 man 的說明吧。 The  popen()  function  opens  a  process ...

自訂 Bash Prompt

以前在使用 shell 的時候,會覺得 prompt 所顯示的「目前所在目錄位置」實在是太長了,但是拿掉又有點不方便。結果看到同事很聰明的讓提示字元換到下一行,所以也決定來這樣幹。 Bash Profile Generator 是一個動態的 Bash Prompt 產生器,就用這個打造自己喜歡的 Prompt 吧。

pthread_create 會多建立一個 thread 的原因

之前在執行某個客戶的案子時,被問到一個問題:「你說你們程式開了兩個thread,那為什麼透過 ps 指令觀察到的個數卻是 4 個呢?」(ps 的指令為 ps -L ,可以透過 PID 和 LWP 這兩個欄位看出來),一時之間不知道怎麼回答。之後由同事 York 在 man pthreads找到了下面的答案。  Linux implementations of POSIX threads        Over time, two threading implementations have been provided by the        GNU C library on Linux:        LinuxThreads               This is the original Pthreads implementation.  Since glibc               2.4, this implementation is no longer supported .        NPTL (Native POSIX Threads Library)               This is the modern Pthreads implementation.  By comparison               with LinuxThre...

Netlink Performance 測試

這篇文章的由來在於客戶說的一句話:「Netlink 的效能似乎不太好」客戶口中的不太好指的是 Throughput 只有 10Mbps。本來嘛,我想之後才處理這件事情,但結果我的一個同事 York 抱著追根究底的精神進行了下面的實驗。首先,他用一支 user space 的程式來產生封包到 kernel space 的模組,模組收到以後就將封包打回 user space。簡單來講究是一個 echo 的行為。下面會列出這兩隻程式: 實驗平台:某平台 Kernel 版本:3.10 User Space: #include #include #include #include #include #include #include #include #include #define NETLINK_TEST 18 //#define MAX_PAYLOAD 1024 //#define MAX_PAYLOAD 2048 //#define MAX_PAYLOAD 4096 #define MAX_PAYLOAD 8192 #define MESSAGE_COUNT 1048576 struct sockaddr_nl src_addr, dest_addr; struct msghdr msg; struct nlmsghdr *nlh = NULL; struct iovec iov; int sock_fd; void main() { int _i = 0; time_t startTime = 0; time_t endTime = 0; sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST); memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); memset...

IPC: Shared Memory Example

圖片
我個人比較喜歡的 IPC 技術是 Unix Domain Socket,因為可以 統一透過 File Descriptor 的處理機制來進行管理 ,如 epoll 或是 select 等。但即便是這樣,還是很多人會跟我說:「你有考慮過 Shared Memory 的方法嗎?效能應該會比較好唷。」我當然知道囉,不過隨著年紀增長,已經不再像以前一樣汲汲於效能上微小的差異(I mean ... 人的感受程度),而會把開發、維護的容易程度放在考量的第一位。所以我的第1選項目前都是:Unix Domain Socket,不但如此,之後也很 容易直接改成網路的 socket 程式 ,何樂而不為。但因為以後可能還是有遇到必須使用 Shared Memory 的情況,所以還是寫篇文章來紀錄相關的資訊。 Shared Memory 的 IPC 方式,顧名思義,就是兩個 Process 直接存取同樣一塊的記憶體空間。一般來說,兩個獨立的 Process 都會有各自的 Virtual Memory 位址管理,彼此之間是無法互相存取到的。而使用了 Shared Memory 的機制,系統核心就會準備一塊記憶體位置並讓兩個 Process 都能互相存取。用下圖可以表示 Shared Memory 和 Unix Domain Socket 的不同: 上圖左邊雖然是寫 Unix Domain Socket,但其實用 lo 來做 UDP socket communication 也是同一類。我個人認為上圖很明顯的說明了兩種方式的效率差異。在網路上看到一篇很不錯的效能比較文章,連結如下: Tcp Socket vs. Unix Domain Socket vs. Pipes vs. Shared Memory 這個網頁只有一個小缺點, 他不應該用 TCP Socket 來比較而應該採用 UDP Socket ,理由是 TCP 的 Overhead 大於 UDP,而本機端的溝通應該用不到那些機制。考量到外部網頁連結不一定會持續存在,因此我將相關的圖節錄如下: 接下來就是撰寫範例程式了。在這裡會撰寫兩隻程式,一支負責傳送資料,另外一支負責接受資料。範例的參考連結在下面: http://www.cs.cf.ac.uk/Dave/C/node27.html...