1991 年 8 月 26 日,芬兰大学生 Linus Benedict Torvalds 向 comp.os.minix 新闻组的成员透露了出于 “业余爱好” 而正在研究操作系统的消息。因此这个时间也被许多爱好者视为 Linux Kernel 的真正生日。
当时 Linus 在邮件中表示自己捣鼓的操作系统只是一个业余性质项目,不会像 GNU 那样庞大和专业。
正在研究一款(自由的)操作系统(就是个兴趣爱好,我不会搞得像 GNU 那么大那么专业),打算让它工作在 386 (486) AT 平台上。它从四月就开始酝酿了,马上就快好了。我希望那些喜欢或不喜欢 minix 的人能够反馈意见,因为我的系统和它有点类似(同样的文件系统的物理布局 —— 由于实际原因,还有些其他的东西)。
我现在已经移植了 bash (1.08) 和 gcc (1.40), 而且看起来奏效了。这意味着我会在几个月内得到一些实用的东西。我想了解大多数人想要的特性是什么,欢迎各位积极提出建议,不过我不保证能实现 :-)
但 Linus 没有在 8 月 26 日这天发布 Linux,而是于 9 月 17 日在芬兰大学和研究网络 (FUNET) 的 FTP 服务器(ftp.funet.fi)上向一部分朋友私下公开了 Linux 0.01(Linus 为它取名 "FREAX"),当时只有一到两个人下载。
Linux 内核首个开源版本 (v0.01) 的体积非常小,仅包含 10,239 行代码。如果除去注释和空行,只剩下 8,670 行。正因它足够小,所以方便理解,是了解类 UNIX 操作系统内核内部结构的良好起点。
/* * console.c * * This module implements the console io functions * 'void con_init(void)' * 'void con_write(struct tty_queue * queue)' * Hopefully this will be a rather complete VT102 implementation. * */ /* * NOTE!!! We sometimes disable and enable interrupts for a short while * (to put a word in video IO), but this will work even for keyboard * interrupts. We know interrupts aren't enabled when getting a keyboard * interrupt, as we use trap-gates. Hopefully all is well. */ #include <linux/sched.h> #include <linux/tty.h> #include <asm/io.h> #include <asm/system.h> #define SCREEN_START 0xb8000 #define SCREEN_END 0xc0000 #define LINES 25 #define COLUMNS 80 #define NPAR 16 extern void keyboard_interrupt(void); static unsigned long origin=SCREEN_START; static unsigned long scr_end=SCREEN_START+LINES*COLUMNS*2; static unsigned long pos; static unsigned long x,y; static unsigned long top=0,bottom=LINES; static unsigned long lines=LINES,columns=COLUMNS; static unsigned long state=0; static unsigned long npar,par[NPAR]; static unsigned long ques=0; static unsigned char attr=0x07; /* * this is what the terminal answers to a ESC-Z or csi0c * query (= vt100 response). */ #define RESPONSE "\033[?1;2c" static inline void gotoxy(unsigned int new_x,unsigned int new_y) { if (new_x>=columns || new_y>=lines) return; x=new_x; y=new_y; pos=origin+((y*columns+x)<<1); } static inline void set_origin(void) { cli(); outb_p(12,0x3d4); outb_p(0xff&((origin-SCREEN_START)>>9),0x3d5); outb_p(13,0x3d4); outb_p(0xff&((origin-SCREEN_START)>>1),0x3d5); sti(); } static void scrup(void) { if (!top && bottom==lines) { origin += columns<<1; pos += columns<<1; scr_end += columns<<1; if (scr_end>SCREEN_END) { __asm__("cld\n\t" "rep\n\t" "movsl\n\t" "movl _columns,%1\n\t" "rep\n\t" "stosw" ::"a" (0x0720), "c" ((lines-1)*columns>>1), "D" (SCREEN_START), "S" (origin) :"cx","di","si"); scr_end -= origin-SCREEN_START; pos -= origin-SCREEN_START; origin = SCREEN_START; } else { __asm__("cld\n\t" "rep\n\t" "stosl" ::"a" (0x07200720), "c" (columns>>1), "D" (scr_end-(columns<<1)) :"cx","di"); } set_origin(); } else { __asm__("cld\n\t" "rep\n\t" "movsl\n\t" "movl _columns,%%ecx\n\t" "rep\n\t" "stosw" ::"a" (0x0720), "c" ((bottom-top-1)*columns>>1), "D" (origin+(columns<<1)*top), "S" (origin+(columns<<1)*(top+1)) :"cx","di","si"); } }