2014年9月25日 星期四

Linux Device Drivers & Kernel

動手寫 Linux Driver

在 Linux 作業系統上寫一個控制I2C

建構編譯環境


首先我們必須要準備開發 Linux 驅動程式所需的環境,在 Debian 上可以用以下的指令達到這個目的:

$ sudo apt-get install build-essential linux-headers-$(uname -r)

其中 build-essential 會安裝 gcc, make 等軟體開發必要的工具,而 linux-headers 會安裝開發 Linux 驅動程式必要的 SDK。因為 linux-headers 會隨核心的版本而有所不同,所以我們要使用 $(uname -r) 取得目前核心的版本。

簡單的驅動程式


所有的 Linux 驅動程式至少要包含一個 MODULE_LICENSE 用以宣告驅動程式的授權,另外還需要一個 init 與一個 exit 函式,分別處理驅動程式的起始與終止。以下就是一個什麼都沒有的空殼:

/* example.c */
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int example_init(void) {
    printk("<1>EXAMPLE: init\n");
    return 0;
}

static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");
}

module_init(example_init);
module_exit(example_exit);

我們可以注意到裡面有一個 printk,他就相當於驅動程式設計當中的 printf。我們如果需要印任何除錯資訊,可以呼叫 printk,然後使用 sudo dmesg 觀看結果。編譯這個檔案之前,我們要先幫他寫 Makefile:

obj-m := example.o

ifeq ($(KERNELDIR),)
KERNELDIR=/lib/modules/$(shell uname -r)/build
endif

all:
    make -C $(KERNELDIR) M=$(PWD) modules

clean:
    make -C $(KERNELDIR) M=$(PWD) clean

在這個 Makefile 裡面,我們會使用 obj-m 這個變數指定我們要編譯的模組,然後再呼叫 make 讓他載入 SDK 的 Makefile。我們先前安裝的 SDK 就會放在 /lib/modules/$(shell uname -r)/build 裡面。

接下來我們就可以用 make 編譯我們的模組,並使用以下指令載入、卸除模組:

$ sudo insmod ./example.ko
$ sudo rmmod example

如果要看我們的模組有沒有輸出任何訊息,可以使用:

$ sudo dmesg | tail

註冊為 Character Device


在 Unix 的設計哲學當中,所有的東西都是檔案,硬體也不例外。我們寫驅動程式的時候要提供一個檔案操作的介面給 Userspace 的程式。為了達到這個目的,我們必須再引入一個標頭檔:

#include <linux/fs.h>

然後定義若干檔案操作與 file_operations 這個資料結構:

static int example_open(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: open\n");
    return 0;
}

static int example_close(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: close\n");
    return 0;
}

static ssize_t example_read(struct file *filp, char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: read  (size=%zu)\n", size);
    return 0;
}

static ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    return size;
}

static struct file_operations example_fops = {
    .open = example_open,
    .release = example_close,
    .read = example_read,
    .write = example_write,
};

然後在 example_init() 當中用 register_chrdev 把這個驅動程式註冊為一個 Character Device。

#define EXAMPLE_MAJOR 60
#define EXAMPLE_NAME "example"

static int example_init(void) {
    int result;
    printk("<1>EXAMPLE: init\n");

    /* Register character device */
    result = register_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME, &example_fops);
    if (result < 0) {
        printk("<1>EXAMPLE: Failed to register character device\n");
        return result;
    }

    return 0;
}

值得一提的是第一個參數 EXAMPLE_MAJOR 可以是 60, 61, 62。如果是正式要釋出的 Driver,就必須要從 Documentation/devices.txt 選取適當的 Major ID。當然,在 example_exit() 我們也必需加上對應的 unregister:

static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");

    /* Unregister character device */
    unregister_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME);
}

在重新編譯之後,我們可以用 insmod 載入驅動程式,然後使用 mknod 建立 Device File。然後我們就可以在 User Space 使用一般的檔案讀寫操作這個 Device。

$ sudo insmod ./example.ko

$ sudo mknod /dev/example c 60 0
# /dev/example 是我們要存放檔案的路徑,c 代表 Character Device,60 是這個驅動程式的 Major ID,0 是驅動程式的 Minor ID。

$ sudo chmod 666 /dev/example
# 為了方便測試,我們把這個 Device 改成所有人都可以讀寫。

$ echo -n 'abcd' > /dev/example

$ sudo dmesg | tail

讀取 User Space 的資料


在前一節當中我們提供了一個 API 讓 User Space 可以操作 Driver。但是其實我們是不能直接存取 buf 的內容。因為 Kernel Space 與 User Space 有不同的位址空間,所以不能直接存取他們。我們必須借助 copy_from_user 這個 API。

在使用這個 API 之前,我們必需引入 <asm/uaccess.h>:

#include <asm/uaccess.h>

然後我們就可以使用 copy_from_user 來存取 User Space 的位址空間,舉例來說:

ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    size_t pos;
    uint8_t byte;
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    for (pos = 0; pos < size; ++pos) {
        if (copy_from_user(&byte, buf + pos, 1) != 0) {
            break;
        }
        printk("<1>EXAMPLE: write  (buf[%zu] = %02x)\n", pos, (unsigned)byte);
    }
    return pos;
}

值得注意的是 copy_from_user() 會回傳剩下未完成的 byte 數。所以一般來說這個回傳值必須是 0 才是成功地讀入資料。要把資料從 Kernel Space 複製到 User Space 則是使用 copy_to_user() 函式,至於使用方法就不再贅述。

$ echo -n 'abcd' > /dev/example
$ sudo dmesg | tail

2012年7月5日 星期四

準備release android app

這兩年寫了不少Android app, 一直想整理放到market, 但因為時間關係沒空整理, 等這幾天決定要將code release出來, 會先放到github上, 希望有人有興趣能幫忙改, 接下來要做的是Android一些分析。 https://github.com/csl 可參考以上目前專案

2011年9月8日 星期四

timer using msleep

最近在改之前寫的slic driver,在送DTMF tone的時候會發生crash,如下:

To send DTMF CID sequence [A11111CR]
POL_FORWARD
getOffhook = 0
Freq1 : 13990
Freq2 : 4657
BUG: scheduling while atomic: ash/0x00000100/2020
Call Trace:
[<800074cc>] dump_stack+0x8/0x34
[<80219a04>] schedule+0x778/0x920
[<8021a7c4>] schedule_timeout+0x70/0xdc
[<80030a18>] msleep+0x24/0x34
[] playDtmfTone+0x148/0x6a4 [slic3217x]
[] playDtmfTone+0x598/0x6a4 [slic3217x]


看來root cause是因為在timer中用了msleep

造成它reschedule就crash

解決方式就是透過將timer的call時間拉長
讓Oscillators播放時間p_cid->dtmf_on_ms拉長一點

init_timer(&cid_dtmf_timer);
cid_dtmf_timer.function = cid_dtmf_do_timer;
cid_dtmf_timer.expires = jiffies + ms2jiffies(p_cid->dtmf_on_ms);
add_timer(&cid_dtmf_timer);


才不會造成dtmf signal lost

2010年9月15日 星期三

取得 wifi parameters for linux kernel

想抓取wifi的資訊可利用linux kernel的struct netdev
所提供的struct iw_handler_def wireless_handlers來拿取
這是透過wireless_handlers內有個standard的指標函數,
可提供wireless device driver實作,
因此我們可透過standard指標函數來拿取wireless相關資訊

struct net_device
{
...
const struct iw_handler_def * wireless_handlers;
...
}


struct iw_handler_def
{
...
/* Array of handlers for standard ioctls
* We will call dev->wireless_handlers->standard[ioctl - SIOCSIWCOMMIT]
*/
const iw_handler * standard;
...
}


typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra);


以zd1211rw driver為例:

#define WX(x) [(x)-SIOCIWFIRST]

static const iw_handler zd_standard_iw_handlers[] = {
WX(SIOCGIWNAME) = iw_get_name,
WX(SIOCGIWNICKN) = iw_get_nick,
WX(SIOCSIWFREQ) = iw_set_freq,
WX(SIOCGIWFREQ) = iw_get_freq,
...
};


可以看到這些Wireless Identification的定義:

/* Wireless Identification */
#define SIOCSIWCOMMIT 0x8B00
#define SIOCGIWNAME 0x8B01

#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */
#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */
#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */
#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */
...
wireless.h:335:#define SIOCIWFIRSTPRIV 0x8BE0
wireless.h:351:#define SIOCIWFIRST 0x8B00


倘若我們想抓取frequency(channel) of current link ,

可直接call function
net_dev -> wireless_handlers ->
standard[SIOCGIWFREQ - SIOCSIWCOMMIT](net_dev, NULL, (void *) & my_freq, NULL);


然後透過my_freq.m就能抓取現在的freq

struct iw_freq my_freq;

struct iw_freq
{
__s32 m; /* Mantissa */
__s16 e; /* Exponent */
__u8 i; /* List index (when in range struct) */
__u8 flags; /* Flags (fixed/auto) */
};


進而推出wifi的rssi

2010年8月17日 星期二

COSCUP 2010

今天的[COSCUP]在中研院舉辦,
去了第四年XD,還是一樣熱血,
而且今年參加人數大爆炸XD

今年的議程主要以Open Web and Mobile Technologies為主

以下為一些議程的心得:

Frontend Development Enviornment, josephj

這個議程滿有趣的,
透過將HTML/CSS/JavaScript 的模組化與自動化
可很嚴謹的制定模組policy
這個design pattern或許也可用於其他地方

Debugging: Linux Kernel by Ftrace, AceLan

Ftrace的工具, 可透過/proc的檔案系統來進行trace kernel function
但很可惜, 目前只僅次於function, 誰call誰, 誰被call

Be 「Android」, Tick Chen + Matt Hsu

很cool的機器人, 透過馬達 <-I2C-> ARM <-buletooth-> Android
來控制機器人, 可惜沒看到開全速的樣子XD

打造特製的 Android Toolchain, jserv

透過"搞系統程式絕對不是「不入流」"開場, 超high
jserv前輩很強~~
一路trace GNU Toolchain發展開發並發現android toolchain的git中無說明
無法自制android toolchain,因此開始打造從無到有的android toolchain,
超cool

2010年1月28日 星期四

Add new system call

kernel版本:2.6.33-rc2

新增自行定義的system call步驟如下:

1. edit arch/x86/kernel/syscall_table_32.S, add

.long sys_mysystemcall /* 338 */

2. edit include/asm-i386/unistd.h, add

#define __NR_mysystemcall 338

3. create new file, arch/x86/kernel/systemcall.c
(defined filename yourself)

#include <linux/linkage.h>
#include <linux/kernel.h>

asmlinkage int sys_mysystemcall(void)
{
printk("mysystemcall\n");
return 0;
}


4. edit arch/x86/kernel/Makefile, add

obj-y += systemcall.o


5. compiler kernel

root@shulong-desktop:/home/shulong/linux-2.6.33-rc2# make clean && make


6. writing code for user mode, create new file test.c

#include <linux/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

#define __NR_mysystemcall 338
#define mysystemcall() syscall(__NR_mysystemcall);

int main ()
{
mysystemcall();
return 0;
}


7. compiler test.c

root@shulong-desktop:~# gcc -Wall test.c -o systemcall

8. execute systemcall

root@shulong-desktop:~# ./systemcall
[ 39.289669] mysystemcall


就可順利加入自行定義的system call。

2009年10月30日 星期五

GNU C Expand

1. __attribute__ ((variable attributes))

variable attributes:

• aligned
• deprecated
• mode
• nocommon
• packed
• section
• transparent_union
• unused
• vector_size
• weak

section ("section-name")'
extern void foobar (void) __attribute__ ((section ("bar")));
puts the function 『foobar' in the 『bar' section.


example:


#define subsys_initcall(fn) __define_initcall("4",fn,4)
//用來將指定的函數pointer fn放到initcall.init section
//是把fn放到.initcall.init的section.initcall4.init


initcall4.init defined in arch/i386/kernel/vmlinux.lds.S

.initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET)
{
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}


2. likely and unlikely

in include/linux/compiler.h

#define __builtin_expect(x, expected_value) (x)

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)


example:

if(likely(value))
{
}
else
{
}


//看起來likely和unlikely是一樣的, 但事實上是有差異的
//likely表示value的值為true的可能性更大一些,那麼執行if的機會大;
//unlikely表示value的值為false的可能性大一些,執行else機會大一些。
//有了這種修飾,gcc編成組語後,
//會將likely使得if後面的執行語句緊跟著前面的;
//unlikely使得else後面的語句緊跟著前面的程序
//這樣就會可以讓cache預先讀取,
//目的是增加程序的執行速度

2009年10月6日 星期二

Meld : Diff and merge tool

比較和合併code的工具,在windows上有[Araxis Merge],但它並非open source software,因此有[WinMerge]可以替代,而在linux上比較常見的是[KDiff3]和[Meld]。

而我最常用的是Meld,它的操作很容易就可上手。

2009年10月2日 星期五

Coding tips, Vim顯示當前行

輸入":set cursorline".
或輸入":set cul".

取消當前行:
輸入":set no cursorline".
或輸入":set no cul"

2009年9月28日 星期一

gcc: warnings being treated as errors

在Ubuntu 9.04,編譯linux kernel for mips時出現了這個問題

gcc: warnings being treated as errors
scripts/kconfig/conf.c:104: error: ignoring return value of 'fgets', declared with attribute warn_unused_result
...

原來是在./src/Makefile的HOSTCFLAGS variables加了-Werror...
把它去掉之後,就能編過了...

2008年11月7日 星期五

Trace code tool

以前在Trace OpenSouce code時,常直接用文字編輯器,感覺很不方便,還要自己找檔案。最近發現一套很好用trace code的工具[GNU Global],它能很神奇的將程式碼變成網頁,而且產生方式超簡單的。

產生網頁的方法:

1. 進入原始碼的目錄下

$ cd ~/speech

2. 執行htags

$ htags -g -F
-g 先執行 gtags
-F 代表要將網頁切成多個Frame

3. 原始碼目錄下就會產生一個叫HTML的目錄

就可以直接用Browser開啟index.html,如下圖所示。



接下來看code就很快了....

2008年8月27日 星期三

Qemu for ARM System emulator

[Qemu]是個相當不錯的模擬器,它不只可以模擬x86,還可模擬arm。最近找了很多相關的資料,發現目前已經有很多人寫了不錯的教學,而且qemu支援的arm平台[ARM System emulator]也相當多。但重點是qemu並非所有arm的平台都支援,所以或許可以找找patch或自己試著修改看看,才能符合自己的平台。

[OPENCSL]這個網站相當不錯,初學者可以很快做出一個"ARM Integrator"(ARMB26EJ-S的架構)的模擬器。另外是OpenMoko[Neo1973],目前已有修改過後的qemu,跑出來就跟真的環境一樣。然後[Linux on Palm 5]也有修改後的模擬器,都可以實際跑起來。

有興趣的朋友,可以玩玩看,我覺得還滿有趣的。

2008年2月22日 星期五

OSDC.TW 2008

一年一度的OSDC.TW又要來了。

去年OSDC.TW 2007我有去參加,覺得主辦人[hcchien]相當用心,而且可以看到很多很厲害的OpenSource高手的"真面目",而且可以見識到現在很多新技術和想法,個人覺得OSDC.TW真的是不錯的Conference...

今年的[議程],也很令人期待,想報名的人請到[報名],要快哦!!不然很快就滿了><"

Reference: [OSDC.TW]

2008年2月20日 星期三

空閒時間看Linux kernel原始碼

看Linux kernel的原始碼是一種很有趣的事情,可以看到很多用C寫奇怪的作法,真很有趣,如jserv所提到的[C 語言的 trigraph/digraph]一文。

只是因為只有空閒時間才能看,所以都只能看片段,沒辦法一層一層的往下找"真相",只有大約知道API怎麼CALL、用什麼樣的結構和演算法等等。

不過令人驚訝的是Linux kernel有超完整的架構,讓我這個初學者超佩服的,這時才知道自己的渺小。

未來,我會找時間來整理一些自己看kernel的想法...

2007年11月27日 星期二

Poring Linux Kernel 2.6 for DIMMPC 520i (Part 1)

最近終於找到時間來Porting板子囉!!

測試完IDE介面,發現可以接硬碟和光碟機...


於是很有趣的將之前收到的ubuntu 7.10放進去跑跑看,結果:

天啊,有畫面也可以跑耶!!但...

kernel還是跑不起來,也只好乖乖porting了...><"

2007年11月18日 星期日

小筆電裝Ubuntu 7.10

今天終於跟裝了兩年多的gentoo說byebye了,
一直都想要換,畢竟我的小筆電速度不夠快,
總不能每裝一套package就一直compiler...吧!!

也剛好昨天回家,
收到數天前到[ShipIt]所申請的光碟片,
超cool的,還有貼紙...

安裝上真的比gentoo方便太多,
在加上用[lazybuntu]來安裝想要的軟體,真的不到一小時就全部裝完囉...

2007年11月14日 星期三

Easy to build toolchain, cross-ng

因DIMM-PC只有32 MByte Flash Disk,
所以用glibc會太大,
因此改用jserv前輩所建議crosstool next generation[cross-ng],
這個crosstool可以編glibc和uclibc,
且介面跟編kernel很類似喔!!
真的方便超多的...^^

1. install

$ wget -c http://ymorin.is-a-geek.org/download/crosstool-ng/
crosstool-ng-0.3.2.tar.bz2
$ tar -jxvf crosstool-ng-0.3.2.tar.bz2
$ cd crosstool-ng-0.3.2
$ ./configure --prefix=/opt/cross-ng
$ make
# make install

2. using cross-ng

$ export PATH="${PATH}:/opt/cross-ng/bin"
$ mkdir toolchain-dev && cd ./toolchain-dev
$ ct-ng menuconfig

需要提供uclibc的設定檔路徑

$ ct-ng build
...
[INFO ] ===============================
[INFO ] Installing C library headers
[INFO ] Installing C library headers: done in 27.63s
[INFO ] ===============================
[INFO ] Core C compiler, pass 2
[INFO ] =============================
[INFO ] Installing static core C compiler
[INFO ] Installing static core C compiler: done in 316.46s
[INFO ] Core C compiler, pass 2: done in 316.50s
[INFO ] ===============================
[INFO ] Installing C library
[INFO ] Installing C library: done in 124.55s
[INFO ] ===============================
[INFO ] Installing final compiler
[INFO ] Installing final compiler: done in 737.85s
[INFO ] ===============================
[INFO ] Installing cross-gdb
[INFO ] Installing cross-gdb: done in 316.19s
[INFO ] ===============================
[INFO ] Installing gdbserver
[INFO ] Installing gdbserver: done in 11.50s
[INFO ] Build completed at 20071114.012858
[INFO ] (elapsed: 28:28.51)

成功囉!!
toolchain的路徑在$HOME/i386-DIMMPC-linux-uclibc

2007年11月11日 星期日

ToolChain for DIMM-PC

由於x86的toolchain建立的文件相當少,
於是使用[crosstool]來建立toolchain可說是相當方便...
[更新]
$ svn co http://crosstool.googlecode.com/svn/trunk/src crosstool
$ cd crosstool
$ cp demo-i686.sh demo-i386.sh
$ vi demo-i386.sh
將eval `cat i686.dat gcc-4.1.0-glibc-2.3.6-tls.dat` sh all.sh --notest
修改成#eval `cat i686.dat gcc-4.1.0-glibc-2.3.6-tls.dat` sh all.sh --notest
將eval `cat i686.dat gcc-4.1.0-glibc-2.3.6.dat` sh all.sh --notest
修改成#eval `cat i386.dat gcc-4.1.0-glibc-2.3.6.dat` sh all.sh --notest

$ vi i386.dat
TARGET=i486-dimmpc-linux-gnu
TARGET_CFLAGS="-O"
$ sudo mkdir /opt/crosstool
$ sudo chown $USER /opt/crosstool
$ sh demo-i386.sh
...
Cross-toolchain build complete. Result in /opt/crosstool/gcc-4.1.0-glibc-2.3.6/i486-dimmpc-linux-gnu.
+ exit 0
+ cd /home/shulong/crosstool
+ sh testhello.sh
+ cd /opt/crosstool/gcc-4.1.0-glibc-2.3.6/i486-dimmpc-linux-gnu
+ test '!' -d tmp
+ mkdir tmp
+ cd tmp
+ test x '!=' x
+ cat
...
+ echo testhello: C compiler can in fact build a trivial program.
testhello: C compiler can in fact build a trivial program.
+ test '' = 1
+ test '' = 1
+ test '' = 1
+ test 1 = ''
+ echo Done.
Done.

看到這個msg就代表成功啦!!

toolchain的路徑在:
/opt/crosstool/gcc-4.1.0-glibc-2.3.6/i486-dimmpc-linux-gnu/

感謝jserv大提供的方向...

2007年10月5日 星期五

COSCUP 2007

傳統商業軟體的開發者(coders)和使用者(users)很少交流,是大家都接受的事實。你的軟體用起來有問題?請打技術支援電話,由客服人員為你服務,他或許已經儘其所能,但未必能解決你的問題。如果問題是軟體的臭蟲,你只好痴痴地等待不知幾年後才開賣的下一版。而你當初之所以選用A軟體而不是B軟體,常常是聽信銷售員(sales)的一面之詞或同儕的推薦。在這個行為模式下,users 面對的是銷售員和客服,coders 面對的還是銷售員和客服,users 遇到的問題要間接地才能反應到 coders 手上,中間還可能會被過濾掉。

Open source 這個在 1998 年出現的名詞,大家早已耳熟能詳。這種在網路上已經進行二三十年的軟體開發模式之所以能成功,有許多原因,其中一個極為關鍵的因素,就是 coder-user 的直接接觸。無屏障的交流形成了問題的快速回報和修補機制,而當這個機制被網路效應放大到極限時,Linus 定律就出現了:《臭蟲難逃眾人法眼》(With enough eyeballs, all bugs are shallow),軟體品質因此顯著提昇。在 open source 的模式中,coders 和 users 中間的人不再是銷售員或客服,而是讓軟體更容易被 users 接受的推廣者(promoters),他們打包套件讓軟體更好裝、寫說明文件讓軟體更易學、辦推廣活動讓軟體被更多人接觸到、在網路上回答問題解決使用者的疑惑,而且不會把 coders 藏在背後產生資訊的不對稱。

Coders、users 和 promoters 是讓 open source 軟體發光發熱的支柱,所以我們的 open source 研討會是專為這三種人舉辦的。你可以是 A 軟體的 coder、B 軟體的 promoter、C 軟體的 user。不論你是已經踏入 open source 領域,還是一直站在門口不知如何入門,歡迎你來參加 COSCUP - Conference for Open Source Coders, Users and Promoters!

今年的 COSCUP 最大的改變,是由各台灣本地社群共同舉辦,也希望能透過這樣的合作,增加社群同好的參與度,正如 COSCUP 的精神,在這個研討會當中,你同時是與會者,推廣者,也參與了大會的運作。

* 時間:2007 年 11 月 3 日(六)- 11 月 4 日(日)
* 地點:台北市和平東路 2 段 106 號 科技大樓 二樓
* 誰應該參加:寫 open source 軟體的開發者、熱衷 open source 軟體的推廣者、熟悉 open source 軟體的使用者、想瞭解 open source 軟體的新手。

網址:http://coscup.org/2007/

2007年9月21日 星期五

Linus says: “C++ is a horrible language”

C++ is a horrible language.
It’s made more horrible by the fact that a lot of substandard programmers use it, to the point where it’s 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.

– Linus