為什麼linux的內核用c不用c++呢?

java虛擬機以及windows都是c++寫的,為什麼linux的內核用c不用c++呢?php解釋器和python解釋器為什麼用c不用c++呢?是不是說明php、python比起java虛擬機更簡單,linux比起windows更簡單呢?


從技術角度猜測一下:

從對編譯的機器代碼的把握度,c比c++好多了。系統程序員幾乎看著c就沒知道編譯器里出來的彙編代碼的樣子。而c++編譯器出來的就複雜多了,一個對象this指針,再加上各種虛表,當然還有各種拷貝構造等等。要完全把控確實不容易。(雖然我沒有寫過操作系統內核,但我總覺得操作系統內核就應該把控到彙編,用c只是為了提高開發效率。)

從c++的高級語言特性來說,模板,多態等特性,到底在系統開發中用還是不用呢?這是一個很現實的問題。除非能夠很好的把控這些高級特性,否則輕導致編譯出來的代碼膨脹,重則導致奇怪的問題。而且內核調試和應用程序調試完全不是一回事啊…所以bochs雖然都轉成c++了,但開發說明裡明確要求不允許使用模板。

最後還有一點不能忽略,linux內核是跨平台的,所有的平台都有對應的c語言編譯器,而且應該都優化的不錯。而不是所有的平台都有c++編譯器,即使有也不一定優化的效率很高。

當然還有其他因素,比如linus的個人好惡…


1. Windows內核也是C的。這幾年才開始有一點點C++進去,但基本上只用了C with class。

2. Linus個人非常討厭C++。

3. Linux kernel 1991年開搞的,C++到1998才定型。

4. 語言和寫的東西是否簡單無關。


操作系統內核是少數幾個需要明確知道電腦到底幹了什麼的情形,用 C 是為數不多的選擇。


這個問題也是當時困擾我的,為什麼Linux kernel沒有引入C++. 各甚用C++重寫,收藏的一個回答。

The reasons specifically for Linux are explained in great detail as
part of the Linux Kernel Mailing List (LKML) Frequently Asked
Questions (FAQ) at:
http://www.tux.org/lkml/
Scroll down to section 15 (titled Programming Religion) and read the
explanation. A summary is:
- Linus started with an 386 computer, Minix, and gcc (no g++ available)
- C is used instead of Assembly for a LOT of reasons (maintenance,
readability, efficiency, ...)
- it is not clear how an object oriented language helps in OS design
- MANY years ago, they tried using g++ to compile the Linux kernel
and found it ran slower than when compiled with gcc (many thought it
should have been the same); they are not willing to do this again
- Linus makes the final decision and hes decided to stick with C
There is also another good explanation of some of the issues at
http://kerneltrap.org/node/2067
which describes one persons attempt to build adapt some C++ code to
incorporate into a kernel module.

For a historical perspective, Thompson and Ritchie developed Unix
using some of the concepts from Multics and CTSS
http://www.multicians.org/unix.html
including implementation of most operations in a higher order language
(Unix - C, Multics - PL/I), common naming of commands, etc. I also
refer you to
http://cm.bell-labs.com/cm/cs/who/dmr/hist.html
which describes the EARLY development of Unix including the conversion
from Assembly on the PDP-7 to C on the PDP-11 (near the end of the
paper).

Building from that basis, the Unix from ATT that was widely
distributed and worked on by thousands of people was a C based
operating system. In the 1970s and 1980s, an alternative to ATTs
distribution was developed at UC Berkeley, see
http://en.wikipedia.org/wiki/Berkeley_Software_Distribution
for a relatively short explanation of the development of BSD. The BSD
version eventually did not use ANY of the ATT code base, but was also
implemented in C. At the end of the article is a list of several BSD
descendants including SunOS (now Solaris).

That is not to say that some applications are better implemented in an
OO language such as C++. There are several Graphical User Interface
(GUI) toolkits such as KDE which are implemented in C++. However,
there are applications that are better suited for a more procedural
language, and at this point operating systems are in that category.

If this answer is unclear or somehow incomplete, please make a
clarification request.
Good luck with your work.

--Maniac

  1. Linus寫kernel的時候還沒有 g++
  2. 有人嘗試了,但是性能下降了
  3. Linus說了就用C,不要吵了


首先,我想用兩段"很長的代碼"來做一個開始。

下面這段代碼叫 min.c:

#include &

/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({
typeof(x) _x = (x);
typeof(y) _y = (y);
(void) (_x == _y);
_x &< _y ? _x : _y; }) int main(void) { int a = 3, b = 5; double d1 = 3.14, d2 = 2.718; printf("min of a b: %d ", min(a, b)); printf("min of d1 d2: %lf ", min(d1, d2)); return 0; }

代碼很簡單,不用做程序員也懂。然後我們

#gcc -S min.c

你會看到有一個文本文件叫做 min.s 生成,這個就是min.c生成的彙編代碼:

.file "min.c"
.section .rodata
.LC2:
.string "min of a b: %d
"
.LC3:
.string "min of d1 d2: %lf
"

.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $64, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $3, -48(%rbp)
movl $5, -44(%rbp)
movsd .LC0(%rip), %xmm0
movsd %xmm0, -24(%rbp)
movsd .LC1(%rip), %xmm0
movsd %xmm0, -16(%rbp)
movl -48(%rbp), %eax
movl %eax, -40(%rbp)
movl -44(%rbp), %eax
movl %eax, -32(%rbp)
movl -32(%rbp), %edx
movl -40(%rbp), %eax
cmpl %eax, %edx
cmovle %edx, %eax
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
movsd -24(%rbp), %xmm0
movsd %xmm0, -40(%rbp)
movsd -16(%rbp), %xmm0
movsd %xmm0, -32(%rbp)
movsd -40(%rbp), %xmm1
movsd -32(%rbp), %xmm0
ucomisd %xmm1, %xmm0
jbe .L8
movq -40(%rbp), %rax
jmp .L4
.L8:
movq -32(%rbp), %rax
.L4:
movq %rax, -56(%rbp)
movsd -56(%rbp), %xmm0
movl $.LC3, %edi
movl $1, %eax
call printf
movl $0, %eax
movq -8(%rbp), %rcx
xorq %fs:40, %rcx
je .L6
call __stack_chk_fail
.L6:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.section .rodata
.align 8
.LC0:
.long 1374389535
.long 1074339512
.align 8
.LC1:
.long 3367254360
.long 1074118262
.ident "GCC: (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010"
.section .note.GNU-stack,"",@progbits

如你所見,彙編代碼有點長。讀不懂沒關係,重點不在這裡,也不需要去讀懂它。只需要看到上面有個符號叫做 main, 從那裡一直到後面 .size main, .-main 那個地方,就是 main 函數的開始和結束。

好,接下來呢我們再寫一個 mincpp.cpp:

#include &

template &
T min(T x, T y)
{
return x &< y ? x : y; } int main(int argc, char *argv[]) { int a = 3, b = 5; double d1 = 3.14, d2 = 2.718; printf("min of a b: %d ", min(a, b)); printf("min of d1 d2: %lf ", min(d1, d2)); return 0; }

代碼也很簡單。實在不明白的也許可以去學點兒C++ ^_^. 至於我為什麼沒用所謂的 C++ 風格(std:cout)的原因很簡單,犯不著,沒必要;也許也可以讓彙編代碼更簡單些。當然了:

#g++ -S mincpp.cpp

你會看到有一個文本文件叫做 mincpp.s 生成,這個自然就是 mincpp.cpp 生成的彙編代碼了:

.file "mincpp.cpp"
.section .rodata
.LC2:
.string "min of a b: %d
"
.LC3:
.string "min of d1 d2: %lf
"

.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $64, %rsp
movl %edi, -36(%rbp)
movq %rsi, -48(%rbp)
movl $3, -24(%rbp)
movl $5, -20(%rbp)
movsd .LC0(%rip), %xmm0
movsd %xmm0, -16(%rbp)
movsd .LC1(%rip), %xmm0
movsd %xmm0, -8(%rbp)
movl -20(%rbp), %edx
movl -24(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call _Z3minIiET_S0_S0_
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
movsd -8(%rbp), %xmm0
movq -16(%rbp), %rax
movapd %xmm0, %xmm1
movq %rax, -56(%rbp)
movsd -56(%rbp), %xmm0
call _Z3minIdET_S0_S0_
movl $.LC3, %edi
movl $1, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main

.section .text._Z3minIiET_S0_S0_,"axG",@progbits,_Z3minIiET_S0_S0_,comdat
.weak _Z3minIiET_S0_S0_
.type _Z3minIiET_S0_S0_, @function
_Z3minIiET_S0_S0_:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
cmpl -8(%rbp), %eax
jge .L4
movl -4(%rbp), %eax
jmp .L6
.L4:
movl -8(%rbp), %eax
.L6:
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size _Z3minIiET_S0_S0_, .-_Z3minIiET_S0_S0_

.section .text._Z3minIdET_S0_S0_,"axG",@progbits,_Z3minIdET_S0_S0_,comdat
.weak _Z3minIdET_S0_S0_
.type _Z3minIdET_S0_S0_, @function

_Z3minIdET_S0_S0_:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movsd %xmm0, -8(%rbp)
movsd %xmm1, -16(%rbp)
movsd -16(%rbp), %xmm0
ucomisd -8(%rbp), %xmm0
jbe .L13
movsd -8(%rbp), %xmm0
jmp .L11
.L13:
movsd -16(%rbp), %xmm0
.L11:
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size _Z3minIdET_S0_S0_, .-_Z3minIdET_S0_S0_
.section .rodata
.align 8
.LC0:
.long 1374389535
.long 1074339512
.align 8
.LC1:
.long 3367254360
.long 1074118262
.ident "GCC: (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010"
.section .note.GNU-stack,"",@progbits

如你所見,彙編代碼好像更長了。當然也不需要去讀哪個寄存器的值放到哪個寄存器之類的,不過還是稍微需要做點兒說明。符號 main 到 size main, .-main 是 main 函數的開始和結束。main 函數結束之後,有個奇怪的符號 _Z3minIiET_S0_S0_, 這個符號的實際意思其實是 int min&(int, int), 要怎麼知道呢? c++filt _Z3minIiET_S0_S0_ 就知道了;同理,_Z3minIdET_S0_S0_ 代表double min&(double, double). 至於函數的結束, size _Z3minIiET_S0_S0_, .-_Z3minIiET_S0_S0_ 自然是 int min&(int, int) 函數的結束,而 size _Z3minIdET_S0_S0_, .-_Z3minIdET_S0_S0_ 自然是 double min&(double, double) 函數的結束了。

好的,有什麼不同呢?

在 min.c 中,使用了宏來實現比較兩個內置數據類型所實例化的對象的大小的功能;在 mincpp.cpp 中,使用了 C++ 的模板機制。

模板機制是 C++ 最引以為豪的機制之一,它圖靈完全(就是說它允許定義常量和變數,支持判斷以及遞歸的結構),執行嚴格的類型檢查,同時於執行效率來說絲毫無損,是實現泛型編程的核心機制,stl 在這上面玩出了花,於是我們有了 vector, list 等容器,有了 stack queue 等容器適配器,有了 find, sort 等泛型演算法。玩兒得更花的當然也有了;限於本人非專業 C++ 程序員的原因,不做過多引用。

不過對於程序員來說,我們在知道一個機制所擁有的威力的同時,我們往往也需要知道使用這個機制時所需要付出的代價。

在 mincpp.cpp 中,我們調用了兩次 min 模板。模板是抽象的邏輯/演算法/步驟表述實體,在使用前,我們需要將此邏輯實體實例化。C++ 編譯器遍歷源代碼,發現對 min 模板的兩種不同類型的調用,於是將此模板實例化兩次,分別生成了函數 int min&(int, int) 和 函數 double min&(double, double).

哦,是的!模板可能會多次實例化!一般 C++ 程序員會知道他表達了一個 "抽象的邏輯實體",然而很少的 C++ 程序員真正地知道 -- 實例化模板意味著什麼(需要付出什麼樣的代價)?

不過上面的例子已經很清楚地表述了,我們會付出什麼樣的代價。

1. 首先,模板是同名的,而我們實例化後的函數不能同名。所以 C++ 需要 name mangling. 啊,多麼美妙的 name mangling, 它能解決同一語法作用域同名函數重載的問題,然而代價是 -- 我艹這些亂糟糟的垃圾符號是怎麼一回事? -- 我得承認儘管有 c++filt, 我還是一點都不想見到這些垃圾符號。

2. 代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹代碼膨脹

代碼簡直無法控制地膨脹

看不到代碼膨脹的,你看不到上面的彙編代碼?

好的,儘管我也許只是舉了一個不那麼恰當的例子。儘管我相信絕對有所謂的 C++ 程序員會馬上舉出一千條理由,比如 -- 「嗯,我們嚴格地控制模板的使用(呵呵,其實你們哪個不是炫耀自己模板用得怎樣怎樣,全然不顧我在旁邊吐得稀里嘩啦)?」

這是華麗的分割線

/***********************************************/

kernel 有嚴格的規則。kernel 代碼需要的精確程度,毫不誇張地說,是位元組級。

kernel 需要控制什麼?

kernel 需要控制,它的第一條執行的機器指令是什麼,它的接下來執行的機器指令又是什麼。

kernel 需要控制,這一段代碼只能放在什麼位置,另一段代碼放在什麼位置比較好。

kernel 需要控制,某一段代碼不多不少,不會為了實際用不著的東西帶來冗餘。

kernel 需要控制,某個位置的某一個符號放得剛剛好。

kernel 需要控制,在性能相關的點,就那麼自然地嵌入一段彙編。

一語以貫之:

C 和 kernel 是最契合的;C 剛好是 kernel 需要的;C 簡直為 kernel 而生,然而不排除如此簡潔優美的語言程序員會不自覺地將其使用在別的地方。

是的,我猜這大概也是 Linus 選 C 的理由。半吊子 C++ 程序員看不到的,Linus 能看到;半吊子 C++ 程序員不要妄圖說服 Linus, 別說 Linus 了,我也想對你翻白眼;別說 Linus 老頑固,史前恐龍什麼的,人家對工具鏈的認識,對 C++ 真正缺陷的了解,半吊子 C++ 程序員和人家根本就不在一個層級嘛(當然,是 Linus 站得比較高點)。

哦,再補一刀吧,據我所知,這個世界上的 C++ 程序員,百分之八十以上都是半吊子程序員。

/***********************************************/


說白了就是:

  1. Linus討厭C++和他的設計目標。抽象對於寫內核只能帶來麻煩卻帶不來什麼好處。
  2. Linus討厭C++程序員。C++的自由性導致了C++程序員水平差異過大,而且不同的人代碼風格差異也很大,面向對象、面向過程烏拉烏拉一大堆混到一塊,會讓整個項目一團糟。
  3. Linus不想讓C++程序員參與到這個項目中來,所以用C語言來寫是最好的方法。
  4. Linus想讓更了解底層的程序員來參與項目,所以用C語言來寫是最好的方法。

終極原因:寫Linux的時候C++沒定型,不敢用。


來自linus的官方回答(雖然原問題是問為什麼git是用c而不是c++寫的,但根據其中列的原因,也適用於題主這個問題):

On Wed, 5 Sep 2007, Dmitry Kakurin wrote:
&>
&> When I first looked at Git source code two things struck me as odd:
&> 1. Pure C as opposed to C++. No idea why. Please dont talk about portability,
&> its BS.

*YOU* are full of bullshit.

C++ is a horrible language. Its made more horrible by the fact that a lot
of substandard programmers use it, to the point where its much much
easier to generate total and utter crap with it. Quite frankly, even if
the choice of C were to do *nothing* but keep the C++ programmers out,
that in itself would be a huge reason to use C.

In other words: the choice of C is the only sane choice. I know Miles
Bader jokingly said "to piss you off", but its actually true. Ive come
to the conclusion that any programmer that would prefer the project to be
in C++ over C is likely a programmer that I really *would* prefer to piss
off, so that he doesnt come and screw up any project Im involved with.

C++ leads to really really bad design choices. You invariably start using
the "nice" library features of the language like STL and Boost and other
total and utter crap, that may "help" you program, but causes:

- infinite amounts of pain when they dont work (and anybody who tells me
that STL and especially Boost are stable and portable is just so full
of BS that its not even funny)

- inefficient abstracted programming models where two years down the road
you notice that some abstraction wasnt very efficient, but now all
your code depends on all the nice object models around it, and you
cannot fix it without rewriting your app.

In other words, the only way to do good, efficient, and system-level and
portable C++ ends up to limit yourself to all the things that are
basically available in C. And limiting your project to C means that people
dont screw that up, and also means that you get a lot of programmers that
do actually understand low-level issues and dont screw things up with any
idiotic "object model" crap.

So Im sorry, but for something like git, where efficiency was a primary
objective, the "advantages" of C++ is just a huge mistake. The fact that
we also piss off people who cannot see that is just a big additional
advantage.

If you want a VCS that is written in C++, go play with Monotone. Really.
They use a "real database". They use "nice object-oriented libraries".
They use "nice C++ abstractions". And quite frankly, as a result of all
these design decisions that sound so appealing to some CS people, the end
result is a horrible and unmaintainable mess.

But Im sure youd like it more than git.

Linus

總結一下,兩個主要原因:

  1. C++的sb程序員太多,用C可以有效避開使用C++的人
  2. Linus認為C++所謂的優勢譬如STL, Boost都不夠穩定,移植性差,有時效率也不高,所以想保證高效和系統級就得拋棄C++這些「高級」功能。

Source: http://article.gmane.org/gmane.comp.version-control.git/57918


很明顯,C++不夠透明。瞞著程序員自己悄悄地幹了太多事,而很多事人們本意並不是如此


系統層的程序更貼近機器,不需要太多的抽象。很多底層的東西C能輕鬆搞定的話就沒必要寫個class


Linus不喜歡C++


更底層,更可靠,更穩定。


內核用C代碼 只看這一行 你都可以精確知道對應的彙編是什麼 不用跳轉到別的地方 有別的語言能做到么 除了彙編


問Linus吧,看看他會不會噴你一臉(?-?)?


由OS的偏硬體、可移植等性質決定的。


基於可靠性考慮,C是比C++更好的選擇,像VxWorks,μC-OS這些可靠性要求高的操作系統也是用C寫的。


C沒有C++那麼複雜hhh~


操作系統基本上都是c寫的,windows也是,塞班是cpp寫的,結果是它死的很慘


哪個簡單用哪個寫,越複雜的操作系統越要用c寫,c++太不穩定了


1. Linux 誕生的那個時代,C++ 相比 C 勢力還不是那麼強大,何況當時作為標杆的 UNIX 都是 C 寫的。

2. 因為 Linus 不喜歡 C++。


推薦閱讀:

培神都開始學PHP了,你還在等什麼。
我應該選擇前端,還是繼續搞PHP?
從0開始學PHPExcel(1)之初探
為什麼 PHP 是最好的語言?現在是,將來也會是

TAG:Python | PHP | C編程語言 | C | Linux內核 |