|
1. 前言 “我的人品有这么差吗!半个小时Linux都没调度我的线程!”。 前些日子同事抱怨,“linux是多线程系统,每个线程都有一个时间片,为什么我的程序似乎一直没被调度”,随即给我演示执行过程。大致归纳成下面的代码逻辑。 程序启动后仅仅输出一行“run”,然后就没有然后了,喝着咖啡、嗑着瓜子、带薪摸鱼,屏幕上干干净净。理想状态下每10秒执行任务完毕后输出“do”。 分析代码逻辑可知 “推断” 代码是否被运行的依据是屏幕上输出 “do” ,”而这真的科学吗?答案是否定的,linux有着雨露均沾的调度系统,很难做到半小时没被调度。问题就出在linux的 “行缓冲”机制 。 - 5 O/ v# z: u+ T' w% p
- void main()
" {* t/ N, Z& t# K - {
9 z- E( N( H/ g- j - printf("run\n");7 s! q$ }4 e' L. }
- while(1) {8 t' e; ?) X* p0 H
- sleep(10);
3 I# f4 w; W. N, v- L8 {' K7 I) S* o - do_thing();
8 ~3 l4 g$ [) Y: D - printf("do");
9 `* p0 I7 U0 C2 z* _ - }+ o4 L- Y, Y, i, V; _
- }
复制代码
$ Y/ y4 z T5 Q3 q' i& R4 Q( u; K9 V2 @5 N. | R2 I
2. 行缓冲 ~+ ]6 ^6 V' U
上文的printf输出目标设备由STDOUT指定,STDOUT可能指向115200波特率的串口设备,也可能指向本地图形设备,相对于CPU而言他们的速度要慢上几个数量级。都是为了提高机器或者程序的性能、提高CPU利用率。协调高速设备和低速设备之间速度的不匹配,操作系统默认在标准I/O上采用行缓冲机制,printf的内容首先存储在内存,当缓冲填满或检测到换行符’’时则输出到目标设备。 Linux上这个行缓冲默认是1024Byte,每10s输出do占用2Byte,为了等待缓冲填满需要等待81min。 知道原理后解决方法就简单了。 方法1:fflush强制刷新标准输出stdout
1 J# U0 z8 c! G- #include
( u8 Y* s6 I4 |! h* z- n2 ~ - void main()
( ]* y- t e9 k1 b+ Z2 O& w - {
4 u" J2 s A7 ^* {$ { - printf("run\n");
6 X! _( |$ Y$ j - while(1) {, s0 r% I3 Q- n% z
- sleep(10);
u* m+ v8 r) ]( z0 w - do_thing();( O7 u% J8 O6 h+ q) y
- fflush(stdout);* D1 r$ A0 N; C/ P; N
- printf("do");! R( Z- L* O% s
- }5 r8 i! o! I8 O2 g
- }/ ~+ e5 T1 I$ Q
复制代码 ' t: L# N4 O- h" h
方法2:输出“换行符”( {8 h+ s- d% ^
" Y" c/ ~% d b/ |- ]- #include 3 }/ |1 `0 {# F% P" ~' S' l
- void main()
6 U+ P; I* ~4 w+ f - {3 w& c; `' p+ F+ _+ n
- printf("run\n");
8 ?/ n. I5 ]! A4 E2 \$ y - while(1) {
4 G. ^. f* {" K3 t. H0 h0 x - sleep(10);, j, j# W0 b) A5 o1 u* L. g d2 a
- do_thing();
+ P6 h0 k, L* H. O - printf("do\n");
+ L7 [ O X, l; A$ j+ E: N - }, U4 g3 \( I% l! b" e
- }
复制代码 ' V+ y+ b0 A( |- Y9 l8 \
|