-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
297 lines (237 loc) · 77.3 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[一个有意思的栈溢出crash]]></title>
<url>/2019/01/06/%E4%B8%80%E4%B8%AA%E6%9C%89%E6%84%8F%E6%80%9D%E7%9A%84%E6%A0%88%E6%BA%A2%E5%87%BAcrash/</url>
<content type="html"><![CDATA[<h2 id="问题引出"><a href="#问题引出" class="headerlink" title="问题引出"></a>问题引出</h2><p>最近在复习操作系统相关知识的时候,回忆起之前在某个版本遇到的Android离奇crash,记得当时这个crash在灰度期间造成的影响面不小,占到了整体crash率的10%,虽然crash堆栈能定位到是哪个位置,但是很多Android手机都难以复现,经过不懈努力,发现小米6能稳定复现这个crash,于是我们召集了小组同事到会议室集体应对这个问题,然而当时四、五个工程师对这个问题束手无策,也只能通过代码回滚的方式,去确认是哪一次提交导致的问题,不停的回滚代码,不停的打包,然而提交次数实在太多,导致一直没有定位到问题。</p>
<p>最后我拿着这个手机带回家,我就不信必现的问题还跟不出来,跟到晚上2点左右,突然发现自己这个版本提交的某个代码会作用到这个crash堆栈,那个提交我只是在某个struct中增加了一个变量,这个变量的大小为1kb左右,于是我把这个变量注释掉,果然crash不再出现,于是我不停的调整这个变量的大小,发现调整到256字节以下就不会有问题,大于256字节有可能出现crash,当时我自己也是百思不得其解,不过毕竟crash解决了,我们能正常发版,也就把这个事给忘了。</p>
<p>到最近,我突然发现这个crash可能和线程栈溢出有极大的关系。</p>
<h2 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h2><p>结构体占据的内存变大,就导致crash,内存变小,就不会有问题。沿着这个线索,我发现在某个函数中,定义了一个该结构体的局部变量,而且这个调用栈的深度达到了18层,于是更加确信是该局部变量过大且沿途的调用栈过大,导致变量在压栈的过程中,栈溢出了。</p>
<p>在linux上,可以很容易看到线程栈的默认大小,我的跳板机输出的线程栈大小为10MB</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ulimit -s <span class="comment">//10240=10MB</span></div></pre></td></tr></table></figure>
<p>然而对于移动端,会稍有不同,因为移动端分为UI绘制主线程和子线程,主线程占的比重和资源自然要多些。我查阅了一番资料后,发现对于Android来说,不同的版本可能不一样,但是基本是主线程栈默认为8MB,子线程栈稍微小于1MB;对于IOS来说,这个默认值就更小了,IOS主线程栈默认为1MB,其他线程为512KB。</p>
<p>而我们的So起的线程,并非主线程,因此通常只有1MB左右的栈空间,那么我们怎么验证crash那一次的函数调用栈真的是栈溢出导致的呢,其实很简单,只需要打印两个地址即可:</p>
<ul>
<li>线程执行的第一个函数,函数入参的压栈地址</li>
<li>该线程执行的最深层函数,函数最后声明的局部变量的地址</li>
</ul>
<p>我使用这个方法,打印出两个地址分别为:0x000000016d09eee0和0x000000016cfac3bc,因为栈是向下增长的,用前一个地址减去后一个地址得到差值为994084字节,很显然已经接近1MB,然而我定位的可能还不是最深层的调用,因此这个调用栈,增大某个局部变量的大小,随时都有可能出现栈溢出的风险。</p>
<h2 id="问题总结"><a href="#问题总结" class="headerlink" title="问题总结"></a>问题总结</h2><p>通过分析这个crash的调用栈发现,这18层调用栈中,前几层都出现了较大的局部变量拷贝的问题,达到50kb左右,导致随着调用层次的加深,局部变量和函数参数越来越多,占据的栈空间也越来越大,随时都可能存在栈溢出的风险。</p>
<p>问题定之后,解决方法也比较简单:</p>
<ul>
<li>创建线程时,扩大线程栈的默认大小</li>
<li>C++编程中,将过大的局部变量从栈空间转到堆空间</li>
<li>函数参数传递过程中,对于占用较大内存的变量,避免值拷贝,使用引用或指针</li>
<li>尽量使用循环代替递归</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><pthread.h></span></span></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_attr_getstacksize</span><span class="params">(<span class="keyword">const</span> <span class="keyword">pthread_attr_t</span> *<span class="keyword">restrict</span> attr, <span class="keyword">size_t</span> *<span class="keyword">restrict</span> stacksize)</span></span>;</div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_attr_setstacksize</span><span class="params">(<span class="keyword">pthread_attr_t</span> *attr, <span class="keyword">size_t</span> stacksize)</span></span>;</div></pre></td></tr></table></figure>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>Android线程堆栈大小:<a href="https://zhuanlan.zhihu.com/p/33562383" target="_blank" rel="external">https://zhuanlan.zhihu.com/p/33562383</a></p>
<p>IOS线程堆栈大小:<a href="https://www.jianshu.com/p/51b9139442b5" target="_blank" rel="external">https://www.jianshu.com/p/51b9139442b5</a></p>
<p>IOS栈溢出crash:<a href="http://initlife.com/blog/2015/10/28/iosli-de-zhan-xian-zhi-yin-fa-de-crash/" target="_blank" rel="external">http://initlife.com/blog/2015/10/28/iosli-de-zhan-xian-zhi-yin-fa-de-crash/</a></p>
<p>线程栈大小设置和获取:</p>
<p><a href="http://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_attr_getstacksize.html" target="_blank" rel="external">http://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_attr_getstacksize.html</a></p>
]]></content>
<tags>
<tag> BUG修复记录 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++性能优化之三:内存泄露检测]]></title>
<url>/2018/12/15/C-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E4%B8%89%EF%BC%9A%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E6%A3%80%E6%B5%8B/</url>
<content type="html"></content>
<tags>
<tag> 性能优化 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[我理解的C++虚函数实现机制]]></title>
<url>/2018/12/15/%E6%88%91%E7%90%86%E8%A7%A3%E7%9A%84C-%E8%99%9A%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6/</url>
<content type="html"><![CDATA[<h1 id="我理解的C-虚函数实现机制"><a href="#我理解的C-虚函数实现机制" class="headerlink" title="我理解的C++虚函数实现机制"></a>我理解的C++虚函数实现机制</h1><p>虚函数使用方法很简单,直接在函数名前面添加关键字virtual声明即可,如果虚函数末尾增加=0则表示为纯虚函数,纯虚函数要求所有派生类都必须重写该该函数,带有纯虚函数的类我们也称为虚基类。<br>虚函数的实现,作为一个老生常谈的问题,要想透彻的讲明白,还是需要对底层机制有进一步的理解的。</p>
<h2 id="问题抛出"><a href="#问题抛出" class="headerlink" title="问题抛出"></a>问题抛出</h2><p>基类指针为什么能调用子类的虚函数?<br>虚函数实现的关键原理和虚函数表指针vptr有莫大关系,vptr实际上是指向一个虚函数表(一维数组),该表存储了每个虚函数的函数地址,那么虚函数是如何借助这个vptr实现运行时对象的多态性,也就是我们常说的动态绑定。</p>
<h2 id="从C-对象内存结构说起"><a href="#从C-对象内存结构说起" class="headerlink" title="从C++对象内存结构说起"></a>从C++对象内存结构说起</h2><p>阅读过《深度探索C++对象模型》的同学应该比较熟悉下面这个结构,每一个派生类对象实际上是由两个部分组成,如下面的图所示 </p>
<ul>
<li>父类的部分,包括成员变量、vptr、成员函数等,都是共享给子类(当然有一定的权限设置)</li>
</ul>
<ul>
<li>子类自身的部分,子类自己成员变量,成员函数</li>
</ul>
<p>所以子类就是个特殊的父类,享有父类所有属性,是is-a的关系。所以子类也就能直接强制转换为父类,我们通常使用dynamic_cast将子类指针转为父类指针,那么这个父类指针的访问域也就变为内存模型中的上半部分,无法再访问子类的任何资源,<strong>但是有个例外,那就是虚表指针vptr,为什么呢?</strong></p>
<p><img src="image-mem.png" alt="C++对象内存结构"></p>
<p>我们来进一步看看vptr在类继承过程中到底是怎么变化?<br>通常函数地址都是在编译的时候就确定了,但是虚函数的调用地址需要到运行的时候才能确定,因为你无法确定一个基类的指针到底是执行基类对象还是子类对象。<br>实际上,虚函数表指针是在对象执行构造函数的确定的。对于基类来说,执行基类构造函数时,直接把虚函数表填充为基类的虚函数地址即可;对于派生类来说,派生类对象构造的时候,会先执行父类的构造函数(<strong>把虚函数表全部填充为基类的虚函数地址</strong>),然后再执行子类构造函数(<strong>对于子类重写的虚函数,修改虚函数表中对于的函数地址,将其改为子类的虚函数地址</strong>),具体过程如下图2个步骤所示:</p>
<p><img src="image-gouzao.png" alt="C++派生类构对象构造"></p>
<h2 id="动态绑定"><a href="#动态绑定" class="headerlink" title="动态绑定"></a>动态绑定</h2><p>有了以上基础后,回到之前的问题,动态绑定是怎么发生的?<br>现在回答这个问题很简单了,对于一个指向子类对象的基类指针,它的vptr其实在子类构造过程被改写过,所以使用基指针调用虚函数的时候,如果子类有重写,会调用子类的虚函数,如果没有重写,则直接调用基类的虚函数,这样就实现了运行时对象的多态性。</p>
<h2 id="代码实例"><a href="#代码实例" class="headerlink" title="代码实例"></a>代码实例</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></div><div class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Animal</span></span></div><div class="line"><span class="class">{</span></div><div class="line"><span class="keyword">public</span>:</div><div class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">sleep</span><span class="params">()</span></span></div><div class="line"><span class="function"> </span>{</div><div class="line"> <span class="built_in">cout</span><<<span class="string">"animal sleep"</span><<<span class="built_in">endl</span>;</div><div class="line"> }</div><div class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">breathe</span><span class="params">()</span></span></div><div class="line"><span class="function"> </span>{</div><div class="line"> <span class="built_in">cout</span><<<span class="string">"animal breathe"</span><<<span class="built_in">endl</span>;</div><div class="line"> }</div><div class="line"> <span class="built_in">string</span> name;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fish</span>:</span><span class="keyword">public</span> Animal</div><div class="line">{</div><div class="line"><span class="keyword">public</span>:</div><div class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">breathe</span><span class="params">()</span></span></div><div class="line"><span class="function"> </span>{</div><div class="line"> <span class="built_in">cout</span><<<span class="string">"fish bubble"</span><<<span class="built_in">endl</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">int</span> skin;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></div><div class="line"><span class="function"></span>{</div><div class="line"> Fish fh;</div><div class="line"> Fish *pFish = &fh;</div><div class="line"> Animal* pAnimal = <span class="keyword">dynamic_cast</span><Animal*>(pFish);</div><div class="line"> pAnimal->breathe();<span class="comment">//fish bubble 执行子类重写的虚函数</span></div><div class="line"> pAnimal->sleep(); <span class="comment">//animal sleep 执行基类的虚函数</span></div><div class="line"> pAnimal->name; <span class="comment">//name位于基类域,能访问</span></div><div class="line"><span class="comment">// pAnimal->skin; skin位于子类域,无法访问</span></div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<tags>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++性能优化之二:右值引用]]></title>
<url>/2018/10/15/C++%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E4%BA%8C%EF%BC%9A%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8/</url>
<content type="html"><![CDATA[<h2 id="来龙去脉"><a href="#来龙去脉" class="headerlink" title="来龙去脉"></a>来龙去脉</h2><p>在我项目里,经常会出现这样一段代码:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">define</span> _C_S(x) String(x)</span></div><div class="line">String str = _C_S(<span class="string">"hello world"</span>);</div></pre></td></tr></table></figure>
<p>这个代码的运行机制实际上是这样的:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function">String <span class="title">tmp</span><span class="params">(<span class="string">"hello world"</span>)</span></span>;</div><div class="line">String str = tmp;</div><div class="line">tmp.~String();</div></pre></td></tr></table></figure>
<p>构造函数生成临时的tmp对象(申请内存块A存放”hello world”),然后通过复制构造函数,将tmp内存里的内容复制到str对象(str申请内存块B,接受从内存块A复制过来的字符串),然后tmp对象脱离作用域调用析构函数(第二行代码结束,释放内存块A)。仔细分析下,发现有冗余的内存申请和释放,这里实际上存在两次内存申请,和一次内存释放,那是不是有办法做到,只申请一次内存就完成上述代码。</p>
<p>答案是有的,我们只需要把tmp对象的内存“移动”到str中即可,这就是C++11的右值引用。(由于我们项目C++代码的基础容器都是自己维护的,并没有使用stl,因此会缺失很多新特性,如C++11的右值引用)</p>
<h2 id="左值右值的定义"><a href="#左值右值的定义" class="headerlink" title="左值右值的定义"></a>左值右值的定义</h2><p>首先说明右值引用之前,先解释下C++里对于右值和左值的定义</p>
<blockquote>
<p>当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p>
</blockquote>
<p>概念有点抽象,举几个例子来看看</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">int</span> a = <span class="number">52</span>; <span class="comment">//a是左值</span></div><div class="line"><span class="keyword">int</span> b = a + c; <span class="comment">//b是左值,a+c是左值</span></div><div class="line"><span class="built_in">string</span> c = <span class="built_in">string</span>(<span class="string">"hello"</span>) <span class="comment">//c是左值,string("hello")是右值</span></div></pre></td></tr></table></figure>
<p>从上面代码可以看出,其实左值和右值的根本区别在于能否获取内存地址,左值有自己的变量名和地址,而右值是函数返回的或者运算符计算得出的临时对象,出了作用域就会被析构。</p>
<h2 id="右值引用的应用"><a href="#右值引用的应用" class="headerlink" title="右值引用的应用"></a>右值引用的应用</h2><p>那么引入右值引用的目的是什么呢?很简单,合理规划临时对象的内存使用。</p>
<p>如果没有右值引用,像使用string这种有指针成员变量的临时对象,去给左值做构造或者赋值时,就会存在多余的内存申请和释放,如果该指针指向的内存块很大,那么这种频繁的临时对象内存的申请和释放很容易导致内存碎片和内存尖峰,进而影响性能。</p>
<p>下面代码是以一个简单的字符串String类为例,实现了String的复制构造函数和赋值运算符的右值引用版本,来说明右值引用的作用。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">String</span></span></div><div class="line"><span class="class">{</span></div><div class="line"><span class="keyword">public</span>:</div><div class="line"> <span class="comment">//构造函数</span></div><div class="line"> String();</div><div class="line"> String(<span class="keyword">const</span> <span class="keyword">char</span>* str);</div><div class="line"> </div><div class="line"> <span class="comment">//复制构造函数</span></div><div class="line"> String(<span class="keyword">const</span> String& str);</div><div class="line"> </div><div class="line"> <span class="comment">//复制构造函数-右值引用</span></div><div class="line"> String(String&& str);</div><div class="line"> </div><div class="line"> <span class="comment">//赋值运算符函数</span></div><div class="line"> String& <span class="keyword">operator</span>=(<span class="keyword">const</span> String& str);</div><div class="line"> </div><div class="line"> <span class="comment">//赋值运算符函数-右值引用</span></div><div class="line"> String& <span class="keyword">operator</span>=(String&& str);</div><div class="line"> </div><div class="line"> <span class="comment">//析构函数</span></div><div class="line"> <span class="keyword">virtual</span> ~String();</div><div class="line"> </div><div class="line"> <span class="comment">//字符串反转</span></div><div class="line"> <span class="function">String <span class="title">reverse</span><span class="params">()</span></span>;</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">()</span></span>{<span class="built_in">cout</span> << _pdata <<<span class="built_in">endl</span>;};</div><div class="line"><span class="keyword">private</span>:</div><div class="line"> <span class="keyword">size_t</span> _len;</div><div class="line"> <span class="keyword">char</span>* _pdata;</div><div class="line">};</div><div class="line"></div><div class="line">String::String()</div><div class="line">{</div><div class="line"> _len = <span class="number">0</span>;</div><div class="line"> _pdata = <span class="literal">nullptr</span>;</div><div class="line">}</div><div class="line"></div><div class="line">String::String(<span class="keyword">const</span> <span class="keyword">char</span>* str)</div><div class="line">{</div><div class="line"> _len = <span class="built_in">strlen</span>(str);</div><div class="line"> _pdata = <span class="keyword">new</span> <span class="keyword">char</span>[_len + <span class="number">1</span>];</div><div class="line"> <span class="keyword">if</span> (_pdata != <span class="literal">nullptr</span>)</div><div class="line"> {</div><div class="line"> <span class="built_in">memcpy</span>(_pdata, str, _len);</div><div class="line"> _pdata[_len] = <span class="string">'\0'</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">String::String(<span class="keyword">const</span> String& str)</div><div class="line">{</div><div class="line"> _len = str._len;</div><div class="line"> _pdata = <span class="keyword">new</span> <span class="keyword">char</span>[_len + <span class="number">1</span>];</div><div class="line"> <span class="keyword">if</span> (_pdata != <span class="literal">nullptr</span>)</div><div class="line"> {</div><div class="line"> <span class="built_in">memcpy</span>(_pdata, str._pdata, _len);</div><div class="line"> _pdata[_len] = <span class="string">'\0'</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">String::String(String&& str)</div><div class="line">{</div><div class="line"> _len = str._len;</div><div class="line"> _pdata = str._pdata;</div><div class="line"> str._len = <span class="number">0</span>;</div><div class="line"> str._pdata = <span class="literal">nullptr</span>;</div><div class="line">}</div><div class="line"></div><div class="line">String& String::<span class="keyword">operator</span>=(String&& str)</div><div class="line">{</div><div class="line"> <span class="keyword">if</span> (_pdata)</div><div class="line"> {</div><div class="line"> <span class="keyword">delete</span> [] _pdata;</div><div class="line"> _pdata = <span class="literal">nullptr</span>;</div><div class="line"> _len = <span class="number">0</span>;</div><div class="line"> }</div><div class="line"> _pdata = str._pdata;</div><div class="line"> _len = str._len;</div><div class="line"> str._len = <span class="number">0</span>;</div><div class="line"> str._pdata = <span class="literal">nullptr</span>;</div><div class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</div><div class="line">}</div><div class="line"></div><div class="line">String& String::<span class="keyword">operator</span>=(<span class="keyword">const</span> String& str)</div><div class="line">{</div><div class="line"> <span class="keyword">if</span> (&str != <span class="keyword">this</span>)</div><div class="line"> {</div><div class="line"> <span class="keyword">if</span> (_len > str._len)</div><div class="line"> {</div><div class="line"> <span class="built_in">memset</span>(_pdata, <span class="number">0</span>, _len);</div><div class="line"> _len = <span class="number">0</span>;</div><div class="line"> <span class="built_in">memcpy</span>(_pdata, str._pdata, str._len);</div><div class="line"> _len = str._len;</div><div class="line"> _pdata[_len] = <span class="string">'\0'</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (_len == str._len)</div><div class="line"> {</div><div class="line"> <span class="built_in">memcpy</span>(_pdata, str._pdata, str._len);</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> <span class="keyword">delete</span> [] _pdata;</div><div class="line"> _pdata = <span class="literal">nullptr</span>;</div><div class="line"> </div><div class="line"> _len = str._len;</div><div class="line"> _pdata = <span class="keyword">new</span> <span class="keyword">char</span>[_len + <span class="number">1</span>];</div><div class="line"> <span class="keyword">if</span> (_pdata != <span class="literal">nullptr</span>)</div><div class="line"> {</div><div class="line"> <span class="built_in">memcpy</span>(_pdata, str._pdata, _len);</div><div class="line"> _pdata[_len] = <span class="string">'\0'</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</div><div class="line">}</div><div class="line"></div><div class="line">String::~String()</div><div class="line">{</div><div class="line"> <span class="keyword">if</span> (_pdata != <span class="literal">nullptr</span>)</div><div class="line"> {</div><div class="line"> <span class="keyword">delete</span> [] _pdata;</div><div class="line"> _pdata = <span class="literal">nullptr</span>;</div><div class="line"> _len = <span class="number">0</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">String String::reverse()</div><div class="line">{</div><div class="line"> String ret = _pdata;</div><div class="line"> <span class="keyword">int</span> sidx = <span class="number">0</span>;</div><div class="line"> <span class="keyword">int</span> eidx = (<span class="keyword">int</span>)(ret._len - <span class="number">1</span>);</div><div class="line"> <span class="keyword">while</span> (sidx <= eidx) {</div><div class="line"> <span class="keyword">char</span> tmp = ret._pdata[sidx];</div><div class="line"> ret._pdata[sidx] = ret._pdata[eidx];</div><div class="line"> ret._pdata[eidx] = tmp;</div><div class="line"> sidx++;</div><div class="line"> eidx--;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> ret; <span class="comment">//调用移动函数-右值引用</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></div><div class="line"><span class="function"></span>{</div><div class="line"> String str = String(<span class="string">"098"</span>); <span class="comment">//调用移动函数-右值引用</span></div><div class="line"> <span class="function">String <span class="title">str2</span><span class="params">(<span class="string">"110"</span>)</span></span>;</div><div class="line"> str2 = String(<span class="string">"098"</span>); <span class="comment">//调用移动函数-右值引用</span></div><div class="line"> </div><div class="line"> <span class="comment">//str2Reverse的地址和reverse函数中ret变量的地址是一致的</span></div><div class="line"> String str2Reverse = str2.reverse(); <span class="comment">//调用移动函数-右值引用</span></div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>注意到,右值引用版本的复制构造和赋值运算符函数,将临时对象的内存“移动”到了左值,从而避免了临时对象的内存浪费,提高了运行效率。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>将该特性移植到我们项目工程代码后,内存申请和调用频次大幅减少,虚存和cpu都有小幅下降,其实不仅仅是拷贝构造和赋值运算符存在临时对象,所有其他用到这两个函数的String成员函数都会涉及到该类问题,我们比如字符串截取函数Mid,Left,Right等,都会返回临时的String对象,使用右值引用后,临时对象内存申请释放存在浪费的问题也就得到解决。C++11中有很多好的特性,但是使用起来也会有点门槛,还是推荐在项目实践的过程中,慢慢学习和理解这些特性。 </p>
]]></content>
<tags>
<tag> 性能优化 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++性能优化之一:合理使用内存]]></title>
<url>/2018/09/24/C++%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E4%B8%80%EF%BC%9A%E5%90%88%E7%90%86%E4%BD%BF%E7%94%A8%E5%86%85%E5%AD%98/</url>
<content type="html"><![CDATA[<p>要想在编码过程中,写出高效的代码,是需要自己长期的总结和不断学习的。工作以来,我自己也总结了一些小技巧,可以让你的程序运行的更快、内存空间使用更合理,同时我还会不断地补充该blog,争取建立出一个属于自己的c++ effective系列。</p>
<p>不多说,直接进入正题,以下都是我再编程过程中,总结出来c++高效编码规则,每个topic对应一个规则。</p>
<h2 id="局部变量合理使用"><a href="#局部变量合理使用" class="headerlink" title="局部变量合理使用"></a>局部变量合理使用</h2><p>让我们先看一段代码:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">1000</span>; ++i)</div><div class="line">{</div><div class="line"> <span class="built_in">string</span> str = <span class="string">"do some thing:"</span> + int2str(i);</div><div class="line"> func(str);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这段代码,在循环中使用局部变量拼装函数func的入参,在每次循环过程中,str对象都会执行一次构造函数和析构函数,那么,在这个for循环中,单单是str的组装就耗费了1000次的内存申请和释放,局部变量占用内存小的话,影响不会很大,如果动辄几十、几百kb,那就会造成系统内存使用的波动,那么是不是有更高效的方法?</p>
<p>其实只需要把str变量放到for循环外部声明即可,如下面代码:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">string</span> str;</div><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">1000</span>; ++i)</div><div class="line">{</div><div class="line"> str = <span class="string">"do some thing:"</span> + int2str(i);</div><div class="line"> func(str);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这段代码会大大降低内存的申请和释放次数,因为首次循环后,str会申请15个字节的内存空间来容纳现有数据,第二次循环时,在赋值运算符函数中,由于str当前空间已经足够容纳第二次循环的数据,因此我们可以考虑对原有str内存进行复用,所以只存在一次数据拷贝,不存在新的内存申请和释放;到第十次循环时,需要16个字节才能容纳现有数据,因此需要释放str原有内存,申请新的内存。以此类推,我们可以算出1000次循环过程中,只有三次内存申请和释放,大大降低了内存的申请和释放次数。</p>
<p>小结:在循环体中,局部变量如果占用内存空间较大,会造成内存使用不合理,可以考虑放到循环体外声明</p>
<h2 id="左值引用的合理使用"><a href="#左值引用的合理使用" class="headerlink" title="左值引用的合理使用"></a>左值引用的合理使用</h2><p>左值引用提升程序性能的应用场景。</p>
<p>首先是函数入参,看下面两个函数的声明,func1会存在一次str副本的拷贝构造的过程,且退出函数体,还需要释放str,而func2直接将str的地址传入函数体内部,不存在拷贝构造,如果str内存很大,那么节约一次拷贝的收益还是很可观的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">(<span class="keyword">const</span> <span class="built_in">string</span> str)</span></span>; <span class="comment">//存在冗余拷贝构造和析构</span></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">(<span class="keyword">const</span> <span class="built_in">string</span>& str)</span></span>; <span class="comment">//直接传递str变量的地址</span></div></pre></td></tr></table></figure>
<p>其次是循环体中,获取数组元素时,如果我们不需要修改原始值,那么应该是使用常引用直接指向数组元素的地址,避免局部变量的冗余的拷贝构造和析构</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < arrstrs.size(); ++i)</div><div class="line">{</div><div class="line"> <span class="built_in">string</span> str = arrstrs[i]; <span class="comment">//存在冗余拷贝构造和析构</span></div><div class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& str = arrstrs[i]; <span class="comment">//直接使用arrstrs[i]变量的地址</span></div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="动态数组容量提前设定"><a href="#动态数组容量提前设定" class="headerlink" title="动态数组容量提前设定"></a>动态数组容量提前设定</h2><p>分层架构的代码中,经常出现需要对不同层次数据规格进行转换,即把其他层次的数据转化为所在层的数据格式,以下是项目中经常看见的一段代码,主要目的是把第二层的数据转化到第一层坐标数据中,代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//变量格式声明</span></div><div class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">FirstLayer_PosData_t</span></span></div><div class="line"><span class="class">{</span></div><div class="line"> <span class="keyword">double</span> x;</div><div class="line"> <span class="keyword">double</span> y;</div><div class="line">}FirstLayer_PosData_t;</div><div class="line"></div><div class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">SecondLayer_PosData_t</span></span></div><div class="line"><span class="class">{</span></div><div class="line"> <span class="keyword">double</span> x;</div><div class="line"> <span class="keyword">double</span> y;</div><div class="line"> <span class="keyword">int</span> tag;</div><div class="line">}SecondLayer_PosData_t;</div><div class="line"></div><div class="line"><span class="built_in">vector</span><FirstLayer_PosData_t> arrfir;</div><div class="line"><span class="built_in">vector</span><SecondLayer_PosData_t> arrsec;</div><div class="line"></div><div class="line"><span class="comment">//层数据转化代码</span></div><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < arrsec.size(); ++i)</div><div class="line">{</div><div class="line"> FirstLayer_PosData_t stFirstLayerPos;</div><div class="line"> stFirstLayerPos.x = arrsec[i].x;</div><div class="line"> stFirstLayerPos.y = arrsec[i].y;</div><div class="line"> arrfir.push_back(stFirstLayerPos);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这段代码可以这样改进,其实我们要拷贝的元素个数是已知的,因此我们可以直接将arrfirst数组大小设置为arrsecond的大小即可,这就避免了在循环体中动态的去扩容(每次扩容的成本是先申请新的内存空间,将旧内存空间数据拷贝到新内存空间,然后释放旧内存空间),改进代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">arrfir.setsize(arrsec.size());</div><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < arrfir.size(); ++i)</div><div class="line">{</div><div class="line"> FirstLayer_PosData_t stFirstLayerPos;</div><div class="line"> stFirstLayerPos.x = arrsec[i].x;</div><div class="line"> stFirstLayerPos.y = arrsec[i].y;</div><div class="line"> arrfir[i] = stFirstLayerPos;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>仔细观察下,其实还有优化空间,局部变量是可以避免的,直接使用引用代替第一层数组的每个元素即可,最终优化代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">arrfir.setsize(arrsecond.size());</div><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < arrfirst.size(); ++i)</div><div class="line">{</div><div class="line"> FirstLayer_PosData_t& stFirstLayerPos = arrfir[i];</div><div class="line"> stFirstLayerPos.x = arrsec[i].x;</div><div class="line"> stFirstLayerPos.y = arrsec[i].y;</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="move语义的合理使用"><a href="#move语义的合理使用" class="headerlink" title="move语义的合理使用"></a>move语义的合理使用</h2>]]></content>
<tags>
<tag> 性能优化 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[一个有趣的死循环问题]]></title>
<url>/2018/09/16/%E4%B8%80%E4%B8%AA%E6%9C%89%E8%B6%A3%E7%9A%84%E6%AD%BB%E5%BE%AA%E7%8E%AF%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<h2 id="问题引出"><a href="#问题引出" class="headerlink" title="问题引出"></a>问题引出</h2><p>最近在工作过程中,遇到一个很有意思的bug,问题出现概率很小,很难复现,但是特别严重,直接导致用户无法使用App,属于不可容忍的问题,因此必须解决。问题出现在Android-App跑压测的过程中,我们提供的第三方库会突然停止工作,但是App的UI还是能正常的运转,于是我以我的职业敏感性当即做出判断(害羞 <逃),导致这个bug是因为我们提供的动态库中的消息线程被堵塞了,从而导致整个库无法工作。</p>
<p>通常情况下,线程被堵塞住,有下面几个原因:</p>
<ul>
<li>执行耗时任务(如网络IO或者文件IO等,导致整个线程卡死)</li>
<li>死锁(消息线程拿着A锁,等待B锁,数据处理线程拿着B锁,等待A锁,相互等待,导致卡死)</li>
<li>死循环(程序卡在一个循环,CPU狂转,无法跳出)</li>
</ul>
<h2 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h2><p>首先第一个思路,就是在App出现卡主问题后,导出所有线程的调用堆栈,查看我们自己的库线程,看哪个线程哪个函数调用存在问题,取线程堆栈,通常有两个方法:</p>
<ul>
<li><p>在Android端,程序ANR后,系统会保留该进程的所有线程堆栈的traces文件。但是有个前提,只有主线程(UI线程)卡主的时候,系统才会生成这个文件,其他子线程或者库线程卡主是不会生成traces文件的。</p>
</li>
<li><p>出现问题后,让App强制crash,生成crash线程堆栈。我们尝试让UI线程强制crash后,却只生成了UI线程的调用栈,没有我们库线程的信息。</p>
</li>
</ul>
<p>抓调用栈的思路否定之后,我们决定先进一步缩小问题范围,出现卡主问题后,我们对Android手机执行了如下命令,找出我们App所有线程的运行状况。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">adb shell top -m 20 -t</div></pre></td></tr></table></figure>
<p>巧合地发现每次采样数据中,CPU使用率最高都是我们App进程中的同一个线程,正是我们动态库的消息线程,而该线程的使用率竟然比图像绘制线程占比还要高了几倍,很明显的异常,因此几乎可以断定是该线程导致的卡死问题,同时也可以排除死锁的可能性,因为死锁会让线程wait住,不会过多的占用CPU,同时由于我们的AppIO操作很少,也就排除了文件IO和网络IO,基本可以确认是死循环导致。</p>
<p>那么这个线程到底是在哪个位置卡死呢?这时候最笨的方法,效果最明显,【二分加Log法】,加log的位置以代码行数进行二分切隔,如果第N行日志没有输出,那么肯定说明问题代码在第N-1~N行之间,于是乎最终定位到了这一段神奇的死循环代码问题。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">TransAngleTo360</span><span class="params">(<span class="keyword">double</span>& dAngle)</span></span></div><div class="line"><span class="function"></span>{</div><div class="line"> <span class="keyword">while</span> (dAngle < <span class="number">0</span>)</div><div class="line"> dAngle += <span class="number">360</span>;</div><div class="line"> </div><div class="line"> <span class="keyword">while</span> (dAngle > <span class="number">360</span>)</div><div class="line"> dAngle -= <span class="number">360</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这段代码,通过两个while循环将输入角度归化到0~360度之间,问题就出在这个输入值这里。通过复现的日志发现,代码走到了异常分支,对某个静态数组 double arrAngele[8],执行了取下标-1的操作,arrAngele[-1],产生了一个未定义的极大值,我们都知道8字节的double的取值范围是:</p>
<blockquote>
<p>负值取值范围为 -1.79769313486231570E+308 到 -4.94065645841246544E-324;<br>正值取值范围为 4.94065645841246544E-324 到 1.79769313486231570E+308。</p>
</blockquote>
<p>如果将1.79e+308这个数传给TransAngleTo360函数,那么函数需要执行5e+305次循环才能结束,假设我们的计算机每秒能执行10亿次机器指令,那执行完这个循环的时间,大约是1e+207秒。。。。</p>
<h2 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h2><p>对于函数输入参数,进行超大或超小值的范围控制,很明显这个方法只是暂时的补丁,且是在你知道入参大小的前提下,做的限制,如果不确定入参范围,很可能会出问题。</p>
<p>那么更好的做法其实是这样的,先看整数部分有多少个360,记为cnt,然后用原有角度减去cnt*360即可,为负数的时候对应处理下就行。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">TransAngleTo360</span><span class="params">(<span class="keyword">double</span>& dAngle)</span></span></div><div class="line"><span class="function"></span>{</div><div class="line"> <span class="keyword">int</span> cnt = dAngle/<span class="number">360</span>;</div><div class="line"> <span class="keyword">if</span> (time >= <span class="number">0</span>)</div><div class="line"> dAngle = dAngle - cnt*<span class="number">360</span>;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> dAngle = dAngle - (cnt - <span class="number">1</span>)*<span class="number">360</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<tags>
<tag> BUG修复记录 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[配置python-protobuf解析环境]]></title>
<url>/2018/01/27/%E9%85%8D%E7%BD%AEpython-protobuf%E8%A7%A3%E6%9E%90%E7%8E%AF%E5%A2%83/</url>
<content type="html"><![CDATA[<p>protobuf是一种跨语言协议,不同语言之间只需定义同一份proto文件,即可实现不同种类的语言的协议沟通。由于工作中使用c++解码较为麻烦,为了提升工作效率,想通过python达到快速解析pb数据的目的(系统为macOS Sierra)。</p>
<p><img src="introduce.jpg" alt="introduce"></p>
<p>出自文章(<a href="https://segmentfault.com/a/1190000010098194)" target="_blank" rel="external">https://segmentfault.com/a/1190000010098194)</a></p>
<h3 id="python版本的protobuf安装"><a href="#python版本的protobuf安装" class="headerlink" title="python版本的protobuf安装"></a>python版本的protobuf安装</h3><p>首先安装官方protobuf,通过执行protoc命令,可以将proto源文件编译成对应语言的数据结构文件和解析代码,如python对应的文件后缀名通常为.py和.pyc;安装官方,<a href="https://github.com/google/protobuf/releases,下载全量源码,然后解压、安装,然后确认安装是否成功。(此外可以通过" target="_blank" rel="external">https://github.com/google/protobuf/releases,下载全量源码,然后解压、安装,然后确认安装是否成功。(此外可以通过</a> brew install protobuf 直接安装)</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">cd 对应目录</div><div class="line">./configure </div><div class="line">make </div><div class="line">make check </div><div class="line">make install</div><div class="line">protoc --version</div></pre></td></tr></table></figure>
<p>其次,安装对应的python protobuf模块,python引入该模块即可编写代码,实现pb文件的解析。安装python protobuf模块,首先安装pip,mac系统的python自带easy_install,因此直接输入命令:sudo easy_install pip;再通过pip来安装protobuf,命令:pip install protobuf;</p>
<p>遇到的问题:mac下python的six模块版本较低,且由于系统原因,无法被卸载安装新版,而pip在安装protobuf时需要,默认会下载安装较新版本的six模块,因此这里出现了冲突。通过参考该链接,<a href="https://github.com/pypa/pip/issues/3165,只需在使用pip安装python模块前,执行该命令即可:" target="_blank" rel="external">https://github.com/pypa/pip/issues/3165,只需在使用pip安装python模块前,执行该命令即可:</a></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pip install --ignore-installed six</div></pre></td></tr></table></figure>
<h3 id="使用python解析pb数据流程"><a href="#使用python解析pb数据流程" class="headerlink" title="使用python解析pb数据流程"></a>使用python解析pb数据流程</h3><p>这里使用c++对数据进行编码,并生成pb二进制文件,再通过python对pb二进制文件解码,得到c++输入的数据。使用c++写的client程序和python写的server服务器程序通信,在cleint端对数据编码,在server端对数据解码,通信方式采用socket,通信数据格式采用protobuf。</p>
<p><font color="red">遇到的问题:</font>当c++客户端-c++服务器,数据能正常编码和解析,而c++客户端-python服务器时,服务端python的recv函数接收的数据是null,无法正常接收。</p>
<p><font color="red">解决方案:</font>怀疑是python接收二进制数据问题,具体原因后续探究。因此把c++编码的protobuf数据再进行base64编码,将二进制proto数据变为字符串,当python服务端收到数据后,先进行base64解码,再对protobuf数据解码。</p>
<p>people.proto源文件:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">package demo;</div><div class="line">message People { </div><div class="line"> required string name = 1; </div><div class="line"> required int32 id = 2; </div><div class="line"> required string email = 3; </div><div class="line">}</div></pre></td></tr></table></figure>
<p>切换到对应的proto源文件目录,执行命令,将产出两个文件people.pb.cc 和people.pb.h</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">protoc -I . --c++_out=. people.proto</div></pre></td></tr></table></figure>
<p>切换到对应的proto源文件目录,执行以下命令,将产出文件people_pb2.pyc</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">protoc -I . --python_out=. people.proto</div></pre></td></tr></table></figure>
<p>c++客户端代码:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><strings.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/types.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/socket.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><netinet/in.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><arpa/inet.h></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"people.pb.h"</span><span class="comment">//引入c++对应的proto文件</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"base64.h"</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> MYPORT 8080</span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> BUFFER_SIZE 1024</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></div><div class="line"><span class="function"></span>{</div><div class="line"> <span class="comment">///定义sockfd</span></div><div class="line"> <span class="keyword">int</span> sock_cli = socket(AF_INET,SOCK_STREAM, <span class="number">0</span>);</div><div class="line"> </div><div class="line"> <span class="comment">///定义sockaddr_in</span></div><div class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></div><div class="line"> <span class="built_in">memset</span>(&servaddr, <span class="number">0</span>, <span class="keyword">sizeof</span>(servaddr));</div><div class="line"> servaddr.sin_family = AF_INET;</div><div class="line"> servaddr.sin_port = htons(MYPORT); <span class="comment">///服务器端口</span></div><div class="line"> servaddr.sin_addr.s_addr = inet_addr(<span class="string">"127.0.0.1"</span>); <span class="comment">///服务器ip</span></div><div class="line"> </div><div class="line"> <span class="comment">///连接服务器,成功返回0,错误返回-1</span></div><div class="line"> <span class="keyword">if</span> (connect(sock_cli, (struct sockaddr *)&servaddr, <span class="keyword">sizeof</span>(servaddr)) < <span class="number">0</span>)</div><div class="line"> {</div><div class="line"> perror(<span class="string">"connect"</span>);</div><div class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="keyword">char</span> recvbuf[BUFFER_SIZE];</div><div class="line"> <span class="keyword">char</span> sendbuf[BUFFER_SIZE];</div><div class="line"> <span class="keyword">int</span> numbytes = (<span class="keyword">int</span>)recv(sock_cli, recvbuf, BUFFER_SIZE, <span class="number">0</span>);</div><div class="line"> recvbuf[numbytes] = <span class="string">'\0'</span>;</div><div class="line"> <span class="built_in">std</span>::<span class="built_in">string</span> strbuf = recvbuf;</div><div class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"Client Message: "</span> << strbuf << <span class="built_in">std</span>::<span class="built_in">endl</span>;</div><div class="line"> <span class="keyword">if</span>(strbuf == <span class="string">"GET PEOPLE"</span>)</div><div class="line"> {</div><div class="line"> <span class="built_in">std</span>::<span class="built_in">string</span> data;</div><div class="line"> demo::People p;</div><div class="line"> p.set_name(<span class="string">"xionghengheng"</span>);</div><div class="line"> p.set_id(<span class="number">1881409</span>);</div><div class="line"> p.set_email(<span class="string">"[email protected]"</span>);</div><div class="line"> p.SerializeToString(&data);<span class="comment">//protobuf数据编码</span></div><div class="line"> <span class="keyword">char</span> bts[data.length()];</div><div class="line"> <span class="built_in">strcpy</span>(bts, data.c_str());</div><div class="line"> Base64 *base64 = <span class="keyword">new</span> Base64();</div><div class="line"> <span class="built_in">std</span>::<span class="built_in">string</span> encode = base64->Encode((<span class="keyword">unsigned</span> <span class="keyword">char</span>*)bts, data.length());<span class="comment">//base64编码</span></div><div class="line"> <span class="built_in">memset</span>(sendbuf, <span class="number">0</span>, <span class="keyword">sizeof</span>(sendbuf));</div><div class="line"> <span class="built_in">strcpy</span>(sendbuf, encode.c_str());</div><div class="line"> send(sock_cli, sendbuf, <span class="built_in">strlen</span>(sendbuf), <span class="number">0</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> send(sock_cli, <span class="string">"Fucking client!\n"</span>, <span class="number">16</span>, <span class="number">0</span>);</div><div class="line"> }</div><div class="line"> <span class="built_in">memset</span>(recvbuf, <span class="number">0</span>, <span class="keyword">sizeof</span>(recvbuf));</div><div class="line"> close(sock_cli);</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>python2.7服务端程序:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># -*- coding: UTF-8 -*-</span></div><div class="line"><span class="keyword">import</span> os</div><div class="line"><span class="keyword">import</span> socket</div><div class="line"><span class="keyword">import</span> time</div><div class="line"><span class="keyword">import</span> threading</div><div class="line"><span class="keyword">import</span> people_pb2 <span class="keyword">as</span> people<span class="comment">#引入python proto对应的文件</span></div><div class="line"><span class="keyword">import</span> base64</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">tcplink</span><span class="params">(sock, addr)</span>:</span></div><div class="line"> print(<span class="string">'Accept new connection from %s:%s...'</span> % addr)</div><div class="line"> sock.send(<span class="string">'GET PEOPLE'</span>.encode())</div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> data = sock.recv(<span class="number">2048</span>)</div><div class="line"> time.sleep(<span class="number">1</span>)</div><div class="line"> <span class="keyword">if</span> data == <span class="string">'exit'</span> <span class="keyword">or</span> <span class="keyword">not</span> data:</div><div class="line"> print(<span class="string">"no data"</span>)</div><div class="line"> <span class="keyword">break</span></div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> data = base64.b64decode(data)<span class="comment">#base64解码</span></div><div class="line"> peopleItem = people.People()</div><div class="line"> peopleItem.ParseFromString(data)<span class="comment">#protobuf数据解码</span></div><div class="line"> print(peopleItem.name)</div><div class="line"> print(peopleItem.id)</div><div class="line"> print(peopleItem.email)</div><div class="line"> sock.close()</div><div class="line"> print(<span class="string">'Connection from %s:%s closed.'</span> % addr)</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment"># 开启ip和端口</span></div><div class="line">ip_port = (<span class="string">'127.0.0.1'</span>, <span class="number">8080</span>)</div><div class="line"><span class="comment"># 生成句柄</span></div><div class="line">web = socket.socket()</div><div class="line"><span class="comment"># 绑定端口</span></div><div class="line">web.bind(ip_port)</div><div class="line"><span class="comment"># 最多连接数</span></div><div class="line">web.listen(<span class="number">5</span>)</div><div class="line"><span class="comment"># 等待信息</span></div><div class="line"><span class="keyword">print</span> (<span class="string">'nginx waiting...'</span>)</div><div class="line"><span class="comment"># 开启死循环</span></div><div class="line"><span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> <span class="comment"># 接受一个新连接:</span></div><div class="line"> sock,addr = web.accept()</div><div class="line"> <span class="comment"># 创建新线程来处理TCP连接:</span></div><div class="line"> t = threading.Thread(target=tcplink, args=(sock, addr))</div><div class="line"> t.start()</div></pre></td></tr></table></figure>
<p>服务端输出结果:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">nginx waiting...</div><div class="line">Accept new connection from 127.0.0.1:50828...</div><div class="line">xionghengheng</div><div class="line">1881409</div><div class="line">[email protected]</div><div class="line">no data</div><div class="line">Connection from 127.0.0.1:50828 closed.</div></pre></td></tr></table></figure>
]]></content>
<tags>
<tag> 环境配置 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[IOS常见语法解惑]]></title>
<url>/2017/09/25/IOS%E5%B8%B8%E8%A7%81%E8%AF%AD%E6%B3%95%E8%A7%A3%E6%83%91/</url>
<content type="html"><![CDATA[<p>由于工作过程中经常需要查看IOS的Objective-C代码,遂把一些常见的、有疑问的OC语法列出,方便之后会看,提升效率。</p>
<h3 id="Objective-C中的-语法"><a href="#Objective-C中的-语法" class="headerlink" title="Objective-C中的@语法"></a>Objective-C中的@语法</h3><p>@interface告诉编译器,我要声明一个新类,包含了类的属性和方法,以@end结尾;</p>
<p>@implementation告诉编译器,这是某个类的具体实现,以@end结尾;</p>
<p>@property是声明属性的语法,它可以快速方便的为类的成员变量创建存取器,并允许我们通过点语法使用存取器,<strong>@property等同于在.h文件中声明实例变量的get/set方法,@synthesize等同于在.m文件中实现实例变量的get/set方法</strong>。需要注意的是@property可以直接给成员变量赋特性,如nonatomic表示非原子的,assign表示值类型。</p>
<p>声明通常放在类的头文件中,代码示例如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">@interface BatteryInfo: NSObject</div><div class="line">{</div><div class="line">NSInteger _batteryLevel;</div><div class="line">CGFloat _current;</div><div class="line">}</div><div class="line">- (void)showBatteryInfo;</div><div class="line">@property (nonatomic, assign) NSInteger batteryLevel;</div><div class="line">@property (nonatomic, assign) CGFloat current;</div><div class="line">@end</div></pre></td></tr></table></figure>
<p>实现通常放在类的.m文件中,代码示例如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">@implementation BatteryInfo</div><div class="line"></div><div class="line">- (void)showBatteryInfo</div><div class="line">{</div><div class="line">//TODO</div><div class="line">}</div><div class="line">@synthesize batteryLevel = _batteryLevel;</div><div class="line">@synthesize current = _current;</div><div class="line"></div><div class="line">@end</div></pre></td></tr></table></figure>
<h3 id="Objective-C中的函数调用语法"><a href="#Objective-C中的函数调用语法" class="headerlink" title="Objective-C中的函数调用语法"></a>Objective-C中的函数调用语法</h3>]]></content>
</entry>
<entry>
<title><![CDATA[开端]]></title>
<url>/2017/09/24/%E5%BC%80%E7%AB%AF/</url>
<content type="html"><![CDATA[<p>善于总结,勤于思考,每个月总结本月所得、所学。今天花了3个小时,搭建了一个基于hexo+github+markdown的blog平台,本意是希望和志同道合的技术好友一起学习,一起进步。</p>
<h3 id="起步"><a href="#起步" class="headerlink" title="起步"></a>起步</h3><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p>blog搭建流程:<a href="http://www.jianshu.com/p/13e64c9e2295" target="_blank" rel="external">http://www.jianshu.com/p/13e64c9e2295</a></p>
<p>Next主题模板使用:<a href="http://theme-next.iissnan.com" target="_blank" rel="external">http://theme-next.iissnan.com</a></p>
<p>markdown基本语法:<a href="http://www.jianshu.com/p/1e402922ee32" target="_blank" rel="external">http://www.jianshu.com/p/1e402922ee32</a></p>
<p>遇到的问题:<a href="https://github.com/hexojs/hexo/issues/961" target="_blank" rel="external">https://github.com/hexojs/hexo/issues/961</a></p>
<p>部署好的文章无法及时更新到github page,原因是因为同步到旧文件造成,所以先清空public下的文件,然后再部署即可(hexo clean;hexo g;hexo deploy;)。ps :目前依然存在该问题,不知道是不是github page服务存在问题。</p>
]]></content>
<tags>
<tag> 环境配置 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[分类]]></title>
<url>/categories/index.html</url>
<content type="html"></content>
</entry>
<entry>
<title><![CDATA[简介]]></title>
<url>/about/index.html</url>
<content type="html"><![CDATA[<h5 id="语言擅长"><a href="#语言擅长" class="headerlink" title="语言擅长"></a>语言擅长</h5><p>1.c++(熟练)</p>
<p>2.python(日常脚本编写)</p>
<p>3.java(做过javaEE后台开发)</p>
<h5 id="技术领域"><a href="#技术领域" class="headerlink" title="技术领域"></a>技术领域</h5><p>移动c++引擎开发</p>
<h5 id="感兴趣的源码阅读"><a href="#感兴趣的源码阅读" class="headerlink" title="感兴趣的源码阅读"></a>感兴趣的源码阅读</h5><p>1.redis(阅读中) <a href="https://github.com/antirez/redis" target="_blank" rel="external">https://github.com/antirez/redis</a></p>
]]></content>
</entry>
<entry>
<title><![CDATA[标签]]></title>
<url>/tags/index.html</url>
<content type="html"></content>
</entry>
</search>