作者descent (「雄辩是银,沉默是金」)
看板C_and_CPP
标题[分享] 打造 c++ 标准程式库 for stm32f407
时间Sun Feb 7 22:50:56 2016
接续的是 simple c++ 标准程式库。我从来没想过我会去写标准程式库, 这是无心插柳之
作。
标准程式库是一个演算法和系统知识的结合, 和之前偏重在系统知识的程式不同, 比较偏
软体, 我的演算法和资料结构能力太弱, 顺道练习不是坏事。
不知道你有没有和我一样的感觉, 教科书上的演算法资料结构, 似乎在平凡人的工作上功
效不大, 总是有基本程式库打造好这些, 我们只要去用就好了, 这也造成我们很难去练习
这样的程式, 毕竟有了 std::list, 有谁能忍住诱惑不去用呢? 你说用 c 的怎麽办, 有
发现在这样环境下的工程师大部份都用 array 吗? 很少用上 list, map 这些资料结构。
simple os 一开始也都是用 array, simple scheme 也是。
现在我迷上 bare-metal 程式, 刚好有个机会来练习这些基本功, 不是坏事, 只是能在学
生时代就练习到是比较好的。
在写 simple os 时, 就已经写了 (抄了) 不少相对应的 c function, 而在完成 simple
scheme bare-metal 的版本时, 又多了一些资料结构, 再来实作 malloc 之後, 我终於有
了动态配置记忆体的能力, 可以写 c++ 的容器 (list, vector, map ...), 好用不少,
再加上实作出 exception handle, 记忆体的管理方便多了, 又在回头改善了 c++ 容器,
让她更像标准程式库的行为。
这是一个累积的成果, 把之前的努力集合起来, 再加上一些程式码, 就是 c++ 标准程式
库了。成果在这里:
https://github.com/descent/simple_stdcpplib (
https://goo.gl/K5Khpe )
目前支援 3 个平台, p103 模拟器, stm32f4discovery board, raspberry pi2 (无法藉
由 uart 输入), 所以也提供了 3 个 linker script。
要移植这个 library 有两部份:
输出/输入 - 在这三个平台都是 uart
c runtime - 如何从组合语言切换到 c/c++
c runtime 每个硬体平台都不同, 这便是从组合语言到 c 这段所需要最辛苦的过程, 从
这里开始就可以摆脱组合语言, 用 c 了。当然如果你喜欢用组合语言来开发程式, 就没
有这个需求。
在 p103/stm32f407 要做:
bss init
data section init
rpi2 要做:
bss init
stack register 的设定
x86 要做:
bss init
stack register 的设定
使用保护模式的 Flat memory model 还要把 %ss, %ds, %cs 设到同一个 selector
c runtime 除了和硬体相关之外还可能和你使用的 c compiler 有相关性, 我只有研究
gcc 这个 c compiler。
而 c++ runtime 则是 c runtime 做的事情, c++ 都要做, 而 c++ 有很多特性, 有些要
在 c++ runtime 支援, ex:
global object
static object
rtti
exception handle
Pure virtual functions
http://wiki.osdev.org/C%2b%2b#Pure_virtual_functions
未知
目前只支援 global object, 本来不想做的, 因为觉得过於麻烦 (这个可得从 linker
script 开始着手) 也怕整个 code 太大, 不过 cout 是 global object, 就做吧! 反正
c++ 就是大。
feature list:
c runtime
c++ runtime
global object ctor/dtor
exception handle (implement by setjmp/longjmp)
printf (support float)
malloc/free
new/delete
vector
string
map
list
想试试的朋友可以从 stm32 p103 模拟器开始:
https://github.com/beckus/qemu_stm32.git (
https://goo.gl/5Sqynm )
编译出支援的模拟器之後, 再编译本 library, 会有一个 mymain.bin, 执行
typescript L2 的 command, ctrl-A+X 离开 qemu。
typescript
1 Script started on Tue 12 Jan 2016 09:38:47 AM CST
2 descent@debian64: ~/git/qemu_stm32/arm-softmmu/qemu-system-arm -M
stm32-p103 -kernel mymain.bin -nographic
3
4 (process:25512): GLib-WARNING **:
/build/glib2.0-VKSJTv/glib2.0-2.46.1/./glib/gmem.c:482: custom memory
allocation vtable not supported
5 STM32_UART: UART1 clock is set to 0 Hz.
6 STM32_UART: UART1 BRR set to 0.
7 STM32_UART: UART1 Baud is set to 0 bits per sec.
8 STM32_UART: UART2 clock is set to 0 Hz.
9 STM32_UART: UART2 BRR set to 0.
10 STM32_UART: UART2 Baud is set to 0 bits per sec.
11 STM32_UART: UART3 clock is set to 0 Hz.
12 STM32_UART: UART3 BRR set to 0.
13 STM32_UART: UART3 Baud is set to 0 bits per sec.
14 STM32_UART: UART4 clock is set to 0 Hz.
15 STM32_UART: UART4 BRR set to 0.
16 STM32_UART: UART4 Baud is set to 0 bits per sec.
17 STM32_UART: UART5 clock is set to 0 Hz.
18 STM32_UART: UART5 BRR set to 0.
19 STM32_UART: UART5 Baud is set to 0 bits per sec.
20 STM32_UART: UART5 clock is set to 0 Hz.
21 STM32_UART: UART5 BRR set to 0.
22 STM32_UART: UART5 Baud is set to 0 bits per sec.
23 STM32_UART: UART4 clock is set to 0 Hz.
24 STM32_UART: UART4 BRR set to 0.
25 STM32_UART: UART4 Baud is set to 0 bits per sec.
26 STM32_UART: UART3 clock is set to 0 Hz.
27 STM32_UART: UART3 BRR set to 0.
28 STM32_UART: UART3 Baud is set to 0 bits per sec.
29 STM32_UART: UART2 clock is set to 0 Hz.
30 STM32_UART: UART2 BRR set to 0.
31 STM32_UART: UART2 Baud is set to 0 bits per sec.
32 STM32_UART: UART1 clock is set to 0 Hz.
33 STM32_UART: UART1 BRR set to 0.
34 STM32_UART: UART1 Baud is set to 0 bits per sec.
35 LED Off
36 CLKTREE: HSI Output Change (SrcClk:None InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
37 CLKTREE: HSI/2 Output Change (SrcClk:HSI InFreq:8000000 OutFreq:4000000
Mul:1 Div:2 Enabled:1)
38 CLKTREE: SYSCLK Output Change (SrcClk:HSI InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
39 CLKTREE: HCLK Output Change (SrcClk:SYSCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
40 STM32_RCC: Cortex SYSTICK frequency set to 8000000 Hz (scale set to 125).
41 STM32_RCC: Cortex SYSTICK ext ref frequency set to 1000000 Hz (scale set
to 1000).
42 CLKTREE: PCLK1 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
43 CLKTREE: PCLK2 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
44 CLKTREE: GPIOA Output Change (SrcClk:PCLK2 InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
45 CLKTREE: AFIO Output Change (SrcClk:PCLK2 InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
46 CLKTREE: UART2 Output Change (SrcClk:PCLK1 InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
47 STM32_UART: UART2 clock is set to 8000000 Hz.
48 STM32_UART: UART2 BRR set to 0.
49 STM32_UART: UART2 Baud is set to 0 bits per sec.
50 STM32_UART: UART2 clock is set to 8000000 Hz.
51 STM32_UART: UART2 BRR set to 833.
52 STM32_UART: UART2 Baud is set to 9603 bits per sec.
53 i am cout ctor: 0
54 ----------------
55 free_index: 0
56
57 0 0 0 0 0 0 0 0
58 0 0 0 0 0 0 0 0
59 0 0 0 0 0 0 0 0
60 0 0 0 0 0 0 0 0
61 0 0 0 0 0 0 0 0
62 0 0 0 0 0 0 0 0
63 0 0 0 0 0 0 0 0
64 0 0 0 0 0 0 0 0
65 ================
66 fill ctor data: obj_count: 1, arg:537002040
67 i am cout ctor: 1
68 ----------------
69 free_index: 0
70
71 0 0 0 0 0 0 0 0
72 0 0 0 0 0 0 0 0
73 0 0 0 0 0 0 0 0
74 0 0 0 0 0 0 0 0
75 0 0 0 0 0 0 0 0
76 0 0 0 0 0 0 0 0
77 0 0 0 0 0 0 0 0
78 0 0 0 0 0 0 0 0
79 ================
80 fill ctor data: obj_count: 2, arg:537002048
81 i am cout ctor: 2
82 ----------------
83 free_index: 0
84
85 0 0 0 0 0 0 0 0
86 0 0 0 0 0 0 0 0
87 0 0 0 0 0 0 0 0
88 0 0 0 0 0 0 0 0
89 0 0 0 0 0 0 0 0
90 0 0 0 0 0 0 0 0
91 0 0 0 0 0 0 0 0
92 0 0 0 0 0 0 0 0
93 ================
94 fill ctor data: obj_count: 3, arg:537002056
95 vector ctor
96 abc
97 xyz
98 123
99 abcdefghijk!@#$%^&*()
100 test vec iterator
101 abc
102 xyz
103 123
104 abcdefghijk!@#$%^&*()
105 vector dtor
106 vector ctor
107 map ctor
108 (111,99)
109 (abc,51)
110 (abcdefghqq,151)
111 (zxcv,5)
112 map dtor
113 vector dtor
114 i: 0
115 i: 1
116 i: 2
117 i: 3
118 i: 4
119 i: 5
120 i: 6
121 i: 7
122 i: 8
123 i: 9
124 i: 10
125 i: 11
126 i: 12
127 i: 13
128 i: 14
129 i: 15
130 i: 16
131 i: 17
132 i: 18
133 i: 19
134 i: 20
135 i: 21
136 i: 22
137 i: 23
138 i: 24
139 i: 25
140 i: 26
141 i: 27
142 i: 28
143 i: 29
144 i: 30
145 i: 31
146 i: 32
147 i: 33
148 i: 34
149 i: 35
150 i: 36
151 i: 37
152 i: 38
153 i: 39
154 i: 40
155 i: 41
156 i: 42
157 i: 43
158 i: 44
159 i: 45
160 i: 46
161 i: 47
162 i: 48
163 i: 49
164 i: 50
165 i: 51
166 i: 52
167 i: 53
168 i: 54
169 i: 55
170 i: 56
171 i: 57
172 i: 58
173 i: 59
174 i: 60
175 i: 61
176 i: 62
177 i: 63
178 no mem
179 i am cout dtor: 0
180 i am cout dtor: 1
181 i am cout dtor: 2
182 QEMU: Terminated
183 ]0;descent@debian64: /media/work/git/jserv-course/simple_stdcpplib
descent@debian64:simple_stdcpplib$ exit
184
185 Script done on Tue 12 Jan 2016 09:38:55 AM CST
mymain.cpp 是一个 bare-metal 程式, 已经和在 os 下的 c++ 程式没什麽分别了。测试
了 vector, map, exception handle, 我的记忆体分配区块只有 64 块, 所以 i 在分配
到 63 之後, 就没有足够的记忆体了, 会引发 exception 然後跑到 L178 的 no mem, 这
就是不用每次去 new 记忆体时都要检查是不是有正确要到记忆体的秘密, 在一般 pc 的
环境下, 你可能根本没看过 new 要不到记忆体的情形吧!
而也是因为有了 malloc/free 加上 exception handle 才能实作出 new/delete, c++ 的
ctor/dtor 非常的厉害, 你也可以说他很邪恶, new/delete 又插了一些程式码来唤起需
要的 ctor, 反正都要呼叫的, 让 c++ compiler 来帮我们一把不是坏事。
在 new 里头的 malloc 要不到记忆体的时候只要丢出一个 exception 就可以了, 不用繁
复的去检查是不是有要到记忆体, 以前都没想到 new 这样的设计竟然是这麽的精细, 对
於记忆体的配置可以省下好大一番功夫, 重点是, 这并不需要付出很大的代价, 用 c 作
一样的事情, 一样要写这些程式码。
mymain.cpp
1 #include "myiostream.h"
2 #include "myvec.h"
3 #include "mymap.h"
4 #include "mystring.h"
5
6 using namespace DS;
7
8 int mymain()
9 {
10 int i=0;
11
12 {
13 vector<string> vec;
14
15 vec.push_back("abc");
16 vec.push_back("xyz");
17 vec.push_back("123");
18 vec.push_back("abcdefghijk!@#$%^&*()");
19
20 for (i=0 ; i < vec.size() ; ++i)
21 cout << vec[i] << endl;
22 cout << "test vec iterator" << endl;
23 for (auto it=vec.begin() ; it!=vec.end() ; ++it)
24 cout << *it << endl;
25 }
26
27 {
28 map<string, int> str_map;
29
30 str_map.insert({"zxcv", 5});
31 str_map.insert({"abc", 51});
32 str_map.insert({"abcdefghqq", 151});
33 str_map.insert({"111", 99});
34
35 for (auto it=str_map.begin() ; it!=str_map.end() ; ++it)
36 cout << "(" << it->k_ << "," << it->v_ << ")" << endl;
37
38 }
39
40 for (i=0 ; i < 100 ; ++i)
41 {
42 char *c = new char;
43 cout << "i: " << i << endl;
44 }
45 cout << "abc" << endl;
46 }
我没有实作所有的 function, 只有实作我需要用到的部份, 而为了区分这是作业系统等
级的程式码, 我用了 mymain 当 c++ 程式进入点, 而不是用 main; namespace 也不是
std 而是 DS。美中不足的是 initializer_list 我看不懂, 直接抄 newlib 的程式码,
这样在使用 map 的时候才可以用 {}, 不用 value_type 这麽恐怖的语法。
printf 算是比较难的 function, cout 容易多了, 而支援 float 的输出也很有成就感,
慢慢学习基本中的基本, 感觉很紮实, 不沈沦於 it 世界中更新快速的技术, 一步一步慢
慢爬, 不急着囫囵吞枣。
gdeque 很类似 std::deque, 不过我这个是不会自己长大的 deque, 有大小限制的, 因为
我写的时候还没有实作 malloc, 所以只用固定大小, 也没什麽不好, 就没改了。
vector 还算简单, 不过 capacity(), max_size(), size() 这些东西你平常有注意到吗
? iterator 的实作也不算太难, 有了 iterator 真的是好用不少。
list 也算是简单的, iterator 也不算太难, 可以凭自己的想法搞定。
map 是其中最难的部份, 我当然不是以红黑树实作, 而是用很一般的 binary search
tree, tree 这个资料结构本身就不容易, 而其 iterator 的实作实在太难了, 我没有实
作出标准程式库的版本。你认为 map::iterator++ 後要跑到哪一个呢? 标准程式库有定
义, 是 inorder 的那个顺序, 刚好是由小到大的那个次序。网路上有人用了另外的方法
实作, 而不是用 tree, iterator 就简单多了。
比较麻烦的是还要提供 template 的版本, 语法看起来就很吓人, 不熟 template 语法,
正好顺道练习。
string 则不是 template 的版本, 而只是很单纯的 char * 包装而已。
目前用到组合语言的部份有:
rpi2 start.s
setjmp/longjmp (my_setjmp.S)
比想像中的少吧! 能尽量用 c++ 可以完成的我就尽量用 c++, 我并不熟悉组合语言, 还
是别自找苦吃, 而且用 c++ 可携性比较好, 之前 x86 c++ runtime 我用组合语言写, 移
植到 arm 就自己找麻烦了, 现在的版本移植回 x86 就简单多了。
也许在 c 设计为主的 os 不需要这样的介面, 不过长期使用 c++ 的我已经习惯这些用法
, 我想把这些东西带到以 c++ 完成的 os kernel (目前尚未完成, 我才刚建好目录而已)
。
本着对 c++ 的热爱, 写了这个 c++ 标准程式库, 也用了部份 c++11 的语法。有机会的
话应该要移植到 x86 上, 让 simple os 也可以使用。这个 library 只支援 c++, 虽然
有 printf 之类的 c 标准程式库 function, 不过都已经是 c++ 了, 无法用 c
compiler, 我没打算支援 c compiler。
如果 c++ 标准程式库是工业等级的程式码, 那我这个 simple c++ 标准程式库就是玩具
等级的程式码, 虽说只是玩具等级, 但也付出不少功夫才完成的。
能掌握所有的程式码真的很有满足感, 没有黑箱, 了解所有程式的来龙去脉, 真的舒服痛
快。
// 本文使用 Blog2BBS 自动将Blog文章转成缩址的BBS纯文字
http://goo.gl/TZ4E17 //
blog 版本
http://descent-incoming.blogspot.tw/2016/01/for-stm32f4discovery-12-c.html
--
纸上得来终觉浅,绝知此事要躬行。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 118.150.231.195
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1454856674.A.455.html
※ 编辑: descent (118.150.231.195), 02/07/2016 22:51:38
1F:推 cebelas: 推 02/08 00:33
2F:推 LPH66: 推 02/08 01:10
3F:推 fr3ak: 推有心! 02/08 01:49
4F:推 Hurricaneger: 只能跪了 02/08 10:40
5F:推 ggBird: 推 02/08 20:17
6F:推 trylovetom: 有神快拜 02/08 21:29
7F:推 winken2004: 推 02/08 22:29
8F:推 ray2501: 推 02/09 17:42
9F:推 cobrasgo: 如果是只想在stm32f系列上跑c++的话,mbed os也是个选 02/09 20:57
10F:→ cobrasgo: 项 02/09 20:57
11F:→ cobrasgo: 目前已知401/411/426都可以动 02/09 20:58
12F:→ cobrasgo: 407应该只要做部份修改 02/09 20:59
13F:推 damody: 赞赞赞 02/11 07:51
14F:推 yoco: 孝心感动天 qq 02/11 13:04
15F:推 BlazarArc: 推 02/15 11:00