文章目录
前言
UNIX系统中大多数文件I/O只需要5个函数:open()、read()、write()、lseek()、close(),这些函数通常被称为不带缓冲的I/O(unbufferedI/O),不带缓冲指的是每个read和write都调用内核中的一个系统调用。
以下是本篇文章正文内容,下面案例可供参考
一、文件描述符
Linux的遵旨是“一切皆文件”。所有内核中打开的文件都将通过文件描述符(filedescriptor,fd)引用,文件描述符是一个非负整数,(且通常是一个小整数),当打开一个现有文件或者创建一个新文件时,内核会向进程返回一个描述符fd,当读写一个文件时,使用open或者create返回的文件描述符标识该文件,并将其作为参数传送给read 或者write。
程序开始运行时,系统会自动打开三个文件描述符0、1、2,这是因为UNIX系统脚本把文件描述符0与进程的标准输入相关联,把文件描述符1和进程的标准输出相关联,把文件描述符2和进程的标准出错相关联。这是各种脚本以及应用程序必须遵循的惯例,与UNIX内核无关。
文件描述符 | 用途 | 符号常量 | 标准I/O文件流 |
---|---|---|---|
0 | 标准输入 | STDIN_FILENO | stdin |
1 | 标准输出 | STDOUT_FILENO | stdout |
2 | 标准出错 | STDERR_FILENO | stderr |
二、文件I/O操作函数
1.open()函数
函数说明:
打开或者创建一个文件,并返回一个文件描述符(fd),该文件描述符是当前进程最小,未使用的文件描述符数值。
函数原型:
#include<fcntl.h>
int open(const char *path,int oflag,.../*mode_t mode*/);
int openat(int fd,const char *path,int oflag,.../*mode_t mode*/);
参数说明:
path是要打开或者创建的文件的名字;
oflag用来说明此函数的多个选项,用下列一个或多个常量进行“或”运算构成oflag参数
必选:
O_RDONLY:只读打开;
O_WRONLY:只写打开;
O_RDWR:读写打开,
可选:
O_EXEC:只执行打开;
O_SEARCH:只搜索打开(应用于目录);
O_APPEND:每次写时追加到文件的末尾;
O_CLOEXEC:把FD_CLOEXEC常量设置为文件描述符标志;
O_CREAT:若此文件不存在则创建它。
O_DIRECTORY:如果path引用的不是目录,则出错;
O_EXCL:如果同时指定了O_CREAT,则文件已经存在,则出错;
O_NOCTTY:如果path引用的是终端设备,则不该将设备分配作为此进程的控制端;
由open函和openat函数返回的文件描述符一定是最小的未用描述符数值。关于open和openat的区别,可参考链接:
open和openat的区别
2.creat()函数
函数说明:
创建一个新文件,若成功,则返回为只写打开的文件描述符,若出错,则返回-1.
函数原型:
#include<fcntl.h>
int creat(const char *path,mode_t mode);
参数说明:
请参考open参数说明。
3.close()函数
函数说明:
关闭一个打开的文件描述符并且释放该进程加在该文件上的所有记录锁,当一个进程终止时,内核将会自动关闭它所有打开的文件。
函数原型:
#include<unistd.h>
int close(int fd);
4.lseek()函数
函数说明:
当我们从文件中读取内容或者写入内容时,系统会记录从起始地址到我们读写结束的地址并且文件在这个过程中使得文件偏移量往后移,每个打开的文件都有一个对应的“文件偏移量”,通常是非负整数,用以度量文件开始时计算的字节数,这就相当于我们用word写文章是,系统会自动记录我们写了多少字,而里面的光标就等同于文件偏移量。
lseek执行成功,则返回新的文件偏移量,
函数原型:
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence);
参数说明:
fd为返回一个文件描述符;
offset和参数whence 的值相关
whence参数 | 位置 | offset |
---|---|---|
SEEK_SET | 文件头 | 文件偏移量在第一字节 |
SEEK_CUR | 当前位置 | 文件偏移量设置为当前位置 |
SEEK_END | 文件尾 | 文件偏移量在最后一个字节 |
**注:lseek只是把当前的文件偏移量记录在内核中,并不引起任何I/O操作;
空洞:
文件偏移量可以大于当前文件的长度,位于文件中但没有被写过的字节读为0.空洞并不要求占用磁盘的存储区,当定位超出文件尾端之后写时,新写的数据需要分配磁盘块,但是文件尾端和新开始写位置之间不需要分配磁盘块。
5.read()函数
函数说明:
从打开的文件中读数据并放到buf指定的内存空间中去,读取成功则返回到读到的字节数,如果已经达到尾端则返回0。大多数情况下,实际读到的字节数少于要求读的字节数。当读取一个没有读取权限的文件时会报错。
函数原型:
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t nbytes);
参数说明:
fd:从打开的文件描述符中读取数据;
*buf:将读取到的数据存放到内存空间中去;
nbytes:读取的数据最多不要超过nbytes个字节。
5.write()函数
函数说明:
往打开的文件中写入数据并放到buf指定的内存空间中去,写入成功则返回到已写的字节数,
函数原型:
#include<unistd.h>
ssize_t write(int fd,const void *buf,size_t nbytes);
参数说明:
其参数说明和read()函数参数说明类似,返回值应当与参数nbytes的值相同,否则标识出错,一般情况下是因为磁盘已满,或者超过了一个给定进程的文件长度限制。
6.dup()函数和dup2()函数
函数说明:
这两个函数都可以用来复制一个新的文件描述符来指向fd对应的文件,这两个系统调用经常用在标准输入、标准输出、标准出错重定向。
函数原型:
int dup(int fd);
int dup2(int fd,int fd2);
说明:
dup()返回的文件描述符一定是当前可用文件描述符中的最小数值;
dup2可以fd2参数来指定新的文件描述符,如果fd2已经打开,则先关闭,如果fd等于fd2,则dup2返回fd2,而不关闭它;否则,fd2中的FD_CLOEXEC文件描述符标志就被清除
dup2(fd,STDIN_FILENO); //标准输入重定向到std.txt文件中去
dup2(fd,STDOUT_FILENO); //标准输入重定向到std.txt文件中去
dup2(fd,STDERR_FILENO); //标准输入重定向到std.txt文件中去
三、C程序读取树莓派DS18B20获取温度
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 #include<dirent.h>
6 #include<string.h>
7 #include<time.h>
8 #include<errno.h>
9
10 int ds18b20_get_temperature(float *temp);//
11
12 int main(int argc, char **argv)
13 {
14 float temp;
15
16 if(ds18b20_get_temperature(&temp)<0)
17 {
18 printf("ERROR:ds18b20 get temperature failure\n");
19 return 1;
20 }
21 printf("ds18b20 get temperature: %.3f\n",temp);
22 return 0;
23 }
24
25 int ds18b20_get_temperature(float *temp)
26 {
27 char w1_path[50] = "/sys/bus/w1/devices/";
28 char chip[20];
29 char buf[128];
30 DIR *dirp;
31 struct dirent *direntp;
32 int fd = -1;
33 char *ptr;
34 float value;
35 int found = 0;
36
37 if(!temp)
38 {
39 return -1;
40 }
41
42 if((dirp = opendir(w1_path)) == NULL)/*打开文件夹并返回给dirp*/
43 {
44 printf("opendir error:%s\n",strerror(errno));
45 return -2;
46 }
47
48 while((direntp = readdir(dirp)) !=NULL) /*从文件夹中读取内容并判断是否为空*/
49 {
50 if(strstr(direntp->d_name,"28-")) /**/
51 {
52 strcpy(chip,direntp->d_name);
53 found = 1;
54 }
55 }
56 closedir(dirp);/*关闭文件夹*/
57
58 if(!found)
59 {
60 printf("can not find ds18b20 in %s\n",w1_path);
61 return -3;
62 }
63 strncat(w1_path,chip,sizeof(w1_path)-strlen(w1_path));
64 strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));
65 if((fd=open(w1_path,O_RDONLY)) < 0)
66 {
67 printf("open %s error: %s\n",w1_path,strerror(errno));
68 return -4;
69 }
70 if(read(fd,buf,sizeof(buf)) < 0)
71 {
72 printf("read %s error: %s\n",w1_path,strerror(errno));
73 return -5;
74 }
75
76 ptr = strstr(buf,"t=");
77 if(!ptr)
78 {
79 printf("ERROR:Can not get temprature\n");
80 return -6;
81 }
82
83 ptr+=2;
84 *temp = atof(ptr)/1000;/*ASCII值转换为浮点型数值*/
85 close(fd);
86 return 0;
87 }
88
运行结果:
恭请指正