posix 是什麼都不知道,就別說你懂 Linux 了!
Linux 開發者越來越多,但是仍然有很多人整不明白 POSIX 是什麼。本文就帶着大家來了解一下到底什麼是 POSIX,瞭解他的歷史和重要性。
一、什麼是 posix?
1. 概念
POSIX:可移植操作系統接口(Portable Operating System Interface of UNIX,縮寫爲 POSIX ),
2. 發佈者 - IEEE
發佈者爲電氣與電子工程師協會(Institute of Electrical and Electronics Engineers),簡稱 IEEE。這個協會老牛了【該組織在太空、計算機、電信、生物醫學、電力及消費性電子產品等領域中都是主要的權威】!
POSIX.1 已經被國際標準化組織(International Standards Organization,ISO)所接受,被命名爲 ISO/IEC 9945-1:1990 標準。
IEEE,總部位於美國紐約,是一個國際性的電子技術與信息科學工程師的協會,也是目前全球最大的非營利性專業技術學會。IEEE 致力於電氣、電子、計算機工程和與科學有關的領域的開發和研究,在太空、計算機、電信、生物醫學、電力及消費性電子產品等領域已制定了 1300 多個行業標準,現已發展成爲具有較大影響力的國際學術組織
3. POSIX 標準下載
主頁:http://blog.csdn.net/ablo_zhou
很多人聽說了 POSIX 標準,但標準具體長什麼樣,在哪裏下載到,則 不清楚。現在我開放出來,供相關人員使用。
Single UNIX Specification V3,IEEE Std 1003.1,2004 Edition
標準線上地址:http://www.unix.org/version3/online.html 註冊後可以在線閱讀或者下載。
IEEE 和 Open Group 的 POSIX 認證:http://www.opengroup.org/certification/idx/posix.html
相關頁面:http://www.unix.org/version3/ieee_std.html
二、POSIX 歷史
1. 起源
POSIX 是 Unix 的標準。
1974 年,貝爾實驗室正式對外發布 Unix。因爲涉及到反壟斷等各種原因,加上早期的 Unix 不夠完善,於是貝爾實驗室以慷慨的條件向學校提供源代碼,所以 Unix 在大專院校裏獲得了很多支持並得以持續發展。
於是出現了好些獨立開發的與 Unix 基本兼容但又不完全兼容的 OS,通稱 Unix-like OS。
包括:
-
美國加州大學伯克利分校的 Unix4.xBSD(Berkeley Software Distribution)。
-
貝爾實驗室發佈的自己的版本,稱爲 System V Unix。
-
其他廠商的版本,比如 Sun Microsystems 的 Solaris 系統, 則是從這些原始的 BSD 和 System V 版本中衍生而來。
局面非常混亂,麻煩也就隨之而來了。
爲了提高兼容性和應用程序的可移植性,阻止這種趨勢, IEEE(電氣和電子工程師協會) 開始努力標準化 Unix 的開發,後來由 Richard Stallman 命名爲 “Posix”。
這套標準涵蓋了很多方面,比如 Unix 系統調用的 C 語言接口、shell 程序和工具、線程及網絡編程。
2. 誰遵循這個標準呢?
首先就是大名鼎鼎的 Unix 和 Linux 了,
除此之外還有蘋果的操作系統也是 Unix-based 的。
有了這個規範,你就可以調用通用的 API 了,Linux 提供的 POSIX 系統調用在 Unix 上也能執行,因此學習 Linux 的底層接口最好就是理解 POSIX 標準。
Windows 從 WinNT 開始就有兼容 POSIX 的考慮。這是因爲當年在要求嚴格的領域,Unix 地位比 Windows 高。爲了把 Unix 用戶拉到 Windows 陣營,被迫支持 POSIX。
現在 Win10 對 Linux/POSIX 支持好,則是因爲 Linux 已經統治了廉價服務器市場。爲了提高 Windows 的競爭力搞的。
所以一切都是以市場爲主導。
3. 支持 POSIX-Linux 成功的最重要一個因素
Linux 之所以能夠成功,有很多因素,但是支持 POSIX 標準無疑是它能夠快速發展的最重要的一個因素。
POSIX 標準的制定最後投票敲定階段大概是 1991~1993 年間,而此時正是 Linux 剛剛起步的時候,這個 UNIX 標準爲 Linux 提供了極爲重要的信息,使得 Linux 能夠在標準的指導下進行開發,並能夠與絕大多數 UNIX 操作系統兼容。
在最初的 Linux 內核源碼(0.01 版、0.11 版)中就已經爲 Linux 系統與 POSIX 標準的兼容做好了準備工作。
在 Linux 0.01 版內核 /include/unistd.h 文件中就已經定義了幾個有關 POSIX 標準要求的符號常數,而且 Linus 在註釋中已寫道:“OK,這也許是個玩笑,但我正在着手研究它呢”。
正是由於 Linux 支持 POSIX 標準,無數可以在 unix 上運行的程序都陸續的移植到 Linux 上,而此時 unix 因爲版權問題,官司打的不可開交,使得 Linux 後來者居上。
時也命也!
下面是祖師爺 Linus 當年申請 POSIX 標準的郵件:
來自:torvalds@klaava.Helsinki.Fi(林納斯·託瓦茲)
討論組:comp.os.minix
主題:Gcc-1.40和一個有關POSIX的問題
信息名稱: 1991 Jul 3, 100050.9886@klaava.Helsinki.Fi
日期: 1991年7月3日, 格林威治時間10: 00: 50
各位網友好!
由於我現在正在MINIX系統下做一個項目, 對POSIX標準很感興趣。 有誰能向我提供
一個(最好) 是機器可讀形式的最新的POSIX規則? 能有FTP地址就更好了。
而 Linus 也在《只是爲了好玩》中講述了 POSIX 的重要性:
POSIX標準是一個可以適用於數以百計的UNIX系統呼叫中的任意一個的一套冗長規則, 計算機要執行任務(從讀、 寫、 開機和關機開始) 就需要這個標準。
POSIX則是指一個UNIX的標準體系, 或一個由來自不同公司的代表所組成的一個組織, 希望按照一個共同的標準進行運作。 對於程序員開發的在該操作系統下的新應用軟件或開發應用軟件的新版本而言, 標準是極其重要的。 從POSIX這樣的系統呼叫(system call) , 尤其是重要的呼叫(call) 中, 我可以獲得一個操作系統應該具有哪些功能的一個單子; 然後我就可以通過自己的方式在自己的系統中實現每一個功能。 通過編寫出這些標準, 我的系統軟件的源代碼將可以被別人使用, 以開發新的應用軟件。
當時我並不知道我本可以直接從POSIX公司買到這些規則的軟盤, 但這無所謂。 哪怕我能買得起, 什麼東西運到芬蘭, 往往會需要很長的時間。 我不願等上那麼久, 因此我四處搜求一個能從FTP地址上直接下載的版本。
沒有人給我提供能找到POSI標準的來源。 於是我開始了計劃B。
我從學校找到運行sun器(sun server)的sun微系統版的UNIX手冊。 該手冊中有一個完全可以湊合使用的系統呼叫的基本版本。 從用戶手冊中能看出系統呼叫的主要功能, 以及爲完成這些功能所需要完成的步驟。 但是, 從中看不出具體的方法, 而只是標明瞭最終的結果。 於是我便着手從安德魯·塔南鮑姆的書中和別的材料中收集一些系統呼叫。
最終有人給我寄來了那幾卷厚厚的POSIX標準。
三、可移植性
聊到 POSIX,那我們就不得不說說到底什麼是可移植性,在講可移植性之前,我們先來了解庫函數和系統調用的區別。
Linux 下對文件操作有兩種方式:系統調用(system call)和庫函數調用(Library functions)。
1. 系統調用
系統調用是通向操作系統本身的接口,是面向底層硬件的。通過系統調用,可以使得用戶態運行的進程與硬件設備 (如 CPU、磁盤、打印機等) 進行交互,是操作系統留給應用程序的一個接口。
2. 庫函數
庫函數(Library function)是把函數放到庫裏,供別人使用的一種方式。
方法是把一些常用到的函數編完放到一個文件裏,供不同的人進行調用。一般放在. lib 文件中。
庫函數調用則是面向應用開發的,庫函數可分爲兩類,
-
一類是 C 語言標準規定的庫函數,
-
一類是編譯器特定的庫函數。
(由於版權原因,庫函數的源代碼一般是不可見的,但在頭文件中你可以看到它對外的接口)。
glibc 爲程序員提供豐富的 API(Application Programming Interface),這些 API 都是遵循 POSIX 標準的,API 的函數名,返回值,參數類型等都必須按照 POSIX 標準來定義。
POSIX 兼容也就指定這些接口函數兼容,但是並不管 API 具體如何實現。
3. 庫函數 API 和系統調用的區別
如上圖所示:
-
(1) 庫函數是語言或應用程序的一部分,而系統調用是內核提供給應用程序的接口,屬於系統的一部分
-
(2) 庫函數在用戶地址空間執行,系統調用是在內核地址空間執行,庫函數運行時間屬於用戶時間,系統調用屬於系統時間,庫函數開銷較小,系統調用開銷較大
-
(3) 系統調用依賴於平臺,庫函數並不依賴
系統調用是爲了方便使用操作系統的接口,而庫函數則是爲了人們編程的方便。
庫函數調用與系統無關,不同的系統,調用庫函數,庫函數會調用不同的底層函數實現,因此可移植性好。
4. 程序的可移植性及其本質
那麼目標代碼和啓動代碼是怎麼生成的呢?答案是編譯器。
編程語言編寫的程序首先要被編譯器編譯成目標代碼(0、1 代碼),然後在目標代碼的前面插入啓動代碼,最終生成了一個完整的程序。
要注意的是,程序中爲訪問特定設備(如顯示器)或者操作系統(如 windows xp 的 API) 的特殊功能而專門編寫的部分通常是不能移植的。
綜上所述,一個編程語言的可移植性取決於
-
不同平臺編譯器的數量
-
對特殊硬件或操作系統的依賴性
移植是基於操作系統的。但是這個時候,我們需要注意一點:基於各種操作系統平臺不同,應用程序在二級制級別是不能直接移植的。
我們只能在代碼層去思考可移植問題,在 API 層面上由於各個操作系統的命名規範、系統調用等自身原因,在 API 層面上實現可移植也是不大可能的。
在各個平臺下,我們默認 C 標準庫中的函數都是一樣的,這樣基本可以實現可移植。但是對於 C 庫本身而言,在各種操作系統平臺下其內部實現是完全不同的,也就是說 C 庫封裝了操作系統 API 在其內部的實現細節。
因此,C 語言提供了我們在代碼級的可移植性,即這種可移植是通過 C 語言這個中間層來完成的。
例如在我們的代碼中下功夫。以下代碼可以幫助我們實現各平臺之間的可移植:
#ifdef _WINDOWS_
CreateThread(); //windows下線程的創建
#else
Pthread_create(); //Linux下線程的創建
#endif
對於頭文件,也使用同樣的預編譯宏來實現。如:
#ifndef _WINDOWS_
#include <windows.h>
#else
#include <thread.h>
#endif
這樣就可以實現代碼的可移植了。在編譯的時候只要通過 #define 就可以選擇在那個平臺下完成程序的編譯。
綜上所述,我們都是將 C,C++ 等各種語言當作中間層,以實現其一定程度上的可移植。如今,語言的跨平臺的程序都是以這樣的方式實現的。但是在不同的平臺下,仍需要重新編譯。
5. 系統開銷
使用系統調用會影響系統的性能,在執行調用時的從用戶態切換到內核態,再返回用戶態會有系統開銷。
爲了減少開銷,因此需要減少系統調用的次數,並且讓每次系統調用盡可能的完成多的任務。
硬件也會限制對底層系統調用一次所能寫的數據塊的大小。
爲了給設備和文件提供更高層的接口,Linux 系統提供了一系列的標準函數庫。
使用標準庫函數,可以高效的寫任意長度的數據塊,庫函數在數據滿足數據塊長度要求時安排執行底層系統調用。
一般地,操作系統爲了考慮實現的難度和管理的方便,它只提供一少部分的系統調用,這些系統調用一般都是由 C 和彙編混合編寫實現的,其接口用 C 來定義,而具體的實現則是彙編,這樣的好處就是執行效率高,而且,極大的方便了上層調用。
隨着系統提供的這些庫函數把系統調用進行封裝或者組合,可以實現更多的功能,這樣的庫函數能夠實現一些對內核來說比較複雜的操作。
比如,read() 函數根據參數,直接就能讀文件,而背後隱藏的比如文件在硬盤的哪個磁道,哪個扇區,加載到內存的哪個位置等等這些操作,程序員是不必關心的,這些操作裏面自然也包含了系統調用。
而對於第三方的庫,它其實和系統庫一樣,只是它直接利用系統調用的可能性要小一些,而是利用系統提供的 API 接口來實現功能 (API 的接口是開放的)。
四、舉例
如下圖是 Linux 系統調用的大概流程。
當應用程序調用 printf() 函數時,printf 函數會調用 C 庫中的 printf,繼而調用 C 庫中的 write,C 庫最後調用內核的 write()。
而另一些則不會使用系統調用,比如 strlen, strcat, memcpy 等。
printf 函數執行過程中,程序運行狀態切換如下:
用戶態–>系統調用–>內核態–>返回用戶態
printf 函數、glibc 庫和系統調用在系統中關係圖如下:
實例代碼如下:
1 #include <stdio.h>
2
3
4 int main(int argc, char **argv)
5 {
6 printf("yikoulinux");
7 return 0;
8 }
編譯執行
root@ubuntu:/home/peng/test# gcc 123.c -o run
root@ubuntu:/home/peng/test# strace ./run
【注意】運行程序前加上 strace,可以追蹤到函數庫調用過程
推薦關注「Linux 愛好者」,提升 Linux 技能
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ql9c1jnWv1G06m_nMSBrSA