IO部分总是很乱。不知道是因为API多还是其它原因:
0. 阻塞的存在
1. glibc的section3手册和linux的section2有同名函数。
2. posix, ISO都有要求
3. terminal IO与其它IO有不同的表现
4. IO缓存的机制
5. 还有众多古老的名词....
EOF
EOF是什么? 什么时候才会产生? 还是和ctrl_c一样是signal ?
如下代码的运行結果是 -1
19 #include <stdio.h>
20
21 int
22 main(int argc, char *argv[])
23 {
24 printf("%d\n", EOF);
25 return 0;
26
27 }
表示EOF是 -1 ?
当某一次使用 ssize_t read(int fd, void *buf, size_t count); 函数从一个file descriptor中读数据时(count>0), 函数得到0个字节表示读到文件的尾部了. 这时read函数返回0.
可读端的pipe关闭后, 只读端pipe在读完后会产生EOF:
2 import os
3 import sys
4 import time
5
6 read_end, write_end = os.pipe()
7
8 pid = os.fork()
9 if pid < 0:
10 print "Error"
11 sys.exit(1)
12 if pid == 0:
13 # parent
14 os.close(write_end)
15 try_time = 1
16 # 因为无法得到child process会发多少数据, 只能使用死循环去读数据
17 while True:
18 print "%s time(s) call read" % try_time
19 content = os.read(read_end, 1)
20 if not content:
21 break
22 os.write(1, content)
23 try_time += 1
24 sys.exit(0)
25
26 else:
27 # child
28 os.close(read_end)
29 os.write(write_end, "#" * 100)
30 # 关了parent就能读到EOF
31 os.close(write_end)
32 sys.exit(0)
文件系统中的文件是有固定大小(某一状态), 但是在terminal IO里,是没有一个固定的尾部的, 需要输入者指定什么时候才是end-of-file, 所以, ctrl_D出现了. ctrl_D是一个特殊控制符。被tty的驱动处理(默认情况下用户程序不能从read函数得到, 除非要求tty不处理)
在了解ctrl_D前, 应该先要了解IO的缓存机制.
standard IO缓存 与 底层IO缓存
在此这前, 我对getchar这个函数不解, 其实就是不解如下的代码:
19 #include <stdio.h>
20
21 int main(int argc, char *argv[]){
22 char c ;
23 while(c = getchar() ){
24 printf("%c\n", c);
25 }
26 }
不要以为简单调用getchar函数可以实现vim这种交互.
当调用read去读stdin的数据时, 是行缓存的.上面的代码在第一次调用getchar时把一行的数据读到buffer里(回车键返回), 下次再调用getchar时会从buffer里取. buffer没有数据后再等待用户输入.
标准IO与系统底层IO这两套IO需要分开. 不然会出现事与愿违的情况, 如:
使用setbuf对FILE对象进行了设置后, 然后使用调用printf操作. 結果没有出现想要的设置效果。例如:
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26
27
28 int main(int argc, char *argv[]){
29 //
30 FILE *input = fdopen(1, "r");
31 FILE *output = fdopen(1, "w");
32 setbuf(input, NULL);
33 setbuf(output, NULL);
34 char c ;
35
36 printf("Hello World");
37 sleep(10);
38 }
上面的printf的内容是过了10秒后才打印的, 明显, setbuf没有起作用。 但是如果修改为如下就不同了:
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26
27
28 int main(int argc, char *argv[]){
29 //
30 FILE *input = fdopen(1, "r");
31 FILE *output = fdopen(1, "w");
32 setbuf(input, NULL);
33 setbuf(output, NULL);
34 char c ;
35
36 fprintf(output, "Hello World");
37 sleep(10);
38 }
上面代码的区别在于:printf使用了stdin这个FILE对象(默认是line buffer), fprintf使用了指定的FILE对象。standard IO的缓存是通过修改FILE对象实现的。
read和write会不会缓存, 缓存机制是什么? 这与read, write操作的对象有很大的关系, 例如:
28 int main(int argc, char *argv[]){
29
30 while(1){
31 char c;
32 (void *)read(0, &c, 1);
33 (void *)write(1, &c, 1);
34 }
35 }
上面虽然使用了read, write, 还是用户输入还是行缓存的, 无法像vim一样交互操作。这时要想非行缓存, 需要操作到tty的缓存机制。 如下的一段python代码设置了tty的缓存方式:
1 #coding:utf-8
2 import os
3 import sys
4 import termios
5
6 STDIN_FILENO = sys.stdin.fileno()
7
8 old_attr = termios.tcgetattr(STDIN_FILENO)
9 new_attr = termios.tcgetattr(STDIN_FILENO)
10
11 new_attr[3] &= ~ (termios.ICANON | termios.ECHO)
12 termios.tcsetattr(STDIN_FILENO, termios.TCSADRAIN, new_attr)
13 try:
14 print '请使用vim的移动键'
15 while True:
16 c= os.read(STDIN_FILENO, 1)
17 if c == 'j':
18 print "下"
19 elif c == 'k':
20 print "上"
21 elif c == 'h':
22 print '左'
23 elif c == 'l':
24 print '右'
25 except KeyboardInterrupt, e:
26 termios.tcsetattr(STDIN_FILENO, termios.TCSADRAIN, old_attr)
回到ctrl_D的问题
在terminal IO里, 因为默认是使用缓存的, 赋于ctrl_D两种功能:
1. 相当于flush操作. 即时把输入的数据返回给read函数, 不用等待'\n'的出现。
2. 让tty返回0字节给read函数. 表示EOF。 在单独输入ctrl_D后产生这种效果
控制符
控制符都是tty驱动提供的一种方便功能, 有少量的控制符会产生signal的:
^Z和^C
还有少量的是无法修改的:
\r和\n
如下代码把“a“作为backspace功能:
1 #coding:utf-8
2 import os
3 import sys
4 import termios
5
6 STDIN_FILENO = sys.stdin.fileno()
7
8 old_attr = termios.tcgetattr(STDIN_FILENO)
9 new_attr = termios.tcgetattr(STDIN_FILENO)
10
11 new_attr[6][termios.VERASE] = 0x61
12 termios.tcsetattr(STDIN_FILENO, termios.TCSADRAIN, new_attr)
13 try:
14 print "请使用a键删除数据"
15 os.read(0,100)
16 except KeyboardInterrupt, e:
17 termios.tcsetattr(STDIN_FILENO, termios.TCSADRAIN, old_attr)
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.