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\n"

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]\n", fd);
     
    if((get_temper(&temper)) < 0)
    {
        printf("Get temperature failure: %s\n", 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\n", 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\n", strerror(errno));
        goto cleanup;
    }
    
    printf("Read %d bytes data from file: %s\n", 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\n",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\n",f_name);
        }
        //closedir(dir);
    }
    closedir(dir);
    /* found == 0; 未找到目的文件夾 */
    if(!found) 
    {
        printf("Can not find the folder\n");   
        return 0;
    }
    
    /* 找到相應文件夾后,切換至該文件夾下以獲取溫度數據 */
    strncat(filepath, f_name, sizeof(filepath)-strlen(filepath));       //將文件夾名連接到filepath路徑后
    strncat(filepath, "/w1_slave", sizeof(filepath)-strlen(filepath));  //將設備文件夾下存放溫度的文件連接到filepath路徑后
    
    //printf("%s\n", f_name);
    printf("%s\n", filepath );
    data_fd=open(filepath, O_RDONLY);
    if(data_fd < 0) 
    {
        printf("open file failure: %s\n", 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\n", 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

其運行結果為:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM