-
Notifications
You must be signed in to change notification settings - Fork 8
/
feed.xml
2216 lines (2216 loc) · 106 KB
/
feed.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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>时光小栈 on 时光小栈</title><link>/</link><language>zh-CN</language><author>rinetd</author><rights>Copyright (c) 2015, rinetd; all rights reserved.</rights><updated>Tue, 24 Nov 2020 15:03:00 CST</updated><item><title>linux kernel timer</title><link>/hardware/kernel/linux-kernel-timer/</link><pubDate>Tue, 24 Nov 2020 15:03:00 CST</pubDate><author>rinetd</author><guid>/hardware/kernel/linux-kernel-timer/</guid><description><p>linux驱动之定时器的使用<br />
被文章摘自一下几位网友。非常感谢他们。</p>
<p><a href="http://blog.sina.com.cn/s/blog_57330c3401011cq3.html" target="_blank">http://blog.sina.com.cn/s/blog_57330c3401011cq3.html</a></p>
<p>Linux的内核中定义了一个定时器的结构:</p>
<p>#include<linux/timer.h></p>
<p>struct timer_list</p>
<p>{</p>
<pre><code> struct list_head list;
</code></pre>
<p> unsigned long expires; //定时器到期时间</p>
<p> unsigned long data; //作为参数被传入定时器处理函数</p>
<p> void (*function)(unsigned long);</p>
<p>};</p>
<p>利用这个结构我们可以在驱动中很方便的使用定时器。</p>
<p>一: timer的API函数:</p>
<p>初始化定时器:</p>
<p>void init_timer(struct timer_list * timer);</p>
<p>增加定时器:</p>
<p>void add_timer(struct timer_list * timer);</p>
<p>删除定时器:</p>
<p>int del_timer(struct timer_list * timer);</p>
<p>修改定时器的expire:</p>
<p>int mod_timer(struct timer_list *timer, unsigned long expires);</p>
<p>二:使用定时器的一般流程为:</p>
<p>(1)创建timer、编写超时定时器处理函数function;</p>
<p>(2)为timer的expires、data、function赋值;</p>
<p>(3)调用add_timer将timer加入列表----添加一个定时器;</p>
<p>(4)在定时器到期时,function被执行;</p>
<p>(5)在程序中涉及timer控制的地方适当地调用del_timer、mod_timer删除timer或修改timer的expires。</p>
<p>三:下面看一个例子:</p>
<pre><code class="language-cpp">#include &lt;linux/module.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/sched.h&gt;//jiffies在此头文件中定义
#include &lt;linux/init.h&gt;
#include &lt;linux/timer.h&gt;
struct timer_list mytimer;//定义一个定时器
void mytimer_ok(unsigned long arg)
{
printk(&quot;Mytimer is ok\n&quot;);
printk(&quot;receive data from timer: %d\n&quot;,arg);
}
static int __init hello_init (void)
{
printk(&quot;hello,world\n&quot;);
init_timer(&amp;mytimer); //初始化定时器
mytimer.expires = jiffies+100;//设定超时时间,100代表1秒
mytimer.data = 250; //传递给定时器超时函数的值
mytimer.function = mytimer_ok;//设置定时器超时函数
add_timer(&amp;mytimer); //添加定时器,定时器开始生效
return 0;
}
static void __exit hello_exit (void)
{
del_timer(&amp;mytimer);//卸载模块时,删除定时器
printk(&quot;Hello module exit\n&quot;);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR(&quot;CXF&quot;);
MODULE_LICENSE(&quot;Dual BSD/GPL&quot;);
</code></pre>
<p>四:交叉编译后,放到开发板上:</p>
<p>#insmod timer.ko</p>
<p>可以发现过一秒后定时器过期函数被执行了,打印出了信息,250也被正确传递了。</p>
<p>#rmmod timer.ko</p>
<p>我们也可以用lsmod | grep timer 来查看是否加载了timer驱动。</p>
<p>可以用dmesg | tail -20 查看驱动打印的信息</p>
<p>dmesg -c 清楚信息</p>
<p>五:进一步理解定时器:</p>
<p>在上面的定时器超时函数mytimer_ok(unsigned long arg)中,添加如下代码:</p>
<p>mytimer.expires = jiffies+100;//设定超时时间,100代表1秒<br />
mytimer.function = mytimer_ok;//设置定时器超时函数</p>
<p>add_timer(&amp;mytimer); //添加定时器,定时器开始生效</p>
<p>交叉编译后,放到开发板上</p>
<p>#insmod timer.o</p>
<p>发现每隔一秒,mytimer_ok函数就执行一次,这是因为每次定时器到期后,都</p>
<p>又重新给它设置了一个新的超时时间,并且新的超时函数指向自己,形成一个递</p>
<p>归,所以就会一直执行下去。</p>
<p>#rmmod timer</p>
<p>可以卸载模块,当然打印也就结束了,注意因为定时器超时函数不停的打印信息</p>
<p>,导致输入上面的命令时会被定时器超时函数不停的打印信息淹没,不用管他,</p>
<p>耐心的把上面的命令输完就可以成功卸载。</p></description></item><item><title>libev_ev_async</title><link>/language/clang/libev/libev_ev_async/</link><pubDate>Sat, 31 Oct 2020 21:52:25 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev_ev_async/</guid><description><pre><code>// 在其它线程发通知事件给EV LOOP主线程
ev_async async_w;
</code></pre>
<pre><code class="language-cpp">#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;pthread.h&gt;
#include &lt;ev.h&gt;
struct ev_loop *loop = NULL;
static ev_async async_watcher;
static void async_cb(EV_P_ ev_async *w, int revents)
{
static int cb_count = 0;
printf(&quot;async_cb() call, cb_count = %d %d\n&quot;, cb_count++,revents);
}
void *ev_create(void *p)
{
printf(&quot;ev_create() call, start!\n&quot;);
loop = ev_loop_new(EVFLAG_AUTO);
ev_async_init(&amp;async_watcher, async_cb);
ev_async_start(loop, &amp;async_watcher);
ev_run(loop, 0); // 如果不在回调中调用stop或者break方法,ev_run后面的代码就永远不会被执行。
printf(&quot;ev_create() call, ev_run end!\n&quot;);
}
int main()
{
pthread_t tid;
pthread_create(&amp;tid, NULL, ev_create, NULL);
sleep(1); // 要等子线程先注册完事件之后,才可以调用下面的ev_async_send函数,否则你懂的。
while(1)
{
static int num = 0;
printf(&quot;num = %d\n&quot;, num);
ev_async_send(loop, &amp;async_watcher); // 既然可以在这里ev_async_send,那么也可以在其他需要的地方ev_async_send
sleep(2);
num++;
}
return 0;
}
</code></pre></description></item><item><title>linux c ipc eventfd</title><link>/language/clang/linux-c-ipc-eventfd/</link><pubDate>Sat, 31 Oct 2020 17:01:06 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-c-ipc-eventfd/</guid><description><p>1.新内核版本为什么要增加eventfd?</p>
<p>首先说明的一点是eventfd是用来实现多进程或多线程的之间的事件通知的,那么我们在没接触eventfd之前用到的事件通知机制都有那些?</p>
<p>1.条件变量<br />
2.管道</p>
<p>我们来逐一比较此俩中机制与eventfd的效果方面的好坏,首先,条件变量必须和互斥锁结合使用,使用起来麻烦,而且性能未必比eventfd好,其次条件变量不能像eventfd一样为I/O事件驱动,因此不能和服务器的I/O模式很好的融合,所以在某些时候不如eventfd好用</p>
<p>接着是管道,虽然管道能与I/O复用很好的融合,但很明显管道相比eventfd多用了一个文件描述符,而且管道的话内核还得给其管理的缓冲区,eventfd则不需要,所以单纯作为事件通知的话还是管道好用</p>
<p>2.event的主要接口</p>
<p>eventfd只有一个接口,形式如下</p>
<p>int eventfd(unsigned int initval, int flags); //成功返回事件驱动的文件描述符fd<br />
说明:initval的范围是0~0xfffffffffffffffe;flags的值可以是如下枚举值:</p>
<p>enum<br />
{</p>
<pre><code>EFD_SEMAPHORE = 00000001, //since Linux 2.6.30
</code></pre>
<p>#define EFD_SEMAPHORE EFD_SEMAPHORE</p>
<pre><code>EFD_CLOEXEC = 02000000, //since Linux 2.6.27
</code></pre>
<p>#define EFD_CLOEXEC EFD_CLOEXEC</p>
<pre><code>EFD_NONBLOCK = 00004000 //since Linux 2.6.27
</code></pre>
<p>#define EFD_NONBLOCK EFD_NONBLOCK<br />
};</p>
<p>如果设置了EFD_SEMAPHORE,则不会出现粘包的情况,即write多少次,就需要read多少次;</p>
<p>如果设置了0(内核2.6.27版本之前必须设置为0),则会出现粘包的可能,write多次,可能read到的是若干次的值的和;</p>
<p>另外两种一般不需要,所以并未进行测试。</p>
<p>eventfd()创建一个文件描述符,这个文件描述符用户可以通过等待其可读来实现事件通知,该通知靠内核来响应用户空间的应用事件。上述接口的第一个参数是一个由内核来保持的64位计数器,这个计数器有参数initval来初始化,关于此计数器的影响我在下文中的具体实例中给大家演示,一般我们可将其设为0</p>
<p>第二个参数flags可以为EFD_NONBLOCK或EFD_CLOEXEC,其含义分别为阻塞文件描述符,和与普通文件的CLOEXEC标志一样的功能<br />
一般来说进程往往会调用fork函数来执行一个子进程,调用execve()执行其他程序,这时候可能就导致子进程中存在一些无用的文件描述符问题。<br />
父进程在fork函数的时候,子进程会拷贝跟父进程一样的地址空间,包括寄存器,文件描述符,堆,栈等。在一开始,父进程与子进程的文件描述符指向的是系统文件表中的同一项(包括文件状态,和文件偏移量)。<br />
当我们用execve执行其他程序的时候,全新的程序会替换子进程中的地址空间,数据段,堆栈,此时保存与父进程文件描述符也就不存在了,也无法进行关闭,这时候就需要FD_CLOEXEC, 表示子进程在执行exec的时候,该文件描述符就需要进行关闭。</p>
<p>通过fcntl设置FD_CLOEXEC标志有什么用?<br />
close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。</p>
<p>实现代码如下:</p>
<pre><code class="language-cpp">int fd=open(&quot;1&quot;,O_RDONLY);
//获取当前文件描述符的相关标志位
int flags=fcntl(fd,F_GETFD,NULL);
flags|=FD_CLOEXEC;
fcntl(fd,F_SETFD,flags);
</code></pre>
<p>3.具体实例</p>
<pre><code>#include &lt;sys/eventfd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;pthread.h&gt;
#include &lt;unistd.h&gt;
int fd;
uint64_t buffer;
void threadFunc(void) //线程函数
{
int t;
while(1)
{
t = read(fd,&amp;buffer,sizeof(buffer)); //阻塞等待fd可读,及通知事件发生
if(sizeof(buffer) &lt; 8)
{
printf(&quot;buffer错误\n&quot;);
}
printf(&quot;t = %llu buffer = %llu\n&quot;,t,buffer);
if(t == 8)
{
printf(&quot;唤醒成功\n&quot;);
}
}
}
int main(void)
{
uint64_t buf = 1;
int ret;
pthread_t tid;
int init_val =3;
if((fd = eventfd(init_val,0)) == -1) //创建事件驱动的文件描述符
{
printf(&quot;创建失败\n&quot;);
}
//创建线程
if(pthread_create(&amp;tid,NULL,threadFunc,NULL) &lt; 0)
{
printf(&quot;线程创建失败\n&quot;);
}
while(1)
{
ret = write(fd,&amp;buf,sizeof(buf)); //通过往fd里写东西来进行事件通知
if(ret != 8)
{
printf(&quot;写错误\n&quot;);
}
sleep(2); //没2s通知一次
}
return 0;
}
</code></pre>
<p>上述代码中,我们创建一个线程,通过主线程往fd里写数据,来通知另一个线程。<br />
代码执行结果如下<br />
这里写图片描述</p>
<p>关于eventfd中的initval参数的作用,我的测试结果如下,前提把上述代码eventfd函数中的0改为3</p>
<p>这里写图片描述<br />
可以看出这个由内核管理的计数器,我们的初始化值,只会影响程序第一次buffer的值,后续buffer中的值依然为1.</p></description></item><item><title>gonggonghao</title><link>/wechat/gonggonghao/</link><pubDate>Sat, 31 Oct 2020 11:51:29 CST</pubDate><author>rinetd</author><guid>/wechat/gonggonghao/</guid><description><p>订阅号:<br />
封面背景 4d8ddd</p>
<p>R 77 G 144 B 221 蓝色</p></description></item><item><title>makefile hisi</title><link>/language/clang/makefile-hisi/</link><pubDate>Wed, 28 Oct 2020 09:09:17 CST</pubDate><author>rinetd</author><guid>/language/clang/makefile-hisi/</guid><description><pre><code class="language-makefile">####################################
# 所有与平台相关的编译参数全放在这里
####################################
#=======================================
#plateform
export CHIP_TYPE := HI_3531A
#=======================================
#tools chain
ifeq ($(CHIP_TYPE), HI_3531A)
TOOL_CHAIN_DIR := /home/share_disk/tool_chain/3531A_030/arm-hisiv300-linux/target/bin/
export CROSS_COMPILE := $(TOOL_CHAIN_DIR)arm-hisiv300-linux-
else
endif
#=======================================
export CC := $(CROSS_COMPILE)gcc
export CPP := $(CROSS_COMPILE)g++
export AR := $(CROSS_COMPILE)ar
export LD := $(CROSS_COMPILE)ld
export STRIP := $(CROSS_COMPILE)strip
export STRIPFLAGS:= -g --strip-unneeded
export MAKEDEPEND =$(CC) -MM -MT
export CFLAGS := -Wall
export CPPFLAGS := -Wall
export MACRO :=
</code></pre>
<pre><code class="language-makefile">include Makefile.param
include Makefile.srcs
include Makefile.libs
TARGET := SmartVC
all:$(TARGET)
$(TARGET):$(OBJS_C) $(OBJS_CPP)
$(CC) $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC) $(LIBS_LIB) $(OBJS_C) $(OBJS_CPP) $(LDFLAGS) -o $@ -g
#$(STRIP) $(STRIPFLAGS) $@
libmdd.so:$(OBJS_C) $(OBJS_CPP)
$(CC) $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(OBJS_C) $(OBJS_CPP) $(LDFLAGS) -o $@ -shared -fPIC
#$(STRIP) $(STRIPFLAGS) $@
libmdd.a:$(OBJS_C) $(OBJS_CPP)
$(AR) rv $@ $(OBJS_C) $(OBJS_CPP)
#$(STRIP) $(STRIPFLAGS) $@
$(OBJS_C):%.o:%.c
$(CC) -o $@ -c $&lt; $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC)
$(OBJS_CPP):%.o:%.cpp
$(CC) -o $@ -c $&lt; $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC)
$(DEF_C):%.d:%.c
$(MAKEDEPEND) $(&lt;:.c=.o) $&lt; $(SRCS_INC) $(LIBS_INC) &gt; $@
$(DEF_CPP):%.d:%.cpp
$(MAKEDEPEND) $(&lt;:.cpp=.o) $&lt; $(SRCS_INC) $(LIBS_INC) &gt; $@
include $(DEF_C) $(DEF_CPP)
clean:
rm -rf $(OBJS_C) $(OBJS_CPP)
rm -rf $(TARGET)
depend:
rm -rf $(DEF_C) $(DEF_CPP)
distclean:
rm -rf $(OBJS_C) $(OBJS_CPP)
rm -rf $(TARGET)
rm -rf $(DEF_C) $(DEF_CPP)
install:
cp src/*.h /home/disk2/zp/work_space/thirdlib/mdd/inc -rf
cp *.so /home/disk2/zp/work_space/thirdlib/mdd/ -rf
.PHONY:$(TARGET)
.PHONY: clean
.PHONY: depend
.PHONY: distclean
.PHONY: install
</code></pre>
<pre><code class="language-makefile">####################################
# 所有与第三方库相关的参数全放在这里
####################################
##########include dir#############
LIBS_INC :=
#LIBS_INC += -I/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include
#LIBS_INC += -I/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include/mkp
#LIBS_INC += -I/home/disk2/zp/work_space/thirdlib/mdd/inc
LIBS_INC += -I/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include
LIBS_INC += -I/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include/mkp
LIBS_INC += -I/home/share_disk/workspace/thirdlib/mdd/inc
##########lib dir#############
LIBS_LIB :=
#LIBS_LIB += -L/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/lib
#LIBS_LIB += -L/home/disk2/zp/work_space/thirdlib/mdd
LIBS_LIB += -L/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/lib
LIBS_LIB += -L/home/share_disk/workspace/thirdlib/mdd
##########load option#############
LDFLAGS :=
LDFLAGS += -lpthread -lm -lstdc++ -lmpi -ljpeg -lhdmi -ltde -lupvqe -lVoiceEngine -ldnvqe -ldl
</code></pre>
<pre><code class="language-makefile">####################################
# 所有与源码相关的参数全放在这里
####################################
SRCS_INC :=
SRCS_INC += -I.
SRCS_C :=
SRCS_C += $(wildcard ./*.c)
SRCS_CPP :=
SRCS_CPP += $(wildcard ./*.cpp)
############public source code############
#UTIL_TOOLS_DIR := /home/disk2/zp/work_space/code/util_tools/code
UTIL_TOOLS_DIR := /home/share_disk/workspace/code/util_tools/code
SRCS_INC += -I$(UTIL_TOOLS_DIR)/base64
SRCS_INC += -I$(UTIL_TOOLS_DIR)/cJSON
SRCS_INC += -I$(UTIL_TOOLS_DIR)/cross_platform
SRCS_INC += -I$(UTIL_TOOLS_DIR)/error
SRCS_INC += -I$(UTIL_TOOLS_DIR)/filefunc
SRCS_INC += -I$(UTIL_TOOLS_DIR)/inirw
SRCS_INC += -I$(UTIL_TOOLS_DIR)/list
SRCS_INC += -I$(UTIL_TOOLS_DIR)/log
SRCS_INC += -I$(UTIL_TOOLS_DIR)/strfunc
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/base64/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/cJSON/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/cross_platform/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/error/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/filefunc/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/inirw/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/list/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/log/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/strfunc/*.c)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/base64/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/cJSON/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/cross_platform/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/error/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/filefunc/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/inirw/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/list/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/log/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/strfunc/*.cpp)
OBJS_C :=
OBJS_C += $(SRCS_C:.c=.o)
OBJS_CPP :=
OBJS_CPP += $(SRCS_CPP:.cpp=.o)
DEF_C :=
DEF_C += $(SRCS_C:.c=.d)
DEF_CPP :=
DEF_CPP += $(SRCS_CPP:.cpp=.d)
SRCS_MACRO :=
SRCS_MACRO += -DPLATFORM=1
export SRCS_INC #source code directory
export SRCS_C # c source code pathname
export SRCS_CPP # cpp source code pathname
export OBJS_C # The c suffix corresponding to the path name of the .o
export OBJS_CPP # The cpp suffix corresponding to the path name of the .o
export DEF_C # The c suffix corresponding to the path name of the .d
export DEF_CPP # The cpp suffix corresponding to the path name of the .d
export SRCS_MACRO # Source related macros
</code></pre></description></item><item><title>linux_c__attribute__pragma_pack</title><link>/language/clang/linux_c__attribute__pragma_pack/</link><pubDate>Tue, 20 Oct 2020 17:02:49 CST</pubDate><author>rinetd</author><guid>/language/clang/linux_c__attribute__pragma_pack/</guid><description>
<h2 id="区别-pragma作用于结构内的成员变量-attribute-aligned-n-作用于结构体分配地址的对齐方式-和-结构体的大小">【区别】 pragma作用于结构内的成员变量;attribute ((aligned(n)))作用于结构体分配地址的对齐方式 和 结构体的大小。</h2>
<pre><code class="language-cpp">typedef struct Student_t
{
int age;
char c;
}__attribute__((aligned (64))) Student;
//影响结构体自身的对齐和大小,每个结构体单独占用64字节 ,不影响其中结构体中元素的对齐
typedef struct Node_t
{
int a;
char c;
} Node __attribute__((aligned(64)));
//只影响Node的对齐, 不影响Node的大小
//对于Node定义的数组,只影响整个数组的起始地址,不影响其中每个数组元素的对齐,每个变量还是占用8个字节,按8字节对齐,总占用内存是64的倍数。
typedef struct Node_p{
int a;
char c;
}__attribute__((packed)) Node_p ;
//影响结构中元素的对齐,通知编译器不做对齐处理等价于 #pragma pack(1)
#pragma pack(push)
#pragma pack(1)
typedef struct
{
int age;
char c;
}user;
#pragma pack(pop)
#pragma pack(push,2)
typedef struct List_t
{
int a;
char c;
}List;
#pragma pack(pop)
//push 和 pop成对出现 ,等价于 #pragma pack(2) 修改结构体中元素对齐规则
typedef int __attribute__((aligned(8))) myint;
// 只影响对齐不影响大小,整个数组按照起始地址8整数倍,数组大小是8的倍数 ,数组元素每个大小还是4
int main()
{
myint MYA[5],mya;
printf(&quot;MYA:%p %p %d mya:%p %d\n&quot;,&amp;MYA[0],&amp;MYA[4],sizeof(MYA),&amp;mya,sizeof(mya));
Student C[5],c;
printf(&quot;C:%p %p %d c:%p %d\n&quot;,&amp;C,&amp;C[4],sizeof(C),&amp;c,sizeof(c)) ;
Node_p cc;
printf(&quot;***%p %p %d***\n&quot;,&amp;(cc.c),&amp;(cc.a),sizeof(Node_p)) ;
Node A[5], a;
printf(&quot;A:%p %p %d a:%p %d\n&quot;,&amp;A,&amp;A[4],sizeof(A),&amp;a,sizeof(a));
List B[5], b;
printf(&quot;B:%p %p %d a:%p %d\n&quot;,&amp;B,&amp;B[4],sizeof(B),&amp;b,sizeof(b)) ;
return 0;
}
</code></pre>
<p>输出结果</p>
<p>x86下,GCC默认按4字节对齐,它会在sex后面跟name后面分别填充三个和两个字节使length和整个结构体对齐。于是我们sizeof(my_stu)会得到长度为20,而不是15.<br />
<br />
四、attribute选项<br />
<br />
struct stu{<br />
char sex;<br />
int length;<br />
char name[10];<br />
}attribute ((aligned (1)));<br />
<br />
struct stu my_stu; <br />
则sizeof(my_stu)可以得到大小为15。<br />
<br />
上面的定义等同于<br />
struct stu{<br />
char sex;<br />
int length;<br />
char name[10];<br />
}attribute ((packed));<br />
struct stu my_stu;<br />
<br />
attribute((packed))得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐.<br />
<br />
五、什么时候需要设置对齐<br />
<br />
在设计不同CPU下的通信协议时,或者编写硬件驱动程序时寄存器的结构这两个地方都需要按一字节对齐。即使看起来本来就自然对齐的也要使其对齐,以免不同的编译器生成的代码不一样.</p>
<p>一、快速理解</p>
<p>什么是字节对齐?<br />
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。</p>
<p>为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.</p>
<p>字节对齐有什么作用?<br />
字节对齐的作用不仅是便于cpu快速访问,同时合理的利用字节对齐可以有效地节省存储空间。</p>
<p>对于32位机来说,4字节对齐能够使cpu访问速度提高,比如说一个long类型的变量,如果跨越了4字节边界存储,那么cpu要读取两次,这样效率就低了。但是在32位机中使用1字节或者2字节对齐,反而会使变量访问速度降低。所以这要考虑处理器类型,另外还得考虑编译器的类型。在vc中默认是4字节对齐的,GNU gcc 也是默认4字节对齐。</p>
<p>更改C编译器的缺省字节对齐方式<br />
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:<br />
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。<br />
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。</p>
<p>另外,还有如下的一种方式:<br />
· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。<br />
· attribute ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。</p>
<p>举例说明<br />
struct test<br />
{<br />
char x1;<br />
short x2;<br />
float x3;<br />
char x4;<br />
};<br />
译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。</p></description></item><item><title>libev_io_socket</title><link>/language/clang/libev/libev_io_socket/</link><pubDate>Mon, 19 Oct 2020 17:50:02 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev_io_socket/</guid><description><pre><code class="language-cpp">/*
* @author :
* @date : 2014-09-04
* @desc : tiny socket server implemented by libev
* to use this, you should install libev at first.
*
* server: just run the program
* client: telnet localhost 8080
*
* @refer : 1). http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod
* 2). http://blog.csdn.net/lengzijian/article/details/8315133
*
*/
#include &lt;ev.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;unistd.h&gt;
#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CONNECTIONS 10
struct ev_io *libevlist[MAX_CONNECTIONS] = {NULL};
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents);
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents);
/*
Server Client
socket socket
| |
v v
bind connect
| |
v v
listen write
| |
v v
accept read
| |
v v
read close
|
v
write
|
v
close
*/
int main() {
struct ev_loop *loop = ev_default_loop(0);
/* socket start */
int sd;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
struct ev_io *socket_watcher = (struct ev_io*)malloc(sizeof(struct ev_io));
struct ev_timer *timeout_watcher = (struct ev_timer*)malloc(sizeof(struct ev_timer));
// socket
sd = socket(PF_INET, SOCK_STREAM, 0);
if (sd &lt; 0) {
printf(&quot;socket error\n&quot;);
return -1;
}
bzero(&amp;addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
// bind
if (bind(sd, (struct sockaddr*) &amp;addr, sizeof(addr)) != 0) {
printf(&quot;bind error\n&quot;);
return -1;
}
// listen
if (listen(sd, SOMAXCONN) &lt; 0) {
printf(&quot;listen error\n&quot;);
return -1;
}
// set sd reuseful
int bReuseaddr = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const char*) &amp;bReuseaddr, sizeof(bReuseaddr)) != 0) {
printf(&quot;setsockopt error in reuseaddr[%d]\n&quot;, sd);
return -1;
}
/* socket end */
ev_io_init(socket_watcher, on_accept, sd, EV_READ);
ev_io_start(loop, socket_watcher);
while(1) {
ev_run(loop, 0);
}
return 1;
}
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents) {
printf(&quot;I am: %d\n&quot;, getpid());
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sd;
// ev_io watcher for client
struct ev_io *client_watcher = (struct ev_io*) malloc(sizeof(struct ev_io));
if (client_watcher == NULL) {
printf(&quot;malloc error in accept_cb\n&quot;);
return;
}
if (EV_ERROR &amp; revents) {
printf(&quot;error event in accept\n&quot;);
return;
}
// socket accept: get file description
client_sd = accept(watcher-&gt;fd, (struct sockaddr*) &amp;client_addr, &amp;client_len);
if (client_sd &lt; 0) {
printf(&quot;accept error\n&quot;);
return;
}
// too much connections
if (client_sd &gt; MAX_CONNECTIONS) {
printf(&quot;fd too large[%d]\n&quot;, client_sd);
close(client_sd);
return;
}
if (libevlist[client_sd] != NULL) {
printf(&quot;client_sd not NULL fd is [%d]\n&quot;, client_sd);
return;
}
printf(&quot;client connected\n&quot;);
// listen new client
ev_io_init(client_watcher, on_read, client_sd, EV_READ);
ev_io_start(loop, client_watcher);
libevlist[client_sd] = client_watcher;
}
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents) {
char buffer[BUFFER_SIZE];
ssize_t read;
if (EV_ERROR &amp; revents) {
printf(&quot;error event in read\n&quot;);
return;
}
// socket recv
read = recv(watcher-&gt;fd, buffer, BUFFER_SIZE, 0); // read stream to buffer
if (read &lt; 0) {
printf(&quot;read error\n&quot;);
return;
}
if (read == 0) {
printf(&quot;client disconnected.\n&quot;);
if (libevlist[watcher-&gt;fd] == NULL) {
printf(&quot;the fd already freed[%d]\n&quot;, watcher-&gt;fd);
}
else {
close(watcher-&gt;fd);
ev_io_stop(loop, libevlist[watcher-&gt;fd]);
free(libevlist[watcher-&gt;fd]);
libevlist[watcher-&gt;fd] = NULL;
}
return;
}
else {
printf(&quot;receive message:%s\n&quot;, buffer);
}
// socket send to client
send(watcher-&gt;fd, buffer, read, 0);
bzero(buffer, read);
}
</code></pre>
<pre><code class="language-cpp">
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt; //inet_addr
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;ev.h&gt;
#define PORT_NO 5000
#define BUFFER_SIZE 10240
#define MAX_BACKLOG 1024
int total_clients = 0;
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents);
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents);
int new_tcp_server(const char *host, short port);
int set_nonblock(int fd);
int set_reuse_socket(int sock);
int set_address (const char *host, short port, struct sockaddr_in *addr);
int main(int argc , char *argv[])
{
struct ev_loop *loop = ev_default_loop(0);
int port = PORT_NO;
if(argc==2){
port = atoi(argv[1]);
}else if(argc &gt; 2){
fprintf(stderr, &quot;usage: server_libev [port]\n&quot;);
return 1;
}
int fd = new_tcp_server(&quot;0.0.0.0&quot;, port);
printf(&quot;Starting tcp server on :%d\n&quot;, port);
ev_io listen_watcher;
ev_io_init(&amp;listen_watcher, on_accept, fd, EV_READ);
ev_io_start(loop, &amp;listen_watcher);
ev_loop(loop, 0);
return 0;
}
int new_tcp_server(const char *host, short port) {
int fd;
struct sockaddr_in address;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror(&quot;socket&quot;);
return -1;
}
set_reuse_socket(fd);
set_nonblock(fd);
set_address(host, port, &amp;address);
if (bind(fd, (struct sockaddr*)&amp;address, sizeof(address)) &lt; 0) {
perror(&quot;bind&quot;);
close(fd);
return -1;
}
if (listen(fd, MAX_BACKLOG) &lt;0){
perror(&quot;listen&quot;);
close(fd);
return -1;
};
return fd;
}
int set_address (const char *host, short port, struct sockaddr_in *addr)
{
memset(addr, 0, sizeof(*addr));
addr-&gt;sin_family = AF_INET;
addr-&gt;sin_addr.s_addr = inet_addr(host);
addr-&gt;sin_port = htons(port );
return 0;
}
int set_nonblock(int fd) {
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}
int set_reuse_socket(int fd) {
int ok = 1;
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &amp;ok, sizeof(ok));
}
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct ev_io *client_watcher = (struct ev_io*)malloc(sizeof(struct ev_io));
if(EV_ERROR &amp; revents) {
perror(&quot;invalid event&quot;);
return;
}
int client_fd = accept(watcher-&gt;fd, (struct sockaddr*)&amp;client_addr, &amp;client_len);
if (client_fd &lt; 0) {
perror(&quot;accept&quot;);
return;
}
set_nonblock(client_fd);
total_clients++;
ev_io_init(client_watcher, on_read, client_fd, EV_READ);
ev_io_start(loop, client_watcher);
}
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
char buffer[BUFFER_SIZE];
ssize_t len;
if(EV_ERROR &amp; revents) {
return;
}
len = recv(watcher-&gt;fd, buffer, BUFFER_SIZE, 0);
if (len &lt; 0) {
return;
} else if (len == 0) {
// disconnected
ev_io_stop(loop, watcher);
close(watcher-&gt;fd);
free(watcher);
total_clients--;
return;
}
send(watcher-&gt;fd, buffer, len, 0);
memset(buffer, 0, BUFFER_SIZE);
}
</code></pre></description></item><item><title>libev</title><link>/language/clang/libev/libev/</link><pubDate>Mon, 19 Oct 2020 16:01:15 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev/</guid><description>
<p><a href="https://blog.csdn.net/weiwangchao_/article/details/52961041" target="_blank">libev教程一:libev简单入门_把握自己。-CSDN博客</a></p>
<p>libev是个非常优秀的基于事件的循环库,很多开源软件,比如nodejs就是使用其实现基础功能。本系列将对该库进行源码分析。<a href="http://blog.csdn.net/breaksoftware/article/details/76066676" target="_blank">(转载请指明出于breaksoftware的csdn博客)</a></p>
<p>不知道是被墙了还是网站不再维护,它的官网(<a href="http://libev.schmorp.de/)在国内已经没法访问了。" target="_blank">http://libev.schmorp.de/)在国内已经没法访问了。</a><br />
但是我们仍然可以从github上下载其源码(<a href="https://github.com/enki/libev)。" target="_blank">https://github.com/enki/libev)。</a></p>
<h2 id="交叉编译">交叉编译</h2>
<p><code>./configure --host=arm-none-linux-gnueabi --prefix=</code>pwd<code>/libev</code><br />
<code>./configure --host=arm-mac-linux-gnueabihf CC=arm-mac-linux-gnueabihf-gcc --prefix=</code>pwd<code>/libev</code></p>
<h2 id="使用样例">使用样例</h2>
<p>libev支持相对时间定时器、绝对时间定时器、文件状态监控和信号监控等功能。我们可以在它基础上,通过少量的代码实现稳健完善的功能。</p>
<p>我们先看一段实现定时器功能的代码</p>
<pre><code class="language-c++">#include &lt;ev.h&gt;
#include &lt;stdio.h&gt;
ev_timer timeout_watcher;
static void
timeout_cb(EV_P_ ev_timer *w, int revents)
{
puts(&quot;timeout&quot;);
ev_break(EV_A_ EVBREAK_ONE);
}
int main(void)
{
struct ev_loop *loop = EV_DEFAULT;
ev_timer_init(&amp;timeout_watcher, timeout_cb, 5.5, 0);
ev_timer_start(loop, &amp;timeout_watcher);
ev_run(loop, 0);
return 0;
}
</code></pre>
<pre><code class="language-cpp">#include &lt;stdio.h&gt;
#include &lt;signal.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/unistd.h&gt;
#include &lt;ev.h&gt;
void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
{
int rst;
char buf[1024];
memset(buf,0,sizeof(buf));
puts(&quot;In IO action&quot;);
read(STDIN_FILENO,buf,sizeof(buf));
buf[1023]='\0';
printf(&quot;String: %s\n&quot;,buf);
ev_io_stop(main_loop,io_w);
}
void timer_action(struct ev_loop *main_loop,ev_timer *time_w,int e)
{
puts(&quot;In Time action&quot;);
ev_timer_stop(main_loop,time_w);
}
void signal_action(struct ev_loop *main_loop,ev_signal *signal_w,int e)
{
puts(&quot;In Signal action&quot;);
ev_signal_stop(main_loop,signal_w);
ev_break(main_loop,EVBREAK_ALL);
}
int main(int argc,char **argv)
{
ev_io io_w;
ev_timer timer_w;
ev_signal signal_w;
struct ev_loop *main_loop = ev_default_loop(0);
ev_init(&amp;io_w,io_action);
ev_io_set(&amp;io_w,STDIN_FILENO,EV_READ);
ev_init(&amp;timer_w,timer_action);
ev_timer_set(&amp;timer_w,2,0);
ev_init(&amp;signal_w,signal_action);
ev_signal_set(&amp;signal_w,SIGINT);
ev_io_start(main_loop,&amp;io_w);
ev_timer_start(main_loop,&amp;timer_w);
ev_signal_start(main_loop,&amp;signal_w);
ev_run(main_loop,0);
return 0;
}
</code></pre>
<p>这段代码的结构非常简单。首先我们要定义一个名为timeout_cb的回调函数用于响应定时器。然后定义一个ev_timer结构(监视器),它通过ev_timer_init进行初始化。初始化的参数包含之前定义的响应函数指针和迭代器超时时间。ev_timer准备好后,通过ev_timer_start将其和一个ev_loop进行绑定。最后使用ev_run方法运行起来这个ev_loop指针,从而实现一个完整的定时器功能。</p>
<p>可见使用libev库非常方便。其实我们之后见到的其他用法和上面步骤是类似的,即:</p>
<ul>
<li>初始化ev_loop。<br /></li>
<li>定义监视器。<br /></li>
<li>定义回调函数。<br /></li>
<li>监视器和回调函数关联。<br /></li>
<li>监视器和ev_loop关联。<br /></li>
<li>ev_run将ev_loop运行起来。<br />
<br /></li>
</ul>
<p>假如上面代码是个框架使用的雏形,那么如果让我们去设计这样的框架,该如何设计呢?</p>
<h2 id="模型设计">模型设计</h2>
<p>首先我们需要考虑到的是使用sleep还是使用事件模型去实现逻辑等待。</p>
<p>如果使用sleep方法,那么我们就要在唤醒后去检测各个事件,比如要检测文件状态是否发生变化,比如定时器时间是否已经超时。于是有个问题,就是sleep多久怎么确定?我们不知道是5秒后还是1秒后文件状态发生变化,那么只能最小粒度sleep了。那么这就意味着线程在短暂挂起后,马上检测一系列可能尚未发生改变的事件。这种设计明显很消耗CPU,而且非常低效。</p>
<p>如果使用事件模型去等待,就可以解决上述问题。但是像定时器,在系统中并没有事件与其对应。于是我们需要考虑下对于没有事件对应的功能怎么通过事件模型去封装。</p>
<p>其次我们需要考虑使用单线程模型还是多线程模型。</p>
<p>单线程模型是让主流程和事件响应函数在一个线程中执行。其伪代码是</p>
<pre><code class="language-c++">If (event is ready) {
event_callback(); // in the main thead
}
</code></pre>
<p>其特点是实现简单,但是事件响应函数的效率将严重影响主流程对事件的响应速度。比如A、B两个事件同时发生,理论上我们希望两个事件的响应函数被同时执行,或者在允许存在的系统调用时间差(比如创建线程的消耗)内执行。然而单线程模型则会让一个响应函数执行完后再去执行另一响应函数,于是就出现排队现象。所以单线程模型无法保证及时响应。</p>
<p>多线程模型则完全避免了上述问题。它可在事件发生后启动一个线程去处理响应函数。当然相对来说多线程模型比较复杂,需要考虑多线程同步问题。</p>
<pre><code class="language-c++">If (event is ready) {
thread_excute(event_callback); // run in another thread
}
</code></pre>
<p>那么libev对上面两个问题是怎么选择的呢?对于sleep和事件模型,libev选择的是后者,所以它是“高性能”的。对于单线程和多线程,libev选择的是前者。至于原因我并不知道,可能是作者希望它足够简单,或者希望它能在不支持多线程的系统上使用。但是要说明一点,并不是说libev不支持多线程。因为一个单线程模型的执行体,是可以放在其他若干个线程中执行的,只要保证数据同步。</p>
<h2 id="单-多线程编译">单/多线程编译</h2>
<p>libev提供了各种编译选项以支持各种特性。比如在支持多线程的系统上,我们可以指定EV_MULTIPLICITY参数,以让libev编译出多线程版本。</p>
<p>libev对于单线程版本的数据都是以全局静态变量形式提供。而对于多线程版本,则提供了一个结构体——ev_loop保存数据,这样不同线程持有各自的数据对象,从而做到数据隔离。</p>
<pre><code class="language-c++">#if EV_MULTIPLICITY
struct ev_loop
{
ev_tstamp ev_rt_now;
#define ev_rt_now ((loop)-&gt;ev_rt_now)
#define VAR(name,decl) decl;
#include &quot;ev_vars.h&quot;
#undef VAR
};
#include &quot;ev_wrap.h&quot;
static struct ev_loop default_loop_struct;
EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */
#else
EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */
#define VAR(name,decl) static decl;
#include &quot;ev_vars.h&quot;
#undef VAR
static int ev_default_loop_ptr;
#endif
</code></pre>
<p>不管是哪个版本,它们都提供了ev_default_loop_ptr变量。多线程版本它将指向全局静态变量default_loop_struct,这样对于使用了多线程版本又不想维护ev_loop结构对象的用户来说,直接使用这个对象就行了,非常方便。</p>
<p>然后再看下ev_vars.h的引入。其实现如下:</p>
<pre><code class="language-c++">#define VARx(type,name)VAR(name, type name)
VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
VARx(ev_tstamp, mn_now) /* monotonic clock &quot;now&quot; */
VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */
/* for reverse feeding of events */
VARx(W *, rfeeds)
VARx(int, rfeedmax)
VARx(int, rfeedcnt)
VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])
</code></pre>
<p>在多线程版本中,它在ev_loop结构体中被引入的。这样在编译器展开文件时,它将会被定义到结构体内部。在单线程版本中,VAR宏被声明为定义一个静态全局变量的形式。这种利用宏和编译展开技术,在不同结构中定义相同类型数据的方式还是很有意思的。</p>
<p>但是又会有个问题,如何去访问这些变量呢?在单线程中,它们是静态变量,所有位置可以直接通过名称访问。而多线程版本中,则需要通过一个ev_loop结构体去引导。相关的代码总不能通过EV_MULTIPLICITY宏来区分不同变量形式吧?如果那样,代码将变得非常难看。我们看下libev怎么巧妙解决这个问题的。</p>
<p>上面代码块的多线程定义区间,引入了ev_wrap.h文件。其实现如下:</p>
<pre><code class="language-c++">#ifndef EV_WRAP_H
#define EV_WRAP_H
#define acquire_cb ((loop)-&gt;acquire_cb)
#define activecnt ((loop)-&gt;activecnt)
#define anfdmax ((loop)-&gt;anfdmax)
#define anfds ((loop)-&gt;anfds)
#define async_pending ((loop)-&gt;async_pending)
#define asynccnt ((loop)-&gt;asynccnt)
</code></pre>
<p>这样使用一个和变量相同名称的宏替代了通过ev_loop结构体对象访问的变量。且这个宏名称和单线程版本中静态变量名相同。这样就让不同版本的关键变量“同名”了。于是代码对这些变量的访问直接使用其原始名称即可——单线程中使用的是真实变量名,多线程中使用的是宏。<br />
这样的设计,又引入一个问题。那就是所有使用这些变量的函数,在多线程版本中,需要提供一个名字为loop的ev_loop结构体对象;而在单线程版本中则不需要。为了固化这个名称,libev还为此专门定义了一系列宏。</p>
<pre><code class="language-c++">#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P, /* a loop as first of multiple parameters */
# define EV_A loop /* a loop as sole argument to a function call */
# define EV_A_ EV_A, /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */
# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
</code></pre>
<p>之后我们在代码中导出可见的EV_P和EV_A就是为了保证不同版本的实现在代码层面是相同的。</p>
<h2 id="libev">libev</h2>
<h3 id="1-1-introduction">1.1 Introduction</h3>
<p>主页<a href="http://software.schmorp.de/pkg/libev.html" target="_blank">http://software.schmorp.de/pkg/libev.html</a>.</p>
<p>文档<a href="http://software.schmorp.de/pkg/libev.html" target="_blank">http://software.schmorp.de/pkg/libev.html</a>.</p>
<p>libev所实现的功能就是一个强大的reactor,可能notify事件主要包括下面这些:</p>
<ul>
<li>ev_io // IO可读可写<br /></li>
<li>ev_stat // 文件属性变化<br /></li>
<li>ev_async // 激活线程<br /></li>
<li>ev_signal // 信号处理<br /></li>
<li>ev_timer // 定时器<br /></li>
<li>ev_periodic // 周期任务<br /></li>
<li>ev_child // 子进程状态变化<br /></li>
<li>ev_fork // 开辟进程<br /></li>
<li>ev_cleanup // event loop退出触发事件<br /></li>
<li>ev_idle // 每次event loop空闲触发事件<br /></li>
<li>ev_embed // TODO(zhangyan04):I have no idea.<br /></li>
<li>ev_prepare // 每次event loop之前事件<br /></li>
<li>ev_check // 每次event loop之后事件<br />
<br /></li>
</ul>
<h3 id="1-2-about-the-code">1.2 About The Code</h3>
<p>代码风格相当严谨而且排版也非常工整,并且从域名看出作者是德国人。但是内部使用了大量的宏造成阅读代码并不是非常方便。</p>
<p>并且从代码角度分析,应该是一开始支持有一个默认的event_loop,但是随着多核产生实际应用中可能会使用到多个event_loop, 猜想作者应该是为了方便的话使用了很多宏进行替换。</p>
<p>允许使用多个event_loop的宏是EV_MULTIPLICITY.</p>
<p>比如下面这段代码</p>
<pre><code class="language-c++">void noinline
ev_io_start (EV_P_ ev_io *w)
{
int fd = w-&gt;fd;
if (expect_false (ev_is_active (w)))
return;
assert ((&quot;libev: ev_io_start called with negative fd&quot;, fd &gt;= 0));
assert ((&quot;libev: ev_io_start called with illegal event mask&quot;, !(w-&gt;events &amp; ~(EV__IOFDSET | EV_READ | EV_WRITE))));
EV_FREQUENT_CHECK;
ev_start (EV_A_ (W)w, 1);
array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
wlist_add (&amp;anfds[fd].head, (WL)w);
fd_change (EV_A_ fd, w-&gt;events &amp; EV__IOFDSET | EV_ANFD_REIFY);
w-&gt;events &amp;= ~EV__IOFDSET;
EV_FREQUENT_CHECK;
}
</code></pre>
<p>初次阅读这个代码会觉得非常难懂。</p>
<table>
<thead>
<tr>
<th>宏</th>
<th>说明</th>
<th>定义</th>
</tr>
</thead>
<tbody>
<tr>
<td>EV_P</td>
<td>event parameter</td>
<td>struct ev_loop* loop</td>
</tr>
<tr>
<td>EV<em>P</em></td>
<td></td>
<td>EV_P,</td>
</tr>
<tr>
<td>EV_A</td>
<td>event argument</td>
<td>loop</td>
</tr>
<tr>
<td>EV<em>A</em></td>
<td></td>
<td>EV_A,</td>
</tr>
</tbody>
</table>
<p>然后很多变量只要是ev_loop成员的话都被封装成为了宏。比如代码里面的anfds,实际上的宏定义是</p>
<pre><code class="language-c++">#define anfds ((loop)-&gt;anfds)
</code></pre>
<p>事实上一个ev_loop里面的字段是相当多的,不过也很正常本身就是一个强大的reactor.但是这造成一个直接后果,就是对于想要了解ev_loop的全貌比较困难,所以想要彻底地了解libev也比较麻烦,所以我们只能够从应用层面来尝试了解它。</p>
<h3 id="1-3-eventloop">1.3 EventLoop</h3>
<p>首先我们关注一下reactor本身。在libev下面reactor对象称为event_loop.</p>
<p>event_loop允许动态创建和销毁,并且允许绑定自定义数据</p>
<pre><code class="language-c++">struct ev_loop * ev_loop_new (unsigned int flags);