文件描述符
在linux中open函数调用成功后会返回一个非负整数,这个非负整数就是文件描述符。
Linux 默认情况下会缺省打开三个文件描述符 标准输入0 标准输出1 标准错误2
0 1 2 对应的物理设备一般是: 键盘 显示器 显示器
文件描述符就是从0开始的小整数。当我们打开文件时,操作系统需要创建相应的数据结构来描述目标文件 file结构体 表示一个已经打开的文件对象。当进程执行open系统调用时必需要将文件和进程关联起来。每个进程都有一个指针files指向一张表files_struct* ,该表中有一个指针数组 file* fd_array[] ,这个数组中的每一个元素都指向一个已打开的文件,而文件描述符就是该指针数组的下标。
文件描述符的分配规则
在files_struct数组当中,找到没有被使用的最小的一个下标作为文件描述符。
在默认情况下,0 1 2 三个文件描述符是已经打开的,新创建的文件从3开始,当0或1,2有被关闭的从最小的开始
重定向
输出重定向
#include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <stdlib.h>
5 #include <fcntl.h>
6
7 int main()
8 {
9 umask(0);
10 close(1);
11 int fd=open("myfile.txt",O_WRONLY|O_CREAT ,0644);
12 if(fd<1)
13 {
14 perror("open");
15 return 1;
16 }
17
18 printf("fd: %d\n",fd);
19 fflush(stdout); //往显示器上刷新缓存区
20
21 close(fd);
22 return 0;
23 }
[wens@localhost code2]$ make
gcc -o test test.c
[wens@localhost code2]$ ./test
[wens@localhost code2]$ cat myfile.txt
fd: 1
本来应该输出到屏幕上的内容,输出到了文件myfile.txt中
printf是c库中的io函数,一般往stdout输出,但是stdout底层访问文件时,找的还是fd=1;但是1被关闭后,mefile.txt被创建后,放入了下标为fd=1的位置,所以原本是stdout的位置,此时已将是myfile.txt的位置了,所以输出的消息都输出进了myfile.txt 进而完成输出重定项。
追加重定项:
关掉 1
创建文件时 参数加入追加O_APPEND
输入重定项: 关掉0 创建文件 再从标准输入读取 即完成
FILE
file 结构体是c库提供的文件结构体,其中包含了文件描述符fd,以及自带的缓冲区
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <stdlib.h>
5 #include <fcntl.h>
6 #include <string.h>
7 #include <unistd.h>
8 int main()
9 {
10 const char* msg0="printf\n";
11 const char* msg1="fwrite\n";
12 const char* msg2="write\n";
13
14 printf("%s",msg0);
15 fwrite(msg1,1,strlen(msg1),stdout);
16 write(1,msg2,strlen(msg2));
17
18 fork();
19 return 0;
20 }
输出结果
wens@localhost code2]$ vim test.c
[wens@localhost code2]$ make
gcc -o test test.c
[wens@localhost code2]$ ./test
printf
fwrite
write
[wens@localhost code2]$ touch file
[wens@localhost code2]$ ./test >file #对输出进行重定项
[wens@localhost code2]$ cat file
write
printf
fwrite
printf
fwrite
[wens@localhost code2]$
缓冲方式
- 无缓冲:不刷新缓冲区
- 行缓冲:按行进行刷新
- 全缓冲:默认情况下只有把缓冲
区写满时才刷新出去,除非强制新
与文件类型有关:
- 写入显示器对应行缓冲
- 写入普通文件对应全缓冲
printf 和fwrite 输出了两次write输出了一次。
c库函数般自带缓冲区,当重定项到普通文件中时,行缓冲转换为全缓冲,此时缓冲区中的文件不会被刷新,直到调用fork之后,父子进程代码共享,因为缓冲区中的数据未被刷新,因此子进程写时拷贝私有一份,直到进程缓冲区刷新。产生两份数据。
write未发生变化,说明write没有缓冲区
printf fwrite C库函数自带缓冲区,write 系统调用接口路没有。
实现简单的模拟shell程序
添加输入 输出 追加 重定项
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/wait.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8
9
10 int main()
11 {
12
13 char cmd[1024];
14 char* myargv[16];
15 int fd=-1;
16 while(1)
17 {
18
19 printf("[wens@localhost myshell]# ");
20
21 fgets(cmd,sizeof(cmd),stdin);
22
23 int i=0;
24
25 cmd[strlen(cmd)-1]='\0';
26
27 myargv[i++]=strtok(cmd," ");//将字符串分割解析
28
29 char *ret=NULL;
30
W> 31 while(ret=strtok(NULL," "))
32 {
33 myargv[i++]=ret;
34 }
35
36 myargv[i]=NULL;
37
38 // for( j=0;j<i;j++)
39 // printf("%s\n",myargv[j]);
40
41
42
43
44
45
46 int j=0;
47 pid_t id=fork();
48 if(id==0)
49 {
50 // printf("child\n");
51
52 for(j=0;j<i;j++)
53 {
54 if(strcmp(myargv[j],">")==0) // 输出重定项
55 {
56 close(1);
57 fd=open(myargv[j+1],O_WRONLY|O_CREAT,0644);
58 //srv printf("%d\n",fd);
59 if(fd<0)
60 {
61 perror("open");
62 exit(1);
63 }
64 myargv[j]=NULL;
65 i=j;
66 break;
67 }
68 else if(strcmp(myargv[j],"<")==0) //输入重定项
69 {
70 close(0);
71 fd=open(myargv[j+1],O_RDONLY,0644);
72 if(fd<0)
73 {
74 perror("open");
75 exit(1);
76 }
77
78 myargv[j]=NULL;
79 i=j;
80 break;
81 } else if(strcmp(myargv[j],">>")==0) //追加重定项
82 {
83 close(1);
84 fd=open(myargv[j+1],O_WRONLY|O_CREAT|O_APPEND,0644);
85 if(fd<0)
86 {
87 perror("open");
88 exit(1);
89 }
90
91 myargv[j]=NULL;
92 i=j;
93 break;
94 }
95
96 }
97 execvp(myargv[0],myargv);
98 //进行程序替换
99
100 exit(1);
101
102 }else{
103 // printf("parent\n");
104 waitpid(id,NULL,0); //等待子进程
105 }
106 }
107
108
109 return 0;
110
111 }
输出重定项
[wens@localhost newshell]$ ./newshell
[wens@localhost myshell]# ls
Makefile newshell newshell.c
[wens@localhost myshell]# ls > pp
[wens@localhost myshell]# cat pp
Makefile
newshell
newshell.c
pp
[wens@localhost myshell]#
输入重定项
[wens@localhost myshell]# wc < pp
9 14 116
[wens@localhost myshell]# cat < pp
Makefile
newshell
newshell.c
pp
newshell:newshell.c
gcc -o newshell newshell.c
.PHONY:clean
clean:
rm -f newshell
[wens@localhost myshell]#
追加重定项
[wens@localhost myshell]# cat Makefile >> pp
[wens@localhost myshell]# cat pp
Makefile
newshell
newshell.c
pp
newshell:newshell.c
gcc -o newshell newshell.c
.PHONY:clean
clean:
rm -f newshell
[wens@localhost myshell]#