1. 程式人生 > >Linux的系統呼叫open,write,read,close,及相關總結

Linux的系統呼叫open,write,read,close,及相關總結

在進行C語言學習的時候我們瞭解到了C語言相關的一些IO操作,如fopen,fwrite,fread,fprintf,fclose等相關函式,他們都是由C庫函式提供的一些函式,是將作業系統的系統呼叫加以封裝,雖說Linux是由C語言實現的,但為了使我們更加的瞭解Linux,就需要了解更接近與底層的一些IO操作,因此就需要來了解下基本的系統呼叫—open,write,read,close

首先我們來了解下open,write,read,close的系統呼叫

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
  • 1
  • 2
  • 3
  • 4
  • 5

open有三個引數 
pathname:要開啟或建立的目標檔名 
flags:對檔案進行多種操作也就有有多個引數,這多個引數可以進行或運算,即就是flags 
引數:

  1. O_RDONLY:只讀開啟
  2. O_WRONLY:只寫開啟
  3. O_RDWR:讀,寫開啟
  4. O_CREAT:若檔案不存在,建立檔案
  5. O_APPEND:追加寫

引數1,2,3,必須制定一個且只能制定一個,使用引數4,必須使用open的第三個引數mode:新檔案的訪問許可權

返回值:成功:新開啟檔案的檔案描述符(fd) 
失敗:-1

write

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
  • 1
  • 2
  • 3
  • 4

fd:檔案描述符 
buf:寫入的緩衝區 
count:寫的字元長度,也就是看你需要寫多少

read

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

  • 1
  • 2
  • 3
  • 4

read的引數和write的引數很相像,只有第二個引數的含義有些不一樣,它的buf是需要讀的緩衝區

close

#include <unistd.h>
int close(int
fd);
  • 1
  • 2
  • 3
  • 4

close的引數就相對簡單了,這一個操作是不能遺漏的,只要了使用fd就必須close它

在這幾個函式中都涉及到了關鍵的引數fd,因此要了解這幾個函式,就必須先了解下檔案描述符(fd)。

什麼是檔案描述符,這是一個相對抽象的概念,我們先來看看下面這張圖

這裡寫圖片描述

在PCB結構體中存在一個files指標,它指向一個file_struct結構體,而在file_struct結構體中存在一個file* fd陣列,這個數組裡面存放的是file指標,用來指向不同的file檔案,而fd就可以理解為這個指標陣列的下標,因此要開啟一個檔案,我們就可以拿到該檔案的fd就可以了。

fd的分配原則: 
在files_struct陣列當中,使用沒有被使用的最小下標,作為新的檔案描述符。 
作業系統預設使用了該陣列的前三個元素,0號下標指向標準輸入(stdin),1號下標指向標準輸出(stdout),2號下標指向標準錯誤(stderr)。 
因此正常情況下,新的fd都是從3開始的,但如果我們關閉了預設的fd,新的檔案的fd就從關閉的fd處開始。

說到了fd,我們就不得不來區分下FILE和fd

FILE是C庫當中提供的一個結構體,而fd是系統呼叫,更加接近於底層,因此FILE中必定封裝了fd。

我們可以來看看FILE的結構體: 
typedef struct _IO_FILE FILE;在/usr/include/stdio.h

它的結構體中有這麼一段:

struct _IO_FILE {
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

//緩衝區相關
  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */ 
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;//fd的封裝
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

可以看到int_fileno就是對fd的封裝,在這一部分的開頭有一大段跟緩衝區相關的內容,為什麼要諾列出它呢,首先可以來看一個很詭異的例子:

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

  int main(){
      const char *msg1 = "hello printf\n";
      const char *msg2 = "hello fwrite\n";
      const char *msg3 = "hello write\n";

      printf(msg1);
      fwrite(msg2, 1, strlen(msg2), stdout);
      write(1, msg3, strlen(msg3));
      fork();
      return 0;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

執行結果: 
[[email protected] test]$ ./hello 
hello printf 
hello fwrite 
hello write

但當我們對程序實現輸出重定向,你就會發現詭異的事情: 
[[email protected] test]$ ./hello > file

[[email protected] test]$ cat file 
hello write 
hello printf 
hello fwrite 
hello printf 
hello fwrite

這是為什麼呢,這是跟C庫的緩衝資料有關,C庫緩衝資料分為三種(1)、無緩衝(2)、行緩衝(3)、全緩衝。 
行緩衝就是往顯示器上寫,全緩衝就是往檔案裡寫。 
在上面的現象中,write不受影響是因為它屬於系統呼叫,沒有緩衝區,而printf和fwrite會自帶緩衝區,當發生重定向到普通檔案的時候,它就會從行緩衝轉變為全緩衝,也就是會往檔案裡面寫寫,但是我們緩衝區裡的資料,即使fork也不會立即被重新整理,當程序退出後會統一重新整理,寫入檔案,但是fork的時候會發生寫時拷貝,也就是當父程序準備重新整理的時候,子程序就已經有了一份相同的資料,所以就會產生上面的現象。

瞭解下重定向。 
重定向分為三種:

  1. 輸出重定向(>) 也就是關閉fd為1下標所指向的內容
  2. 輸入重定向(<) 同理就是關閉fd為0下標所指向的內容
  3. 追加重定向(>>) 後面多一個追加選項

相關推薦

系統呼叫——openwritereadclose

一、檔案描述符 每一個程序都有一個與之相關的檔案描述符,它們是一些小值整數,我們可以通過這些檔案描述符來訪問開啟的檔案。 一般地,一個程式開始執行時,會自動開啟3個檔案描述符: 0——–標準輸入———-stdin 1——–標準輸出———-stdout

linux系統呼叫openwritecloseread以及stat函式詳解

學習筆記 參考連結1 、參考連結2以及百度百科 在進行C語言學習的時候我們瞭解到了C語言相關的一些IO操作,如fopen,fwrite,fread,fprintf,fclose等相關函式,他們都是由C庫函式提供的一些函式,是將作業系

linux 系統呼叫open 七日遊(二)

接著昨日的旅程,我們應該開始處理具體的子路徑了: 【fs/namei.c】 sys_open->do_sys_open->do_filp_open->path_openat->link_path_walk 點選(此處)摺疊或開啟 &n

linux系統呼叫open七日遊(一)

友情提示:您需要一個 kernel 3.15.6,下載地址: https://www.kernel.org/pub/linux/kernel/v3.0/linux-3.15.6.tar.xz     我們將以 Linux 系統呼叫 open 為主線,參

Linux 系統呼叫 open 七日遊(七)

【場景三】open(pathname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)     在這個場景中我們希望建立一個新檔案(O_CREAT),並賦予該檔案使用者可讀(S_IRUSR)和使用者可寫(S_IW

Linux系統呼叫openwritereadclose相關總結

在進行C語言學習的時候我們瞭解到了C語言相關的一些IO操作,如fopen,fwrite,fread,fprintf,fclose等相關函式,他們都是由C庫函式提供的一些函式,是將作業系統的系統呼叫加以封裝,雖說Linux是由C語言實現的,但為了使我們更加的瞭解Linux,就需

組合語言呼叫Linux系統呼叫readwrite

.section .data UserMsg: .ascii "Please input the message:" LenOfUserMsg: .equ lenMsg, LenOfUserMsg - UserMsg #.section .bss # .lcomm r

linux系統網卡配置文件找不到DHCP不工作情況解決

linux今天,我發現了這樣一個情況,那就是網卡配置文件少了一個/etc/init.d/network status ,看到網絡配置少了一個如何做呢?將其他的網卡配置文件復制一份,進入到/etc/sysconfig/network-scripts下,進行copthoyfor instance,cp ifcfg

Linux系統中用腳本安裝虛擬機及其管理快照虛擬機重置

linux---腳本安裝 快照 想要快速安裝多臺虛擬機,可以對原有虛擬機硬盤文件做一個快照,使用快找安裝多個虛擬速度是相當快的,那麽接下來我將介紹如何創建快照以及編寫簡單的shell命令安裝虛擬機的方法。一、安裝虛擬機(1)用shell腳本安裝(2)得到一個虛擬機的全端管理及硬盤文件(3)創建虛擬機快

Linux 檔案系統呼叫open七日遊(三)

接著上回,當對“.”和“..”處理完成後就直接返回進入下一個子路徑迴圈了,但如果當前子路徑不是“.”或“..”呢? 【fs/namei.c】 sys_open > do_sys_open > do_filp_open >&

Linux檔案系統呼叫open 七日遊 (六)

還記得在上一個場景中,build_open_flags裡面有一個對標誌位O_PATH的判斷麼?現在我們就來看看這個標誌位是幹啥的: 【場景二】open(pathname,O_PATH)     這個O_PATH似乎是不常用的,咱們先看看它的使用

linux檔案系統呼叫 open 七日遊(四)

現在,我們的“路徑行走”只剩下最後一個小問題需要處理了——符號連結。 【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat &g

Linux系統--CentOS7下Mysql(docker)映象建立使用者表以及資料初始化

1.docker下載(環境為centos7)yum install docker-engine2.下載完成後啟動docker使用命令:service docker start3.為docker下載映象提速curl -sSL https://get.daocloud.io/da

delphi中writeread的用法?什麼時候需要用?

如你所說,在控制元件或者類的屬性中,read 表示 讀取,write 則表示設定。比如在類中:TTestClass = (Class)privateFOrderCode:String;publicproperty OrderCode:String read FOrderCode write FOrd

linux系統應用層基礎面試題目(很完整暫時沒看)

1. 下面的網路協議中,面向連線的的協議是: A 。 A 傳輸控制協議 B 使用者資料報協議 C 網際協議 D 網際控制報文協議 2. 在/etc/fstab檔案中指定的檔案系統載入引數中, D 引數一般用於CD-ROM等移動裝置。 A defaults B sw C rw和ro D noauto 3. Li

Linux系統中/dev/mtd與/dev/mtdblock的區別即MTD字元裝置和塊裝置的區別

1. /dev/mtdN 是Linux 中的MTD架構中,系統自己實現的mtd分割槽所對應的字元裝置,其裡面添加了一些ioctl,支援很多命令,如MEMGETINFO,MEMERASE等。 而mtd-util中的flash_eraseall等工具,就是以這些ioctl為基礎而

Linux系統下安裝三個或者多個tomcat 步驟詳細。

即然安裝多個tomcat,那麼必然建立在系統已經安裝好了jdk,並且會安裝一個tomcat的基礎上,這裡就不做過多描述,直奔主題。安裝多個tocat的方式其實和安裝一個大同小異,只是需要更改一些配置。先

arm linux 系統呼叫過程

在Linux下系統呼叫是用軟中斷實現的,下面以一個簡單的open例子簡要分析一下應用層的open是如何呼叫到核心中的sys_open的。 t8.c 1: #include <stdio.h> 2: #include <sys/types.h> 3:

一次性講明白Linux系統呼叫(1)

什麼是系統呼叫 Linux核心中設定了很多可以實現各種系統功能的子程式,這些子程式就叫系統呼叫。而系統呼叫和普通函式呼叫的區別主要是在系統呼叫是系統提供的,函式一般是函式庫或者自己提供的。 為什麼要用系統呼叫 其實很多我們平時用的C語言標準函式,在Linux

linux 系統呼叫 inotify & epoll

一、inotify 作用: 監控一個目錄下檔案的增加、刪除事件 1.重要的資料結構 // 發生的event結構 struct inotify_event {     __s32       wd;       &nb