函数原型:int open(const char *pathname,int flags)
pathname:要打开或者创建的目标文件。
flags:打开要做什么,基本上有O_WRONLY(只写) O_RDONLY(只读) O_CREAT(创建)O_APPEND(追加)
返回值:成功返回文件描述符
失败返回 -1;
如下代码:
函数原型:
int write(int _filehandle,const void *_Buf,unsigned int _Maxcharcount)
_filehandle,文件描述符,_Buf写入的字符,_Maxcharcount字符长度
相比较于其他接口,均是如此,如read/close/lseek
但对于open的返回值fd为什么是3呢,此时就牵扯到一个文件描述符的概念。
通过对open的了解,我们知道文件描述符fd是一个整数。
linux系统会默认打开三个接口,0/1/2,这三个接口分别stdin,stdout,stderr,分别对应键盘,显示器,显示器。
而我们也可以使用如下的方式进行输出,通过读0,输出到1和2
- void test2()
- {
- char buf[1024];
- ssize_t s = read(0, buf, sizeof(buf));
- if(s > 0)
- {
- buf[s] = 0;
- write(1, buf, strlen(buf));
- write(2, buf, strlen(buf));
- }
- }
文件描述符就是从0开始的小整数,当我们打开文件后,就类似与创建进程一样,先描述再组织,使用task_struct描述这个文件的信息,被file_struct管理,使用指针指向数组的下标实现,再通过内核去管理这个文件的信息 ,只要知道这个数组下标,就能找到在内核中对应的信息。
- void test3()
- {
- close(1);
- int fd=open("log.txt",O_WRONLY);
- printf("i am printf\n");
- fprintf(stdout,"i am fileskkkk\n");
- fflush(stdout);
- close(fd);
- }
此时我们关闭1号描述符,打开log.txt,使用输出到屏幕,发现屏幕上没有打印任何数据,打开log.txt,发现 i am printf 和i am fileskkkk都写到文件当中了,所以通过这个我们可以看到,
文件描述符会优先分配到未被使用的最小下标。
一般缓冲分为三种:
无缓冲(系统接口)
行缓冲(常见的对显示器进行刷新数据)(方便人机交互)
全缓冲(对文件的写入使用全缓冲)
缓冲一般由语言层提供,os也有缓冲(为了减少对磁盘的访问)
通过上面程序的演示,我们大概可以知道,关闭标准输出,可以将信息输出到文件中,这就是一种重定向,本质是修改文件描述符fd下标对应的struct_file*里面。
我们再来看看下面的程序;
- void test4()
- {
- close(1);
- int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
- if(fd < 0)
- {
- perror("open");
- }
- printf("fd: %d\n", fd);
- fflush(stdout);
- close(fd);
- }
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <,更加说明了这种现象,
使用dup2系统调用
函数原型:
int dup2(int oldfd,int newfd);
dup2(fd,1),如这个一样,用fd覆盖1号文件描述符
这个函数的功能在于覆盖式的把旧的文件描述符给新的文件描述符,两个文件描述符共享权限。
- #include<stdio.h>
- #include<string.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- #include<fcntl.h>
- int main()
- {
- printf("i am printf\n");
- fprintf(stdout,"i am fprintf\n");
- fputs("i am fputs",stdout);
- fork();
- return 0;
- }
我们发现此时打印了6条语句,这是因为什么呢,
其原因在于父进程打印完以后,fork创建出子进程,父子进程共享一段代码,父子数据发生写时拷贝,因为进程具有独立性,子进程也执行了三条语句。
所以会输出6行。
但当我们使用如下代码时,
- int main()
- {
- const char*msg="I am write\n";
- printf("i am printf\n");
- fprintf(stdout,"i am fprintf\n");
- write(1,msg,strlen(msg));
- fork();
- return 0;
- }
会发现打印了5行数据,
这是因为,当我们在往屏幕上打印数据时,会存在一个缓冲的概念,我们使用C库函数打印的时候,会先缓冲到C提供的缓冲区里面,子进程写时拷贝,也会缓冲到缓冲区,由于系统的接口没有缓冲区,所以会直接刷新到屏幕,再把父子进程的缓冲区刷新到屏幕上,所以打印五行数据。
注意:重定向还是不重定向不会更改进程的缓冲方式
C接口打印两次,osAPI打印1次
1,语言层的输入输出均封装了系统给的API接口
2,文件描述符就是类似进程,先描述(task_struct),在组织(file_struct),通过file提供的数组下标存文件的指针,然后系统对这个指针管理,只要知道下标,就能在内核中找到信息。
3,重定向就是把本来要输出(输入)通过文件描述符,输出或者输入到我们想让文件去的地方
4,语言有自己的缓冲区,操作系统也有,互不影响