[Linux]–基础IO
今天在复习的时候,看到Linux的基础I/O,觉得自己有必要再整理下思路,回顾基础I/O的操作。
在这里先写两个关于C语言标准库的输入输出文件的代码
回顾下C语言中的标准库I/O
向Test.c里面写文件
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp=fopen("myfile.txt","w");
if(!=fp)
{
printf("fopen error\n");
}
const char *msg="hello world\n";
int count=5;
while(count--)
{
fwrite(msg,strlen(msg),1,fp);
}
fclose(fp);
return 0;
}
向Test.c里面读文件
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp=fopen("myfile.txt","r");
if(!=fp)
{
printf("fopen error\n");
}
char buff[1024];
const char*msg="hello world";
while(1)
{
ssize_t s=fread(buff,1,strlen(msg),fp);
if(s>0)
{
buff[s]=0;
printf("%s",buff);
}
if(feof(fp)
{
break;
}
}
fclose(fp);
return 0;
}
亲测以下三种C语言方法可以将内容打印到屏幕上
#include <stdio.h>
#include <string.h>
int main()
{
const char*msg="1:hello world\n";
fwrite(msg,strlen(msg),1,stdout);
printf("2:hello world\n");
fprintf(stdout,"3:hello fprintf\n");
return 0;
}
输出的结果是:
1:hello world
2: hello world
3:hello world
准确的说C语言会默认有三个输入输出流,分别是stdin,stdout和stderr
用man手册会发现,这三个流的类型都是FILE*。
系统文件I/O
可以看见,操纵文件除了上述的c接口,c++也同样可以。那Linnux下,设备都是以文件的形式存在的,那当我们要运用这些设备,就是要打开这些文件。那在Linux下我们是如何进行文件的访问呢?
首先是打开文件:
(1) open函数原型:
int open(const char*pathname,int flags,mode_t mode);
函数头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
参数:
pathname:打开或者创建的文件名字,如“myfile.txt";
flags:表示你要进行的操作方式。主要有O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写),O_PPEND(追加写),O_CREAT(创建一个文件),O_TRUNC (打开文件的时候清空原有数据)。其中前三种为必选选项,后三种为可选选项。(当然还有很多选项,读者可自行查阅,这里只列举出常用的几种)
mode:你要创建当前文件的操作权限码,只有在创建的时候,这个参数才会派上用场
返回值:
失败返回-1,成功返回一个非负整数(即文件描述符–打开文件的操作句柄,后面会讲到)
(2) close函数原型
int close(int fd);
头文件:`#include <unistd.h>
参数:这里的参数没什么好说的,调用open后返回的文件操作符
(3) 读取文件
头文件:#include <unistd.h>
函数原型:
ssize_t read(int fd,void buf,size_t count);
参数:
fd:调用open后返回的文件操作符
buf:用来存放从文件中独到的数据的缓冲区
count:读取的字节数
返回值:
成功返回独到的字节数,如果读到文件尾端,则返回0,失败返回-1
(4)写数据
头文件:#include <unistd.,h>
函数原型:ssize_t write(int fd,const voidbuf,size_t count);
参数:
fd:调用open后返回的文件描述符
buf:从未存放数据的缓冲区
count:写入数据的字节数
*返回值:*成功返回已写的字节数,失败返回-1
代码实现
如果说我们要实现开头所说的向Test.c里面写文件,该怎么办呢?这就要用到我们刚才所讲的知识点了,话不多说上代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnl.h>
#include <unistd.h>
#include <string.h>
int main()
{
umask(0);
int fd=open("myfile.txt",O_WRONLY|O_CREAT,0644);
if(fd<0)
{
perror("open error\n");
return -1;
}
int count=5;
const char*msg="hello world\n";
int len=strlen(msg);
while(count--)
{
write(fd,msg,len);
}
close(fd);
return 0;
}
umask是指每次都将文件的权限设置为0
文件描述符
通过对open函数的学习,我们知道文件描述符是一个非负整数,那么他到底是什么东西,能够有这样的能力使我们访问到文件呢?
简单的说,文件操作符就是文件的操作句柄,其实在之前我们还提到过c语言中,标准的输入输出分别是:stdin,stdout,stderr,这刚好就相对于我们的三个文件描述符:0对应的是标准输入,1对应的是标准输出,2对应的是标准错误。因此,一般打开一个现有文件或者创建一个新文件的时候,内核向进程返回一个文件描述符,而也就明白了为什么打开一个文件,文件描述符都是从3开始的,因为前三个已经被占用了。
当我们打开一个文件的时候,在Linux内核中通常会有个task_struct结构体来维护进程相关的表,叫做进程控制块,这个块里面会有一个*file指针指向file_struct的结构体,称为文件描述符表,这个表中包含的是一个指针数组,里面的元素就是指向打开文件的指针,一般情况下,进程是没有办法直接访问文件的,只能通过文件描述符,就是这个表的索引来找到文件。
重定向
先来看一段代码:
#include <stdio.h>
#include <sys/type.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
//close(2);
int fd=open("myfile.txt",O_RDONLY);
if(fd<0)
{
perror("open file");
return 1;
}
printf("fd:%d\n",fd);
close(fd);
return 0;
}
在这段代码中,我关闭了0(或者关闭2),发现输出是fd:0(当关闭2时,输出为fd:2),这是什么意思?就是说我们的文件操作符的分配规则就是,在file_struct数组当中,找到当前没有被使用的最小下标,作为新的文件描述符。
问题来了,那为什么不关掉1呢?
#include <stdio.h>
#include <sys/type.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(1);
int fd=open("myfile.txt",O_WRDONLY|O_CREAT,0664);
if(fd<0)
{
perror("open error");
return 1;
}
printf("fd:%d\n",fd);
fflush(stdout);
close(fd);
return 0;
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件myfile.txt里面,其中fd=1,这种现象叫做重定向。
什么意思呢?原因是因为我们关闭了1,也就是标准输出,当我们打开一个新文件的时候,会按照文件描述符的分配原则将1分配给这个文件,凡是在1号的文件描述符写的内容,都写到了myfile.txt中,而不是写到标准输出。
Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出,而是希望输出到某一文件中,就可以通过Linux重定向来进行这项工作。懂了吗?