從內核到應用層:Linux緩沖機制與語言緩沖區的協同解析

系列文章目錄


文章目錄

  • 系列文章目錄
  • 前言
  • 一、緩沖區
    • 1.1 示例1
    • 1.2 緩沖區的概念
  • 二、緩沖區刷新方案
  • 三、緩沖區的作用及存儲


前言

上篇我們介紹了,文件的重定向操作以及文件描述符的概念,今天我們再來學習一個和文件相關的知識-----------用戶緩沖區。
在操作系統中,緩沖區是實現高效資源管理的關鍵進制,緩沖區可以幫助用戶、系統暫存讀取及寫入數據,規避了用戶頻繁的I/O操作,可以很好的提高系統的性能和用戶的體驗。


一、緩沖區

由于我們對緩沖區接觸的比較少,所以在講解這部分知識時,我們會引入大量的代碼示例,后面我們會對這些示例及結果逐一分析。

1.1 示例1

下列函數均向標準輸出打印

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite\n";7   char *wstr="hello witer\n";8   //c函數9   printf("hello printf\n");10   fprintf(stdout,"hello fprintf\n");11   fwrite(fstr,1,strlen(fstr),stdout);                                                                                           12   //系統調用接口                                                                                                       13   write(1,wstr,strlen(wstr));                                                                                          14   return 0;                                                                                                            15 }   

執行結果1:
在這里插入圖片描述
將輸出重定向至log1.txt

./myfile >log1.txt

執行結果2:
在這里插入圖片描述
到現在執行結果都是我們可以接受的,不要著急繼續向下看。

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite\n";7   char *wstr="hello witer\n";8   //c函數9   printf("hello printf\n");10   fprintf(stdout,"hello fprintf\n");11   fwrite(fstr,1,strlen(fstr),stdout);12   //系統調用接口13   write(1,wstr,strlen(wstr));14   fork();                                                                                                                       15   return 0;                                                 16 }       

我們在文件末尾處創建了一個子進程,重復上面實驗:
執行結果3:
在這里插入圖片描述
將文件重定向輸入到log2.txt

./myfile >log2.txt

執行結果4:
在這里插入圖片描述
通過和前三次執行結果對比,我們可以看到向文件log2.txt打印的結果,庫函數打印了兩次,系統調用接口只打印了一次,通過對比結果2和結果4,我們可以知道一定是fork()函數產生的影響,那么為什么fork()沒有對結果3產影響呢?帶著這兩問題我們繼續往下看。

1.2 緩沖區的概念

我們接著來看下面這兩個示例:

    1 #include<stdio.h>                                            2 #include<string.h>                                           3 #include<unistd.h>                                           4 int main()                                                   5 {                                                            6   char *fstr="hello fwrite\n";                               7   char *wstr="hello witer\n";                                8   //c函數                                                    9   printf("hello printf\n");                                  10   fprintf(stdout,"hello fprintf\n");                         11   fwrite(fstr,1,strlen(fstr),stdout);                        12   //系統調用接口13   write(1,wstr,strlen(wstr));                                                                                                   14   //fork();15   close(1);16   return 0;                      17 }

當執行完上面四個調用后我們使用close()函數關閉文件描述符1的文件。
在這里插入圖片描述
此時并沒有對程序執行結果造成影響,下面我們將\n全部去掉,繼續執行程序。

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite";7   char *wstr="hello witer";8   //c函數9   printf("hello printf");10   fprintf(stdout,"hello fprintf");                                                                                              11   fwrite(fstr,1,strlen(fstr),stdout);12   //系統調用接口13   write(1,wstr,strlen(wstr));14   //fork();15   close(1);16   return 0;17 }

在這里插入圖片描述
可以看到此時只有系統調用接口成功將內容打印了出來,這又是怎么回事呢,相信大家早在學習C語言時,就聽說過緩沖區,下面我們就來慢慢的回答上面一系列問題。

在這里插入圖片描述

結合下面的解釋看這個圖,一定要仔細看,精華全在圖上!!!!!
首先我們要清楚的一點是,printf/fprintf/fwrite全部是封裝的系統調用write。在我們的內核中,進程會擁有對于的task_struct結構體,這個結構體對象包含一個文件結構體指針(上篇我們講了,這里我們認為此時它指向顯示器文件的file對象),通過這個文件對象可以找到內核緩沖區,所以的輸入、輸出,都需要經過這個緩沖區才能到達對應的磁盤中(包含硬件設備),當我們在執行C語言函數時,結果并不會直接打印在屏幕上,而是先存入C語言的緩存區,這一點通過上面的示例也能感受到,當程序執行到合適的時間,就會調用系統接口writewrite通過文件描述符找到對應的文件對象,然后才能將c語言緩沖區的內容輸出到內核緩沖區,當數據到達內核緩沖區,符合條件,就會被刷新到顯示器上(磁盤),這個條件我們后面會介紹。

當程序執行系統調用write時,它會根據我們給他提供的文件描述符,找到對應的文件對象,直接將內容輸出到內核緩沖區。有了這些概念,我們來分析上面代碼。
在這里插入圖片描述
當我們執行的程序執行c函數時,它會先將內容存入C語言的緩沖區,但程序執行系統調用時,他是將內容直接刷新到了系統緩沖區,程序繼續向下執行close(1)顯示器文件被關閉,此時c函數調用系統調用write想要將處在C語言緩沖區的數據輸出道系統緩沖區,但是此時write已經無法找到顯示器結構體對象了,素以無法實現,最后程序結束,系統緩沖區被刷新到顯示器,結果表現為只有系統調用打印成功。到了這里我們算是回答了一個問題,那么為什么打印數據后加\n,就可以輸出成功呢?要回答這個問題我們就要來談一談緩沖區刷新方案了。

二、緩沖區刷新方案

在這里我們只談C語言的緩沖區刷新方案,我們將這種語言及的緩沖區稱為用戶及緩沖區(每個語言都會提供)。
緩沖區刷新方案主要有三種:

  • 無緩沖-------直接刷新
  • 行緩沖--------不刷新,直到碰見\n(一般為向顯示器打印時采用)
  • 全緩沖----------緩沖區滿了,才刷新(一般為向文件打印時采用)

此外當程序執行結束后也會進行刷新。

現在我們可以來回答為什么,這里不受文件關閉的影響了。
在這里插入圖片描述

當程序執行C函數時,會先將數據存入用戶及緩沖區,但用戶及緩沖區判斷數據存在\n就會立即調用write將數據刷新到系統的緩沖區(此時文件還沒有關閉)。

下面我們來回答這個問題

在這里插入圖片描述
為什么我們對程序進行重載后,C函數的結果打印了兩次。
在這里插入圖片描述
當執行這個程序時,我們對他它重定向到文件,此時緩沖區刷新方案由之前的行緩沖變為全緩沖,所以c函數的執行結果會被存儲在用戶級緩沖區,而write的執行結果則會直接存入系統緩沖區,此時創建子進程,程序結束時,父進程要調用write將用戶級緩沖區數據刷新到系統緩沖區上(這個行為會將用戶及緩沖區數據清空),觸發寫時拷貝,子進程結束后也會將數據刷新,這時就有兩份數據打印到了文件中。

不知道大家有沒有注意到,我們上面例子的結果打印順序,也出現了變化,剛剛的這個邏輯同樣能解釋這個問題,到了這里我們算是將問題都解決了

三、緩沖區的作用及存儲

提高用戶效率

在我們調用C函數向顯示器或文件寫入數據時,若沒有緩沖區存在,其底層就會不斷的去調用write函數,執行效率較低。

配合格式化

我們學習的printf/fprintf等函數,都是格式化輸出函數(如使用%d格式化數據)但是在操作系統中并沒有整形、浮點型等概念,在向顯示器或文件打印時統一被作為字符輸出,所以用戶級緩沖區的作用就是將數據格式化處理后,再交有系統接口。

在這里插入圖片描述
用戶級緩沖區存儲位置

用戶緩沖區實際是被定義再FILE結構體中的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74506.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74506.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74506.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

高通camx IOVA內存不足,導致10-15x持續拍照后,點擊拍照鍵定屏無反應,過一會相機閃退

定屏閃退問題分析思路&#xff1a; 定屏問題如果是相機問題&#xff0c;一般會出現返幀&#xff0c;導致預覽卡死。當然還有其他情況&#xff0c;我們先看返幀情況&#xff0c;發現request和result開始都正常&#xff0c;到12:53:05.443038就沒有返幀了&#xff0c;定屏了。往…

AI知識補全(十五):AI可解釋性與透明度是什么?

名人說&#xff1a;一笑出門去&#xff0c;千里落花風。——辛棄疾《水調歌頭我飲不須勸》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知識補全&#xff08;十四&#xff09;&#xff1a;零樣本…

CentOS 7安裝hyperscan

0x00 前言 HyperScan是一款由Intel開發的高性能正則表達式匹配庫&#xff0c;專為需要快速處理大量數據流的應用場景而設計。它支持多平臺運行&#xff0c;包括Linux、Windows和macOS等操作系統&#xff0c;并針對x86架構進行了優化&#xff0c;以提供卓越的性能表現。HyperSc…

機器學習的一百個概念(9)學習曲線

前言 本文隸屬于專欄《機器學習的一百個概念》&#xff0c;該專欄為筆者原創&#xff0c;引用請注明來源&#xff0c;不足和錯誤之處請在評論區幫忙指出&#xff0c;謝謝&#xff01; 本專欄目錄結構和參考文獻請見[《機器學習的一百個概念》 ima 知識庫 知識庫廣場搜索&…

macvlan 和 ipvlan 實現原理及設計案例詳解

一、macvlan 實現原理 1. 核心概念 macvlan 允許在單個物理網絡接口上創建多個虛擬網絡接口&#xff0c;每個虛擬接口擁有 獨立的 MAC 地址 和 IP 地址。工作模式&#xff1a; bridge 模式&#xff08;默認&#xff09;&#xff1a;虛擬接口之間可直接通信&#xff0c;類似交…

linux文件上傳下載lrzsz

lrzsz 是一個在 Linux 系統中用于通過串行端口(如 ZMODEM、XMODEM、YMODEM 等協議)進行文件上傳和下載的工具集。它通常用于在終端環境中通過串口或 SSH 連接傳輸文件。 安裝 lrzsz 在大多數 Linux 發行版中,你可以使用包管理器來安裝 lrzsz。 Debian/Ubuntu: sudo apt-ge…

單片機學習之SPI

物理層 串行全雙工總線 需要四根線&#xff1a;SCLK&#xff08;時鐘線&#xff09;&#xff0c;CS&#xff08;片選線&#xff09;、MOSI(主設備輸出、從設備輸入)&#xff0c;MISO&#xff08;主設備輸入&#xff0c;從設備輸出&#xff09;。 片選信號 片選信號CS是用來…

大模型應用初學指南

隨著人工智能技術的快速發展&#xff0c;檢索增強生成&#xff08;RAG&#xff09;作為一種結合檢索與生成的創新技術&#xff0c;正在重新定義信息檢索的方式&#xff0c;RAG 的核心原理及其在實際應用中的挑戰與解決方案&#xff0c;通用大模型在知識局限性、幻覺問題和數據安…

docker-compose部署prometheus+grafana+node_exporter+alertmanager規則+郵件告警

目錄 一.docker-compose文件 二.配置文件 三.文件層級關系&#xff0c;docker-compose和配置文件位于同級目錄 四.node_exporter頁面json文件 五.效果展示 prometheusalertmanager郵件告警 grafana面板效果 六.涉及離線包 一.docker-compose文件 [rootsulibao prometh…

AI設計再現新引擎,科技創新又添新動能——廣東省首家行業AI設計工程中心獲批成立

近期&#xff0c;大捷智能科技&#xff08;廣東&#xff09;有限公司&#xff08;以下簡稱“大捷智能”&#xff09;憑借其在人工智能與智能制造領域的突出研發實力與創新科技成果&#xff0c;由廣東省科技廳批準設立“廣東省模具智能設計與智能制造工程技術研究中心”。 廣東省…

【MongoDB + 向量搜索引擎】MongoDB Atlas 向量搜索 提供全托管解決方案

在代碼審計項目中&#xff0c;MongoDB可以用于存儲元數據和部分結構化信息&#xff0c;但要高效處理向量相似性搜索&#xff0c;需結合其他工具。以下是具體分析&#xff1a; 1. MongoDB 的適用場景 元數據存儲&#xff1a; 存儲代碼片段的文件路徑、行號、語言類型等結構化信…

基于ANSYS 概率設計和APDL編程的結構可靠性設計分析

01 可靠度基本理論 結構的極限狀態&#xff1a;整個結構的一部分超過某一特定狀態就不能滿足設計規定的某一功能要求。結構的極限狀態實質上是結構工作狀態的一個閥值&#xff0c;如果工作狀態超過這一閥值&#xff0c;則結構處于不安全、不耐久或不適用的狀態&#xff1b;若工…

CyclicBarrier、Semaphore、CountDownLatch的區別,適用場景

CyclicBarrier、Semaphore 和 CountDownLatch 是 Java 并發包中用于線程協作的工具類&#xff0c;它們雖然都與線程同步相關&#xff0c;但設計目的和使用場景有顯著差異。以下是它們的核心區別和典型應用場景&#xff1a; 1. CountDownLatch 核心機制 一次性計數器&#xf…

新能源汽車測試中的信號調理模塊:從原理到實戰應用

摘要 信號調理模塊&#xff08;Signal Conditioning Module, SCM&#xff09;是新能源汽車&#xff08;NEV&#xff09;測試系統中的關鍵環節&#xff0c;直接影響數據采集的精度與可靠性。本文面向HIL測試工程師、電機測試工程師及整車動力經濟性測試工程師&#xff0c;系統性…

Qt5 Mac系統檢查休眠

在開發跨平臺應用程序時,有時候我們需要檢測系統的狀態,比如是否處于休眠或喚醒狀態。Qt是一個強大的跨平臺應用開發框架,支持多種操作系統,包括Windows、Linux、macOS等。在這個場景下,我們關注的是如何在Qt5.10中檢測到系統是否休眠以及在Mac上實現這一功能。本文將深入…

RabbitMQ簡單介紹和安裝

RabbitMQ簡單介紹 一.RabbitMQ介紹二.RabbitMQ的作用1.異步解耦2.流量削峰3.消息分發4.延遲通知 三.RabbitMQ安裝&#xff08;Ubuntu&#xff09;1.先安裝Erlang2.安裝RabbitMQ3.安裝RabbitMQ的管理界面4.創建虛擬機5.端口號信息 四.工作原理圖 一.RabbitMQ介紹 RabbitMQ 是一款…

匯編學習之《call, return指令》

call 指令 call 指令就是調用函數的執行&#xff0c;不過它也是幾個指令的組合 第一步通過jmp 函數地址的方式先跳轉到函數 第二步通過push 指令將函數地址指令額下一行的指令的地址壓入棧中。 我們來驗證下 首先打開OllyDbg,導入你的程序&#xff0c;找到一個call 函數&…

接口自動化學習三:參數化parameterize

使用parametrize之前&#xff1a; def add(x,y):return xy class TestAddFunction(object):def test01(self):resadd(2,4)assert 6resdef test02(self):resadd(4,6)assert 10resparametrize參數化之后&#xff1a; import pytest def add(x,y):return xydata[(10,20,30),(200…

全面解析 Mybatis 與 Mybatis-Plus:深入原理、實踐案例與高級特性對比

全面解析 Mybatis 與 Mybatis-Plus&#xff1a;深入原理、實踐案例與高級特性對比 &#x1f680; 前言一、基礎介紹 ?1. Mybatis 簡介 &#x1f50d;2. Mybatis-Plus 簡介 ? 二、核心區別與高級特性對比 &#x1f50e;1. 開發模式與配置管理2. 功能豐富度與擴展性3. 自動填充…

【區塊鏈安全 | 第十九篇】類型之映射類型

文章目錄 映射類型可迭代映射 映射類型 映射類型使用語法 mapping(KeyType KeyName? > ValueType ValueName?)&#xff0c;映射類型的變量聲明使用語法 mapping(KeyType KeyName? > ValueType ValueName?) VariableName。 KeyType 可以是任何內置值類型、bytes、st…