久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Linux下文件輸入/輸出端口的試煉分析

202次閱讀
沒有評論

共計 7936 個字符,預計需要花費 20 分鐘才能閱讀完成。

這篇文章將為大家詳細講解有關 Linux 下文件輸入 / 輸出端口的試煉分析,丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

文件描述符(File Descriptor)

a small, nonnegative integer for use in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.) ($man 2 open). 一個程序開始運行時一般會有 3 個已經(jīng)打開的文件描述符:

0 :STDIN_FIFLENO, 標準輸入 stdin

1 :STDOUT_FILENO, 標準輸出 stdout

2 :STDERR_FILENO, 標準錯誤 stderror

fd 原理

fd 從 0 開始, 查找最小的未被使用的描述符, 把文件表指針與文件表描述符建立對應關系(VS pid 是一直向上漲,滿了再回來找)

文件描述符就是一個 int, 用于代表一個打開的文件, 但是文件的管理信息不能夠不是存放在文件描述符中, 當使用 open()函數(shù)打開一個文件時, OS 會將文件的相關信息加載到文件表等數(shù)據(jù)結構中, 但出于安全和效率等因素的考慮, 文件表等數(shù)據(jù)結構并不適合直接操作, 而是給該結構指定一個編號, 使用編號來進行操作, 該編號就是文件描述符

OS 會為每個進程內部維護一張文件描述符總表, 當有新的文件描述符需求時, 會去總表中查找最小的未被使用的描述符返回, 文件描述符雖然是 int 類型, 但其實是非負整數(shù), 也就是 0~OPEN_MAX(當前系統(tǒng)中為 1024), 其中 0,1,2 已被系統(tǒng)占用, 分別表示 stdin, stdout,stderror

使用 close()關閉 fd 時, 就是將 fd 和文件表結構之間的對應關系從總表中移除, 但不一定會刪除文件表結構, 只有當文件表沒有與其他任何 fd 對應時 (也就是一個文件表可以同時對應多個 fd) 才會刪除文件表, close()也不會改變文件描述符本身的整數(shù)值, 只會讓該文件描述符無法代表一個文件而已

duplicate fdVS copy fd:dup 是把 old_fd 對應的文件表指針復制給 new_fd, 而不是 int new_fd=old_fd

UNIX 使用三種數(shù)據(jù)結構描述打開的文件:每個進程中用于描述當前進程打開文件的文件描述符表,表示當前文件狀態(tài)的文件狀態(tài)標識表,和用于找到文件 i 節(jié)點 (索引節(jié)點) 的 V 節(jié)點表,Linux 中并不使用這種 Vnode 結構,取而代之的是一種通用的 inode 結構,但本質沒有區(qū)別,inode 是在讀取文件時通過文件系統(tǒng)從磁盤中導入的文件位置

 

文件描述符標志(File Descriptor Flag)

當下的系統(tǒng)只有一個文件描述符標志 close-on-exec,僅僅是一個標志,當進程 fork 一個子進程的時候,在子進程中調用了 exec 函數(shù)時就用到了該標志。意義是執(zhí)行 exec 前是否要關閉這個文件描述符。

一般我們會調用 exec 執(zhí)行另一個程序,此時會用全新的程序替換子進程的正文,數(shù)據(jù),堆和棧等。此時保存文件描述符的變量當然也不存在了,我們就無法關閉無用的文件描述符了。所以通常我們會 fork 子進程后在子進程中直接執(zhí)行 close 關掉無用的文件描述符,然后再執(zhí)行 exec。但是在復雜系統(tǒng)中,有時我們 fork 子進程時已經(jīng)不知道打開了多少個文件描述符(包括 socket 句柄等),這此時進行逐一清理確實有很大難度。我們期望的是能在 fork 子進程前打開某個文件句柄時就指定好: 這個句柄我在 fork 子進程后執(zhí)行 exec 時就關閉”。所以就有了 close-on-exec

每個文件描述符都有一個 close-on-exec 標志。在系統(tǒng)默認情況下,這個標志 *** 一位被設置為 0。即關閉了此標志。那么當子進程調用 exec 函數(shù),子進程將不會關閉該文件描述符。此時,父子進程將共享該文件,它們具有同一個文件表項,也就有了同一個文件偏移量等。

fcntl()的 FD_CLOEXEC 和 open()的 O_CLOEXEC 用來設置文件的 close-on-exec,當將 close-on-exec 標志置為 1 時,即開啟此標志, 此時子進程調用 exec 函數(shù)之前,系統(tǒng)就已經(jīng)讓子進程將此文件描述符關閉。

Note: 雖然新版本支持在 open 時設置 CLOEXEC,但是在編譯的時候還是會提示錯誤 – error: lsquo;O_CLOEXEC rsquo; undeclared (first use in this function)。這個功能需要設置宏 (_GNU_SOURCE) 打開。

#define _GNU_SOURCE // 在源代碼中加入
-D_GNU_SOURCE // 在編譯參數(shù)中加入

文件狀態(tài)標志(File Status Flag)

File status flags 用來表示打開文件的屬性,file status flag 可以通過 duplicate 一個文件描述符來共享同一個打開的文件的狀態(tài),而 file descrptor flag 則不行

Access Modes: 指明文件的 access 方式:read-only,write-only,read-write。通過 open()設置,通過 fcntl()返回,但不能被改變

Open-time Flags: 指明在 open()執(zhí)行的時候的操作,open()執(zhí)行完畢這個 flag 不會被保存

Operating Modes: 影響 read,write 操作,通過 open()設置,但可以用 fcntl()讀取或改變

open()

// 給定一個文件路徑名,按照相應的選項打開文件,就是將一個 fd 和文件連接到一起,成功返回文件描述符,失敗返回 - 1 設 errno #include fcntl.h  int open(const char *pathname, int flags) int open(const char *pathname, int flags, mode_t mode) // 不是函數(shù)重載,C 中沒有重載,  是可變長參數(shù)列表  //pathname: 文件或設備路徑  //flags :file status flags=Access mode+Open-time flags+Operating Modes、 /*Access Mode(必選一個): O_RDONLY:0 O_WRONLY:1 O_RDWR:2 */ /*Open-time Flags(Bitwise Or): O_CLOEXEC :為新打開的文件描述符使能 close-on-exec。可以避免程序再用 fcntl()的 F_SETFD 來設置 FD_CLOEXEC O_CREAT :如果文件不存在就創(chuàng)建文件,并返回它的文件描述符,如果文件存在就忽略這個選項,必須在保護模式下使用,eg:0664 O_DIRECTORY :如果 opendir()在一個 FIFO 或 tape 中調用的話,這個選項可以避免 denial-of-service 問題,  如果路徑指向的不是一個目錄,就會打開失敗。 O_EXCL :確保 open()能夠穿件一個文件,如果文件已經(jīng)存在,則會導致打開失敗,總是和 O_CREAT 一同使用。 O_NOCTTY :如果路徑指向一個終端設備,那么這個設備不會成為這個進程的控制終端,即使這個進程沒有一個控制終端  O_NOFOLLOW :如果路徑是一個符號鏈接,就打開它鏈接的文件 //If pathname is a symbolic link, then the open fails. O_TMPFILE :創(chuàng)建一個無名的臨時文件,文件系統(tǒng)中會創(chuàng)建一個無名的 inode,當 *** 一個文件描述符被關閉的時候,所有寫入這個文件的內容都會丟失,除非在此之前給了它一個名字  O_TRUNC :清空文件  O_TTY_INIT *//*Operating Modes(Bitwise Or) O_APPEND :以追加的方式打開文件,  默認寫入結尾,在當下的 Unix/Linux 系統(tǒng)中,這個選項已經(jīng)被定義為一個原子操作  O_ASYNC :使能 signal-driven I/O O_DIRECT :試圖最小化來自 I / O 和這個文件的 cache effect//Try to minimize cache effects of the I/O to and from this file. O_DSYNC :每次寫操作都會等待 I / O 操作的完成,但如果文件屬性的更新不影響讀取剛剛寫入的數(shù)據(jù)的話,就不會等待文件屬性的更新  。 O_LARGEFILE :允許打開一個大小超過 off_t(但沒超過 off64_t)表示范圍的文件  O_NOATIME :不更改文件的 st_time(last access time) O_NONBLOCK /O_NDELAY :如果可能的話,用 nonblock 模式打開文件  O_SYNC :每次寫操作都會等待 I / O 操作的完成,包括 write()引起的文件屬性的更新。 O_PATH :獲得一個能表示文件在文件系統(tǒng)中位置的文件描述符
#include fcntl.h  #include stdlib.h  int fd=open(b.txt ,O_RDWR|O_CREAT|O_EXCL,0664); if(-1==fd) perror(open),exit(-1);

FA: 猜想有以下模型: 用一串某一位是 1 其余全是 0 的字符串表示一個選項, 選項們作“按位與”就可得到 0 / 1 字符串, 表示整個 flags 的狀態(tài), Note: 低三位表示 Access Mode

creat()

等價于以 O_WRONLY |O_TRUNC|O_CREAT 的 flag 調用 open()

#include fcntl.h  int creat(const char *pathname, mode_t mode);

dup()、dup2()、dup3()

/ 復制一個文件描述符的指向,新的文件描述符的 flags 和原來的一樣,成功返回 new_file_descriptor,  失敗返回 - 1 并設 errno #include  unistd.h  int dup(int oldfd); // 使用未被占用的最小的文件描述符編號作為新的文件描述符  int dup2(int oldfd, int newfd);
#include  fcntl.h  #include  unistd.h  int dup3(int oldfd, int newfd, int flags);
#include unistd.h  #include stdlib.h  int res=dup2(fd,fd2); if(-1==res){ perror( dup2),exit(-1); }

read()

// 從 fd 對應的文件中讀 count 個 byte 的數(shù)據(jù)到以 buf 開頭的緩沖區(qū)中,成功返回成功讀取到的 byte 的數(shù)目,失敗返回 - 1 設 errno #include  unistd.h  ssize_t read(int fd, void *buf, size_t count);
#include  unistd.h  #include stdlib.h  int res=read(fd,buf,6); if(-1==fd) perror(read),exit(-1);

write()

// 從 buf 指向的緩沖區(qū)中讀取 count 個 byte 的數(shù)據(jù)寫入到 fd 對應的文件中,成功返回成功寫入的 byte 數(shù)目,文件的位置指針會向前移動這個數(shù)目,失敗返回 - 1 設 errno #include  unistd.h  ssize_t write(int fd, const void *buf, size_t count);// 不需要對 buf 操作,  所以有 const, VS read()沒有 const
#include  unistd.h  #include stdlib.h  int res=write(fd, hello ,sizeof( hello)); if(-1==res) perror(write),exit(-1);

Note: 上例中即使只有一個字符 rsquo;A rsquo;, 也要寫”A”, 因為”A”才是地址, rsquo;A rsquo; 只是個 int

lseek()

l 表示 long int, 歷史原因

// 根據(jù)移動基準 whence 和移動距離 offset 對文件的位置指針進行重新定位,返回移動后的位置指針與文件開頭的距離,失敗返回 - 1 設 errno #include  unistd.h  #include  sys/types.h  off_t lseek(int fd, off_t offset, int whence); /*whence: SEEK_SET:以文件開頭為基準進行偏移,0 一般不能向前偏  SEEK_CUR:以當前位置指針的位置為基準進行偏移,1 向前向后均可  SEEK_END:以文件的結尾為基準進行偏移,2 向前向后均可?向后形成”文件空洞”
#include unistd.h  #include stdlib  int len=lseek(fd,-3,SEEK_SET); if(-1==len){ perror( lseek),exit(-1); }

fcntl()

// 對 fd 進行各種操作,成功返回 0, 失敗返回 - 1 設 errno #include  unistd.h  #include  fcntl.h  int fcntl(int fd, int cmd, ... ); //... 表示可變長參數(shù)  /*cmd: Adversory record locking: F_SETLK(struct flock*) // 設建議鎖  F_SETLKW(struct flock*) // 設建議鎖,如果文件上有沖突的鎖,且在等待的時候捕獲了一個信號,則調用被打斷并在信號捕獲之后立即返回一個錯誤,如果等待期間沒有信號,則一直等待  F_GETLK(struct flock*) // 嘗試放鎖,如果能放鎖,則不會放鎖,而是返回一個含有 F_UNLCK 而其他不變的 l_type 類型,如果不能放鎖,那么 fcntl()會將新類型的鎖加在文件上,并把當前 PID 留在鎖上  Duplicating a file descriptor: F_DUPFD (int) // 找到 =arg 的最小的可以使用的文件描述符,并把這個文件描述符用作 fd 的一個副本  F_DUPFD_CLOEXEC(int)// 和 F_DUPFD 一樣,除了會在新的文件描述符上設置 close-on-execF_GETFD (void) // 讀取 fd 的 flag,忽略 arg 的值  F_SETFD (int) // 將 fd 的 flags 設置成 arg 的值. F_GETFL (void) // 讀取 fd 的 Access Mode 和其他的 file status flags;  忽略 arg F_SETFL (long) // 設置 file status flags 為 arg F_GETOWN(void) // 返回 fd 上接受 SIGIO 和 SIGURG 的 PID 或進程組 ID F_SETOWN(int) // 設置 fd 上接受 SIGIO 和 SIGURG 的 PID 或進程組 ID 為 arg F_GETOWN_EX(struct f_owner_ex*) // 返回當前文件被之前的 F_SETOWN_EX 操作定義的文件描述符 R  F_SETOWN_EX(struct f_owner_ex*) // 和 F_SETOWN 類似,允許調用程序將 fd 的 I / O 信號處理權限直接交給一個線程,進程或進程組  F_GETSIG(void) // 當文件的輸入輸出可用時返回一個信號  F_SETSIG(int) // 當文件的輸入輸出可用時發(fā)送 arg 指定的信號  */ /* hellip;:  可選參素,是否需要得看 cmd,如果是加鎖,這里應是 struct flock* struct flock { short l_type; //%d Type of lock: F_RDLCK(讀鎖), F_WRLCK(寫鎖), F_UNLCK(解鎖) short l_whence; //%d How to interpret l_start,  加鎖的位置參考標準:SEEK_SET, SEEK_CUR, SEEK_END off_t l_start; //%ld Starting offset for lock,  加鎖的起始位置  off_t l_len; //%ld Number of bytes to lock ,  鎖定的字節(jié)數(shù)  pid_t l_pid; // PID of process blocking our lock, (F_GETLK only)加鎖的進程號,, 默認給 -1}; */

建議鎖(Adversory Lock)

限制加鎖, 但不限制讀寫, 所以只對加鎖成功才讀寫的程序有效, 用來解決不同的進程 同時對同一個文件的同一個位置“寫”導致的沖突問題

讀鎖是一把共享鎖(S 鎖): 共享鎖 + 共享鎖 + 共享鎖 + 共享鎖 + 共享鎖 + 共享鎖

寫鎖是一把排他鎖(X 鎖): 永遠孤苦伶仃

釋放鎖的方法(逐級提高):

將鎖的類型改為:F_UNLCK, 再使用 fcntl()函數(shù)重新設置

close()關閉 fd 時, 調用進程在該 fd 上加的所有鎖都會自動釋放

進程結束時會自動釋放所有該進程加過的文件鎖

Q: 為什么加了寫鎖還能 gedit 或 vim 寫???

A: 可以寫, 鎖只可以控制能否加鎖成功, 不能控制對文件的讀寫, 所以叫”建議”鎖, 我加了鎖就是不想讓你寫, 你非要寫我也沒辦法. vim/gedit 不通過能否加鎖成功來決定是否讀寫, 所以可以直接上

Q: So 如何實現(xiàn)文件鎖控制文件的讀寫操作????

A: 可以在讀操作前嘗試加讀鎖, 寫操作前嘗試加寫鎖, 根據(jù)能否加鎖成功決定能否進行讀寫操作

int fd=open(./a.txt ,O_RDWR); // 得到 fd if(-1==fd) perror(open),exit(-1);struct flock lock={F_RDLCK,SEEK_SET,2,5,-1}; // 設置鎖  // 此處從第 3 個 byte 開始 (包含第三) 鎖 5byte int res=fcntl(fd,F_SETLK, lock); // 給 fd 加鎖  if(-1==res) perror(fcntl),exit(-1);

ioct1()

這個函數(shù)可以實現(xiàn)其他文件操作函數(shù)所沒有的功能,大多數(shù)情況下都用在設備驅動程序里,每個設備驅動程序可以定義自己專用的一組 ioctl 命令,系統(tǒng)則為不同種類的設備提供通用的 ioctl 命令

// 操作特殊文件的設備參數(shù),成功返回 0, 失敗返回 - 1 設 errno #include  sys/ioctl.h  int ioctl(int d, int request, ...); //d:an open file descriptor.//request: a device-dependent request code

close()

// 關閉 fd,這樣這個 fd 就可以重新用于連接其他文件,成功返回 0, 失敗返回 - 1 設 errno #include  unistd.h  int close(int fd);
#include  unistd.h  #include stdlib.h  int res=close(fd); if(-1==res) perror(close),exit(-1);

關于“Linux 下文件輸入 / 輸出端口的試煉分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

正文完
 
丸趣
版權聲明:本站原創(chuàng)文章,由 丸趣 2023-08-25發(fā)表,共計7936字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網(wǎng)絡搜集發(fā)布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 淮滨县| 胶州市| 进贤县| 安乡县| 达州市| 闽侯县| 保康县| 南涧| 麟游县| 清徐县| 茶陵县| 双辽市| 本溪| 昌黎县| 昆山市| 周口市| 秭归县| 潼南县| 丰都县| 嘉定区| 枝江市| 云林县| 陆良县| 开封县| 金昌市| 涿州市| 应城市| 房山区| 石狮市| 原阳县| 汶川县| 普格县| 故城县| 安徽省| 防城港市| 洪雅县| 白河县| 衡阳县| 景泰县| 合阳县| 平湖市|