一、read系統調用
一旦有了與一個打開文件描述相關連的文件描述符,只要該文件是用O_RDONLY或O_RDWR標志打開的,就可以用read()系統調用從該文件中讀取字節
函數原型:
ssize_t read(int fd, void *buf, size_t count);
參數:
fd :想要讀的文件的文件描述符
buf : 指向內存塊的指針,從文件中讀取來的字節放到這個內存塊中
count : 從該文件復制到buf中的字節個數
返回值:
如果出現錯誤,返回-1;讀文件結束,返回0;否則返回從該文件復制到規定的緩沖區中的字節數
二、write系統調用
用write()系統調用將數據寫到一個文件中
函數原型:
ssize_t write(int fd, const void *buf, size_t count);
函數參數:
fd:要寫入的文件的文件描述符
buf: 指向內存塊的指針,從這個內存塊中讀取數據寫入 到文件中
count: 要寫入文件的字節個數
返回值:如果出現錯誤,返回-1;如果寫入成功,則返回寫入到文件中的字節個數
三、ioctl 函數
ioctl用於向設備發控制和配置命令,有些命令也需要讀寫一些數據,但這些數據是不能用read/write讀寫的,稱為Out-of-band數據。也就是說,read/write讀寫的數據是in-band數據,是I/O操作的主體,而ioctl命令傳送的是控制信息,其中的數據是輔助的數據。例如,在串口線上收發數據通過read/write操作,而串口的波特率、校驗位、停止位通過ioctl設置,A/D轉換的結果通過read讀取,而A/D轉換的精度和工作頻率通過ioctl設置。
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
d是某個設備的文件描述符。request是ioctl的命令,可變參數取決於request,通常是一個指向變量或結構體的指針。若出錯則返回-1,若成功則返回其他值,返回值也是取決於request。
以下程序使用TIOCGWINSZ命令獲得終端設備的窗口大小。
#include <stdio.h> #include <stdlib.h #include <unistd.h> #include <sys/ioctl.h int main(void) { struct winsize size; if (isatty(STDOUT_FILENO) == 0) exit(1); if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0) { perror("ioctl TIOCGWINSZ error"); exit(1); } printf("%d rows, %d columns\n", size.ws_row, size.ws_col); return 0; }
在圖形界面的終端裡多次改變終端窗口的大小並運行該程序,觀察結果。
四、文件的隨機讀寫
到目前為止的所有文件訪問都是順序訪問。這是因為所有的讀和寫都從當前文件的偏移位置開始,然後文件偏移值自動地增加到剛好超出讀或寫結束時的位置,使它為下一次訪問作好准備。
有個文件偏移這樣的機制,在Linux系統中,隨機訪問就變得很簡單,你所需做的只是將當前文件移值改變到有關的位置,它將迫使一次read()或write()發生在這一位置。(除非文件被O_APPEND打開,在這種情況下,任何write調用仍將發生在文件結束處)
lseek系統調用:
功能說明:通過指定相對於開始位置、當前位置或末尾位置的字節數來重定位,這取決於 lseek() 函數中指定的位置
函數原型:off_t lseek (int fd, off_t offset, int base);
函數參數:
fd:需要設置的文件描述符
offset:偏移量
base:偏移基位置
返回值:返回新的文件偏移值
base 表示搜索的起始位置,有以下幾個值:(這些值定義在<unistd.h>)
base 文件位置
SEEK_SET 從文件開始處計算偏移
SEEK_CUR 從當前文件的偏移值計算偏移
SEEK_END 從文件的結束處計算偏移
示例程序如下:
/*************************************************************************
> File Name: file_cp.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
int infd;
int outfd;
if (argc != 3)
{
fprintf(stderr, "Usage %s src dest\n", argv[0]);
exit(EXIT_FAILURE);
}
infd = open(argv[1], O_RDONLY);
if (infd == -1)
ERR_EXIT("open src error");
if ((outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
ERR_EXIT("open dest error");
char buf[1024];
ssize_t nread;
while ((nread = read(infd, buf, 1024)) > 0)
write(outfd, buf, nread); // 可以調用fsync同步內核緩沖區的數據到磁盤文件
// 或者打開文件時標志為O_SYNC
close(infd);
close(outfd);
/********************************************************************************************/
int fd = open("test.txt", O_RDONLY);
if (fd == -1)
ERR_EXIT("open error");
char buf2[1024] = {0};
int ret = read(fd, buf2, 5);
if (ret == -1)
ERR_EXIT("read error");
ret = lseek(fd, 0, SEEK_CUR); // 從當前位置偏移0個字節
if (ret == -1)
ERR_EXIT("lseek");
printf("current offset=%d\n", ret);
fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
ERR_EXIT("open error");
write(fd, "ABCDE", 5);
ret = lseek(fd, 1012 * 1024 * 1024, SEEK_CUR);
if (ret == -1)
ERR_EXIT("lseek error");
write(fd, "hello", 5);
/* 中間的空字符不占用磁盤空間,如ls -lh hole.txt 與 du -h hole.txt
* 看到的文件大小不一樣*/
close(fd);
return 0;
}
程序的前部分實現了拷貝文件的基本功能,後部分示例了lseek的用法,因為有些程序需要輸入參數,且討論程序輸出結果也比較繁瑣,比如上述關於hole.txt文件的實際大小問題,大家可以自己拷貝程序進行測試,印象也更加深刻。