2022年3月21日 星期一

【Linux】json-c環境設定及使用範例

【install libjson-c】

apt-get install libjson-c-dev


【check libjson-c】

apt show libjson-c-dev


【compile libjson-c from source】

./autogen.sh

./configure

export CFLAGS='-Wno-implicit-fallthrough'

make

make install 


【compile libjson-c example】

gcc example_json.c -ljson-c  -o example_json

編譯過程中在鏈結時,gcc 會帶上已經遇到的符號,如果在原始檔之前使用 -ljson,gcc 不會鏈結任何東西,因為在它編繹原始碼之前就做了鏈結函式庫的動作,此時對於函式庫還沒產生任何需求,需求是在編譯原始碼時才知道的,所以應該要在原始檔之後放置要鏈接的庫。


【記憶體的釋放】

  • json_object_new_object生成的物件必須調用json_object_put釋放。
  • json_tokener_parse生成的物件,必須使用json_object_put釋放。
  • 通過json_object_object_get獲取的物件不能單獨釋放,因爲它仍然歸父節點所有。
  • 通過json_object_object_add添加到其他節點的不能再單獨釋放,因爲他已經成爲別人的子節點,他的生命週期由父節點維護了。
  • 爲json_object_to_json_string只是把json物件內部的指針借你用而已,千萬別釋放。


EX

#include<stdio.h>
#include<json-c/json.h>

#define NAME_DEF "name"
#define HP_DEF "HP"
#define EQUIPMENT_DEF "equipment"
#define SWARD_DEF "sward"
#define ARMOR_DEF "armor"

void myTest()
{
json_object *pJsonTxRootObj = NULL;
json_object *pJsonTxDataObj = NULL;
json_object *pJsonRxDataObj = NULL;
json_object *pJsonTmpObj = NULL;

//new
pJsonTxRootObj = json_object_new_object();

//compose
json_object_object_add(pJsonTxRootObj, NAME_DEF, json_object_new_string("Jason"));
json_object_object_add(pJsonTxRootObj, HP_DEF, json_object_new_int(100));

pJsonTxDataObj = json_object_new_object();
json_object_object_add(pJsonTxDataObj, SWARD_DEF, json_object_new_string("flame sward"));
json_object_object_add(pJsonTxDataObj, ARMOR_DEF, json_object_new_string("dragon armor"));
json_object_object_add(pJsonTxRootObj, EQUIPMENT_DEF, pJsonTxDataObj);

//json to string
char *pStr = json_object_to_json_string(pJsonTxRootObj);
printf("json str = %s\n", pStr);

//string to json
json_object *pJsonRxRootObj = json_tokener_parse(pStr);

//decompose
pJsonTmpObj = json_object_object_get(pJsonRxRootObj, NAME_DEF);
printf("%s = %s\n", NAME_DEF, json_object_get_string(pJsonTmpObj));

pJsonTmpObj = json_object_object_get(pJsonRxRootObj, HP_DEF);
printf("%s = %d\n", HP_DEF, json_object_get_int(pJsonTmpObj));

//get sub object
pJsonRxDataObj = json_object_object_get(pJsonRxRootObj, EQUIPMENT_DEF);

pJsonTmpObj = json_object_object_get(pJsonRxDataObj, SWARD_DEF);
printf("%s = %s\n", SWARD_DEF, json_object_get_string(pJsonTmpObj));

pJsonTmpObj = json_object_object_get(pJsonRxDataObj, ARMOR_DEF);
printf("%s = %s\n", SWARD_DEF, json_object_get_string(pJsonTmpObj));

//free
json_object_put(pJsonTxRootObj);
json_object_put(pJsonTxRootObj);
json_object_put(pJsonTxDataObj);
}


在 makefile 檢查目錄是否存在

my_target:

ifeq "$(wildcard $(MY_PATH))" ""

mkdir $(MY_PATH)

echo "directory not existed"

else

echo "directory existed"

endif



當要建立二層以上的目錄時,例如 mkdir dir1/dir2,dir1 和 dir2 都不存在,平時都是要先 mkdir dir1,才能 mkdir dir1/dir2。使用參數 -p ,mkdri -p dir1/dir2,會自動建立dir1 及 dir2。

内存泄漏检测工具mtrace


Json­-C用法释疑


实际项目中发现Json-C用法不当导致的内存泄露、踩内存问题,大都是因为不清楚下面几个接口的用法。
以下分析基于https://github.com/json-c/json-c( 0.12.1 release)。

1. json_object_new_object生成的对象要不要释放

int main(int argc, char **argv)
{
	struct json_object* obj;
	mtrace();
	obj = json_object_new_object();
	//json_object_put(obj);
	return 0;
}  

上面的代码执行后,你会发现泄漏下面这些内存:

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000b6a460     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185
0x0000000000b6a4b0     0x58  at json-c-json-c-0.12.1-20160607/linkhash.c:435
0x0000000000b6a510    0x200  at json-c-json-c-0.12.1-20160607/linkhash.c:440

所以,json_object_new_object生成的对象必须调用json_object_put释放。

2. json_tokener_parse生成的对象要不要釋放

int main(int argc, char **argv)
{
    mtrace();
    const char *str = "{\"a\":1}";
    struct json_object* obj = json_tokener_parse(str);
    //json_object_put(obj);
    return 0;
}

上面这些代码执行后,你会发现下面这些 内存泄漏:

Memory not freed:
-----------------
           Address     Size     Caller
0x00000000022e7930     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185
0x00000000022e7980     0x58  at json-c-json-c-0.12.1-20160607/linkhash.c:435
0x00000000022e79e0    0x200  at json-c-json-c-0.12.1-20160607/linkhash.c:440
0x00000000022e7c10     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185

以,json_tokener_parse生成的对象,必须使用json_object_put释放。

3. json_object_object_get出来的对象要不要释放

int main(int argc, char **argv)
{
    struct json_object* obj;
    struct json_object *child;
     
    obj = json_object_new_object();
     
    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "b", json_object_new_int(2));
     
    child = json_object_object_get(obj,"a");
    json_object_put(child);     //Oh, No!!!
    json_object_put(obj);
    return 0;
}

助内存越界检测工具efence和gdb,运行代码发现段错误,其中test.c:22指向json_object_put(obj)这一行.
这是因为child节点被释放过了,现在又去释放, 使用了野指针(不借助工具,程序会正常结束,这也是这种错误的可怕之处)。
这种不会立即终止程序的错误太可怕 ,让你都不知道怎么死的。

Program received signal SIGSEGV, Segmentation fault.
json_object_put (jso=0x7ffff7ee2fb8) at json_object.c:154
154                     jso->_ref_count--;
(gdb) bt
#0  json_object_put (jso=0x7ffff7ee2fb8) at json_object.c:154
#1  0x0000000000403346 in lh_table_free (t=0x7ffff7edefa8) at linkhash.c:485
#2  0x000000000040190d in json_object_object_delete (jso=0x7ffff7edcfb8) at json_object.c:354
#3  0x0000000000401edd in json_object_put (jso=0x7ffff7edcfb8) at json_object.c:159
#4  json_object_put (jso=0x7ffff7edcfb8) at json_object.c:150
#5  0x0000000000401515 in main (argc=1, argv=0x7fffffffdfd8) at test.c:22

以,通过json_object_object_get获取的对象不能单独释放,因为它仍然归父节点所有。
4. 通过json_object_object_add添加到其他节点的,能不能释放。

int main(int argc, char **argv)
{
    struct json_object* obj;
    struct json_object *child;
    
    child = json_object_new_object();

    obj = json_object_new_object();
    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "child", child);
     
    json_object_put(child);     //Oh, No!!!
    json_object_put(obj);
    return 0;
} 

这个运行后,产生的错误和3中类似,也是因为重复释放。 所以,通过json_object_object_add添加到其他节点的不能再单独释放,因为他已经成为别人的子节点,他的生命周期由父节点维护了。

5. json_object_to_json_string获取到的字串要不要释放

int main(int argc, char **argv)
{
    struct json_object* obj;
    char *str;
    obj = json_object_new_object();
    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "b", json_object_new_int(2));
    str =  json_object_to_json_string(obj);
     
    free(str);     //Oh, No!!!
    json_object_put(obj);
    return 0;
}


个free也是非法的,因为json_object_to_json_string只是把json对象内部的指针暴露给你了,借你用下而已,千万别释放。

沒有留言:

張貼留言