用戶態與內核態 & 文件流與文件描述符

用戶態和內核態

程序代碼的依賴和調用關係如下圖所示:

用戶態與內核態 & 文件流與文件描述符

  • Lib:標準ASCI C函數,幾乎所有的平臺都支持該庫函數,因此依賴該庫的程序可移植性好;
  • System Function:系統調用函數,與系統內核進行交互,不同平臺具備不同的函數接口,因此可移植性較差

區分用戶態和內核態主要是由於系統資源的有限性,不能無限制的隨意分配給用戶使用,必須由系統進行統一管理

  • User mode:不能直接對系統資源進行訪問,如果要操作系統資源,必須轉化為內核態
  • Kernel mode:管理系統資源,可直接對系統資源進行控制和訪問

內核為用戶提供了統一的API供其使用,不同的系統的API接口不同,為了便於代碼的移植,出臺了POSIX標準,類Unix系統(Unix、Linux、BSD、SunOS等)均支持該標準。

文件流與文件描述符

  • 問題
  • 由上圖我們可看到,每執行一次系統調用,都要涉及到CPU狀態的切換,即從用戶態切換到內核態,即從用戶空間切換到內核空間,實現上下文切換的過程,會消耗相當一部分的CPU資源,因此頻繁的磁盤訪問對程序的執行效率將造成很大影響。
  • 解決方案
  • 為了解決以上的難題,採用了緩衝區的概念,當對磁盤文件進行操作時,可一次性從磁盤文件中讀出大量的數據暫放到緩衝區中,以後對這部分數據的訪問就不需要再進行系統調用了;當對文件行操作後,可將處理後的數據暫存到輸出緩衝區,待文件緩衝區滿後,一次性寫入到磁盤。
用戶態與內核態 & 文件流與文件描述符

以上,數據的輸入輸出就像是水在流動一樣,因此我們採用了流的概念。

  • 文件流
  • 簡單來說就是建立在面向對象基礎上的一種抽象的處理數據的工具。在流中,定義了一些處理數據的基本操作,如讀取數據,寫入數據等,程序員是對流進行所有操作的,而不用關心流的另一頭數據的真正流向;
  • 文件流用結構體表示:struct FILE.
  • FILE的結構體又是怎麼樣的呢?我們可以進行查找一下:
 [niesh@niesh ~]$ vim /usr/include/stdio.h

我們看到了 stdio.h的文件中有一行:

__BEGIN_NAMESPACE_STD
/* The opaque type of streams. This is the definition used elsewhere. */
typedef struct _IO_FILE FILE;
__END_NAMESPACE_STD

顯然,FILE 是 _IO_FILE的類型替換,那麼我們找一下 _IO_FILE在哪裡呢?

[niesh@niesh ~]$ grep -rn "\<_io_file>" /usr/include/
/usr/include/c++/4.8.2/streambuf:178: * This is based on _IO_FILE, just reordered to be more consistent,
/usr/include/libio.h:145:struct _IO_jump_t; struct _IO_FILE;
/usr/include/libio.h:163: struct _IO_FILE *_sbuf;
/usr/include/libio.h:246:struct _IO_FILE { //此處正解
/usr/include/libio.h:267: struct _IO_FILE *_chain;
/usr/include/libio.h:291: struct _IO_FILE _file;
/usr/include/libio.h:299: struct _IO_FILE *_freeres_list;
/usr/include/libio.h:316:typedef struct _IO_FILE _IO_FILE;
/usr/include/libio.h:325:#define _IO_stdin ((_IO_FILE*)(&_IO_2_1_stdin_))
/usr/include/libio.h:326:#define _IO_stdout ((_IO_FILE*)(&_IO_2_1_stdout_))
/usr/include/libio.h:327:#define _IO_stderr ((_IO_FILE*)(&_IO_2_1_stderr_))
/usr/include/libio.h:329:extern _IO_FILE *_IO_stdin attribute_hidden;
/usr/include/libio.h:330:extern _IO_FILE *_IO_stdout attribute_hidden;
/usr/include/libio.h:331:extern _IO_FILE *_IO_stderr attribute_hidden;
/usr/include/libio.h:391:extern int __underflow (_IO_FILE *);
/usr/include/libio.h:392:extern int __uflow (_IO_FILE *);
/usr/include/libio.h:393:extern int __overflow (_IO_FILE *, int);
/usr/include/libio.h:395:extern _IO_wint_t __wunderflow (_IO_FILE *);
/usr/include/libio.h:396:extern _IO_wint_t __wuflow (_IO_FILE *);
/usr/include/libio.h:397:extern _IO_wint_t __woverflow (_IO_FILE *, _IO_wint_t);
/usr/include/libio.h:435:extern int _IO_getc (_IO_FILE *__fp);
/usr/include/libio.h:436:extern int _IO_putc (int __c, _IO_FILE *__fp);
/usr/include/libio.h:437:extern int _IO_feof (_IO_FILE *__fp) __THROW;
/usr/include/libio.h:438:extern int _IO_ferror (_IO_FILE *__fp) __THROW;
/usr/include/libio.h:440:extern int _IO_peekc_locked (_IO_FILE *__fp);
/usr/include/libio.h:446:extern void _IO_flockfile (_IO_FILE *) __THROW;
/usr/include/libio.h:447:extern void _IO_funlockfile (_IO_FILE *) __THROW;
/usr/include/libio.h:448:extern int _IO_ftrylockfile (_IO_FILE *) __THROW;
/usr/include/libio.h:465:extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict,
/usr/include/libio.h:467:extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict,
/usr/include/libio.h:469:extern _IO_ssize_t _IO_padn (_IO_FILE *, int, _IO_ssize_t);
/usr/include/libio.h:470:extern _IO_size_t _IO_sgetn (_IO_FILE *, void *, _IO_size_t);
/usr/include/libio.h:472:extern _IO_off64_t _IO_seekoff (_IO_FILE *, _IO_off64_t, int, int);
/usr/include/libio.h:473:extern _IO_off64_t _IO_seekpos (_IO_FILE *, _IO_off64_t, int);
/usr/include/libio.h:475:extern void _IO_free_backup_area (_IO_FILE *) __THROW;
/usr/include/libio.h:478:extern _IO_wint_t _IO_getwc (_IO_FILE *__fp);
/usr/include/libio.h:479:extern _IO_wint_t _IO_putwc (wchar_t __wc, _IO_FILE *__fp);
/usr/include/libio.h:480:extern int _IO_fwide (_IO_FILE *__fp, int __mode) __THROW;
/usr/include/libio.h:514:extern int _IO_vfwscanf (_IO_FILE * __restrict, const wchar_t * __restrict,
/usr/include/libio.h:516:extern int _IO_vfwprintf (_IO_FILE *__restrict, const wchar_t *__restrict,
/usr/include/libio.h:518:extern _IO_ssize_t _IO_wpadn (_IO_FILE *, wint_t, _IO_ssize_t);
/usr/include/libio.h:519:extern void _IO_free_wbackup_area (_IO_FILE *) __THROW;
/usr/include/stdio.h:44:struct _IO_FILE;
/usr/include/stdio.h:48:typedef struct _IO_FILE FILE;
/usr/include/stdio.h:64:typedef struct _IO_FILE __FILE;
/usr/include/stdio.h:168:extern struct _IO_FILE *stdin; /* Standard input stream. */
/usr/include/stdio.h:169:extern struct _IO_FILE *stdout; /* Standard output stream. */
/usr/include/stdio.h:170:extern struct _IO_FILE *stderr; /* Standard error output stream. */

(⊙o⊙)…,還挺多啊,不過仔細觀察,發現只有 libio.h的246行是對該結構體的定義,我們打開瞅瞅!

[niesh@niesh ~]$ vim /usr/include/libio.h

struct FILE 的結構體成員如下代碼所示:

 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;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */

#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];


/* char* _save_gptr; char* _save_egptr; */

_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
  • 文件描述符
  • 每個進程,當打開一個文件後,內核會為其建立一個打開文件的數組 (數組的前三個為stdin,stdout,stderr),然後返回打開文件位於數組的索引值(下標),該所以只即為文件描述符,只要文件不關閉,用戶便可以根據該描述符對文件進行訪問和操作。
  • 不同點
  • 文件描述符:表示為int類型的對象:如stdin對應文件描述符0,stdout對應文件描述符1;
  • :表示為指向結構FILE的指針FILE* ,因此流也稱為文件指針
  • 若需要對特定設備進行控制操作,必須使用文件描述符方式,沒有函數能對流進行這類操作
  • 如果需要按照特殊的方式進行I/O操作(例如非阻塞的方式),必須使用文件描述符方式,也沒有函數能對流進行這類操作。
  • 聯繫
  • 流給用戶程序提供了更高一級的(功能更強大,使用更簡化)的I/O接口,它處在文件描述符方式的上層,也就是說,流函數是通過文件描述符函數來實現的


分享到:


相關文章: