一,概述:
所有的IO操作的系统的系统调用都是以文件描述符,一个非负整数来指代打开的文件,文件描述符用于表示所有类型的已打开文件,包括特殊文件(管道,socket
套接字)等。
在linux
的shell
操作中有三个默认的文件描述符
文件描述符 | 用途 |
---|---|
0 | 标准输入 |
1 | 标准输出 |
2 | 标准错误 |
注:对应的POSIX
接口分别为:STDIN_FILENO
STDOUT_FILENO
STDERR_FILENO
,而相对应的流分别为stdin
,stdout
,stderr
。
如果调用POSIX
标准名称则需要包含头文件<unistd.h>
注:linux I/O模型的显著特点之一是其输入和输出的通用性概念,这说明使用4个同样的系统调用open(),read(),write(),close()可以对所有类型的文件进行IO操作,包括终端设备如果
应用程序需要访问文件系统或设备的专有功能时,可以使用ioctl()系统调用,该调用为除了IO通用模型外提供了别的访问接口。
二,一些文件系统调用
1>open函数打开文件并且返回文件描述符
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname,int flags/*mode_t mode*/*);/*mode为创建文件的权限比如0600,0666*/
pathname为符号链接,也就是文件路径。
flags为掩码指定问及那的访问形式,下表是常用的的
标志 | 用途 |
---|---|
O_RDONLY | 只读权限打开 |
O_WRONLY | 只写的权限打开 |
O_RDWR | 可读可写的权限打开 |
O_CREAT | 若文件不存在则创建 |
O_DIRECT | 无缓冲的输入输出 |
O_EXCL | 结合O_CREAT使用专门用于创建文件 |
O_NONBLOCK | 以非阻塞的方式打开 |
上表的常量分为三组:
*文件访问模式标志:可读可写,只读,只写只能选择一个,并且调用fcntl()的F_GETFL可以获取访问模式
*文件创建标志:创建O_CREAT等
*已打开的文件状态标志:使用fcntl()的F_GETFL和F_SETFL来检索和修改
2>使用creat()直接创建文件
#include<fcntl.h>
int creat(const char *pathname,mode_t mode);
注:若文件存在则打开文件,并且清空文件内容,将其长度请0,返回fd
和open使用open(pathname,O_WRONLY|O_CREAT|O_TRUNC,mdoe);
3>使用read()函数调用
#include <unistd.h>
ssize_t read(int fd,void*buf,size_t count);
/*read遇到\n等东西时候,read调用会停止
如果要读取带\n的东西时候需要追加\0*/
4>write函数
#include <unistd.h>
具体函数
ssize_t write(int fd,const void * buf,size_t n);
示例:
使用read函数和write函数简单实现对文件操作:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
char *buf = {"hello world"};
fd = open("./file1",O_RDWR);
if(fd == -1){
printf("open file1 error\n");
fd = open("./file1",O_RDWR|O_CREAT,0600);
if(fd > 0){
printf("create file1\n");
}
}
printf("open suceess! fd = %d\n",fd);
int n_write = write(fd,buf,strlen(buf));
if(n_write !=-1){
printf("write %d byte success!\n",n_write);
}
close(fd);
open("./file1",O_RDWR);
char *buf2;
buf2 = (char *)malloc(sizeof(char)*n_write + 1);
int n_read = read(fd,buf2,n_write);
printf("read %d , contant : %s\n",n_read,buf2);
close(fd);
return 0;
}
上面代码有一个缺陷二次读写是无法读到数据的,因为对于每个打开的文件来说,系统内核都会记录文件偏移量(有时称为读写偏移量或者指针),文件偏移量是执行下次read,write操作的文件首位置,会以相抵与文件头部起始点的文件当前位置来表示,文件第一个字节的偏移量为0;
5>lseek()函数改变文件偏移量
函数原型
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*函数掉用成功后会返回新的文件偏移量*/
offset指定了一个以字节为单位的数值,
whence参数如下:
宏 | 意义 |
---|---|
SEEK_SET | 将文件偏移量设置为相对于文件开头的offset个字节 |
SEEK_CUR | 相对于当前文件偏移量,将文件偏移量调整offset个字节 |
SEEK_END | 相对于文件尾部的offset个字节 |
使用lseek函数简单实现一个测试文件大小的demo
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = {"hello world!"};
fd = open("./file1",O_RDWR);
int filesize = lseek(fd,0,SEEK_END);
printf("file is %d\n",filesize);
close(fd);
return 0;
}
三,通用IO模型外的操作
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
注:常用于未纳入IO模型的所有设备。经常用于设备驱动等
四,几个简单的小demo
1>实现简单的复制
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc ,char **argv)
{
int fdsrc,fddes;
char *buf2 = NULL ;
if(argc != 3){
printf("error\n");
exit(-1);
}
fdsrc = open(argv[1],O_RDWR);
int size = lseek(fdsrc,0,SEEK_END);
buf2 = (char *)malloc(sizeof(char)*size + 8);
lseek(fdsrc,0,SEEK_SET);
int n_read = read(fdsrc,buf2,size);
fddes = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
int n_write = write(fddes,buf2,n_read);
close(fdsrc);
close(fddes);
return 0;
}
2>写入一些特殊的数据,比如结构体
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct test{
int a;
char c;
};
//linke read one by one
int main()
{
int fd;
struct test data[2] ={{ 100,'a'},{101,'b'}};
struct test data2[2];
fd = open("./file1",O_RDWR);
int n_write = write(fd,&data,sizeof(struct test)*2);
lseek(fd,0,SEEK_SET);
int n_read = read(fd,&data2,sizeof(struct test)*2);
printf("data2 = %d\n",data2[0].a);
printf("data2.c = %c\n",data2[0].c);
printf("%d %c\n",data2[1].a,data2[1].c);
close(fd);
return 0;
}
~
3>标准库的操作(使用fopen(),fclose(),fread(),fwrite()等)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct test{
int a;
char c;
};
int main(int argc ,char **argv)
{
FILE *fp;
struct test data ={ 100,'a'};
struct test data2;
fp = fopen("./file1","w+");
int n_write = fwrite(&data,sizeof(struct test),1,fp);
fseek(fp,0,SEEK_SET);//相当于lseek函数,改变文件偏移量
int n_read = fread(&data2,sizeof(struct test),1,fp);
printf("data2 = %d\n",data2.a);
printf("data2.c = %c\n",data2.c);
fclose(fp);
return 0;
}
~