一个简单的模块
#define MODULE
#include <linux/module.h>
int init_module(void)
{
printk("<1> Hello World!\n");
return 0;
}
void cleanup_module(void)
{
printk(" <1>Goodbye!\n");
}
开始的时候用gcc -c hello.c
hello.c:2:26: 错误:linux/module.h:没有那个文件或目录
之后编写了一个Makefile文件
obj-m := hello.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD ?= $(shell pwd)
defalult:
make -C ${KERNELDIR} M=${PWD} modules
install:
insmod hello.ko
uninstall:
rmmod hello.ko
clear:
make -C ${KERNELDIR} M=${PWD} clean
之后,用make就可以编译成功了!
加载模块#insmod hello.ko
卸载模块#rmmod hello
查看#tail /var/log/messages当然也可以用dmesg查看!
May 3 22:23:26 localhost kernel: Hello World!
May 3 22:24:11 localhost kernel: <1>Goodbye!
如何不编写Makefile文件也可以编译成功呢?思考中!
lsmod命令
调用lsmod程序将显示当前系统中正在使用的模块信息。实际上这个程序的功能就是读取/proc文件系统中的文件/proc/modules中的信息,其命令格式为:#lsmod
ksyms命令
ksyms这个程序用来显示内核符号和模块符号表的信息。与lsmod相似,它的功能是读取/proc文件系统中的另一个文件/proc/kallsyms。
2009年5月3日星期日
2009年3月14日星期六
编译内核
[gaowei@localhost linux-2.6.28]$ ls
arch crypto fs Kbuild Makefile REPORTING-BUGS sound
block Documentation include kernel mm samples usr
COPYING drivers init lib net scripts virt
CREDITS firmware ipc MAINTAINERS README security
[gaowei@localhost linux-2.6.28]$ make mrproper
#这是删除一些一切留下来的.o文件!
接着设置编译选项。
[gaowei@localhost linux-2.6.28]$ make menuconfig将会弹出一个图形的!
这里便是要设置内核的一些功能了!
内核的设置应尽量采用下面的原则:
1.确定内核需要的功能,直接编译进内核。
2.可能在将来会用到的功能,直接编译进内核。
3.不知道要做什么,看HELP,看不懂则采用默认值或者变为模块
4.可以看到内核可选的项目真的很多,真的要好好看看!
选择好了这些你就应该,保存设置了,在15个选项下面有一个是Save Configuration to an Alternate File选项,然后为其命名一个喜欢的文件名,这个文件内容就是刚刚编译完成的数据,然后选择,再保存一次到系统中,接下来准备编译!
之后我的有警告
scripts/kconfig/mconf arch/x86/Kconfig
#
# using defaults found in /boot/config-2.6.23.1-42.fc8
#
/boot/config-2.6.23.1-42.fc8:1437:warning: symbol value 'm' invalid for FIXED_PHY
/boot/config-2.6.23.1-42.fc8:1665:warning: symbol value 'm' invalid for IWL4965
/boot/config-2.6.23.1-42.fc8:1756:warning: symbol value 'm' invalid for ISDN
*** End of Linux kernel configuration.
*** Execute 'make' to build the kernel or try 'make help'.
[gaowei@localhost linux-2.6.28]$ make clean
1.除掉原来的生成的*.o文件,这样就不会产生新旧版本的误差了。
[gaowei@localhost linux-2.6.28]$ make bzImage
2.制作出内核的文件。
[gaowei@localhost linux-2.6.28]$ make modules
3.制作出模块相关的文件。
4.如果,编译过程中发生问题,通常可能是挑选的项有问题,这样就需要重新挑选设定的内容make munuconfig,之后编译就可以成功了。执行"make bzImage"后,
我的错误是
WARNING: modpost: Found 1 section mismatch(es).
To see full details build your kernel with:
'make CONFIG_DEBUG_SECTION_MISMATCH=y'
GEN .version
CHK include/linux/compile.h
UPD include/linux/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
.tmp_vmlinux2: final close failed: No space left on device
make: *** [.tmp_vmlinux2] 错误 1
这样就莫名其妙的好了!
[gaowei@localhost linux-2.6.28]$
Root device is (8, 9)
Setup is 12568 bytes (padded to 12800 bytes).
System is 2109 kB
CRC 47b80fb0
Kernel: arch/x86/boot/bzImage is ready (#1)
[gaowei@localhost linux-2.6.28]$ ll arch/i386/boot/bzImage
lrwxrwxrwx 1 gaowei gaowei 22 03-14 13:01 arch/i386/boot/bzImage -> ../../x86/boot/bzImage内核就已经编译好而且放在上面这个目录下了。
[gaowei@localhost linux-2.6.28]$
5.编译内核的模块部分
[gaowei@localhost linux-2.6.28]$ make modules_install
[gaowei@localhost linux-2.6.28]$ sudo make modules_install
口令:
cp: 无法 stat “/home/gaowei/linux/linux-2.6.28/modules.order”: 没有那个文件或目录
make: *** [_modinst_] 错误 1
[gaowei@localhost linux-2.6.28]$
接下来就是安装内核了!主要分为俩步:
1.将模块安装到/lib/modules/ 'uname -r',现在编译好的镜像还是在你的目录下,所以需要将内核放置在启动内核的默认目录/boot下。
把vmliuz-2.6.23.1复制到/boot/vmlinuz-2.6.23.1 然后把linux/System.map 发到/boot/System.map-2.6.23.1
我的设置如下
sudo cp arch/i386/boot/bzImage /boot/vmliuz-2.6.28
[gaowei@localhost linux-2.6.28]$ sudo cp System.map /boot/System.map-2.6.28
2.进行开机的设定。
修改grub配置文件,/boot/grub/grub.conf文件
添加如下:
title kernel 2.6.28
root (hd0,8)
kernel /boot/vmliuz-2.6.28 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
好了,等待重启而后再看结果!
arch crypto fs Kbuild Makefile REPORTING-BUGS sound
block Documentation include kernel mm samples usr
COPYING drivers init lib net scripts virt
CREDITS firmware ipc MAINTAINERS README security
[gaowei@localhost linux-2.6.28]$ make mrproper
#这是删除一些一切留下来的.o文件!
接着设置编译选项。
[gaowei@localhost linux-2.6.28]$ make menuconfig将会弹出一个图形的!
这里便是要设置内核的一些功能了!
内核的设置应尽量采用下面的原则:
1.确定内核需要的功能,直接编译进内核。
2.可能在将来会用到的功能,直接编译进内核。
3.不知道要做什么,看HELP,看不懂则采用默认值或者变为模块
4.可以看到内核可选的项目真的很多,真的要好好看看!
选择好了这些你就应该,保存设置了,在15个选项下面有一个是Save Configuration to an Alternate File选项,然后为其命名一个喜欢的文件名,这个文件内容就是刚刚编译完成的数据,然后选择
之后我的有警告
scripts/kconfig/mconf arch/x86/Kconfig
#
# using defaults found in /boot/config-2.6.23.1-42.fc8
#
/boot/config-2.6.23.1-42.fc8:1437:warning: symbol value 'm' invalid for FIXED_PHY
/boot/config-2.6.23.1-42.fc8:1665:warning: symbol value 'm' invalid for IWL4965
/boot/config-2.6.23.1-42.fc8:1756:warning: symbol value 'm' invalid for ISDN
*** End of Linux kernel configuration.
*** Execute 'make' to build the kernel or try 'make help'.
[gaowei@localhost linux-2.6.28]$ make clean
1.除掉原来的生成的*.o文件,这样就不会产生新旧版本的误差了。
[gaowei@localhost linux-2.6.28]$ make bzImage
2.制作出内核的文件。
[gaowei@localhost linux-2.6.28]$ make modules
3.制作出模块相关的文件。
4.如果,编译过程中发生问题,通常可能是挑选的项有问题,这样就需要重新挑选设定的内容make munuconfig,之后编译就可以成功了。执行"make bzImage"后,
我的错误是
WARNING: modpost: Found 1 section mismatch(es).
To see full details build your kernel with:
'make CONFIG_DEBUG_SECTION_MISMATCH=y'
GEN .version
CHK include/linux/compile.h
UPD include/linux/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
.tmp_vmlinux2: final close failed: No space left on device
make: *** [.tmp_vmlinux2] 错误 1
这样就莫名其妙的好了!
[gaowei@localhost linux-2.6.28]$
Root device is (8, 9)
Setup is 12568 bytes (padded to 12800 bytes).
System is 2109 kB
CRC 47b80fb0
Kernel: arch/x86/boot/bzImage is ready (#1)
[gaowei@localhost linux-2.6.28]$ ll arch/i386/boot/bzImage
lrwxrwxrwx 1 gaowei gaowei 22 03-14 13:01 arch/i386/boot/bzImage -> ../../x86/boot/bzImage内核就已经编译好而且放在上面这个目录下了。
[gaowei@localhost linux-2.6.28]$
5.编译内核的模块部分
[gaowei@localhost linux-2.6.28]$ make modules_install
[gaowei@localhost linux-2.6.28]$ sudo make modules_install
口令:
cp: 无法 stat “/home/gaowei/linux/linux-2.6.28/modules.order”: 没有那个文件或目录
make: *** [_modinst_] 错误 1
[gaowei@localhost linux-2.6.28]$
接下来就是安装内核了!主要分为俩步:
1.将模块安装到/lib/modules/ 'uname -r',现在编译好的镜像还是在你的目录下,所以需要将内核放置在启动内核的默认目录/boot下。
把vmliuz-2.6.23.1复制到/boot/vmlinuz-2.6.23.1 然后把linux/System.map 发到/boot/System.map-2.6.23.1
我的设置如下
sudo cp arch/i386/boot/bzImage /boot/vmliuz-2.6.28
[gaowei@localhost linux-2.6.28]$ sudo cp System.map /boot/System.map-2.6.28
2.进行开机的设定。
修改grub配置文件,/boot/grub/grub.conf文件
添加如下:
title kernel 2.6.28
root (hd0,8)
kernel /boot/vmliuz-2.6.28 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
好了,等待重启而后再看结果!
2009年3月3日星期二
Linux0.01分析笔记(4)
[gaowei@localhost ~]$ sh -e
sh-3.2$ exit
exit
[gaowei@localhost ~]$ man sh
-e参数指定“\"不用做转义字符。
当shell启动时,会读取默认的配置文件/profile。
[gaowei@localhost ~]$ \ls这样可以但是[gaowei@localhost ~]$ ls\ls 就bash: lsls: command not found
[gaowei@localhost ~]$ sh \ ls
sh: ls: 没有那个文件或目录
[gaowei@localhost ~]$ sh \
>
[gaowei@localhost ~]$ sh
sh-3.2$ \
>
[gaowei@localhost ~]$ "ls" kernel/
book C kang kernel linux-0.11 man-pages-3.10
当执行shell时,用户键入一个命令,shell首先除去所有的"\"字符之前的字符(如果没有使用-e参数)和所有的引号中的字符。然后,shell按照如下的规则处理"!"引起的shell命令历史代替:
!! 代表前一个执行的命令
! 代表前个执行的命令
! 代表字符串开始的命令
然后,shell会将输入的命令字符串切换成为令牌(token)字符串。在引导中的所有字符串成为一个令牌。空格是隔开各个令牌的分割符。
然后,shell使用如下的规则处理命令行的通配符:
包含通配符(当前设定为*)的令牌被假定成文件名,通配符被扩展成为匹配的文件名(*匹配任何字符)。
shell执行I/O重定向(I/O readirections):
'> word' 重定向标准输出(redirect stdout)
'>> word' 重定向标准输出,添加在指定的文件后
'>& amp;word' 重定向标准输出和标准错误输出(redirects stdout and stderr)
'< word' 重定向标准输入(redirects stdin)
shell处理的文件描述符一般有三个:文件句柄0(stdin,标准输入),文件句柄1(stdout,标准输出),文件句柄2(stderr,标准错误输出)。
dup()系统调用的功能是返回一个新的文件描述符,它指向传入参数文件描述符指向的文件流。dup()使用进程文件分配表中的第一个没有使用的文件句柄复制为新的文件句柄。dup()经常使用的一个功能就是完成标准I/O的重定向。使用dup()进行I/O重定向的代码片段如下:
int fd,pid;
fd = open(path,mode);
if((pid=fork())==0) {
close(0);/*Close stdin.*/
/* Make stdin come from fd.Dup() will reuse the lowest.
*number unused file descriptor,0 in this case.*/
dup(fd);
close(fd); /*No longer needed.*/
execve(...); /*Run command,with input from fd..*/
} else {
/*Parent.Wait for child processes to exit.*/
while(wait(NULL)>0);
}
pipe()函数可以创建一个FIFO流(First In First Out stream,先进先出流)来完成进程间的通信。pipe()系统调用生成俩个文件句柄,一个可以向FIFO流中写入,一个可以从FIFO流中读取。可以通过在父进程中使用pipe()函数来创建管道,然后再生成fork()子进程,每一个生成的子进程中都有父进程调用pipe()函数生成的文件句柄的复制。在子进程中,>可以把父进程中通过pipe()生成的句柄使用dup()函数来重定向子进程的标准输入(stdin),标准输出(stdout)和标准错误输出,这样父进程就可以控制子进程的标准I/O了。
int i,pid,mypipe[2];/*Two fds,for pipe ends.*/
pipe(mypipe); /*Create pipe.*/
/*Create reader.*/
if((pid=fork())==0) {
close(0); /*Close stdin.*/
dup(pipe[0]); /*Stdin comes from pipe.*/
close(pipe[0]); /*No longer needed.*/
close(pipe[1]); /*Not needed.*/
execve(...); /*Run command,with input from pipe.*/
}
/*Create write.*/
if((pid=fork())==0) {
close(1); /*Close stout.*/
dup(pipe[1]); /*stdout goes to pipe.*/
close(pipe[1]); /*No longer needed.*/
close(pipe[0]); /*Not needed.*/
execve(...); /*Run command,with output to pipe.*/
}
/*Parent.*/
close(pipe[0]);
close(pipe[1]);
while(wait(NULL)>0); /*Wait for child processes to exit.*/
一个例子:
[gaowei@localhost 10SHELL编程技术和实例]$ gcc -o dup dup.c
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ ./dup
This is to stdout
This is to stderr
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ cat ./out
This is to stdout
[gaowei@localhost 10SHELL编程技术和实例]$ cat ./err
This is to stderr
[gaowei@localhost 10SHELL编程技术和实例]$ ls -a
. .. 10 .10.swp a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$
一个成熟的shell应该支持管道操作总体结构如下:
(cmd1 | cmd2) /* cmd1的标准输出为cmd2的标准输入 */
pipe[fdarr]; /*定义管道 */
if((pid1 = fork()) == 0) {
close(1); /*关闭cmd1的stdout */
dup(fdarr[1]); /* 设置fdarr[1]为cmd1的stdout */
close(fdarr[0]);
aptr[0]="cmd1"; aptr[1]=NULL;
execve("cmd1",aptr,eptr);
}
if((pid2=fork()) ==0) {
close(0); /*关闭cmd2的stdin */
dup(fdarr[0]); /*设置fdarr[0]为cmd2的stdin */
close(fdarr[0]);
close(fdarr[1]);
aptr[0]="cmd2";aptr[1]=NULL;
execve("cmd2",aptr,eptr);
}
close(fdarr[0]);
close(fdarr[1]);
wait() for both pid1 and pid2;
shell程序的主程序组成如下:
#include "def.h"
main(void)
{
int i;
initcold();
for(;;) {
initwarm();
if(getline())
if(i=parse())
execute(i);
}
}
第十一章
分析Linux0.01中实现系统调用的俩个文件system_call.s和sys.c来展示Linux0.01的系统调用的实现方式。标准的C语言库函数,在不同的操作系统上有不同的内部实现。
应用程序可以通过一个固定的过程,从而调用内核提供的功能,在Intel体系结构的计算机中,这是通过执行中断0x80h实现的。
应用程序通常是一个进程,进程在调用内核时,跳转到内核代码中的位置一般标记为system_call(在Linux0.01中,system_call是汇编程序system_call.s中的一段代码的入口点的标记)。在system_call位置的代码将检查系统调用号,依据系统调用号告诉系统内核进程请求的系统服务是什么。然后,它再查找系统调用表sys_call_table[],找到希望调用的内核函数的地址,并调用此函数,最后将控制权返回应用程序。编写一个自己的函数,然后改变sys_call_table[]中的指针并指向该函数。
Linux用于实现系统调用异常的实际指令是:int $0x80
定义系统调用的预定义宏为: _syscallN(parameters)
在include/unistd.h中可以找到_syscallN(parameters)还有__NR_name的形式定义了66个常数,这些常数就是系统调用函数name的函数指针在系统的调用表中的偏移量。
系统调用表是一张表格,按照顺序定义了系统中所有的系统调用的入口函数地址。在Linux0.01中,系统调用表定义在include/linux/sys.h中,如下所示:
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp,sys_setsid};
在数组sys_call_table[]中的每一个元素都是一个函数指针(在C语言中,函数名代表指向函数入口的指针),按系统调用号(即前面提到的_NR_name)排列了所有系统调用函数的指针,以供系统调用入口函数查找。从这张表可以看出,Linux给它所支持的系统调用函数取名叫sys_name。
系统调用入口函数定义在/linux/kernel/system_call.s文件中。是使用汇编语言书写的,包含了系统调用的主要处理程序,同时也包含了时钟中断处理程序,硬盘中断处理程序也包含在本文件中。
从system_call入口的汇编程序的主要功能是:
检查是否为合法的系统调用。
保存寄存器的当前值。
根据系统调用表_sys_call_table和EAX寄存器持有的系统调用号找出并转入系统调用响应函数。从该响应函数返回后,让EAX寄存器保存函数返回值,跳转至ret_from_sys_call,>当ret_from_sys_call结束后,将执行进程调度。
在执行位于用户程序中系统调用命令后面余下的指令之前,若INT 0x80h的返回值非负,则直接按类型type返回;否则,将INT 0x80h的返回值取绝对值,保留在errno变量中,返回-
第十二章
关于这一章我记录的比较少
尽管在Linux0.01中没有写网络的部分,但是现在是个网络的时代,这本书中也讲了关于网络的部分,而且我又是学习网络的,所以就在写一写了。
Linux是一个网络操作系统。
TCP/IP协议是一套数据通信协议,其名字是由这些协议中的俩个主要的协议组成的,即传输控制协议(Transmission Control Protocol,TCP)和网间协议(Internet Protocol,IP)。
sh-3.2$ exit
exit
[gaowei@localhost ~]$ man sh
-e参数指定“\"不用做转义字符。
当shell启动时,会读取默认的配置文件/profile。
[gaowei@localhost ~]$ \ls这样可以但是[gaowei@localhost ~]$ ls\ls 就bash: lsls: command not found
[gaowei@localhost ~]$ sh \ ls
sh: ls: 没有那个文件或目录
[gaowei@localhost ~]$ sh \
>
[gaowei@localhost ~]$ sh
sh-3.2$ \
>
[gaowei@localhost ~]$ "ls" kernel/
book C kang kernel linux-0.11 man-pages-3.10
当执行shell时,用户键入一个命令,shell首先除去所有的"\"字符之前的字符(如果没有使用-e参数)和所有的引号中的字符。然后,shell按照如下的规则处理"!"引起的shell命令历史代替:
!! 代表前一个执行的命令
!
!
然后,shell会将输入的命令字符串切换成为令牌(token)字符串。在引导中的所有字符串成为一个令牌。空格是隔开各个令牌的分割符。
然后,shell使用如下的规则处理命令行的通配符:
包含通配符(当前设定为*)的令牌被假定成文件名,通配符被扩展成为匹配的文件名(*匹配任何字符)。
shell执行I/O重定向(I/O readirections):
'> word' 重定向标准输出(redirect stdout)
'>> word' 重定向标准输出,添加在指定的文件后
'>& amp;word' 重定向标准输出和标准错误输出(redirects stdout and stderr)
'< word' 重定向标准输入(redirects stdin)
shell处理的文件描述符一般有三个:文件句柄0(stdin,标准输入),文件句柄1(stdout,标准输出),文件句柄2(stderr,标准错误输出)。
dup()系统调用的功能是返回一个新的文件描述符,它指向传入参数文件描述符指向的文件流。dup()使用进程文件分配表中的第一个没有使用的文件句柄复制为新的文件句柄。dup()经常使用的一个功能就是完成标准I/O的重定向。使用dup()进行I/O重定向的代码片段如下:
int fd,pid;
fd = open(path,mode);
if((pid=fork())==0) {
close(0);/*Close stdin.*/
/* Make stdin come from fd.Dup() will reuse the lowest.
*number unused file descriptor,0 in this case.*/
dup(fd);
close(fd); /*No longer needed.*/
execve(...); /*Run command,with input from fd..*/
} else {
/*Parent.Wait for child processes to exit.*/
while(wait(NULL)>0);
}
pipe()函数可以创建一个FIFO流(First In First Out stream,先进先出流)来完成进程间的通信。pipe()系统调用生成俩个文件句柄,一个可以向FIFO流中写入,一个可以从FIFO流中读取。可以通过在父进程中使用pipe()函数来创建管道,然后再生成fork()子进程,每一个生成的子进程中都有父进程调用pipe()函数生成的文件句柄的复制。在子进程中,>可以把父进程中通过pipe()生成的句柄使用dup()函数来重定向子进程的标准输入(stdin),标准输出(stdout)和标准错误输出,这样父进程就可以控制子进程的标准I/O了。
int i,pid,mypipe[2];/*Two fds,for pipe ends.*/
pipe(mypipe); /*Create pipe.*/
/*Create reader.*/
if((pid=fork())==0) {
close(0); /*Close stdin.*/
dup(pipe[0]); /*Stdin comes from pipe.*/
close(pipe[0]); /*No longer needed.*/
close(pipe[1]); /*Not needed.*/
execve(...); /*Run command,with input from pipe.*/
}
/*Create write.*/
if((pid=fork())==0) {
close(1); /*Close stout.*/
dup(pipe[1]); /*stdout goes to pipe.*/
close(pipe[1]); /*No longer needed.*/
close(pipe[0]); /*Not needed.*/
execve(...); /*Run command,with output to pipe.*/
}
/*Parent.*/
close(pipe[0]);
close(pipe[1]);
while(wait(NULL)>0); /*Wait for child processes to exit.*/
一个例子:
[gaowei@localhost 10SHELL编程技术和实例]$ gcc -o dup dup.c
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ ./dup
This is to stdout
This is to stderr
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ cat ./out
This is to stdout
[gaowei@localhost 10SHELL编程技术和实例]$ cat ./err
This is to stderr
[gaowei@localhost 10SHELL编程技术和实例]$ ls -a
. .. 10 .10.swp a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$ ls
10 a.out dup dup.c err out sh1.c sh2 sh2.c
[gaowei@localhost 10SHELL编程技术和实例]$
一个成熟的shell应该支持管道操作总体结构如下:
(cmd1 | cmd2) /* cmd1的标准输出为cmd2的标准输入 */
pipe[fdarr]; /*定义管道 */
if((pid1 = fork()) == 0) {
close(1); /*关闭cmd1的stdout */
dup(fdarr[1]); /* 设置fdarr[1]为cmd1的stdout */
close(fdarr[0]);
aptr[0]="cmd1"; aptr[1]=NULL;
execve("cmd1",aptr,eptr);
}
if((pid2=fork()) ==0) {
close(0); /*关闭cmd2的stdin */
dup(fdarr[0]); /*设置fdarr[0]为cmd2的stdin */
close(fdarr[0]);
close(fdarr[1]);
aptr[0]="cmd2";aptr[1]=NULL;
execve("cmd2",aptr,eptr);
}
close(fdarr[0]);
close(fdarr[1]);
wait() for both pid1 and pid2;
shell程序的主程序组成如下:
#include "def.h"
main(void)
{
int i;
initcold();
for(;;) {
initwarm();
if(getline())
if(i=parse())
execute(i);
}
}
第十一章
分析Linux0.01中实现系统调用的俩个文件system_call.s和sys.c来展示Linux0.01的系统调用的实现方式。标准的C语言库函数,在不同的操作系统上有不同的内部实现。
应用程序可以通过一个固定的过程,从而调用内核提供的功能,在Intel体系结构的计算机中,这是通过执行中断0x80h实现的。
应用程序通常是一个进程,进程在调用内核时,跳转到内核代码中的位置一般标记为system_call(在Linux0.01中,system_call是汇编程序system_call.s中的一段代码的入口点的标记)。在system_call位置的代码将检查系统调用号,依据系统调用号告诉系统内核进程请求的系统服务是什么。然后,它再查找系统调用表sys_call_table[],找到希望调用的内核函数的地址,并调用此函数,最后将控制权返回应用程序。编写一个自己的函数,然后改变sys_call_table[]中的指针并指向该函数。
Linux用于实现系统调用异常的实际指令是:int $0x80
定义系统调用的预定义宏为: _syscallN(parameters)
在include/unistd.h中可以找到_syscallN(parameters)还有__NR_name的形式定义了66个常数,这些常数就是系统调用函数name的函数指针在系统的调用表中的偏移量。
系统调用表是一张表格,按照顺序定义了系统中所有的系统调用的入口函数地址。在Linux0.01中,系统调用表定义在include/linux/sys.h中,如下所示:
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp,sys_setsid};
在数组sys_call_table[]中的每一个元素都是一个函数指针(在C语言中,函数名代表指向函数入口的指针),按系统调用号(即前面提到的_NR_name)排列了所有系统调用函数的指针,以供系统调用入口函数查找。从这张表可以看出,Linux给它所支持的系统调用函数取名叫sys_name。
系统调用入口函数定义在/linux/kernel/system_call.s文件中。是使用汇编语言书写的,包含了系统调用的主要处理程序,同时也包含了时钟中断处理程序,硬盘中断处理程序也包含在本文件中。
从system_call入口的汇编程序的主要功能是:
检查是否为合法的系统调用。
保存寄存器的当前值。
根据系统调用表_sys_call_table和EAX寄存器持有的系统调用号找出并转入系统调用响应函数。从该响应函数返回后,让EAX寄存器保存函数返回值,跳转至ret_from_sys_call,>当ret_from_sys_call结束后,将执行进程调度。
在执行位于用户程序中系统调用命令后面余下的指令之前,若INT 0x80h的返回值非负,则直接按类型type返回;否则,将INT 0x80h的返回值取绝对值,保留在errno变量中,返回-
第十二章
关于这一章我记录的比较少
尽管在Linux0.01中没有写网络的部分,但是现在是个网络的时代,这本书中也讲了关于网络的部分,而且我又是学习网络的,所以就在写一写了。
Linux是一个网络操作系统。
TCP/IP协议是一套数据通信协议,其名字是由这些协议中的俩个主要的协议组成的,即传输控制协议(Transmission Control Protocol,TCP)和网间协议(Internet Protocol,IP)。
Linux0.01分析笔记(3)
第七章
在Linux0.01内核中的include/linux/sched.h文件中,定义了进程的不同状态,
#define TASK_RUNNING 0 表示进程在"Ready List"中,这个进程除了CPU以外
,获得了所有的其他资源
#define TASK_INTERRUPTIBLE 1 进程在睡眠中,正在等待一个信号或者一个资源
#define TASK_UNINTERRUPTIBLE 2 进程等待一个资源,当前进程在“Wait Queue“
#define TASK_ZOMBIE 3 僵尸进程(没有父进程的子进程)
#define TASK_STOPPED 4 标识进程在被调试
进程控制表中的每一项都是一个task_struct结构,而task_struct结构中存储各种低级和高级的信息,包括从一些硬件设备的寄存器复制到进程的工作目录的链接点。
进程控制表既是一个数组,又是一个双向链表,同时又是一个树。其实现是一个包括多个指针的静态数组。此数组的长度NR_TASKS
#define NR_TASKS 64
#define HZ 100
数组中的结构则保存在系统预留的内存页中,定义如下:
#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]
系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的全局指针变量current用来记录正在运行的进程。变量current只能由kernel/sched.c中的进程调度改变。
内核线程在Linux2.0.0的核心中就可以被看到,例如在linux-2.0.1\linux\arch\alpha\kernel\entry.S中的代码。Linux系统使用系统调用fork()来创建一个进程,用exit()来结束一个进程。fork()和exit()的源程序保存在kernel/fork.c和kernel/exit.c中。fork()主要任务是初始化要创建进程的数据结构,其主要的步骤有:
(1)申请一个空闲的页面来保存task_struct。
(2)查找一个空的进程槽(find_empty_process())。
(3)为kernel_stack_page申请另一个空闲的内存页作为堆栈。
(4)将父进程的LDT表复制给子进程。
(5)复制父进程的内存映射信息。
(6)管理文件描述符和链接点。使用fork()创建一个进程后,程序的俩个复制都在运行。通常一个复制使用exec()调用系统的应用程序。
进程的调度(schedule()函数)
用户级线程(User-level threads) 是由与应用程序链接的线程库实现。核心不知道线程的
存在,也就不能独立调度这些线程了。
多任务系统可以划分为单处理器多任务系统和多处理器任务系统。
Linux0.01的核心进程源代码主要包括sched.c,fork.c,kill.c等。
sched.c是Linux的主要核心文件,它实现了Linux的进程调度功能,包括实现进程的各种状
态:睡眠(sleep_on),唤醒(wakeup),调度(schedule)等。
fork.c包含了实现系统调用fork()的辅助功能函数。
kill.c主要实现了系统调用kill(),kill()的作用主要是杀死一个进程。
system_call.s包含了系统的低级处理函数,主要都是一些汇编处理程序。
第八章
设备管理系统通常是设备驱动程序和操作系统的其余部分和应用程序的唯一接口。
设备管理系统需要实现如下的功能:
(1)隔离设备驱动程序和操作系统核心。
(2)隔离硬件和用户程序。
一般操作系统的设备管理程序具有如下特性:
(1)异步I/O(Asynchronous I/O)
(2)即插即用(Plug and Play)
在Unix下可以使用read()和write()来读和写设备,用ioctl()来对设备进行设置;而在Windows使用ReadFile()和WrintFile()来读和写设备,用DeviceIoControl()来对设备进行设置。
在Linux系统中俩种典型的情况:读RAMDISK和读IDE设备。
对于read请求,如果read一个RAMDISK,典型的过程如下:
(1)从RAMDISK内存空间复制内存到用户空间(user space)。
(2)结束调用,返回用户空间。
一个IDE驱动程序,典型的read处理过程如下:
(1)接受一个read请求,然后把这个请求放到I/O队列中。
(2)如果I/O队列为空,将这个读写磁盘扇区的请求发送到磁盘驱动器。
(3)将调用read系统调用的进程切换为SLEEP状态。
(4)当磁盘驱动器完成磁盘扇区读操作后,会发出一个中断。IDE驱动程序接收到这个中断后,把读出的数据从磁盘控制器传送到核心的buffer中,然后把数据复制到用户空间,接下来唤醒调用进程。
(5)当调用浚进程被唤醒时,系统的控制权回到用户空间的用户进程。
CPU的内部中断又可以叫异常,异常的主要作用是报告一些程序运行中的错误和处理缺页中断(page_fault)。这些异常都是通过set_trap_gate宏来设置的。set_trap_gate宏是定义在include/asm/system.h文件中的一个宏,被用来设置CPU的trap,起定义如下:
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
在Linux0.01中,中断处理主要做了俩个部分的工作:
(1)trap.s将各个CPU的trap信息填入到idt中;
(2)asm.s处理当出现trap的时候后续的一系列处理。
在Linux0.01中处理的外部中断有时钟中断(0x20),串行通信口中断(0x23和0x24),硬盘中断(0x2e),键盘中断(0x21),这些中断具体的处理函数在文件rs_io.s,hd.c和keyboard.s中。
如果多个设备共享一个中断,那么每当一个设备产生一个中断时,CPU会执行所有的isr中断服务程序。具体的一个完整的中断产生和处理流程是:
(1)产生中断
(2)CPU应答
(3)查找idt中的对应向量
(4)在gdt中查找idt项的代码段
(5)对比当前的cpl和描述符的dpl看是否产生越级保护
(6)检查是否发生特权级的变化,如果是就保存ss和esp,否则不保存
(7)保存eflags,cs,eip和错误码
(8)将idt对应描述地址装入cs和eip中以便执行
(9)执行irp_interup
(10)执行do_irp
(11)循环执行isr
(12)中断返回。
trap.c处理了硬件中断和运行中可能产生的错误。
asm.s包含了处理低级硬件错误的代码,asm.s也使用TS位来处理协处理器错误。
ISR(Interrupt Service Routine,ISR),中断处理程序。
x86体系结构是一个中断驱动的系统,外部发生的事件总是通过中断服务程序来进行处理。
在x86体系结构中,中断处理程序的入口地址是保留在系统的IDT(Interrupt Descriptor Table,中断描述表)中的。
中断处理程序和普通程序的区别是:中断处理程序必须非常简单,而且需要处理CPU的状态
。ISR在结束时,都必须调用"Interrupt Return(IRET)",而普通应用程序在结束时调用的是"Return (RET)"或者"Far Return(RETF)"。
BIOS数据区提供了大量的硬件设备信息,通过读取BIOS的数据,可以方便地了解计算机的硬件设备情况。
在IMB AT和IBMPS/2键盘系统中,CPU并不直接和键盘进行通信,而是通过一个8042芯片或者其他与之兼容的芯片。键盘本身也有自己的芯片(Intel8084及其兼容芯片)。
console.c主要实现了控制台功能,包含con_init()和con_write()函数。
serial.c实现了rs232串口通信功能,主要函数是:rs_write(),rs_init()以及相关的中断功能。
第九章
Linux系统继承了Unix中"everything is a file"的思想,使用文件系统接口,从而可以控
制Linux系统中的所有设备。通过引入VFS(Virtual File System)的概念,可以使Linux文件系统的主要部分和具体的物理文件系统类型无关。
对硬盘进行访问的基础是对硬盘的磁盘扇区进行寻址。通常,对于硬盘的寻址使用CHS(Cylinder/Head/Sector)参数。CHS就是使用柱面数(Cylinders),磁头数(Heads),和扇区数(Sectors per track)来定位硬盘驱动器任何一个扇区的方法。
Cylinders表示硬盘每一面盘片上有几条磁道,最大为1024(用10个二进制位存储).
Heads表示硬盘总共有几个磁头,也就是有几面磁片,最大为256(用8个二进制位存储)。
Sectors per track表示每一条磁道上有几个扇区,最大为63(用6个二进制位存储)。
每一个扇区一般是512B所以磁盘最大容量为
256*1024*63*512/1048576=8064GB (1M=1048576B)
BIOS INT 13h调用是BIOS提供的磁盘基本输入输出中断调用,它可以完成磁盘(包括硬盘和软盘)的复位,读写,校验,定位,诊断,格式化等功能。它使用的就是CHS寻址方式,因此最大能访问8GB左右的硬盘。
典型的扇区存放形式:
扇区在磁道上连续存放;带磁盘内部Cache的现代磁盘。
Unix文件系统结构
Boot block |Super block |Inode table...........|Data block zone
引导块(Boot block)通常位于文件卷最开始的第一扇区,这512B是文件系统的引导代码,为根文件系统所特有,其他文件系统中这512B为空。
超级块(Sper block)紧跟引导块之后,用于描述文件系统的结构,如i节点长度,文件系统大小等信息。
i节点表(Inode table)存放在超级块之后,其长度由超级块中i节点长度字段决定,起作用是用来描述文件的属性,长度,属主,属组,数据块表等信息。
数据区(Data block zone)跟在i节点表后面,用于存放文件的数据。
在Unix系统中,一个普通的文件通常由俩部分组成:i节点+数据块
用fsck可以修正文件系统的不一致。
fsck - check and repair a Linux file system
日志文件系统(Log-Structured File System)的设计思想是跟踪文件系统的变化而不是文件系统的内容,所有对文件系统的更新都被记录在日志中。
Linux文件系统其实可以分为三个部分,第一部分叫VFS。这是Linux文件系统对外的接口,
任何要使用文件系统的程序都必须经由这层接口来使用它。另外俩部分属于文件系统的内部,其中一个是Cache,另一个就是真正最底层的文件系统,像Ext2,FAT之类的。
在文件系统里的每一个文件,系统都给它一个inode,只要inode不一样,就表示这俩个文件不相同,inode是由VFS定义的。
一个硬盘最多可以有8个分区(partition),其中4个是主分区(Primary partition),另4个则是扩展分区(extended partition)。除了分区,硬盘第一个扇区称为MBR(Master Boot Record,主启动录)。
文件系统的一些过程:
创建一个文件,其步骤如下:
(1)分配和初始化inode
(2)将inode和文件名的对应关系写入到文件目录表项中(通常在当前的工作目录)
(3)将上述数据写入磁盘。
修改一个磁盘文件,起步骤如下:
(1)将此文件的inode装载到内存中
(2)分配空闲的磁盘块(将这些磁盘块在freelist中标记为已经分配)
(3)修改这个文件的inode,指向这个新分配的磁盘块
(4)将用户的数据写入这个磁盘块
(5)将这个文件所有的修改都写入磁盘。
删除一个磁盘文件,其步骤如下:
(1)将此文件的inode装载到内存中。
(2)依据inode数据查找到将要删除的数据所在的磁盘块
(3)标记这个磁盘块为空闲
(4)将这个文件所有的修改都写入磁盘。
在系统启动时,只执行一次的函数sys_setup读取所有的IDE硬盘上的分区表。在kernel/hd.c源程序中包含对各种IDE接口的驱动代码。此外rw_hd()函数完成了从各种IDE设备上读取
和写入数据块的功能。
在Linux0.01的文件系统中hd.c()代码演示了从IDE设备上读取和写入数据块,而fs/block_dev.c中包含了对块设备文件系统的具体支持。
kernel/hd.c处理IDE中断和数据块写入,读出队列
fs/block_dev.c使用hd.c中提供的功能,例如:block_write()来向硬件驱动器中写入或者读出数据
fs/read_write.c中包含了sys_read(),sys_write(),and sys_lseek()的代码。
super.c文件中包含了Linux0.01的文件系统处理超级块表(super-block tables)的功能。
read_write.c文件包含了Linux0.01文件系统中对字符设备和块设备的读操作和写操作的实现过程。
open.c包含了Linux0.01中打开文件的操作过程。
inode.c包含了Linux0.01中对文件系统inode表的维护过程。
buffer.c实现了文件系统中的缓冲功能。
bitmap.c包含了处理文件系统中inode和文件系统中位图的功能。
在Linux0.01内核中的include/linux/sched.h文件中,定义了进程的不同状态,
#define TASK_RUNNING 0 表示进程在"Ready List"中,这个进程除了CPU以外
,获得了所有的其他资源
#define TASK_INTERRUPTIBLE 1 进程在睡眠中,正在等待一个信号或者一个资源
#define TASK_UNINTERRUPTIBLE 2 进程等待一个资源,当前进程在“Wait Queue“
#define TASK_ZOMBIE 3 僵尸进程(没有父进程的子进程)
#define TASK_STOPPED 4 标识进程在被调试
进程控制表中的每一项都是一个task_struct结构,而task_struct结构中存储各种低级和高级的信息,包括从一些硬件设备的寄存器复制到进程的工作目录的链接点。
进程控制表既是一个数组,又是一个双向链表,同时又是一个树。其实现是一个包括多个指针的静态数组。此数组的长度NR_TASKS
#define NR_TASKS 64
#define HZ 100
数组中的结构则保存在系统预留的内存页中,定义如下:
#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]
系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的全局指针变量current用来记录正在运行的进程。变量current只能由kernel/sched.c中的进程调度改变。
内核线程在Linux2.0.0的核心中就可以被看到,例如在linux-2.0.1\linux\arch\alpha\kernel\entry.S中的代码。Linux系统使用系统调用fork()来创建一个进程,用exit()来结束一个进程。fork()和exit()的源程序保存在kernel/fork.c和kernel/exit.c中。fork()主要任务是初始化要创建进程的数据结构,其主要的步骤有:
(1)申请一个空闲的页面来保存task_struct。
(2)查找一个空的进程槽(find_empty_process())。
(3)为kernel_stack_page申请另一个空闲的内存页作为堆栈。
(4)将父进程的LDT表复制给子进程。
(5)复制父进程的内存映射信息。
(6)管理文件描述符和链接点。使用fork()创建一个进程后,程序的俩个复制都在运行。通常一个复制使用exec()调用系统的应用程序。
进程的调度(schedule()函数)
用户级线程(User-level threads) 是由与应用程序链接的线程库实现。核心不知道线程的
存在,也就不能独立调度这些线程了。
多任务系统可以划分为单处理器多任务系统和多处理器任务系统。
Linux0.01的核心进程源代码主要包括sched.c,fork.c,kill.c等。
sched.c是Linux的主要核心文件,它实现了Linux的进程调度功能,包括实现进程的各种状
态:睡眠(sleep_on),唤醒(wakeup),调度(schedule)等。
fork.c包含了实现系统调用fork()的辅助功能函数。
kill.c主要实现了系统调用kill(),kill()的作用主要是杀死一个进程。
system_call.s包含了系统的低级处理函数,主要都是一些汇编处理程序。
第八章
设备管理系统通常是设备驱动程序和操作系统的其余部分和应用程序的唯一接口。
设备管理系统需要实现如下的功能:
(1)隔离设备驱动程序和操作系统核心。
(2)隔离硬件和用户程序。
一般操作系统的设备管理程序具有如下特性:
(1)异步I/O(Asynchronous I/O)
(2)即插即用(Plug and Play)
在Unix下可以使用read()和write()来读和写设备,用ioctl()来对设备进行设置;而在Windows使用ReadFile()和WrintFile()来读和写设备,用DeviceIoControl()来对设备进行设置。
在Linux系统中俩种典型的情况:读RAMDISK和读IDE设备。
对于read请求,如果read一个RAMDISK,典型的过程如下:
(1)从RAMDISK内存空间复制内存到用户空间(user space)。
(2)结束调用,返回用户空间。
一个IDE驱动程序,典型的read处理过程如下:
(1)接受一个read请求,然后把这个请求放到I/O队列中。
(2)如果I/O队列为空,将这个读写磁盘扇区的请求发送到磁盘驱动器。
(3)将调用read系统调用的进程切换为SLEEP状态。
(4)当磁盘驱动器完成磁盘扇区读操作后,会发出一个中断。IDE驱动程序接收到这个中断后,把读出的数据从磁盘控制器传送到核心的buffer中,然后把数据复制到用户空间,接下来唤醒调用进程。
(5)当调用浚进程被唤醒时,系统的控制权回到用户空间的用户进程。
CPU的内部中断又可以叫异常,异常的主要作用是报告一些程序运行中的错误和处理缺页中断(page_fault)。这些异常都是通过set_trap_gate宏来设置的。set_trap_gate宏是定义在include/asm/system.h文件中的一个宏,被用来设置CPU的trap,起定义如下:
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
在Linux0.01中,中断处理主要做了俩个部分的工作:
(1)trap.s将各个CPU的trap信息填入到idt中;
(2)asm.s处理当出现trap的时候后续的一系列处理。
在Linux0.01中处理的外部中断有时钟中断(0x20),串行通信口中断(0x23和0x24),硬盘中断(0x2e),键盘中断(0x21),这些中断具体的处理函数在文件rs_io.s,hd.c和keyboard.s中。
如果多个设备共享一个中断,那么每当一个设备产生一个中断时,CPU会执行所有的isr中断服务程序。具体的一个完整的中断产生和处理流程是:
(1)产生中断
(2)CPU应答
(3)查找idt中的对应向量
(4)在gdt中查找idt项的代码段
(5)对比当前的cpl和描述符的dpl看是否产生越级保护
(6)检查是否发生特权级的变化,如果是就保存ss和esp,否则不保存
(7)保存eflags,cs,eip和错误码
(8)将idt对应描述地址装入cs和eip中以便执行
(9)执行irp_interup
(10)执行do_irp
(11)循环执行isr
(12)中断返回。
trap.c处理了硬件中断和运行中可能产生的错误。
asm.s包含了处理低级硬件错误的代码,asm.s也使用TS位来处理协处理器错误。
ISR(Interrupt Service Routine,ISR),中断处理程序。
x86体系结构是一个中断驱动的系统,外部发生的事件总是通过中断服务程序来进行处理。
在x86体系结构中,中断处理程序的入口地址是保留在系统的IDT(Interrupt Descriptor Table,中断描述表)中的。
中断处理程序和普通程序的区别是:中断处理程序必须非常简单,而且需要处理CPU的状态
。ISR在结束时,都必须调用"Interrupt Return(IRET)",而普通应用程序在结束时调用的是"Return (RET)"或者"Far Return(RETF)"。
BIOS数据区提供了大量的硬件设备信息,通过读取BIOS的数据,可以方便地了解计算机的硬件设备情况。
在IMB AT和IBMPS/2键盘系统中,CPU并不直接和键盘进行通信,而是通过一个8042芯片或者其他与之兼容的芯片。键盘本身也有自己的芯片(Intel8084及其兼容芯片)。
console.c主要实现了控制台功能,包含con_init()和con_write()函数。
serial.c实现了rs232串口通信功能,主要函数是:rs_write(),rs_init()以及相关的中断功能。
第九章
Linux系统继承了Unix中"everything is a file"的思想,使用文件系统接口,从而可以控
制Linux系统中的所有设备。通过引入VFS(Virtual File System)的概念,可以使Linux文件系统的主要部分和具体的物理文件系统类型无关。
对硬盘进行访问的基础是对硬盘的磁盘扇区进行寻址。通常,对于硬盘的寻址使用CHS(Cylinder/Head/Sector)参数。CHS就是使用柱面数(Cylinders),磁头数(Heads),和扇区数(Sectors per track)来定位硬盘驱动器任何一个扇区的方法。
Cylinders表示硬盘每一面盘片上有几条磁道,最大为1024(用10个二进制位存储).
Heads表示硬盘总共有几个磁头,也就是有几面磁片,最大为256(用8个二进制位存储)。
Sectors per track表示每一条磁道上有几个扇区,最大为63(用6个二进制位存储)。
每一个扇区一般是512B所以磁盘最大容量为
256*1024*63*512/1048576=8064GB (1M=1048576B)
BIOS INT 13h调用是BIOS提供的磁盘基本输入输出中断调用,它可以完成磁盘(包括硬盘和软盘)的复位,读写,校验,定位,诊断,格式化等功能。它使用的就是CHS寻址方式,因此最大能访问8GB左右的硬盘。
典型的扇区存放形式:
扇区在磁道上连续存放;带磁盘内部Cache的现代磁盘。
Unix文件系统结构
Boot block |Super block |Inode table...........|Data block zone
引导块(Boot block)通常位于文件卷最开始的第一扇区,这512B是文件系统的引导代码,为根文件系统所特有,其他文件系统中这512B为空。
超级块(Sper block)紧跟引导块之后,用于描述文件系统的结构,如i节点长度,文件系统大小等信息。
i节点表(Inode table)存放在超级块之后,其长度由超级块中i节点长度字段决定,起作用是用来描述文件的属性,长度,属主,属组,数据块表等信息。
数据区(Data block zone)跟在i节点表后面,用于存放文件的数据。
在Unix系统中,一个普通的文件通常由俩部分组成:i节点+数据块
用fsck可以修正文件系统的不一致。
fsck - check and repair a Linux file system
日志文件系统(Log-Structured File System)的设计思想是跟踪文件系统的变化而不是文件系统的内容,所有对文件系统的更新都被记录在日志中。
Linux文件系统其实可以分为三个部分,第一部分叫VFS。这是Linux文件系统对外的接口,
任何要使用文件系统的程序都必须经由这层接口来使用它。另外俩部分属于文件系统的内部,其中一个是Cache,另一个就是真正最底层的文件系统,像Ext2,FAT之类的。
在文件系统里的每一个文件,系统都给它一个inode,只要inode不一样,就表示这俩个文件不相同,inode是由VFS定义的。
一个硬盘最多可以有8个分区(partition),其中4个是主分区(Primary partition),另4个则是扩展分区(extended partition)。除了分区,硬盘第一个扇区称为MBR(Master Boot Record,主启动录)。
文件系统的一些过程:
创建一个文件,其步骤如下:
(1)分配和初始化inode
(2)将inode和文件名的对应关系写入到文件目录表项中(通常在当前的工作目录)
(3)将上述数据写入磁盘。
修改一个磁盘文件,起步骤如下:
(1)将此文件的inode装载到内存中
(2)分配空闲的磁盘块(将这些磁盘块在freelist中标记为已经分配)
(3)修改这个文件的inode,指向这个新分配的磁盘块
(4)将用户的数据写入这个磁盘块
(5)将这个文件所有的修改都写入磁盘。
删除一个磁盘文件,其步骤如下:
(1)将此文件的inode装载到内存中。
(2)依据inode数据查找到将要删除的数据所在的磁盘块
(3)标记这个磁盘块为空闲
(4)将这个文件所有的修改都写入磁盘。
在系统启动时,只执行一次的函数sys_setup读取所有的IDE硬盘上的分区表。在kernel/hd.c源程序中包含对各种IDE接口的驱动代码。此外rw_hd()函数完成了从各种IDE设备上读取
和写入数据块的功能。
在Linux0.01的文件系统中hd.c()代码演示了从IDE设备上读取和写入数据块,而fs/block_dev.c中包含了对块设备文件系统的具体支持。
kernel/hd.c处理IDE中断和数据块写入,读出队列
fs/block_dev.c使用hd.c中提供的功能,例如:block_write()来向硬件驱动器中写入或者读出数据
fs/read_write.c中包含了sys_read(),sys_write(),and sys_lseek()的代码。
super.c文件中包含了Linux0.01的文件系统处理超级块表(super-block tables)的功能。
read_write.c文件包含了Linux0.01文件系统中对字符设备和块设备的读操作和写操作的实现过程。
open.c包含了Linux0.01中打开文件的操作过程。
inode.c包含了Linux0.01中对文件系统inode表的维护过程。
buffer.c实现了文件系统中的缓冲功能。
bitmap.c包含了处理文件系统中inode和文件系统中位图的功能。
Linux0.01分析笔记(2)
第四章
在过去写过了,所以就不在重复了!请看!
第五章
格式化的作用是在磁盘上划分不同的区域来存储不同的数据。
磁盘经过格式化后主要包括的区域有:主引导记录区(只有硬盘有),引导记录区,文件分配表(FAT),目录区和数据区。
1988年,国际标准化组织(International Standards Organization,ISO)公布了CD-ROM文
件结构标准,这个标准被称之为ISO 9660。
CD-ROM盘上可以存放信息的区域称为卷空间(volume space).卷空间分成俩个区:从LSN0~LSN16称为系统区,它的具体内容没有规定;从LSN16开始到最后一个逻辑扇区称为数据区,它用来记录卷描述符(volume descriptors),文件目录,路径表,文件数据等内容。
可启动CD-ROM的工作原理:BIOS首先检查光盘的第17个扇区(sector 17),查找其中的代码,若发现其中的启动记录卷描述表(Boot Record Volume Descripter),就根据表中的地址继续查找启动目录(Booting Catalog),找到启动目录后,再根据其中描述的启动入口(Boot Entry)找到相应的启动磁盘镜像(Bootable Disk Image)或启动引导文件,找到启动磁盘镜像后,读取其中的数据,并执行相应的开机动作。相对于单重启动CD-ROM而言,多数启动CD-ROM的启动目录中包含多个启动入口,指向多个启动磁盘镜像。《Bootable CD-ROM Format Specification》
下面是在LA32架构的系统引导过程。
1.硬盘启动步骤
PC机启动过程是遵循一定顺序的,过程如下:
(1)机器加电
(2)BIOS加电自检(Power On Self Test - POST)。BIOS在内存中的起始地址为0FFFF:0000,
BIOS的加电自检就是从地址0FFFF:0000开始执行。BIOS加电自检的主要工作包括:CPU,内存及硬盘等关键设备的检测,即插即用设备的检测。完成这些检查后,将依据结果更新ESCD(Extended System Configuration Data,扩展系统配置数据).
(3)计算机将硬盘0柱面0磁头1扇区(主引导区MBR)中的512B读入内存0000:7C00处并跳到0000:7C00处执行;在读取过程中,计算机并不检查该扇区的内容是什么。
(4)检查(WORD)0000:7DEE是否等于0xAA55,若不等于则转去其他启动介质,如果没有其他启动介质则显示“No ROM BASIC“,然后死机。
(5)跳转到0000:7C00处执行MBR中的程序。
(6)MBR首先将自己复制到0000:0600处,然后继续执行。
(7)MBR在主分区表中搜索标志活动的分区,如果发现没有活动分区或有不止一个活动分区,则停止。
(8)将活动分区的第一个扇区读入内存地址0000:7C00处。
(9)检查(WORD)0000:7DFE是否等于0xAA55,若不等于则显示"Missing Operationing System"然后停止,或者尝试软盘启动。
(10)跳转到0000:7C00处继续执行特定系统的启动程序。
(11)启动系统。
而软盘启动而(1)(2)(3)(4)(5)同上(6)是启动系统。
硬盘主引导扇区基本结构
主引导扇区是硬盘的第一个扇区,它由主引导记录(MBR,Master Boot Record),硬盘分区表
(DPT,Disk Partiton Table)和引导扇区标识(Boot Record ID)三部分组成。
硬盘的一个扇区是512B。主引导记录占用主引导扇区的前446B(字节编号是0~0x1BD)存放系统主引导程序(它负责从活动分区中转载并运行操作系统引导程序)。
硬盘分区表占64B(字节编号0x1BE~0x1FD),记录了磁盘的基本分区信息。硬盘分区表分为4个分区项,每一项16B,分别记录了每个主分区的信息(一个硬盘因此最多可以有4个主分区)。
引导扇区标识占用俩个字节(字节序号是从0x1FE~0x1FF)。对于合法引导区,这个标识为0xAA55。这通常计算机判断引导扇区是否合法的标志。
主引导记录中包含了硬盘的一系列参数和一段引导程序。引导程序主要是用来在系统硬件自
检完后引导具有激活标志分区上的操作系统,它最后执行的指令是一条JMP指令,这条指令使计算机可以跳转执行操作系统本身的引导程序。
DOS命令fdisk /mbr可以修复这段程序。
在Linux系统中可以执行如下的命令来恢复MBR:
dd if=/dev/hda of=/boot/boot.NNNN bs=446 count=1 #备份MBR
dd if=/boot/boot.NNNN of=/dev/hda bs=446 count=1 #恢复MBR
硬盘主引导扇区的应用
1.清除硬盘引导功能
2.加密整个磁盘
3.加密单个硬盘分区
4.加入硬盘启动口令识别
5.先于DOS驻留内存程序
6.实现同一硬盘多个操作系统的选择启动
7.实现硬盘主引导扇区或DOS引导扇区的自我修复
Linux0.01的引导部分主要包括俩个源文件:boot.s和head.s。boot.s是计算机上电启动时,由BIOS执行的系统引导代码。head.s是Linux的32位引导代码,在其中将调用main(),最后完成操作系统的引导。
1.boot.s:BIOS引导代码
在计算机加电时,boot.s被BIOS的引导程序加载到地址0x7C00处,然后boot.s将自己移动>到地址0x90000处,然后跳转到地址0x90000处开始执行。然后,boot.s使用BIOS中断功能,加载系统核心到地址0x10000处。接下来,boot.s禁止所有中断,移动系统核心到地址0x0000处,切换处理器到保护模式,然后调用系统的开始程序。系统核心开始运行后,将重新设置保护模式,然后打开需要的中断。
2.head.s:Linux的32位引导代码
这段代码被加载到地址0x00000000处开始运行。地址0x00000000也是系统页表的地址,当系统完成引导后,这段地址将被系统页表重新覆盖使用。
gcc -c my_kernel.c
"-c"开关告诉GCC只是编译成为一个目标文件而不是链接。然后,可以使用如下的命令链接核心,并且设定核心转载的地址为0x100000:
ld my_kernel.o -o kernel.bin -oformat binary -Ttext 0x100000
用“-oformat binary"开关来运行ld告诉链接器输出文件是plain的,没有重定向,没有头信息,只是一个glat的二进制镜像。
“-Ttext 0x100000"告诉链接器用户想要的"text"(代码段)地址在0x100000的内存标记处。
引导代码必须遵循如下的规则:
(1)BIOS会把引导程序装载到固定地址07C00h。因此,引导程序的段地址和飘移地址是固定的。
(2)引导代码必须编译成为plain binary file类型。
(3)plain binary file的尺寸必须是512B(512B是一个扇区的大小)
(4)文件必须以0xAA55h标志结果。
"times 510-($-$$)db 0"代码是一句只有nasm能够理解的代码。
“二次转载”是指:由BIOS从磁盘上装入引导扇区(0头0磁道1扇区)到内存地址07C0:0000,>并跳转到该地址执行;再由从07C0:0000地址开始的引导代码把存放在其他扇区中的程序转
入内存,并跳转到该程序中执行。
第六章
连续分配存储管理方式分为:单一连续分配,固定分区分配和动态分区分配。
虚存提供的功能:扩大地址空间,内存保护,内存映射,公平分配内存,虚存共享。
Inter-Process Communication,IPC 进程间的通信机制
虚拟存储器基本特征是:虚拟扩充,部分装入,离散分配,多次对换。
虚存中的置换算法:先进先出算法(FIFO),最佳置换算法(OPT),最近最少使用置换算法(LRU),最近未使用置换算法(NUR)。
非连续存储管理机制中主要包括:页式管理,段式管理和段页式管理。
任何一个物理地址都可以映射为:段地址+段内偏移。
操作系统必须知道系统物理内存的容量,才能够有效地使用和管理这些物理内存。在CPU处于实模式时,CPU可以访问的物理内存最大只能达到1MB+64KB(在A20 Gate被打开的情况下,否则最大只能访问1MB)。因此,在实模式下通常无法直接通过内存访问来获取内存容量。
可以通过BIOS的15h中断从计算机的CMOS中读取计算机的物理内存的大小,并把这个数据保留在操作系统核心中。BIOS INT15h中断提供了三个子中断来获得系统的物理内存大小,分别是88和E801h和E820h。
malloc()函数作用是从一大块内存中分配出所需要的空间。在分配时需要注意,要留出一部分malloc()不能分配的内核空间(为了便于存储保护,内核地址空间和用户地址空间是相互隔开的)以备内核使用。
内核malloc()函数已经具备了存储分配功能,但是还不具备虚拟存储功能,通过缺页处理来实现虚拟存储管理。
可执行文件的头信息格式有许多标准,例如:coff,elf和pe格式等。
在malloc()函数实现中使用了首次适配方法来进行存储分配。当使用free()函数释放内存时,会对相邻的存储空间进行合并。
page.s是一段汇编语言程序,提供了Linux0.01中缺页异常处理功能。page.s仅仅提供了和
硬件存储管理器的接口功能,大部分功能代码在memory.c文件中。
在过去写过了,所以就不在重复了!请看!
第五章
格式化的作用是在磁盘上划分不同的区域来存储不同的数据。
磁盘经过格式化后主要包括的区域有:主引导记录区(只有硬盘有),引导记录区,文件分配表(FAT),目录区和数据区。
1988年,国际标准化组织(International Standards Organization,ISO)公布了CD-ROM文
件结构标准,这个标准被称之为ISO 9660。
CD-ROM盘上可以存放信息的区域称为卷空间(volume space).卷空间分成俩个区:从LSN0~LSN16称为系统区,它的具体内容没有规定;从LSN16开始到最后一个逻辑扇区称为数据区,它用来记录卷描述符(volume descriptors),文件目录,路径表,文件数据等内容。
可启动CD-ROM的工作原理:BIOS首先检查光盘的第17个扇区(sector 17),查找其中的代码,若发现其中的启动记录卷描述表(Boot Record Volume Descripter),就根据表中的地址继续查找启动目录(Booting Catalog),找到启动目录后,再根据其中描述的启动入口(Boot Entry)找到相应的启动磁盘镜像(Bootable Disk Image)或启动引导文件,找到启动磁盘镜像后,读取其中的数据,并执行相应的开机动作。相对于单重启动CD-ROM而言,多数启动CD-ROM的启动目录中包含多个启动入口,指向多个启动磁盘镜像。《Bootable CD-ROM Format Specification》
下面是在LA32架构的系统引导过程。
1.硬盘启动步骤
PC机启动过程是遵循一定顺序的,过程如下:
(1)机器加电
(2)BIOS加电自检(Power On Self Test - POST)。BIOS在内存中的起始地址为0FFFF:0000,
BIOS的加电自检就是从地址0FFFF:0000开始执行。BIOS加电自检的主要工作包括:CPU,内存及硬盘等关键设备的检测,即插即用设备的检测。完成这些检查后,将依据结果更新ESCD(Extended System Configuration Data,扩展系统配置数据).
(3)计算机将硬盘0柱面0磁头1扇区(主引导区MBR)中的512B读入内存0000:7C00处并跳到0000:7C00处执行;在读取过程中,计算机并不检查该扇区的内容是什么。
(4)检查(WORD)0000:7DEE是否等于0xAA55,若不等于则转去其他启动介质,如果没有其他启动介质则显示“No ROM BASIC“,然后死机。
(5)跳转到0000:7C00处执行MBR中的程序。
(6)MBR首先将自己复制到0000:0600处,然后继续执行。
(7)MBR在主分区表中搜索标志活动的分区,如果发现没有活动分区或有不止一个活动分区,则停止。
(8)将活动分区的第一个扇区读入内存地址0000:7C00处。
(9)检查(WORD)0000:7DFE是否等于0xAA55,若不等于则显示"Missing Operationing System"然后停止,或者尝试软盘启动。
(10)跳转到0000:7C00处继续执行特定系统的启动程序。
(11)启动系统。
而软盘启动而(1)(2)(3)(4)(5)同上(6)是启动系统。
硬盘主引导扇区基本结构
主引导扇区是硬盘的第一个扇区,它由主引导记录(MBR,Master Boot Record),硬盘分区表
(DPT,Disk Partiton Table)和引导扇区标识(Boot Record ID)三部分组成。
硬盘的一个扇区是512B。主引导记录占用主引导扇区的前446B(字节编号是0~0x1BD)存放系统主引导程序(它负责从活动分区中转载并运行操作系统引导程序)。
硬盘分区表占64B(字节编号0x1BE~0x1FD),记录了磁盘的基本分区信息。硬盘分区表分为4个分区项,每一项16B,分别记录了每个主分区的信息(一个硬盘因此最多可以有4个主分区)。
引导扇区标识占用俩个字节(字节序号是从0x1FE~0x1FF)。对于合法引导区,这个标识为0xAA55。这通常计算机判断引导扇区是否合法的标志。
主引导记录中包含了硬盘的一系列参数和一段引导程序。引导程序主要是用来在系统硬件自
检完后引导具有激活标志分区上的操作系统,它最后执行的指令是一条JMP指令,这条指令使计算机可以跳转执行操作系统本身的引导程序。
DOS命令fdisk /mbr可以修复这段程序。
在Linux系统中可以执行如下的命令来恢复MBR:
dd if=/dev/hda of=/boot/boot.NNNN bs=446 count=1 #备份MBR
dd if=/boot/boot.NNNN of=/dev/hda bs=446 count=1 #恢复MBR
硬盘主引导扇区的应用
1.清除硬盘引导功能
2.加密整个磁盘
3.加密单个硬盘分区
4.加入硬盘启动口令识别
5.先于DOS驻留内存程序
6.实现同一硬盘多个操作系统的选择启动
7.实现硬盘主引导扇区或DOS引导扇区的自我修复
Linux0.01的引导部分主要包括俩个源文件:boot.s和head.s。boot.s是计算机上电启动时,由BIOS执行的系统引导代码。head.s是Linux的32位引导代码,在其中将调用main(),最后完成操作系统的引导。
1.boot.s:BIOS引导代码
在计算机加电时,boot.s被BIOS的引导程序加载到地址0x7C00处,然后boot.s将自己移动>到地址0x90000处,然后跳转到地址0x90000处开始执行。然后,boot.s使用BIOS中断功能,加载系统核心到地址0x10000处。接下来,boot.s禁止所有中断,移动系统核心到地址0x0000处,切换处理器到保护模式,然后调用系统的开始程序。系统核心开始运行后,将重新设置保护模式,然后打开需要的中断。
2.head.s:Linux的32位引导代码
这段代码被加载到地址0x00000000处开始运行。地址0x00000000也是系统页表的地址,当系统完成引导后,这段地址将被系统页表重新覆盖使用。
gcc -c my_kernel.c
"-c"开关告诉GCC只是编译成为一个目标文件而不是链接。然后,可以使用如下的命令链接核心,并且设定核心转载的地址为0x100000:
ld my_kernel.o -o kernel.bin -oformat binary -Ttext 0x100000
用“-oformat binary"开关来运行ld告诉链接器输出文件是plain的,没有重定向,没有头信息,只是一个glat的二进制镜像。
“-Ttext 0x100000"告诉链接器用户想要的"text"(代码段)地址在0x100000的内存标记处。
引导代码必须遵循如下的规则:
(1)BIOS会把引导程序装载到固定地址07C00h。因此,引导程序的段地址和飘移地址是固定的。
(2)引导代码必须编译成为plain binary file类型。
(3)plain binary file的尺寸必须是512B(512B是一个扇区的大小)
(4)文件必须以0xAA55h标志结果。
"times 510-($-$$)db 0"代码是一句只有nasm能够理解的代码。
“二次转载”是指:由BIOS从磁盘上装入引导扇区(0头0磁道1扇区)到内存地址07C0:0000,>并跳转到该地址执行;再由从07C0:0000地址开始的引导代码把存放在其他扇区中的程序转
入内存,并跳转到该程序中执行。
第六章
连续分配存储管理方式分为:单一连续分配,固定分区分配和动态分区分配。
虚存提供的功能:扩大地址空间,内存保护,内存映射,公平分配内存,虚存共享。
Inter-Process Communication,IPC 进程间的通信机制
虚拟存储器基本特征是:虚拟扩充,部分装入,离散分配,多次对换。
虚存中的置换算法:先进先出算法(FIFO),最佳置换算法(OPT),最近最少使用置换算法(LRU),最近未使用置换算法(NUR)。
非连续存储管理机制中主要包括:页式管理,段式管理和段页式管理。
任何一个物理地址都可以映射为:段地址+段内偏移。
操作系统必须知道系统物理内存的容量,才能够有效地使用和管理这些物理内存。在CPU处于实模式时,CPU可以访问的物理内存最大只能达到1MB+64KB(在A20 Gate被打开的情况下,否则最大只能访问1MB)。因此,在实模式下通常无法直接通过内存访问来获取内存容量。
可以通过BIOS的15h中断从计算机的CMOS中读取计算机的物理内存的大小,并把这个数据保留在操作系统核心中。BIOS INT15h中断提供了三个子中断来获得系统的物理内存大小,分别是88和E801h和E820h。
malloc()函数作用是从一大块内存中分配出所需要的空间。在分配时需要注意,要留出一部分malloc()不能分配的内核空间(为了便于存储保护,内核地址空间和用户地址空间是相互隔开的)以备内核使用。
内核malloc()函数已经具备了存储分配功能,但是还不具备虚拟存储功能,通过缺页处理来实现虚拟存储管理。
可执行文件的头信息格式有许多标准,例如:coff,elf和pe格式等。
在malloc()函数实现中使用了首次适配方法来进行存储分配。当使用free()函数释放内存时,会对相邻的存储空间进行合并。
page.s是一段汇编语言程序,提供了Linux0.01中缺页异常处理功能。page.s仅仅提供了和
硬件存储管理器的接口功能,大部分功能代码在memory.c文件中。
Linux0.01分析笔记(1)
我是对我读的那本书作的笔记,以后会添加自己的分析。
第一章
我过去也看过这本书,但是很多没有理解,有很多忘记了,故今天又重新看,而且把笔记写下!
GUN的主要精神是软件源代码应该自由流通!
GUN是GUN‘s Not Unix的缩写,是由自由软件基金会(Free Software Foundation,FSF)的董事长Richard M.Stallman(RMS)于1984年发起的。GUN制定了GPL(The General Public License,GUN通用公共准许证)
自由软件的自由包括:使用的自由(可以不受任何限制来使用软件),研究的自由(可以研究软件运作方式并使其合适个人需要),散布的自由(可以自由地复制此软件散布给他人),改良的自由(可以自行改良软件并散布改良后的版本以使全体用户受益)。
1991年8月,一位来自芬兰赫尔辛基大学的年轻人Linus Benedict Torvalds,对外发布了一套全新的操作系统Linux
黑客是指计算机技术上的行家或热衷于解决问题,克服限制的人。黑客乐意解决和发现新问题,并乐意相互帮助。他们相信计算机中充满各种各样有趣的问题,并且这些问题一旦被自己解决,他们愿意让解决方法被所有人共享。他们感觉发现问题,解决问题,共享答案的过程是有趣的,并着迷地去做。他们将解决问题视为乐趣,相信技术应该是自由的,应该被所有人共享。黑客是对拥有高科技和崇高理念的孜孜不倦的计算机爱好者,工作者的尊称。
第二章
现代操作系统都具有:并发,共享,虚拟,异步的特性,同时还具有:存储器管理,处理器管理,设备管理,文件管理,用户接口。
Run_Time Library,RTL,运行时函数库。
Bochs是一个x86 PC的模拟器。
Wine/DOSEmu是先把把原有系统的应用软件通过他们把原操作系统API,那些软件,再经过模拟操作系统的API,成为模拟操作系统应用软件。
软件的美在于它的功能,在于它的内部结构。对用户来说,通过直观,简单的界面呈现出具有恰当特性的程序就是美。对于软件设计者来说,被简单,直观地分割,并具有最小内部耦合的软件结构就是美!

第三章
DJGPP是DOS下的自由源代码的保护模式C语言编译器。
还有RHIDE是DJGPP的图形界面。
学make和ld还有nasm
Dos下一个汇编程序
;---------------------------------------------------------------------
.model small
.stack 100h
.data
Welcome db 13,10,'Hell,world!',13,10,'$'
.code
start:
mov ax,@data
mov ds,ax
lea dx,welcome
mov ah,9
int 21h
; end the program
mov ah,4ch
int 21h
end start
;--------------------------------------------------------------------
用masm hello.asm
link hello.obj就可以了。
Linux下和它下面一样功能的程序
section .text
global main
main:
mov eax,4 ;4号调用
mov ebx,1 ;ebx送1表示stdout
mov ecx,msg ;字符串的首地址送入ecx
mov edx,14 ;字符串的长度送入edx
int 80h ;输出字串
mov eax,1 ;1号调用
int 80h ;结束
msg:
db"Hello World!",0ah,0dh
用下面的命令就可以了!
[gaowei@localhost nasm]$ nasm -f elf hello.asm
[gaowei@localhost nasm]$ ls
hello.asm hello.o
[gaowei@localhost nasm]$ gcc -o hello hello.o
[gaowei@localhost nasm]$ ls
hello hello.asm hello.o
[gaowei@localhost nasm]$ ./hello
Hello World!
为了辨别CPU的类型,需要使用CPUID汇编指令(CPUID是一个特殊的汇编指令,其机器0FHA2H。如果编译器不支持CPUID指令,可以直接输入CPUID的机器码)。该指令可以被以下CPU识别:Intel 486以上的CPU。Cyrix M1以上的CPU。AMD Am468以上的CPU。
1.判断CPU的厂商
通过CPUID汇编指令,返回CPU OEM字符串,从而判断CPU厂商,规则如下:
先让EAX=0,再调用CPUID。
Inel的CPU将返回:
EBX:756E6547H 'Genu'
EDX:49656E69H 'ineI'
ECX:6C65746EH 'ntel'
EBX,EDX,ECX连接起来是"GenuineIntel",含义为真正的Intel。
Cyrix的CPU将返回:
ENX:43797269H
EDX:78496E73H
ECX:74656164H
"CyrixInstead","Cyrix来代替"
AMD的CPU将返回:
EBX:41757468H
EDX:656E7469H
ECX:63414D44H
"AuthenticAMD",可信的AMD。
2.判断CPU类型
先让EAX=1,再调用CPUID,EAX的8~11位就表明CPU类型。例如:
3-368
4-468
5-Pentium
6-Pentium Pro Pentium II
2-Dual Processors
EDX的第0位:代表有无FPU。
EDX的第23位:CPU是否支持IA MMX,这点很重要,如果想用57条新增的指令,要先检查这一
位,否则Windows就会出现“该程序执行了非法指令,将被关闭”。
3.专门检测是否P6架构
先让EAX=1,再调用CPUID,如果AL=1,就是Pentium Pro或Pentium II。
4.专门检测AMD的CPU信息
先让EAX=80000001H,在调用CPUID,如果EAX=51H,就是AMD K5;如果EAX=66H,则是K6。
EDX第0位:是否有FPU。
EDX第23位:CPU是否支持MMX。
在PC中,彩色显示器的字符屏幕显存起始地址在0xB8000000处,单色显示器的视频缓冲区起
始地址在0xB8000000处。
第一章
我过去也看过这本书,但是很多没有理解,有很多忘记了,故今天又重新看,而且把笔记写下!
GUN的主要精神是软件源代码应该自由流通!
GUN是GUN‘s Not Unix的缩写,是由自由软件基金会(Free Software Foundation,FSF)的董事长Richard M.Stallman(RMS)于1984年发起的。GUN制定了GPL(The General Public License,GUN通用公共准许证)
自由软件的自由包括:使用的自由(可以不受任何限制来使用软件),研究的自由(可以研究软件运作方式并使其合适个人需要),散布的自由(可以自由地复制此软件散布给他人),改良的自由(可以自行改良软件并散布改良后的版本以使全体用户受益)。
1991年8月,一位来自芬兰赫尔辛基大学的年轻人Linus Benedict Torvalds,对外发布了一套全新的操作系统Linux
黑客是指计算机技术上的行家或热衷于解决问题,克服限制的人。黑客乐意解决和发现新问题,并乐意相互帮助。他们相信计算机中充满各种各样有趣的问题,并且这些问题一旦被自己解决,他们愿意让解决方法被所有人共享。他们感觉发现问题,解决问题,共享答案的过程是有趣的,并着迷地去做。他们将解决问题视为乐趣,相信技术应该是自由的,应该被所有人共享。黑客是对拥有高科技和崇高理念的孜孜不倦的计算机爱好者,工作者的尊称。
第二章
现代操作系统都具有:并发,共享,虚拟,异步的特性,同时还具有:存储器管理,处理器管理,设备管理,文件管理,用户接口。
Run_Time Library,RTL,运行时函数库。
Bochs是一个x86 PC的模拟器。
Wine/DOSEmu是先把把原有系统的应用软件通过他们把原操作系统API,那些软件,再经过模拟操作系统的API,成为模拟操作系统应用软件。
软件的美在于它的功能,在于它的内部结构。对用户来说,通过直观,简单的界面呈现出具有恰当特性的程序就是美。对于软件设计者来说,被简单,直观地分割,并具有最小内部耦合的软件结构就是美!
第三章
DJGPP是DOS下的自由源代码的保护模式C语言编译器。
还有RHIDE是DJGPP的图形界面。
学make和ld还有nasm
Dos下一个汇编程序
;---------------------------------------------------------------------
.model small
.stack 100h
.data
Welcome db 13,10,'Hell,world!',13,10,'$'
.code
start:
mov ax,@data
mov ds,ax
lea dx,welcome
mov ah,9
int 21h
; end the program
mov ah,4ch
int 21h
end start
;--------------------------------------------------------------------
用masm hello.asm
link hello.obj就可以了。
Linux下和它下面一样功能的程序
section .text
global main
main:
mov eax,4 ;4号调用
mov ebx,1 ;ebx送1表示stdout
mov ecx,msg ;字符串的首地址送入ecx
mov edx,14 ;字符串的长度送入edx
int 80h ;输出字串
mov eax,1 ;1号调用
int 80h ;结束
msg:
db"Hello World!",0ah,0dh
用下面的命令就可以了!
[gaowei@localhost nasm]$ nasm -f elf hello.asm
[gaowei@localhost nasm]$ ls
hello.asm hello.o
[gaowei@localhost nasm]$ gcc -o hello hello.o
[gaowei@localhost nasm]$ ls
hello hello.asm hello.o
[gaowei@localhost nasm]$ ./hello
Hello World!
为了辨别CPU的类型,需要使用CPUID汇编指令(CPUID是一个特殊的汇编指令,其机器0FHA2H。如果编译器不支持CPUID指令,可以直接输入CPUID的机器码)。该指令可以被以下CPU识别:Intel 486以上的CPU。Cyrix M1以上的CPU。AMD Am468以上的CPU。
1.判断CPU的厂商
通过CPUID汇编指令,返回CPU OEM字符串,从而判断CPU厂商,规则如下:
先让EAX=0,再调用CPUID。
Inel的CPU将返回:
EBX:756E6547H 'Genu'
EDX:49656E69H 'ineI'
ECX:6C65746EH 'ntel'
EBX,EDX,ECX连接起来是"GenuineIntel",含义为真正的Intel。
Cyrix的CPU将返回:
ENX:43797269H
EDX:78496E73H
ECX:74656164H
"CyrixInstead","Cyrix来代替"
AMD的CPU将返回:
EBX:41757468H
EDX:656E7469H
ECX:63414D44H
"AuthenticAMD",可信的AMD。
2.判断CPU类型
先让EAX=1,再调用CPUID,EAX的8~11位就表明CPU类型。例如:
3-368
4-468
5-Pentium
6-Pentium Pro Pentium II
2-Dual Processors
EDX的第0位:代表有无FPU。
EDX的第23位:CPU是否支持IA MMX,这点很重要,如果想用57条新增的指令,要先检查这一
位,否则Windows就会出现“该程序执行了非法指令,将被关闭”。
3.专门检测是否P6架构
先让EAX=1,再调用CPUID,如果AL=1,就是Pentium Pro或Pentium II。
4.专门检测AMD的CPU信息
先让EAX=80000001H,在调用CPUID,如果EAX=51H,就是AMD K5;如果EAX=66H,则是K6。
EDX第0位:是否有FPU。
EDX第23位:CPU是否支持MMX。
在PC中,彩色显示器的字符屏幕显存起始地址在0xB8000000处,单色显示器的视频缓冲区起
始地址在0xB8000000处。
2008年7月25日星期五
学习kernel网络体系结构(3)
尽管,现在开始学习内核,我感觉自己的基础还是很不好的,没有进行一些系统的学习,而且基础部分也是七零八散的。没有把过去学到运用起来。所以现在还是以学习基础为主,不要好高骛远了!
这次主要写的是关于net的也就是说很多函数在dirvers/net/下还有在net/core/下
除了字符设备和块设备,网络设备是Linux内核中的第三类适配器。他们在设备文件系统/dev/中没有表示,这就意味着不能通过简单的读写来操作来访问他们。网络适配器不能被邦定到单个进程,他们运行在内核中并且独立于各用户进程[RuCo01]。
顺便添加几本好的书哦![RuCo01] Alessandro Rubini and Jonathan Corbet. Linux Device Drivers. Sebastopol, CA: O'Reilly & Associates, 2d ed., 2001. Online version at http://www.oreilly.com/catalog/linuxdrive2/index.html. (Visited on January 10, 2004.)
[BBDK+01] Michael Beck, Harald Böhme, Mirko Dziadzka, Ulrich Kunitz et al. Linux Kernel Programming. Boston: Addison-Wesley, 3d ed., 2002.
[BoCe00] Daniel P. Bovet and Marco Cesati. Understanding the Linux Kernel. Beijing and Cambridge, MA: O'Reilly, 2000
有空要看看哦!
我在include/linux/netdevice.h 这个目录下看net_device结构,此结构是内核中的所有网络设备的基础数据结构。它不仅包含网络适配器的硬件信息(中断,端口,驱动程序函数等等),还包括面向更高层网络协议的网络设备配置数据(IP地址,子网掩码等)。其中包含了大量的函数指针,更高层协议通过它们的全局名称来调用它们,随后驱动程序的硬件相关方法被各网络设备调用。
我认为还是先大体浏览一边而后在进行,认识细致的读和理会,它们中的每一个数据结构的意思都要明白和了解。
网络设备的注册与注销,哦,drivers/net/net_init.c这个文件竟然在2.6.26中没有找到。
MAC协议(FDDI,HIPPI),难道是媒体介质访问控制(Medium Access Control,MAC)协议,应该看看了。
网络设备的开启和关闭,相关代码在net/core/dev.c下,同样,创建和寻找网络设备的代码,还有为状态改变而设计的通知链,在网络设备上进行传输,网络设备中的初始化网络适配器。
分配系统资源的帮助者函数
request_region() , release_region() , check_region()在kernel/resource.c下
request_irq() , free_irq()在kernel/irq.c下
request_dma() , free_dma()在kernel/dma.c下
但是,打开和关闭网络适配器,传输数据,包传输过程的问题,运行配置,适配器相关的ioctl()命令,网络设备的统计信息,适配器层次的多播支持,的主要函数在drivers/net/isa_skeleton.c下
这次主要写的是关于net的也就是说很多函数在dirvers/net/下还有在net/core/下
除了字符设备和块设备,网络设备是Linux内核中的第三类适配器。他们在设备文件系统/dev/中没有表示,这就意味着不能通过简单的读写来操作来访问他们。网络适配器不能被邦定到单个进程,他们运行在内核中并且独立于各用户进程[RuCo01]。
顺便添加几本好的书哦![RuCo01] Alessandro Rubini and Jonathan Corbet. Linux Device Drivers. Sebastopol, CA: O'Reilly & Associates, 2d ed., 2001. Online version at http://www.oreilly.com/catalog/linuxdrive2/index.html. (Visited on January 10, 2004.)
[BBDK+01] Michael Beck, Harald Böhme, Mirko Dziadzka, Ulrich Kunitz et al. Linux Kernel Programming. Boston: Addison-Wesley, 3d ed., 2002.
[BoCe00] Daniel P. Bovet and Marco Cesati. Understanding the Linux Kernel. Beijing and Cambridge, MA: O'Reilly, 2000
有空要看看哦!
我在include/linux/netdevice.h 这个目录下看net_device结构,此结构是内核中的所有网络设备的基础数据结构。它不仅包含网络适配器的硬件信息(中断,端口,驱动程序函数等等),还包括面向更高层网络协议的网络设备配置数据(IP地址,子网掩码等)。其中包含了大量的函数指针,更高层协议通过它们的全局名称来调用它们,随后驱动程序的硬件相关方法被各网络设备调用。
我认为还是先大体浏览一边而后在进行,认识细致的读和理会,它们中的每一个数据结构的意思都要明白和了解。
现在,大体把其中的主要的部分列举,它们中有网络设备的通用字段;硬件相关字段;网络层上的字段。Linux内核中的所有网络设备都被一个线性表连接起来。
网络设备的注册与注销,哦,drivers/net/net_init.c这个文件竟然在2.6.26中没有找到。
MAC协议(FDDI,HIPPI),难道是媒体介质访问控制(Medium Access Control,MAC)协议,应该看看了。
网络设备的开启和关闭,相关代码在net/core/dev.c下,同样,创建和寻找网络设备的代码,还有为状态改变而设计的通知链,在网络设备上进行传输,网络设备中的初始化网络适配器。
分配系统资源的帮助者函数
request_region() , release_region() , check_region()在kernel/resource.c下
request_irq() , free_irq()在kernel/irq.c下
request_dma() , free_dma()在kernel/dma.c下
但是,打开和关闭网络适配器,传输数据,包传输过程的问题,运行配置,适配器相关的ioctl()命令,网络设备的统计信息,适配器层次的多播支持,的主要函数在drivers/net/isa_skeleton.c下
2008年7月24日星期四
学习kernel网络体系结构(2)
尽管,看过了,但是还是不能深刻的记忆和理解,我感觉只有一步一步的实践了之后,你才会正真的理解其中的奥秘。我再次,重新看。Linux网络实现的灵活性和高效性的一个主要因素是缓冲体系结构,它管理了网络报文——所谓的套接字缓存(socket buffer) ,skb。
一个套接字缓存由俩部分组成:报文数据和管理数据。
看了这个图,我们将分析他们中的一些具体的参数。
我们要明白其中的指针指向的位置。

其实,内容很多,我们还是简单的了解一些,而后多读源代码,现在把一些代码粘贴,看看,那些和图里面是如何对应的。
自 2.6.22 版的核心開始,sk_buff 結構變更
struct sk_buff_head {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
__u32 qlen;
spinlock_t lock;
};
但是,下面的和以前一些版本有些变化。
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
struct sock *sk;
ktime_t tstamp;
struct net_device *dev;
union {
struct dst_entry *dst;
struct rtable *rtable;
};
struct sec_path *sp;
char cb[48];
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
__be16 protocol;
void (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
int iif;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
__u16 queue_mapping;
#endif
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
/* 14 bit hole */
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
sk_buff_data_t transport_header;
sk_buff_data_t network_header;
sk_buff_data_t mac_header;
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
atomic_t users;
};
套接字缓存是用来定位和管理一个报文在内核中被处理的整个周期的。
以上的代码都是在include/linux/skbuff.h 里面,有空在好好的看看其他的。
struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。
这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。 L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同层之间拷贝数据的效率更高。由于在缓冲区的头部 添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve来完成这个功能。协议栈中的每一层在往下一层传 递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。
skb_reserve同样被设备驱动使用来对齐接收到包的包头。如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。例如,L2的头部只有在 网络驱动处理L2的协议时有用,L3是不会关心它的信息的。但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做, 可以节省CPU时间。
http://hi.baidu.com/zkheartboy/blog/item/2dd5207a28eb2fee2f73b355.html不错哦!
Linux内核为我们提供了很多函数来操作套接字缓存,如:1.创建,释放和复制套接字缓存 2.操作sk_buff结构里的参数和指针 3.管理套接字缓存队列
而这些函数一般在net/core/skbuff.c 和include/linux/skbuff.h函数中。
一个套接字缓存由俩部分组成:报文数据和管理数据。
看了这个图,我们将分析他们中的一些具体的参数。
我们要明白其中的指针指向的位置。

其实,内容很多,我们还是简单的了解一些,而后多读源代码,现在把一些代码粘贴,看看,那些和图里面是如何对应的。
自 2.6.22 版的核心開始,sk_buff 結構變更
struct sk_buff_head {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
__u32 qlen;
spinlock_t lock;
};
但是,下面的和以前一些版本有些变化。
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
struct sock *sk;
ktime_t tstamp;
struct net_device *dev;
union {
struct dst_entry *dst;
struct rtable *rtable;
};
struct sec_path *sp;
char cb[48];
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
__be16 protocol;
void (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
int iif;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
__u16 queue_mapping;
#endif
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
/* 14 bit hole */
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
sk_buff_data_t transport_header;
sk_buff_data_t network_header;
sk_buff_data_t mac_header;
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
atomic_t users;
};
套接字缓存是用来定位和管理一个报文在内核中被处理的整个周期的。
以上的代码都是在include/linux/skbuff.h 里面,有空在好好的看看其他的。
struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。
这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。 L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同层之间拷贝数据的效率更高。由于在缓冲区的头部 添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve来完成这个功能。协议栈中的每一层在往下一层传 递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。
skb_reserve同样被设备驱动使用来对齐接收到包的包头。如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。例如,L2的头部只有在 网络驱动处理L2的协议时有用,L3是不会关心它的信息的。但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做, 可以节省CPU时间。
http://hi.baidu.com/zkheartboy/blog/item/2dd5207a28eb2fee2f73b355.html不错哦!
Linux内核为我们提供了很多函数来操作套接字缓存,如:1.创建,释放和复制套接字缓存 2.操作sk_buff结构里的参数和指针 3.管理套接字缓存队列
而这些函数一般在net/core/skbuff.c 和include/linux/skbuff.h函数中。
2008年7月22日星期二
学习kernel网络体系结构(1)
最近在看The Linux Network Architecture其实这本书的名字很长的我就简写了。
学习内核,文件系统很重要,因为Linux内核中几乎所有项目都是通过文件系统接口加以处理的。
内核分为:整体式内核,微内核。
Linux是采用整体式内核的,但是2.0开始把某些功能变成了模块;可载人内核也可移出内核。
模块化的好处:接口的定义会始终如一。
在linux-2.6/net的目录下有很多关于网络方面的源代码,在linux-2.6/Documentation/networking目录下也很多的相关的文档是不是也要读一读呢?应该的,至少在暑假期间把这些都看一遍的。
[RuCo01] [BBDK+01] [BoCe00]这些资料应该看看。
学习网络,我们应该知道ISO/OSI参考模型,虽然它们现在用的不是很广泛但是对我们理解网络还是很有忙助的。

这边是模型图,但是现在应用比较多的还是TCP/IP,在看看它们的对比。好好观测它们之间的不同,和相同的地方,还有就是TCP/IP的优点。每一层之间的关系,和下一层,还有上一层的关系。

今天,感觉理解了服务和协议。我就简单的介绍一下。
一些预定义的规则控制不同计算机上相同层的俩个实例之间的通信,这些规则被称为协议。
一个层(第N层)向更高的层(第N+1层)提供一个功能集合,这个功能集合被称为服务。
我们也就是要理解同层与同层之间的关系,不同层与不同层之间的关系。
* 应用层 (Application):应用层是个很广泛的概念,有一些基本相同的系统级 TCP/IP 应用以及应用协议,也有许多的企业商业应用和互联网应用。
* 传输层 (Transport):传输层包括 UDP 和 TCP,UDP 几乎不对报文进行检查,而 TCP 提供传输保证。
* 网络层 (Network):网络层协议由一系列协议组成,包括 ICMP、IGMP、RIP、OSPF、IP(v4,v6) 等。
* 链路层 (Link):又称为物理数据网络接口层,负责报文传输。
好!明天继续!
学习内核,文件系统很重要,因为Linux内核中几乎所有项目都是通过文件系统接口加以处理的。
内核分为:整体式内核,微内核。
Linux是采用整体式内核的,但是2.0开始把某些功能变成了模块;可载人内核也可移出内核。
模块化的好处:接口的定义会始终如一。
在linux-2.6/net的目录下有很多关于网络方面的源代码,在linux-2.6/Documentation/networking目录下也很多的相关的文档是不是也要读一读呢?应该的,至少在暑假期间把这些都看一遍的。
[RuCo01] [BBDK+01] [BoCe00]这些资料应该看看。
学习网络,我们应该知道ISO/OSI参考模型,虽然它们现在用的不是很广泛但是对我们理解网络还是很有忙助的。

这边是模型图,但是现在应用比较多的还是TCP/IP,在看看它们的对比。好好观测它们之间的不同,和相同的地方,还有就是TCP/IP的优点。每一层之间的关系,和下一层,还有上一层的关系。

今天,感觉理解了服务和协议。我就简单的介绍一下。
一些预定义的规则控制不同计算机上相同层的俩个实例之间的通信,这些规则被称为协议。
一个层(第N层)向更高的层(第N+1层)提供一个功能集合,这个功能集合被称为服务。
我们也就是要理解同层与同层之间的关系,不同层与不同层之间的关系。
* 应用层 (Application):应用层是个很广泛的概念,有一些基本相同的系统级 TCP/IP 应用以及应用协议,也有许多的企业商业应用和互联网应用。
* 传输层 (Transport):传输层包括 UDP 和 TCP,UDP 几乎不对报文进行检查,而 TCP 提供传输保证。
* 网络层 (Network):网络层协议由一系列协议组成,包括 ICMP、IGMP、RIP、OSPF、IP(v4,v6) 等。
* 链路层 (Link):又称为物理数据网络接口层,负责报文传输。
好!明天继续!
订阅:
博文 (Atom)