Try   HackMD

如何看懂Open Source : 帶你閱讀一座程式I

tags: libmodbus

Copyright 2021, 月下麒麟


專案Source Code下載處-

git clone https://github.com/stephane/libmodbus.git (參考來源)

概要

這份筆記主要用來記錄學習該libmodbus的疑問,
故於順序上會較凌亂,採用各個擊破的方式。
另外,因網路上這方面的文章較少,也想分享有關如何自學Open Source的方法。
(附上網路上的前輩寫的實例研討: 從 C++ 學習 C 高級技巧)

什麼是Modbus

Modbus為眾多工業通訊協定中的一種,
就是讓機器用來互相溝通的一種語言。

該libmodbus就是一個開源程式碼,讓終端使用者下載到指定的機器設備上,
透由安裝與設定,就能在該機器設備上使用Modbus協定。

關於機器與通訊協定的關係,可參考下圖-

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(白話例子:一個說英語,德語的人,可以跟只會說德語的人溝通)
這樣是不是更能明白關於通訊協定在機器上應用了呢

學習前的建議

建議可先閱讀三份Modbus Specification,
閱讀specification一定很枯燥乏味,但務必要耐住性子,
至少先把第一份看完,嘗試去理解Modbus的行為,

其實文件內容就是機器間的溝通規則,並不難以理解,
就跟人溝通一樣,也是滿有趣。

1.Modbus功能代碼解釋及資料格式-
Modbus Application Protocol Specification V1.1b

2.Modbus應用於串列通訊
Modbus over Serial Line Specification & Implementation Guide V1.02

3.Modbus應用於TCP/IP通訊
Modbus Message on TCP/IP Implementation Guide V1.0a

學習思路

1.從makefile理解安裝過程
2.從header理解有哪些函式宣告與定義
3.從unit test,可了解到設計者如何呼叫函式及實作
3-1.看printf,要印什麼東西,從輸出反推程式的意思!
4.再次閱讀Specification
(重複以上學習流程)


Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

以上,學習方法僅供參考,畢竟每個人的學習方式不同,
只要找到最適合自己的即可。

如不想直接閱讀Modbus Specification也是可以的,
那就跟著筆者一起摸索Open Source Code有趣的世界吧,
一起 Trace程式碼 Go~

Get start

Reference:從這裡開始瀏覽libmodbus原始碼程式
欲認識對方的程式碼,可先從對方寫的範例程式或是測試程式開始

int main(int argc, char*argv[]) { //... modbus_t *ctx; //...

這邊提及一下有個struct會在source code裡面大量出現,
為指標ctx指向的modbus_t struct,
所以這一篇幅會先認識struct與其應用。

Question & Answer

#Q1,認識struct

//libmodbus/tests/unit-test-server.c modbus_t* ctx; //line 35

搜尋modbus_t其定義。

//libmodbus/src/modbus.h typedef struct _modbus modbus_t; //line 155

發現該modbus_t為_modbus結構的別名。
接著,再搜尋_modbus其定義,會發現到它是一個匿名結構。

//libmodbus/src/modbus-private.h struct _modbus { //line 92 /* Slave address */ int slave; /* Socket or file descriptor */ int s; //... };

#A1

method 1

typedef struct _modbus modbus_t; //typedef別名 struct _modbus { int slave; }; int main(){ modbus_t* ctx = (void*) malloc(sizeof(int)); //modbus_t前面不用加 struct return 0; }

method 2,這個寫法更簡短

typedef struct _modbus { //利用typedef與結構之member(modbus_t)合併一起 int slave; } modbus_t ; int main(){ modbus_t* ctx = (modbus*) malloc(sizeof(int)); //modbus_t前面不用加 struct return 0; }

method 3,不使用typedef,較冗長

struct _modbus{ int slave; }modbus_t; int main(){ struct modbus_t* ctx = (struct modbus_t*) malloc(sizeof(int)); //凡用到modbus_t的都要在前面加上"struct" return 0; }

#Q2,理解malloc

// #1 char* ptr = (void*)malloc(4*sizeof(int));//較常看到的 // #2 modbus_t* ctx = ( modbus_t*)malloc(sizeof(modbus_t)); //變型 //請搭配Q1的struct

#A2

#include <stdio.h> #include <stdlib.h> int main() { char* ptr = (void*)malloc(4*sizeof(int)); printf("*ptr: %ld\n",sizeof(*ptr)); // 回傳指標ptr指向的型別(char)的大小 printf("ptr: %ld\n",sizeof(ptr)); // 指標的大小 return 0; } //Output /* *ptr: 1 ptr: 8 */

誠如上述,這個malloc用法較為常見
在64位元架構下,指標大小都為8 bytes(64 bits / 8 bits )。
32位元之指標大小則為4 bytes。

#include <stdio.h> #include <stdlib.h> typedef struct _modbus{ int a; //int size is 4 int b; // 4 char c; // 1 char d; // 1 float e; // 4 float f; // 4 } modbus_t; int main(){ modbus_t* ptr = (modbus_t*) malloc(sizeof(modbus_t)); printf("*ptr: %ld\n",sizeof(*ptr)); printf("ptr: %ld\n" ,sizeof(ptr)); printf("modbus_t*: %ld\n",sizeof(modbus_t*)); printf("modbus_t: %ld\n" ,sizeof(modbus_t)); return 0; } //Output /* *ptr: 20 ptr: 8 modbus_t*: 8 modbus_t: 20 */

*ptr為指標ptr的型別modbus_t的大小。
ptr為指標ptr本身的大小 (白話,就是指標大小)。
modbus_t* 為指標modbus_t本身的大小 (白話,就是指標大小)。
modbus_t為_modbus結構其所有成員加總的大小。
(如加總大小超過或不足4的倍數,得到的結構大小有所不同,筆者這部分涉獵不深,故無法詳細描述)

另外,malloc語法設計上會回傳指向一個不指定型別的指標

void* malloc (size_t size);

故再實際應用上,需要在加上欲指向之型別,詳細解釋可參考C_CPP看板

modbus_t *ctx;
ctx = (modbus_t *)malloc(sizeof(modbus_t));

補充

int att = 10; int *ppt; ppt = &att; printf("ppt: %lu\n",sizeof(ppt)); printf("*ppt: %lu\n",sizeof(*ppt)); printf("address of ppt: %p\n", ppt); printf("value of ppt: %d\n", *ppt); //Output // ppt: 8 // *ppt: 4 // address of ppt: 0x7fff021c8598 // value of ppt: 10