0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux系統(tǒng)下I/O操作講解

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-08 15:13 ? 次閱讀

Linux系統(tǒng)下I/O

一、I/O簡(jiǎn)介

I/O(輸入/輸出)是在主存和外部設(shè)備(磁盤驅(qū)動(dòng)器、網(wǎng)絡(luò)、終端)之間復(fù)制數(shù)據(jù)的過程。輸入是從外部設(shè)備復(fù)制到主存,輸出是從主存復(fù)制到外部設(shè)備。

在Linux系統(tǒng)中所有的I/O設(shè)備都被映射稱為文件,所有的輸入輸出都被當(dāng)做相應(yīng)文件的讀和寫來執(zhí)行,所以內(nèi)核提供了系統(tǒng)級(jí)的I/O函數(shù)接口,使得所有輸入輸出都以統(tǒng)一且一致的方式來執(zhí)行。

  1. 打開文件,返回一個(gè)非負(fù)整數(shù),叫做描述符
  2. 每個(gè)進(jìn)程都默認(rèn)打開三個(gè)描述符,標(biāo)準(zhǔn)輸入 STDIN_FILENO(描述符0)、標(biāo)準(zhǔn)輸出 STDOUT_FILENO(描述符1)、標(biāo)準(zhǔn)出錯(cuò) STDERR_FILENO(描述符2)。
  3. 讀寫文件,讀就是從文件復(fù)制n個(gè)字節(jié)到內(nèi)存,寫就是從內(nèi)存復(fù)制n個(gè)字節(jié)到文件。
  4. 文件偏移:默認(rèn)打開文件是從文件開頭起始的字節(jié)偏移量,可以使用seek來操作。
  5. 關(guān)閉文件。

今天從四個(gè)方面來說I/O,文件I/O、標(biāo)準(zhǔn)I/O庫、高級(jí)I/O、終端I/O。

  1. 文件I/O: 文件的打卡、讀寫、關(guān)閉、偏移。
  2. 標(biāo)準(zhǔn)I/O庫:Linux提供的標(biāo)準(zhǔn)I/O庫函數(shù)
  3. 高級(jí)I/O:非阻塞I/O、I/O多路轉(zhuǎn)接、異步I/O
  4. 終端I/O: 更改終端屬性操作的函數(shù)

二、文件I/O

Linux系統(tǒng)中文件I/O一般只用到以下五個(gè)函數(shù):open、read、write、lseek、close。每次read、write都是一次系統(tǒng)調(diào)用(從用戶層拷貝到內(nèi)核層再拷貝到用戶層)且不帶緩沖。

  1. 文件描述符

對(duì)于內(nèi)核而言,每個(gè)打開的文件都是通過文件描述符引用的,每個(gè)文件描述符都是一個(gè)非負(fù)整數(shù),打開或者創(chuàng)建一個(gè)文件都會(huì)返回一個(gè)文件描述符,通過這個(gè)文件描述符來進(jìn)行讀寫,

  1. 打開/創(chuàng)建文件
#include
#include
#include

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:創(chuàng)建或者打開一個(gè)文件,返回一個(gè)文件描述符
參數(shù): pathname:路徑名/文件名
flags:標(biāo)志位
O_RDONLY 只讀

O_WRONLY 只寫

O_RDWR 既可以讀也可以寫

O_APPEND 以追加的方式操作文件

O_CREAT 如果文件不存在,則創(chuàng)建

O_TRUNC 如果文件存在,則清空文件的數(shù)據(jù)

O_EXCL 表示文件已經(jīng)存在,而又重復(fù)創(chuàng)建一次,open函數(shù)會(huì)返回錯(cuò)誤,
返回文件已經(jīng)存在的錯(cuò)誤,對(duì)錯(cuò)誤做處理之后,直接打開文件就可以

O_APPEND 從文件末尾位置追加寫入

O_SYNC 每次write等物理I/O操作完成,包括由該write操作引起的文件屬性更新所需的I/O,(后邊會(huì)用到)

O_RSYNC 每個(gè)以文件描述符作為參數(shù)進(jìn)行的read操作等待,直到所有對(duì)文件同一部分掛起的寫操作都完成。

mode:如果是創(chuàng)建一個(gè)文件,需要添加對(duì)應(yīng)文件的屬性,模式屬性一般用一個(gè)八進(jìn)制數(shù)代替,如果屬性成立,為1,不成立,則為0
rwxr-x-wx --> 0753
rw-rw-r-- --> 0664

返回值:成功:文件描述符
失?。?1
  1. 關(guān)閉文件

關(guān)閉一個(gè)文件時(shí)會(huì)自動(dòng)釋放加在該文件上的所有鎖,當(dāng)進(jìn)程終止時(shí)會(huì)自動(dòng)關(guān)閉所有打開的文件。

#include

int close(int fd);
參數(shù):fd:open返回的文件描述符
返回值:成功 0, 失敗 -1.
  1. 文件偏移

通常所有讀寫操作都是從當(dāng)前文件偏移量處開始,并使偏移量增加讀寫的字節(jié)數(shù),默認(rèn)是0??梢允褂胠seek顯式打開文件設(shè)置偏移量。

#include
#include

off_t lseek(int fd, off_t offset, int whence);
參數(shù):fd : open函數(shù)打開的文件
offset: 與whence有關(guān)
whence: 基準(zhǔn)點(diǎn)
SEEK_SET 將讀寫位置指向文件頭后再增加offset個(gè)位移量。
SEEK_CUR 以目前的讀寫位置往后增加offset個(gè)位移量。
SEEK_END 將讀寫位置指向文件尾后再增加offset個(gè)位移量(使用該參數(shù)可以算出文件字節(jié)數(shù))
當(dāng)whence 值為SEEK_CUR 或SEEK_END時(shí),參數(shù)offet允許負(fù)值的出現(xiàn)。
返回值:成功,返回文件偏移量,失敗 -1.
注釋:文件偏移量可以大于文件長度,這樣就會(huì)構(gòu)成空洞文件,對(duì)于多出的這些字節(jié)被讀出為0.空洞文件在磁盤中不占用存儲(chǔ)區(qū)。
  1. 讀文件
#include

ssize_t read(int fd, void *buf, size_t count);
參數(shù):
fd:文件描述符
buf:讀取到的數(shù)據(jù)
const:每一次最多讀取到的字節(jié)數(shù)
返回值:
成功:讀取的字節(jié)數(shù) 如果是0 代表結(jié)尾
失?。?1
  1. 寫文件
#include

ssize_t write(int fd, const void *buf, size_t count);
功能:向一個(gè)文件描述符寫數(shù)據(jù)
參數(shù):
fd:文件描述符
buf:要寫入的數(shù)據(jù)
const:每一次最多寫入到的字節(jié)數(shù)
返回值:
成功:寫入的字節(jié)個(gè)數(shù)
失敗:-1
失敗原因多是磁盤已滿或者超過一個(gè)給定進(jìn)程的文件長度限制。
  1. 文件共享

Linux系統(tǒng)支持不同進(jìn)程間共享打開文件,在此先說一下內(nèi)核用于所以I/O的數(shù)據(jù)結(jié)構(gòu)。

內(nèi)核使用三種數(shù)據(jù)結(jié)構(gòu)表示打開的文件,他們之間的關(guān)系決定了文件共享中一個(gè)進(jìn)程對(duì)另一個(gè)進(jìn)程的影響。 首先每個(gè)進(jìn)程在進(jìn)程表中有一個(gè)記錄項(xiàng),每個(gè)記錄項(xiàng)包含一張打開的文件描述符,每個(gè)描述符占用一項(xiàng),與文件描述符有關(guān)的是:

1.文件描述符標(biāo)志

2.指向文件表項(xiàng)的指針

其次內(nèi)核為每個(gè)打開文件維持一張文件表,文件表項(xiàng)包含:

1.文件狀態(tài)標(biāo)志(讀、寫、阻塞等)

2.當(dāng)前文件偏移量

3.指向該文件v節(jié)點(diǎn)表項(xiàng)指針     最后每個(gè)打開文件(設(shè)備)都有一個(gè)v節(jié)點(diǎn)結(jié)構(gòu),它包含了:

     1.文件類型

     2.對(duì)該文件進(jìn)行各種操作的指針。

     3.i節(jié)點(diǎn)(i-node),包含了文件的長度、所以者、指向文件實(shí)際數(shù)據(jù)塊在磁盤的位置。

這些信息都是在打開文件時(shí)候從磁盤拷貝到內(nèi)存,所以這些信息都是隨時(shí)可用的??偨Y(jié)一下這三張表關(guān)系

進(jìn)程表項(xiàng): fd標(biāo)志
文件指針(文件表項(xiàng)):文件狀態(tài)標(biāo)志
當(dāng)前文件偏移量
v節(jié)點(diǎn)指針(v節(jié)點(diǎn)表項(xiàng)): v節(jié)點(diǎn)信息
v_data: i節(jié)點(diǎn)(i節(jié)點(diǎn)表項(xiàng)): i節(jié)點(diǎn)信息
當(dāng)前文件長度

了解了內(nèi)核的這三個(gè)數(shù)據(jù)結(jié)構(gòu)之后我們回過頭來看文件共享。

假定一個(gè)進(jìn)程打開了一個(gè)文件,返回文件描述符是4,另一個(gè)進(jìn)程也打開了這個(gè)文件描述符返回的文件描述符是5,打開該文件的每個(gè)進(jìn)程都有一個(gè)文件表項(xiàng)(進(jìn)程對(duì)該文件的當(dāng)前偏移量),但是該文件只有一個(gè)v節(jié)點(diǎn)。

  1. 每當(dāng)write之后,文件表項(xiàng)中擔(dān)負(fù)起偏移量會(huì)增加寫入的字節(jié)數(shù),如果當(dāng)前文件偏移量超出了當(dāng)前文件長度則i節(jié)點(diǎn)表項(xiàng)中文件長度也增加。
  2. 如果使用O_APPEND打開一個(gè)文件,相應(yīng)的標(biāo)志被設(shè)置到文件表項(xiàng)中的文件狀態(tài)標(biāo)志,每次對(duì)該文件寫操作時(shí),文件表項(xiàng)中當(dāng)前文件偏移量會(huì)被設(shè)置為i節(jié)點(diǎn)表項(xiàng)的文件長度。
  3. 當(dāng)使用lseek函數(shù)定位到文件尾端時(shí)候,文件表項(xiàng)中的當(dāng)前文件偏移量被設(shè)置為i節(jié)點(diǎn)表項(xiàng)中的文件長度。
  4. 存在多個(gè)文件描述符指向同一個(gè)文件的情況。

需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加qun579733396獲?。?a href="http://srfitnesspt.com/soft/special/" target="_blank">資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費(fèi)分享

圖片

  1. 原子操作

當(dāng)有多個(gè)進(jìn)程操作一個(gè)文件時(shí)候?yàn)榱藬?shù)據(jù)同步Linux系統(tǒng)提供了原子操作。

1.open一個(gè)文件時(shí)候使用 O_APPEND 標(biāo)志

2.使用pread 和 pwrite 函數(shù) pread/pwrite 相當(dāng)于調(diào)用lseek之后調(diào)用read/write,但是區(qū)別在于調(diào)用pread/pwrite時(shí),無法中斷其定位和讀寫操作,而且不更新當(dāng)前文件的偏移量。

#include

ssize_t pread(int fd, void *buf, size_t count, off_t offset);
功能:讀文件
參數(shù):fd:文件描述符
buf:讀緩沖區(qū)
count:緩沖區(qū)大小
offset:偏移量
返回值: 成功:讀到的字節(jié)數(shù),如果讀到文件尾返回0, 失敗-1

ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
功能:寫文件
參數(shù):fd:文件描述符
buf:寫緩沖區(qū)
count:緩沖區(qū)大小
offset:偏移量
返回值: 成功:讀到的字節(jié)數(shù), 失敗-1
  1. 將緩沖區(qū)數(shù)據(jù)寫到磁盤

在傳統(tǒng)Unix系統(tǒng)實(shí)現(xiàn)中大多數(shù)磁盤I/O通過緩沖區(qū)進(jìn)行的,當(dāng)我們向文件寫數(shù)據(jù)時(shí),內(nèi)核通常將數(shù)據(jù)復(fù)制到緩沖區(qū)中,之后再寫到磁盤,這種方式稱為延遲寫。下面函數(shù)將緩沖區(qū)數(shù)據(jù)寫入到磁盤。

#include

void sync(void);
將修改過的塊緩沖區(qū)排隊(duì)寫到隊(duì)列就返回,數(shù)據(jù)并不一定寫入到磁盤。命令sync就是調(diào)用sync函數(shù)。update系統(tǒng)守護(hù)進(jìn)程每30s調(diào)用一次該函數(shù)。

int fsync(int fd);
只對(duì)一個(gè)文件描述符其作用,并且磁盤操作結(jié)束后才返回。

int fdatasync(int fd);
等同于fsync,但是同時(shí)更新文件屬性。
  1. 修改已打開的文件屬性
#include
#include

int fcntl(int fd, int cmd, ... /* arg */ );
功能:修改已打開文件屬性
參數(shù):fd:文件描述符
cmd: F_DUPFD:復(fù)制文件描述符,新的文件描述符作為返回值返回。新文件描述符與舊fd共享同一文件表項(xiàng),但是有自己的文件描述符標(biāo)志,其FD_CLOEXXEC文件描述符標(biāo)志被取消
F_DUPFD_CLOEXEC:復(fù)制文件描述符,設(shè)置與新文件描述符關(guān)聯(lián)的FD_CLOEXXEC文件描述符標(biāo)志的值,返回新文件描述符
F_GETFD:對(duì)應(yīng)于fd的文件描述符標(biāo)志作為函數(shù)返回值
F_SETFD:對(duì)應(yīng)fd設(shè)置文件描述符標(biāo)志,新值為第三參數(shù)值
F_GETFL:對(duì)應(yīng)fd的文件狀態(tài)標(biāo)志作為函數(shù)返回值
F_SETFL:將文件狀態(tài)標(biāo)志設(shè)置為第三個(gè)參數(shù)的值
F_GETOWN:獲取當(dāng)前SIGIO和SIGURG信號(hào)的進(jìn)程ID和組ID
F_SETOWN:設(shè)置接收SIGIO和SIGURG信號(hào)的進(jìn)程ID和組ID
第三參數(shù):總是一個(gè)整數(shù),一般0
返回值:出錯(cuò):-1
成功:其他
  1. ioctl 函數(shù)

ioctl函數(shù)是I/O操作的萬金油,內(nèi)核對(duì)設(shè)備的IO通道控制操作函數(shù),多用于驅(qū)動(dòng)程序。

#include

int ioctl(int fd, int request, ...);
參數(shù):@fd :文件描述符的序號(hào)
@request :請(qǐng)求 代表不同操作的數(shù)字值
@... :可變參數(shù),(寫或者不寫根據(jù)請(qǐng)求決定)
:傳遞的是整數(shù),或者地址
返回值:出錯(cuò):-1
成功:其他

ioctl函數(shù)的實(shí)現(xiàn)需要一種命令碼
32位
比特位 含義
31 - 30 00 : 命令不帶參數(shù)
01 : 命令從驅(qū)動(dòng)中獲取數(shù)據(jù),讀方向
10 : 命令把數(shù)據(jù)寫入驅(qū)動(dòng),寫方向
11 : 命令即寫又讀:雙向
29 - 16 類型的大小
15 - 8 類型
7 - 0 序號(hào)

三 、標(biāo)準(zhǔn)I/O庫

標(biāo)志I/O庫處理了很多細(xì)節(jié),比如緩沖區(qū)的分配、優(yōu)化塊長度執(zhí)行I/O等,更方便大家進(jìn)行I/O操作

在前面說的I/O函數(shù)都是圍繞著文件描述符進(jìn)行操作的,在標(biāo)準(zhǔn)I/O庫里對(duì)應(yīng)的是 流 進(jìn)行操作的,當(dāng)打開一個(gè)一個(gè)流時(shí),標(biāo)準(zhǔn)I/O庫函數(shù)fopen返回一個(gè)指向FILE對(duì)象的指針。它是一個(gè)結(jié)構(gòu)體包含了標(biāo)準(zhǔn)I/O庫

所管理該流的所有信息,包括用于實(shí)際I/O的文件描述符、指向用于該流的緩沖區(qū)指針、緩沖區(qū)長度、以及當(dāng)前緩沖區(qū)中的字符等。

對(duì)應(yīng)文件描述符每個(gè)進(jìn)程定義了三個(gè)流,標(biāo)準(zhǔn)輸入(stdin)、標(biāo)準(zhǔn)輸出(stdout)、標(biāo)準(zhǔn)出錯(cuò)(stderr)

2.緩沖區(qū)

標(biāo)準(zhǔn)I/O庫提供緩沖區(qū)的目的是為了盡可能減少使用read和write(太消耗資源了),它對(duì)每個(gè)I/O流自動(dòng)地進(jìn)行緩沖管理,庫函數(shù)提供的接口,在內(nèi)存中創(chuàng)建一塊緩沖區(qū),直到滿足一定條件,才會(huì)真正寫入,本質(zhì)上還是系統(tǒng)調(diào)用,可以在不同系統(tǒng)間進(jìn)行數(shù)據(jù)傳輸。

有以下三種緩沖

1.全緩沖,操作的文件,3個(gè)條件:

  1. 緩沖區(qū)滿,則會(huì)刷新緩沖區(qū) 4096byte
  2. 程序正常結(jié)束
  3. fflush刷新緩沖區(qū)(將內(nèi)容寫到磁盤,在驅(qū)動(dòng)程序表示丟棄緩沖區(qū)數(shù)據(jù))

2.行緩沖:指針對(duì)終端進(jìn)行操作,4個(gè)條件:

  1. 緩沖區(qū)滿,則會(huì)刷新緩沖區(qū) 1024byte
  2. 程序正常結(jié)束
  3. fflush刷新緩沖區(qū)
  4. “n”

3.無緩沖:指針終端進(jìn)行操作

修改系統(tǒng)默認(rèn)緩沖(一定要在流打開之后修改)

#include

void setbuf(FILE *stream, char *buf);
功能:打開或者關(guān)閉緩沖機(jī)制
參數(shù):stream:打開的流
buf:指向一個(gè)長度為BUFSIZE的緩沖區(qū),設(shè)置為null則關(guān)閉緩沖
返回值:成功0,失敗非0

int setvbuf(FILE *stream, char *buf, int mode, size_t size);
功能:打開或者關(guān)閉緩沖機(jī)制
參數(shù):stream:打開的流
buf:指向一個(gè)長度為BUFSIZE的緩沖區(qū),設(shè)置為null則系統(tǒng)自動(dòng)分配
mode:_IONBF :無緩沖,此選項(xiàng)可以忽略buf和size
_IOLBF :行緩沖
_IOFBF :全緩沖
返回值:成功0,失敗非0

刷新緩沖區(qū),將所有未寫的數(shù)據(jù)傳輸?shù)絻?nèi)核。如果stream為null,則刷新所有緩沖區(qū)。

#include

int fflush(FILE *stream);
  1. 打開流

打開一個(gè)流默認(rèn)是全緩沖,當(dāng)打開終端設(shè)備時(shí)候默認(rèn)為行緩沖。

#include

FILE *fopen(const char *path, const char *mode);
功能:打開一個(gè)標(biāo)準(zhǔn)I/O流
參數(shù):path:文件名
mode:打開模式 (b:二進(jìn)制文件)
r/rb:打開文件對(duì)文件進(jìn)行讀操作,文件必須存在,
r+/r+b/rb+:打開文件對(duì)文件進(jìn)行讀寫操作,文件必須存在
w/wb:打開或者創(chuàng)建文件,對(duì)文件進(jìn)行寫入
w+/w+b/wb+:打開或者創(chuàng)建文件,對(duì)文件進(jìn)行讀寫操作
a/ab:打開或者創(chuàng)建文件,從文件末尾位置追加數(shù)據(jù)(多個(gè)進(jìn)程追加一個(gè)文件也可以正確寫入)
a+/a+b/ab+:打開或者創(chuàng)建文件,從文件末尾進(jìn)行讀取、追加文件。如果文件不存在創(chuàng)建文件,從文件起始處讀寫。

返回值:成功返回文件指針,失敗返回null

FILE *fdopen(int fd, const char *mode);
功能:取一個(gè)文件描述符,并使標(biāo)準(zhǔn)I/O流與之相關(guān)聯(lián),此函數(shù)常用于由創(chuàng)建管道和網(wǎng)絡(luò)通信管道函數(shù)返回的描述符。因?yàn)檫@些特色文件不能用fopen打開。
返回值:成功返回文件指針,失敗返回null

FILE *freopen(const char *path, const char *mode, FILE *stream);
功能:在一個(gè)指定流上打開一個(gè)文件,如果已經(jīng)打開則先關(guān)閉再打開,此函數(shù)一般用于將一個(gè)文件打開為一個(gè)預(yù)定義的流:stdin、stdout、stderr
參數(shù):path:文件名
mode:
返回值:成功返回文件指針,失敗返回null
  1. 關(guān)閉流

當(dāng)關(guān)閉一個(gè)流時(shí)候,緩沖區(qū)所有數(shù)據(jù)都被丟棄。

#include

int fclose(FILE *fp);
返回值:成功0,失敗EOF(-1)
  1. 讀流和寫流

每次打開一個(gè)I/O可以使用三種不同方式進(jìn)程讀寫流 1. 每次讀寫一個(gè)字符的I/O 2. 每次讀寫一行的I/O,沒次以換行符終止 3. 直接I/O,直接讀寫某種指定長度的對(duì)象,常用于二進(jìn)制和結(jié)構(gòu)體讀寫。

讀寫一個(gè)字符

#include

int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
功能:讀取數(shù)據(jù)
參數(shù):流
返回值: 成功 讀取的字符,失敗 -1(EOF)
區(qū)別:getc為宏。fgetc為函數(shù),所以fgetc可以當(dāng)做地址作為參數(shù)傳遞,getc不可以。

#include

int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
功能:寫入文件數(shù)據(jù)
參數(shù):c 寫入的字符 stream 流
返回值:成功 寫入的字符,失敗 EOF
  1. 讀寫一行字符
#include
char *fgets(char *s, int size, FILE *stream);
char *gets(char *s);(不推薦使用,因?yàn)闊o法指定長度,可以造成緩沖區(qū)溢出)
功能:讀取文件中的一行字符,遇到n 結(jié)束
參數(shù):s 指向用戶開辟的緩沖區(qū),實(shí)現(xiàn)定義一個(gè)數(shù)組
size:要求讀取字節(jié)個(gè)數(shù)
stream:流
返回值:成功 讀取的字符串,失敗 EOF;

#include

int fputs(const char *s, FILE *stream);
int puts(const char *s);
功能:輸出以null結(jié)尾的字符串?dāng)?shù)據(jù)數(shù)據(jù)到指定文件中
參數(shù):s 指定要被讀取數(shù)據(jù)的緩沖區(qū)
輸出 n 但不能輸出?
返回值:成功 讀取的字符串,失敗 EOF;
  1. 二進(jìn)制I/O讀寫
#include
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:讀文件
參數(shù):ptr:事先定義的變量,需要傳遞變量的
size:每個(gè)對(duì)象的大小
number:對(duì)象個(gè)數(shù)
stream:流
返回值:成功:返回實(shí)際讀取到對(duì)象的個(gè)數(shù)
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:寫文件
參數(shù):ptr:事先定義的變量,需要傳遞變量的
size:每個(gè)對(duì)象的大小
number:對(duì)象個(gè)數(shù)
stream:流
返回值:成功:返回實(shí)際寫對(duì)象的個(gè)數(shù)
注釋:這兩個(gè)函數(shù)存在一個(gè)問題就是只能讀寫同一系統(tǒng)上的數(shù)據(jù),如果是不同系統(tǒng)則會(huì)造成問題。因?yàn)樵诓煌到y(tǒng)同一結(jié)構(gòu)的同一成員偏移量可能不同。
  1. 定位流
#include

int fseek(FILE *stream, long offset, int whence);
功能:文件定位
參數(shù):stream 流
offset:偏移量
whence:基準(zhǔn)點(diǎn)
SEEK_SET 文件開頭位置
SEEK_CUR 文件當(dāng)前位置
SEEK_END 文件末尾位置
從后往前偏移加 - 號(hào)
返回值:成功 0 失敗 -1

long ftell(FILE *stream);
功能 返回當(dāng)前文件位置指針的位置是在那個(gè)地址,使用數(shù)字的形式表示
參數(shù):stream 流
返回值:成功返回文件當(dāng)前位置,出錯(cuò)-1.

void rewind(FILE *stream);
參數(shù):stream 流
功能: 把文件指針指向開頭

int fgetpos(FILE *stream, fpos_t *pos);
功能:將文件位置指示器的當(dāng)前值存入pos指向的對(duì)象中

int fsetpos(FILE *stream, fpos_t *pos);
功能:將文件位置定位到pos指示的值位置。
  1. 格式化I/O
格式化輸出

#include

int printf(const char *format, ...);
功能:發(fā)送格式化輸出到標(biāo)準(zhǔn)輸出 stdout。
參數(shù):format -- 這是字符串,包含了要被寫入到標(biāo)準(zhǔn)輸出 stdout 的文本

int fprintf(FILE *stream, const char *format, ...);
功能:寫入到指定的流。

int sprintf(char *str, const char *format, ...);
功能:將格式化字符串寫入到str中,自動(dòng)會(huì)加一個(gè)null字節(jié)
參數(shù):str:保存格式化的字符串
format -- 這是字符串
返回值:成功:返回寫入到str中字符數(shù)(不包含null),失敗負(fù)數(shù)

int snprintf(char *str, size_t size, const char *format, ...);
功能:同sprintf,但是sprintf可能會(huì)造成緩沖區(qū)溢出功能,所以snprintf會(huì)限定寫入字節(jié)數(shù)。
參數(shù):str:保存格式化的字符串,自動(dòng)會(huì)加一個(gè)null字節(jié)
size:字符串大小
format -- 這是字符串
返回值:如果格式化后的字符串長度小于等于 size,則會(huì)把字符串全部復(fù)制到 str 中,并給其后添加一個(gè)字符串結(jié)束符 ?;
如果格式化后的字符串長度大于 size,超過 size 的部分會(huì)被截?cái)?,只將其中?(size-1) 個(gè)字符復(fù)制到 str 中,并給其后添加一個(gè)字符串結(jié)束符 ?,返回值為欲寫入的字符串長度。
失敗:負(fù)數(shù)
格式字符: %h:輸出short型
%d 十進(jìn)制有符號(hào)整數(shù)
%md:m為指定的輸出字段的寬度。如果數(shù)據(jù)的位數(shù)小于m,則左端補(bǔ)以空格,若大于m,則按實(shí)際位數(shù)輸出。
%ld:輸出長整型數(shù)據(jù)。
%lld: long long型
%u 十進(jìn)制無符號(hào)整數(shù)
%f 浮點(diǎn)數(shù) 輸出float
%lf 浮點(diǎn)數(shù) 輸出double
%m.nf:輸出共占m列,其中有n位小數(shù),如數(shù)值寬度小于m左端補(bǔ)空格。
%-m.nf:輸出共占m列,其中有n位小數(shù),如數(shù)值寬度小于m右端補(bǔ)空格。
%s 字符串
%c 單個(gè)字符
%p 指針的值
%% 百分號(hào)本身
%e 指數(shù)形式的浮點(diǎn)數(shù)
%x, %X 無符號(hào)以十六進(jìn)制表示的整數(shù)
%o 無符號(hào)以八進(jìn)制表示的整數(shù)
%g(%G) 浮點(diǎn)數(shù)不顯無意義的零"0"
%p 輸出地址符
%lu 32位無符號(hào)整數(shù)
%llu 64位無符號(hào)整數(shù)
附加格式說明符
m 輸出數(shù)據(jù)域?qū)?數(shù)據(jù)長度 .n 對(duì)實(shí)數(shù),指定小數(shù)點(diǎn)后位數(shù)(四舍五入)
- 輸出數(shù)據(jù)在域內(nèi)左對(duì)齊(缺省右對(duì)齊)

+ 指定在有符號(hào)數(shù)的正數(shù)前顯示正號(hào)(+)
0 輸出數(shù)值時(shí)指定左面不使用的空位置自動(dòng)填0
# 在八進(jìn)制和十六進(jìn)制數(shù)前顯示前導(dǎo)0,0x
l long類型輸出 %ld
double類型輸出 %lf

格式化輸入:
#include

int scanf(const char *format, ...);
功能:按照格式從終端輸入數(shù)據(jù)
參數(shù):
format:格式控制串
%d 十進(jìn)制整數(shù)
%c 字符數(shù)據(jù)
%s 字符串
%f 浮點(diǎn)類型

arg:可變參
如果要將輸入的數(shù)據(jù)保存在arg變量里面,需要傳arg的地址
返回值:成功:輸入的個(gè)數(shù) 失敗EOF

int fscanf(FILE *stream, const char *format, ...);
功能:從流 stream 讀取格式化輸入
參數(shù):stream :這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
format :這是 C 字符串,包含了以下各項(xiàng)中的一個(gè)或多個(gè):空格字符、非空格字符 和 format 說明符。
返回值:如果成功,該函數(shù)返回成功匹配和賦值的個(gè)數(shù)。如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。

int sscanf(const char *str, const char *format, ...);
功能:從字符串讀取格式化輸入。
參數(shù):str:這是 C 字符串,是函數(shù)檢索數(shù)據(jù)的源。
format :這是 C 字符串,包含了以下各項(xiàng)中的一個(gè)或多個(gè):空格字符、非空格字符 和 format 說明符
返回值:如果成功,該函數(shù)返回成功匹配和賦值的個(gè)數(shù)。如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。

格式字符:同格式化輸出,左補(bǔ)空格;否則按實(shí)際輸出
  1. 臨時(shí)文件
#include

char *tmpnam(char *s);
功能:產(chǎn)生一個(gè)與現(xiàn)有文件不同名的文件,每次調(diào)用都會(huì)產(chǎn)生不同路徑的臨時(shí)文件
參數(shù):保存返回的路徑名
返回值:返回文件路徑名

#include

FILE *tmpfile(void);
功能:產(chǎn)生一個(gè)臨時(shí)二進(jìn)制文件(wb+),關(guān)閉該文件時(shí)會(huì)自動(dòng)刪除該文件
參數(shù):保存返回的路徑名
返回值:返回文件路徑名

11 內(nèi)存流

標(biāo)準(zhǔn)I/O庫都是是將文件中數(shù)據(jù)取出來緩沖在內(nèi)存中,現(xiàn)在我們可以直接通過緩沖區(qū)與主存直接來回傳遞數(shù)據(jù),不依賴文件。仍然使用FILE指針,這些流看起來像文件流,其實(shí)是內(nèi)存流。

內(nèi)存流不訪問文件只訪問主存,所以如果標(biāo)準(zhǔn)I/O流作為參數(shù)用于臨時(shí)文件的話,用內(nèi)存流替代會(huì)有很大性能提高。

#include

FILE *fmemopen(void *buf, size_t size, const char *mode);
功能:內(nèi)存流創(chuàng)建
參數(shù):buf:指向緩沖區(qū)的開始位置,如果為null,讀寫都沒有任何意義。
size:指定緩沖區(qū)大小的字節(jié)數(shù),如果buf為null,則自動(dòng)分配大小
mode:同fopen的mode
返回值:成功 返回流指針,失敗null

FILE *open_memstream(char **ptr, size_t *sizeloc);
功能:創(chuàng)建流面向字節(jié)

#include

FILE *open_wmemstream(wchar_t **ptr, size_t *sizeloc);
功能:創(chuàng)建流面向?qū)捵止?jié)

四、高級(jí)I/O

非阻塞I/O、I/O多路轉(zhuǎn)接、異步I/O、記錄鎖,這些都會(huì)在進(jìn)程間通信用到

  1. 非阻塞I/O

對(duì)于給定的文件描述符,有兩種方法指定為非阻塞I/O。

  1. 調(diào)用open獲得描述符時(shí)候指定 O_NONBLOCK標(biāo)志
  2. 對(duì)于打開的文件描述符,調(diào)用fcntl函數(shù),將O_NONBLOCK標(biāo)志打開
  3. 記錄鎖

記錄鎖:當(dāng)一個(gè)進(jìn)程正在讀或者寫一個(gè)文件某部分的時(shí)候,使用記錄鎖可以阻止其他進(jìn)程修改同一文件區(qū)。

int fcntl(int fd, int cmd, ... /* arg */ );
對(duì)于記錄鎖,cmd的參數(shù)為 F_GETKL、F_SETLK、F_SETLKW。第三個(gè)參數(shù)為指向flock結(jié)構(gòu)的指針
struct flock {
short l_type; 鎖的類型:F_RDLCK(共享讀鎖)、F_WRLCK(獨(dú)占性寫鎖)、F_UNLCK(解鎖)
short l_whence; SEEK_CUR、SEEK_SET、SEEK_END
off_t l_start; 加鎖或者解鎖的區(qū)域起始偏移量
off_t l_len; 區(qū)域長度
pid_t l_pid; 持有鎖阻塞當(dāng)前的進(jìn)程
};
如果len為0,表示鎖的范圍無限大,不管向文件追加多少數(shù)據(jù)都在鎖范圍內(nèi)。
對(duì)整個(gè)文件加鎖,len=0,whence=EEK_SET。
共享讀鎖:任意多個(gè)進(jìn)程可以在給定字節(jié)上有一把共享讀鎖,
獨(dú)占性寫鎖:如果給定字節(jié)已經(jīng)有寫鎖,那么不可再加任何鎖。
F_GETKL:判斷由flock結(jié)構(gòu)的指針?biāo)枋龅逆i是否會(huì)被另外一把鎖排斥。如果存在一把鎖,它阻止創(chuàng)建由flock結(jié)構(gòu)的指針?biāo)枋龅逆i,如果不存在則吧type修改為F_UNLCK
F_SETLK:由flock結(jié)構(gòu)的指針?biāo)枋龅逆i,如果試圖獲取一把鎖,系統(tǒng)阻止給我們鎖則返回錯(cuò)誤
F_SETLKW:如果請(qǐng)求鎖,因?yàn)槠渌M(jìn)程在使用,則調(diào)用進(jìn)程進(jìn)入休眠,直到鎖可用被喚醒。

當(dāng)一個(gè)進(jìn)程終止時(shí)候,它所建立的所有鎖都會(huì)釋放,同樣關(guān)閉一個(gè)文件描述符,與該文件描述符相關(guān)的鎖都會(huì)釋放。
fork產(chǎn)生的子進(jìn)程不繼承父進(jìn)程設(shè)置的鎖。
  1. I/O多路轉(zhuǎn)接

1.對(duì)于從一個(gè)文件描述符讀,然后又寫另一個(gè)文件描述符這樣的操作,我們通常這樣寫

while(read(fd,buf,size)) {
write(fd,buf,size);
}

這種阻塞I/O操作,我們經(jīng)常見,也是最低級(jí)的寫法,因?yàn)榭赡芤驗(yàn)樽x阻塞導(dǎo)致寫阻塞。這時(shí)候我們使用異步I/O,進(jìn)程告訴內(nèi)核,當(dāng)描述符準(zhǔn)備好時(shí)候通過信號(hào)通知內(nèi)核,但是他也有限制,只有在描述符是

網(wǎng)絡(luò)或者終端設(shè)備時(shí)候才會(huì)起作用。

2.IO多路復(fù)用基本思想

先構(gòu)造一張有關(guān)描述符的表,然后調(diào)用一個(gè)函數(shù),當(dāng)這些文件描述符中的一個(gè)或多個(gè)已準(zhǔn)備好進(jìn)行IO時(shí)函數(shù)才返回,函數(shù)返回時(shí)告訴進(jìn)程已經(jīng)有描述符就緒,可以進(jìn)行IO操作。

3.實(shí)現(xiàn)函數(shù)select

select函數(shù)可以使我們執(zhí)行I/O多路轉(zhuǎn)接,通過傳給select函數(shù)的參數(shù)可以告訴內(nèi)核:

a.我們所關(guān)心的描述符

b.對(duì)于每個(gè)描述符我們所關(guān)心的條件,是否想從一個(gè)給定描述符讀/寫,是否關(guān)心描述符異常

c.愿意等待多長時(shí)間

也可以通過返回值得到以下信息

a.已經(jīng)準(zhǔn)備好的文件描述符

b. 對(duì)于讀、寫、異常者三個(gè)條件中每一個(gè),哪些已經(jīng)準(zhǔn)備好

然后我們就可以使用read和write函數(shù)讀寫。

#include
#include
#include

int select(int nfds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout);
參數(shù): nfds 所有監(jiān)控文件描述符最大的那一個(gè) +1.(因?yàn)槲募枋龇幪?hào)從0開始,所以要加1)
read_fds 所有可讀的文件描述符集合。 沒有則為NULL
write_fds 所有可寫的文件描述符集合。 沒有則為NULL
except_fds 處于異常條件的文件描述符 沒有則為NULL
timeval: 超時(shí)設(shè)置。 NULL:一直阻塞,直到有文件描述符就緒或出錯(cuò)
0 :僅僅監(jiān)測(cè)文件描述符集的狀態(tài),然后立即返回
非0 :在指定時(shí)間內(nèi),如果沒有事件發(fā)生,則超時(shí)返回
返回值:當(dāng)timeval設(shè)置為NULL:返回值 -1 表示出錯(cuò)
>0 表示集合中有多少個(gè)描述符準(zhǔn)備好
當(dāng)設(shè)置timeval非0時(shí): 返回值 -1:表示出錯(cuò)
>0: 表示集合中有多少描述符準(zhǔn)備好
=0: 表示時(shí)間到了還沒有描述符準(zhǔn)備好

對(duì)于fd_set數(shù)據(jù)類型有以下四種處理方式 fd:文件描述符、 fdset文件描述符集合
void FD_SET(int fd,fd_set *fdset): 將fd加入到fdest
void FD_CLR(int fd,fd_set *fdest): 將fd從fdest里面清除
void FD_ZERO(fd_set *fdest): 從fdest中清除所有文件描述符
void FD_ISSET(int fd,fd_set *fdest):判斷fd是否在fdest集合中
這些接口實(shí)現(xiàn)為宏或者函數(shù),調(diào)用 FD_ZERO 將fd_set變量的所有位置設(shè)置為0,如果要開啟描述符集合的某一位,可以調(diào)用 FD_SET ,調(diào)用FD_CLR 可以清除某一位,F(xiàn)D_ISSET用來檢測(cè)某一位是否打開。
在申明了一個(gè)描述符集合之后,必須使用FD_ZERO將其清零,下面是使用操作:
fd_set reset;
int fd;
FD_ZERO(&reset);
FD_SET(fd, &reset);
FD_ZERO(STDIN_FILENO, &reset);
if (FD_ISSET(fd, &reset)) {}

對(duì)于“準(zhǔn)備好” 這個(gè)詞這里說明一下,什么才是準(zhǔn)備好,什么是沒有準(zhǔn)備好,如果對(duì)讀集(read_fds/write_fds) 中的一個(gè)描述符進(jìn)行read/write操作沒有阻塞則認(rèn)為是準(zhǔn)備好,或者對(duì)except_fds有一個(gè)未決異常條件,則認(rèn)為準(zhǔn)備好。
一個(gè)描述符的阻塞并不影響整個(gè)select的阻塞。當(dāng)文件描述符讀到文件結(jié)尾時(shí)候,read返回0.

4.實(shí)現(xiàn)函數(shù)poll

poll函數(shù)與select函數(shù)相似,不同的是,poll不是為每個(gè)條件(讀、寫、異常)構(gòu)造一個(gè)文件描述符,而是構(gòu)造一個(gè)pollfd結(jié)構(gòu)數(shù)組,每個(gè)數(shù)組元素指定一個(gè)描述符編號(hào),poll函數(shù)可以用于任何類型的文件描述符。

#include

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
參數(shù):fds:pollfd結(jié)構(gòu)數(shù)組
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 請(qǐng)求事件 */
short revents; /* 返回事件 */
};
events:需要將events設(shè)置為以下一個(gè)或者多個(gè)值,這些值會(huì)告訴內(nèi)核哪些是我們關(guān)系的文件描述符
POLLIN 不阻塞地讀高優(yōu)先級(jí)數(shù)據(jù)意外的數(shù)據(jù)
POLLRDNORM 不阻塞地讀普通數(shù)據(jù)
POLLRDBAND 不阻塞地讀優(yōu)先級(jí)數(shù)據(jù)
POLLPRI 不阻塞地讀高優(yōu)先級(jí)數(shù)據(jù)
POLLOUT 普不阻塞地讀寫普通數(shù)據(jù)
POLLWRNORM 同POLLOUT
POLLWRBAND 不阻塞地寫低優(yōu)先級(jí)數(shù)據(jù)
POLLERR 發(fā)生錯(cuò)誤
POLLHUP 發(fā)生掛起(當(dāng)掛起后就不可以再寫該描述符,但是可以讀)
POLLNVAL 描述字不是一個(gè)打開的文件
revents:返回的文件描述符,用于說明描述符發(fā)生了哪些事件。
nfds:數(shù)組中元素?cái)?shù)
timeout:等待時(shí)間
= -1:永遠(yuǎn)等待,直到有一個(gè)描述符準(zhǔn)備好,或者捕捉到一個(gè)信號(hào),如果捕捉到信號(hào)返回-1。
= 0 :不等待,立即返回。這是輪詢的方法。
> 0: 等待的毫秒數(shù),有文件描述符準(zhǔn)備好或者timeout超時(shí)立即返回。超時(shí)返回值為0.

5.散布讀和聚集寫

就是在一次函數(shù)調(diào)用中讀、寫多個(gè)非連續(xù)的緩沖區(qū)。

#include

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
功能:散布讀
參數(shù):fd:文件描述符
iov:iovec結(jié)構(gòu)指針
struct iovec {
void *iov_base; 緩沖地址
size_t iov_len; 緩沖大小
};
iovcnt:iov數(shù)組元素個(gè)數(shù)
返回值:成功:已讀個(gè)數(shù),失?。?1

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
功能:聚集寫
參數(shù):fd:文件描述符
iov:iovec結(jié)構(gòu)指針
struct iovec {
void *iov_base; 緩沖地址
size_t iov_len; 緩沖大小
};
iovcnt:iov數(shù)組元素個(gè)數(shù)
返回值:成功:已寫個(gè)數(shù),失?。?1

6.存儲(chǔ)映射I/O

存儲(chǔ)映射I/O,將一個(gè)磁盤文件映射到內(nèi)存中的一個(gè)緩沖區(qū)上,從這個(gè)緩沖區(qū)讀寫數(shù)據(jù)就相當(dāng)于讀寫文件數(shù)據(jù),就可以不再使用read、write。

#include
void *mmap(void *addr,size_t len,int prot,int flags,int fd,off_t offset);
功能:將文件或設(shè)備空間映射到共享內(nèi)存區(qū),因此當(dāng)從共享內(nèi)存讀數(shù)據(jù)時(shí)就相當(dāng)于從文件中讀取數(shù)據(jù)
參數(shù): addr:要映射的起始地址,通常為NULL,讓內(nèi)核自動(dòng)分配
len:映射到進(jìn)程地址空間的字節(jié)數(shù)
port:映射區(qū)保護(hù)方式 PROT_READ 映射區(qū)可讀
PROT_WRITE 映射區(qū)可寫
PROC_EXEC 映射區(qū)可執(zhí)行
PROC_NONE 映射區(qū)不可訪問

flags: MAP_SHARED 變動(dòng)是共享的
MAP_PRIVATE 變動(dòng)是私有的
MAP_FIXED 準(zhǔn)確解釋addr參數(shù), 如果不指定該參數(shù), 則會(huì)以4K大小的內(nèi)存進(jìn)行對(duì)齊
MAP_ANONYMOUS 建立匿名映射區(qū), 不涉及文件
fd: 文件描述符,使用前必須先打開文件。
offset:從文件頭開始偏移量為0

p=(STU*)mmap(NULL,sizeof(STU)*5,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0) //STU* 數(shù)據(jù)類型

五、終端I/O

終端I/O系統(tǒng)是一個(gè)非常復(fù)雜的東西,我們不會(huì)去講解它,但是它有幾個(gè)非常重要的函數(shù)需要我們學(xué),這幾個(gè)函數(shù)用來去嵌入式的串口編程

  1. 獲取/設(shè)置終端參數(shù)
#include
#include

int tcgetattr(int fd, struct termios *termios_p);
功能:獲取終端屬性
參數(shù):fd:打開串口設(shè)備節(jié)點(diǎn)描述符
termios_p:終端屬性結(jié)構(gòu)體指針

int tcsetattr(int fd, int optional_actions,
const struct termios *termios_p);
功能:設(shè)置終端屬性
參數(shù):fd:打開串口設(shè)備節(jié)點(diǎn)描述符
termios_p:終端屬性結(jié)構(gòu)體指針:有70多種標(biāo)志(這里不詳細(xì)介紹,后面會(huì)說)
optional_actions:TCSANOW: 更改立即發(fā)生
TCSADRAIIN: 發(fā)送所有輸出后更改才發(fā)生,更改輸出參數(shù)選用這個(gè)
TCSAFLUSH: 發(fā)送所有輸出后更改才發(fā)生,更改時(shí)所有未讀數(shù)據(jù)全部丟棄
  1. 波特率
#include
#include

speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
功能:獲取/設(shè)置波特率
參數(shù):termios_p:struct termios結(jié)構(gòu)體指針
speed:波特率:B50、B75、B110、B150、B200、B300、B600、B1200、B1800、B2400、B4800、B9600、B19200、B38400、B57600、B115200
返回值:成功0 失敗-1.

在調(diào)用cfget函數(shù)之前先調(diào)用tcgetattr函數(shù)獲取struct termios結(jié)構(gòu)指針
  1. 控制函數(shù)
int tcflush(int fd, int queue_selector);
功能:沖洗緩沖區(qū)
參數(shù):fd:打開串口設(shè)備節(jié)點(diǎn)描述符
queue_selector:TCIFLUSH:沖洗輸入隊(duì)列
TCOFLUSH:沖洗輸出隊(duì)列
TCIOFLUSH:沖洗輸入和輸出緩沖隊(duì)列
返回值:成功0 失敗-1

int tcsendbreak(int fd, int duration);
功能:指定時(shí)間區(qū)間內(nèi)發(fā)送連續(xù)的0值位流
參數(shù):fd:打開串口設(shè)備節(jié)點(diǎn)描述符
duration: 0:傳遞延續(xù)0.25-0.5s
非0:傳遞時(shí)間依賴于實(shí)現(xiàn)
返回值:成功0 失敗-1

int tcdrain(int fd);
功能:等待所以輸出都被傳遞
返回值:成功0 失敗-1

int tcflow(int fd, int action);
功能:對(duì)輸入輸出流進(jìn)行控制
參數(shù):action:TCOOFF:輸出被掛起
TCOON:?jiǎn)?dòng)被掛起的輸出
TCIOOFF:發(fā)送一個(gè)stop,終端設(shè)備停止發(fā)送數(shù)據(jù)
TCION: 發(fā)送一個(gè)START,終端設(shè)備繼續(xù)發(fā)送數(shù)據(jù)
返回值:成功0 失敗-1
  1. Linux系統(tǒng)下串口編程實(shí)現(xiàn)demo
#include
#include
#include
#include

#include
#include
#include
#include
#include

#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
#define MAX_BAUD 115200
#define UART_IDX "/dev/ttyUSB0"
#define CRC_OK 0
#define CRC_FAIL -1

typedef enum {
STANDARD_INPUT_MODE = 1,
RAWDATA_MODE
} uart_mode_e;

static int fd;
static pthread_t read_thread_id;
static int usb_thread_run;
static char recv_buf[1024 * 100];
static unsigned char recvmsg[1024 * 100];



static int ws_uart_send(char *buf, int len)
{
unsigned int total_byte = 0;
int send_byte;
while (len > 0) {
if (len < 1024)
send_byte = write(fd, buf+total_byte, len);
else
send_byte = write(fd, buf+total_byte, 1024);
if (send_byte < 0) {
tcflush(fd, TCOFLUSH);
printf("data send errorn");
return -1;
}
len -= send_byte;
total_byte += send_byte;
printf("len = %d total_byte = %dn", len, total_byte);
}
return 0;
}


static void *read_thread(void *arg)
{
int count,ret = 0;
int cnt;
int total;
char buf[64] = {0};
usb_thread_run = 1;
while (usb_thread_run) {
memset(recv_buf, 0, sizeof(recv_buf));
cnt = 0;
total = 0;
count = read(fd, buf, 64);

printf("cnt = %dn", count);
printf("buf = %sn", buf);
write(fd, buf, count);

}
return NULL;
}
/**
* @brief
* @note
* @param fd:
* @param baud_rate:
* @retval
*/
int set_uart_baud_rate(int fd, int baud_rate)
{
int ret = 0;
struct termios param;
int speed_arr[] = { B921600, B576000, B500000, B460800, B230400,B115200, B38400, B19200, B9600, B4800, B2400, B1200 };
int name_arr[] = { 921600, 576000, 500000, 460800, 230400,115200, 38400, 19200, 9600, 4800, 2400, 1200 };
int i = 0;
int status;

status = tcgetattr(fd, ¶m);
if(0 != status)
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
for(i = 0; i < ARRAY_SIZE(speed_arr); i++)
{
if(baud_rate == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
if(0 != cfsetispeed(¶m, speed_arr[i]))
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
if(0 != cfsetospeed(¶m, speed_arr[i]))
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
status = tcsetattr(fd, TCSANOW, ¶m);
if(0 != status)
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
tcflush(fd, TCIOFLUSH);
break;
}
}
if(i == ARRAY_SIZE(speed_arr))
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
return ret;
}


/**
* @brief
* @note
* @param fd:
* @param databits:
* @param stopbits:
* @param parity:
* @param mode:
* @retval
*/
int set_uart_parity(int fd, int databits, int stopbits, int parity, uart_mode_e mode)
{
int ret = 0;
struct termios param;
if(tcgetattr(fd, ¶m) != 0)
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
param.c_cflag &= ~CSIZE;
switch(databits) /*設(shè)置數(shù)據(jù)位數(shù)*/
{
case 7:
param.c_cflag |= CS7;
break;
case 8:
param.c_cflag |= CS8;
break;
default:
return ret;
}
switch(parity)
{
case 'n':
case 'N':
param.c_cflag &= ~PARENB; /* Clear parity enable */
param.c_iflag &= ~(INPCK | ICRNL | IXON); /* Enable parity checking */
break;
case 'o':
case 'O':
param.c_cflag |= (PARODD | PARENB); /* 設(shè)置為奇效驗(yàn)*/
param.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
param.c_cflag |= PARENB; /* Enable parity */
param.c_cflag &= ~PARODD; /* 轉(zhuǎn)換為偶效驗(yàn)*/
param.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
param.c_cflag &= ~PARENB;
param.c_cflag &= ~CSTOPB;
break;
default:
return ret;
}
/* 設(shè)置停止位*/
switch(stopbits)
{
case 1:
param.c_cflag &= ~CSTOPB;
break;
case 2:
param.c_cflag |= CSTOPB;
break;
default:
return ret;
}
if(mode == STANDARD_INPUT_MODE)
/*標(biāo)準(zhǔn)輸入設(shè)置*/
{
param.c_lflag &= ~(ECHO); //關(guān)閉回顯
param.c_lflag |= (ICANON);
param.c_oflag |= OPOST; //
}
/*raw data mode*/
else if(mode == RAWDATA_MODE)
{
param.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
param.c_oflag &= ~OPOST; //raw output
}
/* Set input parity option */
if(parity != 'n')
{
param.c_iflag |= INPCK;
}

param.c_cc[VTIME] = 10; //10 // 1 seconds
param.c_cc[VMIN] = 0;

tcflush(fd, TCIFLUSH);

/* Update the options and do it NOW */
if(tcsetattr(fd, TCSANOW, ¶m) != 0)
{
printf("%s:%d error = %dn",__func__, __LINE__, ret);
return ret;
}
return ret;
}

void uart_recv_start(void)
{
printf("recv start");
pthread_create(&read_thread_id, NULL, read_thread, NULL);
pthread_detach(read_thread_id);
}

void uart_deinit()
{
usb_thread_run = 0;
close(fd);
pthread_join(read_thread_id, NULL);
}

int main(int argc, char* argv[])
{
struct termios oldtio, newtio;
int ret = 0;
char buf[256];
fd = open(argv[1], O_RDWR | O_NOCTTY);
if (fd < 0) {
printf("Open %s failedn", argv[1]);
return -1;
} else
printf("Open %s successfullyn", argv[1]);

set_uart_baud_rate(fd, 115200);
ret = set_uart_parity(fd, 8, 1, 'n', RAWDATA_MODE);
uart_recv_start();
while (1) {
sleep(1);
}

}
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 驅(qū)動(dòng)器
    +關(guān)注

    關(guān)注

    52

    文章

    8055

    瀏覽量

    145623
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6756

    瀏覽量

    88614
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2945

    瀏覽量

    73729
  • Linux系統(tǒng)
    +關(guān)注

    關(guān)注

    4

    文章

    588

    瀏覽量

    27265
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    linux的一些文件的簡(jiǎn)單操作

    Linux 應(yīng)用編程中最需要掌握的基礎(chǔ)就是文件 I/O操作,學(xué)習(xí)過linux或者有過了解的應(yīng)該都會(huì)聽過一句話:
    發(fā)表于 01-11 15:40 ?507次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>下</b>的一些文件的簡(jiǎn)單<b class='flag-5'>操作</b>

    Linux系統(tǒng)中網(wǎng)絡(luò)I/O性能改進(jìn)方法的研究

    選擇并設(shè)計(jì)高效的網(wǎng)絡(luò)I/O模型是改善服務(wù)器性能的關(guān)鍵。該文通過對(duì)Linux系統(tǒng)中幾種網(wǎng)絡(luò)I/O
    發(fā)表于 04-09 09:41 ?28次下載

    Linux 系統(tǒng)應(yīng)用編程之標(biāo)準(zhǔn)I/O詳解

    本章前面幾節(jié)所述的文件及I/O讀寫都是基于文件描述符的。這些都是基本的I/O控制,是不帶緩存的。而本節(jié)所要討論的I/
    發(fā)表于 10-18 15:45 ?0次下載

    需要了解Linux的文件I/O編程

    linuxC語言對(duì)于文件的操作,我們會(huì)經(jīng)常用到fopen(),fclose(),fwrite(),fread(),fgets()等一系列庫函數(shù),基本和是和windows下學(xué)習(xí)C語言一樣的,其實(shí)這些庫函數(shù)就是在linuxx
    發(fā)表于 05-12 10:09 ?525次閱讀

    如何更改 LinuxI/O 調(diào)度器

    LinuxI/O 調(diào)度器是一個(gè)以塊式 I/O 訪問存儲(chǔ)卷的進(jìn)程,有時(shí)也叫磁盤調(diào)度器。Linux
    發(fā)表于 05-15 15:54 ?823次閱讀
    如何更改 <b class='flag-5'>Linux</b> 的 <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 調(diào)度器

    關(guān)于標(biāo)準(zhǔn)I/O庫執(zhí)行I/O操作

    當(dāng)在輸入和輸出中遇到換行符時(shí),標(biāo)準(zhǔn)I/O庫執(zhí)行I/O操作。這允許我們一次輸出一個(gè)字符,但只有在寫了一行之后才進(jìn)行實(shí)際
    的頭像 發(fā)表于 07-01 17:17 ?2319次閱讀

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)內(nèi)存

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)內(nèi)存
    的頭像 發(fā)表于 08-28 10:30 ?2303次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系統(tǒng)</b>知識(shí)<b class='flag-5'>講解</b>:走進(jìn)內(nèi)存

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)linux 內(nèi)存地址空間

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)linux 內(nèi)存地址空間
    的頭像 發(fā)表于 08-28 10:45 ?4957次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系統(tǒng)</b>知識(shí)<b class='flag-5'>講解</b>:走進(jìn)<b class='flag-5'>linux</b> 內(nèi)存地址空間

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)Linux 內(nèi)存分配算法

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)Linux 內(nèi)存分配算法
    的頭像 發(fā)表于 08-28 10:57 ?5376次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系統(tǒng)</b>知識(shí)<b class='flag-5'>講解</b>:走進(jìn)<b class='flag-5'>Linux</b> 內(nèi)存分配算法

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)Linux 內(nèi)存使用場(chǎng)景

    Linux操作系統(tǒng)知識(shí)講解:走進(jìn)Linux 內(nèi)存使用場(chǎng)景
    的頭像 發(fā)表于 08-28 11:04 ?2895次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系統(tǒng)</b>知識(shí)<b class='flag-5'>講解</b>:走進(jìn)<b class='flag-5'>Linux</b> 內(nèi)存使用場(chǎng)景

    Linux操作系統(tǒng)知識(shí)講解:避免內(nèi)存使用七大坑

    Linux操作系統(tǒng)知識(shí)講解:避免內(nèi)存使用七大坑
    的頭像 發(fā)表于 08-28 11:12 ?2776次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系統(tǒng)</b>知識(shí)<b class='flag-5'>講解</b>:避免內(nèi)存使用七大坑

    Linux中如何使用信號(hào)驅(qū)動(dòng)式I/O?

    I/O系統(tǒng)調(diào)用可能因?yàn)闊o法立即完成而被操作系統(tǒng)掛起,直到等待的事件發(fā)生為止。 點(diǎn)擊查看大圖 非阻塞式 I/
    的頭像 發(fā)表于 03-12 14:47 ?2353次閱讀
    <b class='flag-5'>Linux</b>中如何使用信號(hào)驅(qū)動(dòng)式<b class='flag-5'>I</b>/<b class='flag-5'>O</b>?

    Linux磁盤I/O的性能指標(biāo)和查看性能工具

    在我之前的文章:《探討 Linux 的磁盤 I/O》中,我談到了 Linux 磁盤 I/O 的工
    的頭像 發(fā)表于 05-14 15:21 ?2528次閱讀

    深入理解 LinuxI/O 系統(tǒng)

    傳統(tǒng)的 System Call I/OLinux 系統(tǒng)中,傳統(tǒng)的訪問方式是通過 write() 和 read() 兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn)的,
    發(fā)表于 05-26 09:31 ?365次閱讀
    深入理解 <b class='flag-5'>Linux</b> 的 <b class='flag-5'>I</b>/<b class='flag-5'>O</b> <b class='flag-5'>系統(tǒng)</b>

    Linux I/O 接口的類型及處理流程

    Linux I/O 接口 Linux I/O 接口可以分為以下幾種類型: 文件
    的頭像 發(fā)表于 11-08 16:43 ?789次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 接口的類型及處理流程