|
1. 前言 “我的人品有这么差吗!半个小时Linux都没调度我的线程!”。 前些日子同事抱怨,“linux是多线程系统,每个线程都有一个时间片,为什么我的程序似乎一直没被调度”,随即给我演示执行过程。大致归纳成下面的代码逻辑。 程序启动后仅仅输出一行“run”,然后就没有然后了,喝着咖啡、嗑着瓜子、带薪摸鱼,屏幕上干干净净。理想状态下每10秒执行任务完毕后输出“do”。 分析代码逻辑可知 “推断” 代码是否被运行的依据是屏幕上输出 “do” ,”而这真的科学吗?答案是否定的,linux有着雨露均沾的调度系统,很难做到半小时没被调度。问题就出在linux的 “行缓冲”机制 。 - & h$ a8 p* ]8 B @0 _7 N; n
- void main()
, q3 F6 K! I& u) I- E - {2 b4 N E3 j" ?. p
- printf("run\n");" x' m) R" }+ V, {5 q+ \
- while(1) {
. x- M: |& p/ [4 \- x - sleep(10);7 Z0 a% `* J+ p) }
- do_thing();
5 b' ?- Z$ M, t3 o - printf("do");
2 v& ^9 B! \7 u$ D" @* x - }
7 j$ k( i% o8 K0 r/ L+ ` - }
复制代码
7 g7 v* r; n% v+ p! G! A4 m* o2 |" b& \
2. 行缓冲
$ E0 b$ M( A" m
上文的printf输出目标设备由STDOUT指定,STDOUT可能指向115200波特率的串口设备,也可能指向本地图形设备,相对于CPU而言他们的速度要慢上几个数量级。都是为了提高机器或者程序的性能、提高CPU利用率。协调高速设备和低速设备之间速度的不匹配,操作系统默认在标准I/O上采用行缓冲机制,printf的内容首先存储在内存,当缓冲填满或检测到换行符’’时则输出到目标设备。 Linux上这个行缓冲默认是1024Byte,每10s输出do占用2Byte,为了等待缓冲填满需要等待81min。 知道原理后解决方法就简单了。 方法1:fflush强制刷新标准输出stdout
7 Z) P6 g5 X! p3 G* v4 Y' R- #include ) R! q' M! }2 K8 l2 m
- void main()
3 ~* A' |1 ~$ Q3 W- b - {' F0 P0 Z0 I# X q3 y( T
- printf("run\n");
5 m, E4 J7 P# d( x2 A - while(1) {/ v; u8 J; ]5 I! ]
- sleep(10);, D7 z0 \; n9 ^ }1 s% {
- do_thing();
G; w& z. m }" W2 K y7 P - fflush(stdout);
4 c2 ~. F& |. Z; i7 B/ P7 w' |+ c - printf("do");9 Q' |( V9 O" \! L- C
- }
- P: E8 U( T' W" z - }
0 x' j3 M& y1 ~5 I* x! r& {' a
复制代码
% Y7 u! K) k5 M, Z6 J方法2:输出“换行符”
- y. Y2 o2 @+ `3 \- S- i( B
- i8 _: j. C3 e B/ }- ]7 P$ d- #include 0 z& V9 c% @6 }+ `1 {
- void main()7 G4 n Y' i8 V- _4 v
- {+ h8 J5 K3 f; V) o! ]8 y7 ]
- printf("run\n");
( o8 t1 p+ c% k7 E! s - while(1) {7 p2 l; p% N+ z8 u8 R9 W4 |
- sleep(10);# q9 F" s4 d* f, N7 D, c$ s$ n' K
- do_thing();
% C) u' _0 n. H9 l7 S. e% p- n - printf("do\n");
. w; B/ p; a& A6 z9 V) k- e - }, n; N! e, }# x( B' a& a; A J
- }
复制代码
3 D; F: h: `4 b- r3 Y |