Linux下文件I/O详解与DS18B20温度采集

1. 文件I/O与标准I/O区别

文件I/O:文件I/O称之为不带缓存的IO(unbuffered I/O),不带缓存指的是每个read和write都调用内核中的一个系统调用,也就是一般所说的低级I/O——操作系统提供的基本IO服务,与os绑定,特定于Linux或Unix平台。这些不带缓存的I/O函数不是ANSI C的组成部分,但是是P O S I X . 1和X P G 3的组成部分。

标准I/O:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性。标准I/O库处理很多细节。例如缓存分配,以优化长度执行I/O等。标准的I/O提供了三种类型的缓存。

它们函数使用的区别如下:在这里插入图片描述
这里不再过多讨论标准I/O,目前只以文件I/O作为讲解。

大多数Unix文件I/O只需用到5个函数:open、read、write、lseek 和 close

2. 文件描述符

对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开
一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用o p e n或c r e a t返回的文件描述符标识该文件,将其作为参数传送给 r e a d或w r i t e。
默认情况下,程序在开始运行时,系统会自动打开三个文件描述符,0是标准输入,1是标准输出,2是标准错误。POSIX标准要求每次打开文件时(含socket)必须使用当前进程 中最小可用的文件描述符号码,因此第一次打开的文件描述符一定是3。

文件描述符 用途 POSIX文件描述符 标准I/O文件流
0 标准输入 STDIN_FILENO stdin
1 标准输出 STDOUT_FILENO stdout
2 标准出错 STDERR_FILENO stderr

3. open / creat函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int oflag,  ...  /* mode_t m o d e */ ) ;
int creat(const char *pathname, mode_t mode);

//返回:若成功为文件描述符,若出错为- 1

open()系统调用用来打开一个文件,并返回一个文件描述符(file description), 并且该文件描述符是当前进程最小、未使用的 文件描述符数值。

参数:
pathname: 要打开的文件、设备的路径;
oflag: 由多个选项进行或运算构造oflag参数。
必选:

  • O_RDONLY (只读)
  • O_WRONLY(只写)
  • O_RDWR(读写)

可选:

  • O_APPEND 每次写时都追加到文件的尾端。
  • O_CREAT 文件不存在则创建它,使用该选项需要第三个参数mode
  • O_TRUNC 如果文件存在,而且为只写或读写成功打开,则将其长度截取为0;
  • O_NONBLOCK 如果path是一个FIFO、块设备、字符特殊文件则此选项为文件的本次打开和后续的I/O操作 设置非阻塞模式方式。
  • O_EXEC、O_SEARCH、O_CLOEXEC、O_NOCTTY…

mode: oflag带O_CREAT选项时可以用来创建文件,这时必须带该参数用来指定创建文件的权限模式,如0666。 否则不需要。
注意,以下此函数等价:

/* 两者等价 */
creat(pathname, mode);
open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)  

在早期的U N I X版本中, o p e n的第二个参数只能是 0、 1或2。没有办法打开一
个尚未存在的文件,因此需要另一个系统调用 c r e a t以创建新文件。现在, o p e n函
数提供了选择项O _ C R E AT和O _ T R U N C,于是也就不再需要c r e a t函数了。

c r e a t的一个不足之处是它以只写方式打开所创建的文件。在提供 o p e n的新版本之前,如果要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用 c r e a t, c l o s e,然后再调用o p e n。现在则可用下列方式调用o p e n:

open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode)  

4. close函数

可用c l o s e函数关闭一个打开文件:

#include <unistd.h>
/* 关闭打开的文件
 * @return      成功返回0,出错返回-1 */
int close(int fd);

功能:指定相应的文件描述符就可以关闭打开的文件。
参数:fd文件描述符,是open或者creat返回的非负整数。

5. lseek函数

每个打开文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,用以度量
从文件开始处计算的字节数。 通常,读、写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定O _ A P P E N D选择项,否则该位移量被设置为0。
可以调用l s e e k显式地定位一个打开文件。

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int filedes, off_to ffset, int whence) ;
//返回:若成功为新的文件位移,若出错为- 1

对参数offset 的解释与参数w h e n c e的值有关。

  • 若w h e n c e是S E E K _ S E T,则将该文件的位移量设置为距文件开始处 offset 个字节。
  • 若w h e n c e是S E E K _ C U R,则将该文件的位移量设置为其当前值加offset, offset可为正或负。
  • 若w h e n c e是S E E K _ E N D,则将该文件的位移量设置为文件长度加offset, offset可为正或负。

普通文件的偏移量必须是非负整数。偏移量可以大于文件的长度,这样之后的写会形成一个空洞,空洞不占存储,其中的字节被读为0。

6. read / write函数

用r e a d函数从打开文件中读数据。

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//返回:读到的字节数,若已到文件尾为 0,若出错为- 1

ssize_t write(int fd, const void *buf, size_t count);
//返回:若成功为已写的字节数,若出错为- 1

带符号整数( ssize_t);不带符号整数(size_t)。
参数:
fd:相应文件的文件描述符;
buf:读/写的缓冲区;
count:对read,为要读的字节数; 对write,为buf的大小。

read有多种情况可使实际读到的字节数少于要求读字节数:

  • 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有3 0个字节,而要求读1 0 0个字节,则r e a d返回3 0,下一次再调用r e a d时,它将返回0 (文件尾端);
  • 当从终端设备读时,通常一次最多读一行;
  • 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数;
  • 某些面向记录的设备,例如磁带,一次最多返回一个记录。

7. DS18B20温度采集

文件file_io.c如下:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "file_io.h"

#define BUFSIZE 1024
//#define MSG_STR "Hello World
"

int main(int argc, char *argv[])
{
    int     fd = -1;
    int     rv = -1;
    char    buf[BUFSIZE];
    float   temper;  

    fd=open("test.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
    if(fd < 0)
    {

        perror("Open/Create file test.txt failure");
        return 0;
    }
    printf("Open file returned file descriptor [%d]
", fd);
     
    if((get_temper(&temper)) < 0)
    {
        printf("Get temperature failure: %s
", strerror(errno));
    	goto cleanup;
    }

    memset(buf, 0, sizeof(buf));
    snprintf(buf, sizeof(buf), "%.2f%c", temper, 'C');

    if( (rv=write(fd, buf, strlen(buf))) < 0 )
    {
        printf("Write %d bytes into file failure: %s
", rv, strerror(errno));
        goto cleanup;
    }

    lseek(fd, 0, SEEK_SET);

    memset(buf, 0, sizeof(buf));
    if( (rv=read(fd, buf, sizeof(buf))) < 0 )
    {
        printf("Read data from file failure: %s
", strerror(errno));
        goto cleanup;
    }
    
    printf("Read %d bytes data from file: %s
", rv, buf);

cleanup:
    close(fd);
    return 0;

}

文件file_io.h如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>

/*  温度放置的路径 */
//#define filepath /28-041731f7c0ff/w1_slave

int get_temper(float *temper);

int get_temper(float *temper)
{
    char 	        filepath[120]="/";
    char	        f_name[50];
    char            data_array[1024];
    char            *data_p=NULL;
    struct dirent   *file=NULL;
    DIR             *dir=NULL;
    int             data_fd;
    int             found = -1;
    //float           *temper;

    if((dir = opendir(filepath)) < 0)
    {
        printf("opendir file failure: %s
",strerror(errno));
        return -1;
    }

    while((file = readdir(dir)) != NULL)
    {
        //if((strcmp(file->d_name, ".", 1) == 0) || (strcmp(file->d_name, ".", 1) == 0))
        //continue;               //ignore '.' and '..' file
        if(strstr(file->d_name, "28-")) 
        {   //memset(f_name, 0, sizeof(f_name));
            strncpy(f_name, file->d_name, sizeof(f_name));       //locate reserve temperature data file path
            found = 1;
            printf("reserve temperature data file path: %s
",f_name);
        }
        //closedir(dir);
    }
    closedir(dir);
    /* found == 0; 未找到目的文件夹 */
    if(!found) 
    {
        printf("Can not find the folder
");   
        return 0;
    }
    
    /* 找到相应文件夹后,切换至该文件夹下以获取温度数据 */
    strncat(filepath, f_name, sizeof(filepath)-strlen(filepath));       //将文件夹名连接到filepath路径后
    strncat(filepath, "/w1_slave", sizeof(filepath)-strlen(filepath));  //将设备文件夹下存放温度的文件连接到filepath路径后
    
    //printf("%s
", f_name);
    printf("%s
", filepath );
    data_fd=open(filepath, O_RDONLY);
    if(data_fd < 0) 
    {
        printf("open file failure: %s
", strerror(errno));
        return -2;
    }
    memset(data_array, 0, sizeof(data_array));
    if(read(data_fd, data_array, sizeof(data_array)) < 0)
    {
        printf("read file failure: %s
", strerror(errno));
        return -3;
    }
    /* data_p指针后移两个字符单位,其后即为温度数据 */
    data_p=strstr(data_array, "t=");
    data_p=data_p+2;        //local temperature data 
    *temper=atof(data_p)/1000;    //"()" priority super "/"
    
    close(data_fd);

    return 0;
}

这里是DS18B20温度的采集内容,这里我只取一次温度采集数据,然后分析出温度值。
路径为:/28-041731f7c0ff
文件w1_slave内容如下:

fe 00 4b 46 7f ff 0c 10 56 : crc=56 YES
fe 00 4b 46 7f ff 0c 10 56 t=15875

其运行结果为:

原文地址:https://www.cnblogs.com/Tavi/p/12514020.html