1. 程式人生 > >Linux C高階程式設計——檔案操作之系統呼叫

Linux C高階程式設計——檔案操作之系統呼叫

Linux C高階程式設計檔案操作之系統呼叫

宗旨:技術的學習是有限的,分享的精神是無限的!

        庫函式是一些完成特定功能的函式,一般由某個標準組織製作釋出,並形成一定的標準。使用庫函式編寫的函式一般可以應用於不同的平臺而不需要做任何修改,具有很好的可移植性。

        系統呼叫函式與作業系統直接相關,不同的作業系統所使用的系統呼叫可能不太一樣,因此,如果兩個作業系統差異很大,系統呼叫函式的可移植性就不高。例如windows採用的系統呼叫的應用程式不能直接在Linux下編譯執行。

        之所以使用系統呼叫是因為系統資源的有限性以及核心管理的方便,系統呼叫將上層內的應用開發與底層的硬體實現分開,上層應用不需要關注底層硬體的具體實現。Linux的系統呼叫使用軟中斷實現,使用系統呼叫後,該程式的狀態將從使用者態切換到核心態。庫函式實現最終也要呼叫系統呼叫函式,但它封裝了系統呼叫操作,從而增加了程式碼的可移植性。

1open()函式

——用於開啟或者建立一個檔案

(1)函式原型:

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(constchar* pathname, int flags, ...)

(2)引數

pathname:要建立或者開啟的檔名

flags: 指定檔案的開啟模式、標誌等資訊

必須指定一個:O_RDONLY ——只讀   O_WRONLY——只寫 O_RDWR——讀寫

可選標誌(按位或):O_APPEND——追加

O_TRUNC——若檔案存在,讀寫方式開啟或只寫開啟,則檔案長度為0

O_CREAT——若檔案不存在,則建立檔案,此時,open需要第三個引數,用於指定該  檔案的訪問許可權(umask可以看掩碼)

O_EXCL——若同時指定為O_CREAT標誌,而檔案已經存在,則會出錯,可用於檔案  是否存在

O_NONBLOCK對於裝置檔案,以O_NONBLOCK方式開啟可以做非阻塞I/O

(3)返回值

整數型別——成功時,返回檔案描述符;出錯時,返回-1 

(4)檔案描述符

(檔案描述符——已開啟檔案的索引——通過索引找到已開啟檔案)

檔案描述符是一個非負的整數

檔案描述符0,1,2分別表示標準輸入,標準輸出,標準錯誤輸出,在程序建立時,已經開啟。open返回的檔案描述符一定是該程序尚未使用的最小描述符 

(5)出錯處理

errno.h標頭檔案中,定義了errno,當API調用出錯時,errno說明出錯的具體原因

可簡單的將errno理解成整型資料

出錯資訊轉換成可讀字串

#include<string.h>

char* strerror(int errno);

perror函式根據當前的errno,輸出一條出錯資訊(void perror(constchar* msg))

#include<stdio.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(int argc, char *argv[])
{
  int fd;
  if(argc < 2)
  {
    puts("please input the open filepathname!\n");
    exit(1);
  }

  //如果flag引數裡有O_CREAT表示,該檔案如果不存在,系統則會建立該檔案,該文          件的許可權由第三個引數決定,此處為0755
  //如果flah引數裡沒有O_CREAT引數,則第三個引數不起作用.此時,如果要開啟的          檔案不存在,則會報錯.
  //所以fd=open(argv[1],O_RDWR),僅僅只是開啟指定檔案
  if((fd = open(argv[1], O_CREAT | O_RDWR, 0755)) < 0)
  {
    perror("open filefailure!\n");
    exit(1);
  }
  else
  {
    printf("open file %d  success!\n", fd);

  }
  close(fd);

  return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

2 creat()函式

——用於建立一個新檔案

(1)函式原型

int creat(const char *pathname,mode_t mode)

(2)引數

pathname:要建立的檔名(包括路徑資訊)

mode:同open的第二個引數,討論檔案的訪問許可權位時分析:

S_IRUSR——可讀       S_IWUSR——可寫      S_IXUSR——可執行     S_IRWXU——可讀、寫、執行

除了用上述巨集之外,還可以用數字來表示檔案的許可權:

可讀——4  可寫——2  可執行——1  無任何許可權——0 

(3)返回值

成功返回只寫開啟的檔案描述符,出錯返回-1

(4)creat函式缺點:它以只寫方式開啟建立的檔案。若要建立一個臨時檔案,並先寫該檔案,然後又讀該檔案,則必須先呼叫creat,close,然後再open.簡便方法:

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

#include<stdio.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void  create_file(char *filename)
{

  /*建立的檔案具有什麼樣的屬性?*/
  if(creat(filename, 0755) < 0)
  {
    printf("create file %sfailure!\n", filename);
    exit(EXIT_FAILURE);
  }
  else
  {
    printf("create file %s success!\n", filename);
  }
}

int main(int argc, char *argv[])
{
  int i;
  if(argc < 2)
  {
    perror("you haven't input thefilename,please try again!\n");
    exit(EXIT_FAILURE);
  }

  for(i = 1; i < argc; i++)
  {
    create_file(argv[i]);
  }

  return 0;
}
 

3lseek函式(fseek類似)

——用於修改當前檔案偏移量

(當前檔案偏移量的作用:規定了從檔案什麼地方開始進行讀、寫操作)

——通常,讀寫操作結束時,會使檔案偏移量增加讀寫的位元組數

——當開啟一個檔案時,除非指定了O_APPEND標誌,否則偏移量被設定為0

(1) 函式原型

#include<sys/types.h>
#include<unistd.h>
off_t lseek(int filedes, off_t offset, int whence)

(2) 引數

第一個引數filedes:open/creat函式返回的檔案描述符

第二個引數offset:

相對偏移量:需結合whence才能計算出真正的偏移量

型別off_t:通常情況下是32為資料型別

第三個引數whence:該引數取值是三個常量

SEEK_SET:當前檔案偏移量為——距檔案開始出的offset個位元組

SEEK_CUR:當前檔案偏移量 + offset(可正可負)

SEEK_END:當前檔案長度 + offset(可正可負)

(3)返回值:

成功返回新的檔案偏移量,失敗返回-1

(4)獲取當前的偏移量:

Off_tcurrent_position;

Current_position= lseek(fd,0,SEEK_CUR);

lseek操作並不引起任何I/O操作,只是修改核心中的記錄(並不引起磁碟的訪問    操作)

(5)空洞檔案

——使用lseek修改檔案偏移量後,當前檔案偏移量有可能大於檔案的長度

——在這種情況下,對該檔案的下一次寫操作,將加長該檔案

——這樣檔案中形成了一個空洞。對空洞區域進行讀,均返回0

4read函式

——用於從檔案中讀出資料

(1)函式原型

#include<unistd.h>
ssize_t read(int fd, void *buff, size_t nbytes)

(2)引數

第一個引數fd:檔案描述符

第二個引數buff:指向緩衝區,用於存放從檔案讀出的資料

第三個引數nbytes:unsigned int;需要從檔案中讀出的位元組數

——緩衝區的大小 >= nbytes 

(3)返回值

返回值型別:ssize_t,即int

出錯返回-1成功:返回從檔案中實際讀到的位元組數,當檔案讀到結尾時,則返回0

(4)很多情況下,read實際讀出的位元組數都小於要求讀出的位元組數

——讀普通檔案,在讀到要求的位元組數之前,就到達了檔案尾端

——當從終端裝置讀時,通常一次最多讀一行

——當從網路讀時,網路中的緩衝機構可能造成read函式返回值小於所要求讀出          的位元組數

——某些面向記錄的裝置,如磁碟,一次最多返回一個記錄 

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>

#define LENGTH 100

int main(void)
{
 int fd, len;
 char str[LENGTH];
  /* 建立並開啟檔案 */
 fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
  if(fd)     
  {
    /* 寫入 Hello, software weekly字串 */
  write(fd, "Hello,Software Weekly", strlen("Hello, software weekly"));
  close(fd);
     
  }
 fd = open("hello.txt", O_RDWR);
 len = read(fd, str, LENGTH);/* 讀取檔案內容 */
 str[len] = '\0';
 printf("%s\n", str);
 close(fd);

  return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

5write函式

——用於向檔案裡面寫入資料

(1) 函式原型

#include<unistd.h>

ssize_t write(int fd,const void*buff,size_t nbytes)

(2) 引數

第一個引數fd:檔案描述符

第二個引數buff:指向緩衝區,存放了需要寫入檔案的資料

第三個引數nbytes:需要寫入檔案的位元組數

(3) 返回值

返回值型別:ssize_t,即int

出錯返回-1,成功返回實際寫入檔案的位元組數

(4) write出錯的原因

——磁碟滿

——沒有訪問許可權

——超過了給定程序的檔案長度限制

6close函式

——用於關閉一個已開啟的檔案

(1)函式原型

int close(int filedes)

(2) 返回值

成功返回0,出錯返回-1

(3)引數

filedes:檔案描述符

(4)當close函式關閉檔案時,會釋放程序加在該檔案上的所有記錄鎖

核心會對程序開啟檔案表,檔案物件,索引節點表項等結構進行修改,釋放相關的       資源

當程序退出時,會關閉當前所有已開啟的檔案描述符                         

7ioctl函式

——用於向裝置發控制和配置命令(這些資料不能用read/write讀寫)

(1) 函式原型

#include <sys/ioctl.h>
int ioctl(int d, int request, ...)

(2) 引數

第一個引數d:某個裝置的檔案描述符

第二個引數request:是ioctl的命令,可變引數取決於request,通常是是一個指向  變數或者結構體的指標

(3)返回值

若出錯返回-1,成功返回其他值,取決於request

ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)——獲得終端裝置的視窗大小

manioctl_list——可以看require的各種引數 

8mmap函式

——可以把磁碟檔案的一部分直接對映到記憶體,這樣檔案中的位置直接就有對應的記憶體地址,對檔案的讀寫可以直接用指標來做而不需要read/write函式

(1) 函式原型

#include<sys/mman.h>

void* mmap(void *addr,size_t len,int prot,int flag,int filedes,off_t off)

(2) 引數

addr:NULL——核心會自己在程序地址空間中選擇合適的地址建立對映

len:需要對映的那部分檔案的長度

off:從檔案的什麼位置開始對映,必須是頁大小的整數倍(32位——4K)

filedes:該檔案的檔案描述符

prot:

PROT_EXEC——對映的這一段可執行,例如對映的共享庫

PROT_READ——對映的這一段可讀

PROT_WRITE——對映的這一段可寫

PROT_NONE——對映的這一段不可訪問

flag:

MAP_SHARED——多個程序對同一檔案的對映是共享的,一個程序對對映的記憶體做了修改,另一個程序也會看到這種變化。

MAP_PRIVATE——多個程序對同一檔案的對映不是共享的,一個程序對對映的記憶體做了修改,另一個程序不會看到這種變化,也不會真的寫到檔案中去。

(3)返回值

成功則返回對映的首地址,出錯返回常數MAP_FAILED。

9access函式

——判斷檔案是否可以進行某種操作

(1)函式原型

int access(const char *pathname,int mode)

(2)引數

pathname:檔名

mode:判斷的訪問許可權:R_OK:檔案可讀  W_OK:檔案可寫

   X_OK:檔案可執行  F_OK:檔案存在

(3)返回值

成功時,返回0;如果一個條件不符合時,返回-1

10 dup函式

——複製檔案描述符

(1) 函式原型

#include <unistd.h>
int dup(int oldfd);

(2) 引數

oldfd:待複製的檔案描述符

(3)返回值

成功返回新的檔案描述符;失敗返回-1

11 dup2函式

——複製檔案描述符

(1) 函式原型

#include <unistd.h>
int dup2(int oldfd, int newfd);

(2) 引數

oldfd:待複製的檔案描述符

newfd:新的檔案描述符

(3) 返回值

成功返回新的檔案描述符;失敗返回-1

//小專案:用系統呼叫函式實現檔案拷貝的功能:
/*************************************************************************
    > File Name: copy.c
    > Author: libang
    > Mail: [email protected]
    > Created Time: 2014年07月16日 星期三 00時15分12秒
 ************************************************************************/

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

int copy_file(intsrc_fd, int dest_fd);

int main(intargc, char *argv[]) //注意先搭建主框架
{
  if(argc < 3)
  {
    printf("inputerror!\n");
    exit(1);
  }
  int src_fd, dest_fd;

  src_fd = open(argv[1], O_RDONLY);
  dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
  if(src_fd < 0 | dest_fd < 0)
  {
    printf("file openfail!\n");
    exit(0);
  }

  copy_file(src_fd, dest_fd);

  printf("success!\n");
  close(src_fd);
  close(dest_fd);

  return 0;
}

int copy_file(intsrc, int dest)
{
  char buf[128];
  int r_ret, w_ret;
  char *tmp;
  //memset(buf,0,128);
  printf("success!\n");
  while((r_ret = read(src, buf, 128)) > 0) //經典的拷貝演算法
  {
    tmp = buf;
    while(r_ret)
    {
      w_ret = write(dest, tmp, r_ret);
      r_ret = r_ret - w_ret;
      printf("%d\n", r_ret);
      tmp += w_ret;
    }
    //memset(buf,0,128);
  }
  printf("success!\n");

  return 0;
}