经验

目 录

  1. 经验
    1. 新手入门
    2. 一个简单程序的分析----深至内核
      1. open
      2. read
    3. lisolog文章检索
      1. index
      2. list1
      3. list2
      4. list3
      5. list4
      6. list5
    4. AKA推荐书籍
    5. linux论坛推荐资源
    6. 数据结构
    7. 重新编译
    8. 重建内核选项
    9. 调试技术
    10. ptrace进程跟踪
    11. 宏#与##
    12. lxr和glimpse
    13. 内核阅读介绍
    14. 内核重编译常见故障


经验

[目录]


新手入门

入门

    针对好多Linux 爱好者对内核很有兴趣却无从下口,本文旨在介绍一种解读linux内核源码的入门方法,而不是解说linux复杂的内核机制;

一.核心源程序的文件组织:

    1.Linux核心源程序通常都安装在/usr/src/linux下,而且它有一个非常简单的编号约定:任何偶数的核心(例如2.0.30)都是一个稳定地发行的核心,而任何奇数的核心(例如2.1.42)都是一个开发中的核心。

    本文基于稳定的2.2.5源代码,第二部分的实现平台为 Redhat Linux 6.0。

    2.核心源程序的文件按树形结构进行组织,在源程序树的最上层你会看到这样一些目录:

    ●Arch :arch子目录包括了所有和体系结构相关的核心代码。它的每一个子目录都代表一种支持的体系结构,例如i386就是关于intel cpu及与之相兼容体系结构的子目录。PC机一般都基于此目录;

    ●Include: include子目录包括编译核心所需要的大部分头文件。与平台无关的头文件在 include/linux子目录下,与 intel cpu相关的头文件在include/asm-i386子目录下,而include/scsi目录则是有关scsi设备的头文件目录;

    ●Init: 这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点。

    ●Mm :这个目录包括所有独立于 cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等;而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/Fault.c

    ●Kernel:主要的核心代码,此目录下的文件实现了大多数linux系统的内核函数,其中最重要的文件当属sched.c;同样,和体系结构相关的代码在arch/*/kernel中;

    ●Drivers: 放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录:如,/block 下为块设备驱动程序,比如ide(ide.c)。如果你希望查看所有可能包含文件系统的设备是如何初始化的,你可以看drivers/block/genhd.c中的device_setup()。它不仅初始化硬盘,也初始化网络,因为安装nfs文件系统的时候需要网络其他: 如, Lib放置核心的库代码; Net,核心与网络相关的代码; Ipc,这个目录包含核心的进程间通讯的代码; Fs ,所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件系统,例如fat和ext2;

    ●Scripts, 此目录包含用于配置核心的脚本文件等。

    一般,在每个目录下,都有一个 .depend 文件和一个 Makefile 文件,这两个文件都是编译时使用的辅助文件,仔细阅读这两个文件对弄清各个文件这间的联系和依托关系很有帮助;而且,在有的目录下还有Readme 文件,它是对该目录下的文件的一些说明,同样有利于我们对内核源码的理解;

二.解读实战:为你的内核增加一个系统调用

    虽然,Linux 的内核源码用树形结构组织得非常合理、科学,把功能相关联的文件都放在同一个子目录下,这样使得程序更具可读性。然而,Linux 的内核源码实在是太大而且非常复杂,即便采用了很合理的文件组织方法,在不同目录下的文件之间还是有很多的关联,分析核心的一部分代码通常会要查看其它的几个相关的文件,而且可能这些文件还不在同一个子目录下。

    体系的庞大复杂和文件之间关联的错综复杂,可能就是很多人对其望而生畏的主要原因。当然,这种令人生畏的劳动所带来的回报也是非常令人着迷的:你不仅可以从中学到很多的计算机的底层的知识(如下面将讲到的系统的引导),体会到整个操作系统体系结构的精妙和在解决某个具体细节问题时,算法的巧妙;而且更重要的是:在源码的分析过程中,你就会被一点一点地、潜移默化地专业化;甚至,只要分析十分之一的代码后,你就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。

    为了使读者能更好的体会到这一特点,下面举了一个具体的内核分析实例,希望能通过这个实例,使读者对 Linux的内核的组织有些具体的认识,从中读者也可以学到一些对内核的分析方法。

以下即为分析实例:

【一】操作平台:

硬件:cpu intel Pentium II ;

软件:Redhat Linux 6.0; 内核版本2.2.5【二】相关内核源代码分析:

    1.系统的引导和初始化:Linux 系统的引导有好几种方式:常见的有 Lilo, Loadin引导和Linux的自举引导

    (bootsect-loader),而后者所对应源程序为arch/i386/boot/bootsect.S,它为实模式的汇编程序,限于篇幅在此不做分析;无论是哪种引导方式,最后都要跳转到 arch/i386/Kernel/setup.S, setup.S主要是进行时模式下的初始化,为系统进入保护模式做准备;此后,系统执行 arch/i386/kernel/head.S (对经压缩后存放的内核要先执行 arch/i386/boot/compressed/head.S); head.S 中定义的一段汇编程序setup_idt ,它负责建立一张256项的 idt 表(Interrupt Descriptor Table),此表保存着所有自陷和中断的入口地址;其中包括系统调用总控程序 system_call 的入口地址;当然,除此之外,head.S还要做一些其他的初始化工作;

    2.系统初始化后运行的第一个内核程序asmlinkage void __init start_kernel(void) 定义在/usr/src/linux/init/main.c中,它通过调用usr/src/linux/arch/i386/kernel/traps.c 中的一个函数

    void __init trap_init(void) 把各自陷和中断服务程序的入口地址设置到 idt 表中,其中系统调用总控程序system_cal就是中断服务程序之一;void __init trap_init(void) 函数则通过调用一个宏

    set_system_gate(SYSCALL_VECTOR,&system_call); 把系统调用总控程序的入口挂在中断0x80上;

    其中SYSCALL_VECTOR是定义在 /usr/src/linux/arch/i386/kernel/irq.h中的一个常量0x80; 而 system_call 即为中断总控程序的入口地址;中断总控程序用汇编语言定义在/usr/src/linux/arch/i386/kernel/entry.S中;

    3.中断总控程序主要负责保存处理机执行系统调用前的状态,检验当前调用是否合法, 并根据系统调用向量,使处理机跳转到保存在 sys_call_table 表中的相应系统服务例程的入口; 从系统服务例程返回后恢复处理机状态退回用户程序;

    而系统调用向量则定义在/usr/src/linux/include/asm-386/unistd.h 中;sys_call_table 表定义在/usr/src/linux/arch/i386/kernel/entry.S 中; 同时在 /usr/src/linux/include/asm-386/unistd.h 中也定义了系统调用的用户编程接口;

    4.由此可见 , linux 的系统调用也象 dos 系统的 int 21h 中断服务, 它把0x80 中断作为总的入口, 然后转到保存在 sys_call_table 表中的各种中断服务例程的入口地址 , 形成各种不同的中断服务;

    由以上源代码分析可知, 要增加一个系统调用就必须在 sys_call_table 表中增加一项 , 并在其中保存好自己的系统服务例程的入口地址,然后重新编译内核,当然,系统服务例程是必不可少的。

    由此可知在此版linux内核源程序中,与系统调用相关的源程序文件就包括以下这些:

1.arch/i386/boot/bootsect.S
2.arch/i386/Kernel/setup.S
3.arch/i386/boot/compressed/head.S
4.arch/i386/kernel/head.S
5.init/main.c
6.arch/i386/kernel/traps.c
7.arch/i386/kernel/entry.S
8.arch/i386/kernel/irq.h
9.include/asm-386/unistd.h

    当然,这只是涉及到的几个主要文件。而事实上,增加系统调用真正要修改文件只有include/asm-386/unistd.h和arch/i386/kernel/entry.S两个;

【三】 对内核源码的修改:

    1.在kernel/sys.c中增加系统服务例程如下:

asmlinkage int sys_addtotal(int numdata)
{
int i=0,enddata=0;
while(i<=numdata)
enddata+=i++;
return enddata;
}

    该函数有一个 int 型入口参数 numdata , 并返回从 0 到 numdata 的累加值; 当然也可以把系统服务例程放在一个自己定义的文件或其他文件中,只是要在相应文件中作必要的说明;

    2.把 asmlinkage int sys_addtotal( int) 的入口地址加到sys_call_table表中:

    arch/i386/kernel/entry.S 中的最后几行源代码修改前为:

... ...

.long SYMBOL_NAME(sys_sendfile)
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
.long SYMBOL_NAME(sys_vfork) /* 190 */
.rept NR_syscalls-190
.long SYMBOL_NAME(sys_ni_syscall)
.endr

    修改后为:

... ...
.long SYMBOL_NAME(sys_sendfile)
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
.long SYMBOL_NAME(sys_vfork) /* 190 */
/* add by I */
.long SYMBOL_NAME(sys_addtotal)
.rept NR_syscalls-191
.long SYMBOL_NAME(sys_ni_syscall)
.endr

    3. 把增加的 sys_call_table 表项所对应的向量,在include/asm-386/unistd.h 中进行必要申明,以供用户进程和其他系统进程查询或调用:

    增加后的部分 /usr/src/linux/include/asm-386/unistd.h 文件如下:

... ...

#define __NR_sendfile 187
#define __NR_getpmsg 188
#define __NR_putpmsg 189
#define __NR_vfork 190

/* add by I */

#define __NR_addtotal 191

4.测试程序(test.c)如下:

#include
#include
_syscall1(int,addtotal,int, num)

main()
{
int i,j;
  do
printf("Please input a number\n");
while(scanf("%d",&i)==EOF);
if((j=addtotal(i))==-1)
printf("Error occurred in syscall-addtotal();\n");
printf("Total from 0 to %d is %d \n",i,j);
}

    对修改后的新的内核进行编译,并引导它作为新的操作系统,运行几个程序后可以发现一切正常;在新的系统下对测试程序进行编译(*注:由于原内核并未提供此系统调用,所以只有在编译后的新内核下,此测试程序才能可能被编译通过),运行情况如下:

$gcc -o test test.c
$./test
Please input a number

36
Total from 0 to 36 is 666

    可见,修改成功;

    而且,对相关源码的进一步分析可知,在此版本的内核中,从/usr/src/linux/arch/i386/kernel/entry.S

    文件中对 sys_call_table 表的设置可以看出,有好几个系统调用的服务例程都是定义在/usr/src/linux/kernel/sys.c 中的同一个函数:

asmlinkage int sys_ni_syscall(void)
{
return -ENOSYS;
}

    例如第188项和第189项就是如此:

... ...

.long SYMBOL_NAME(sys_sendfile)
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
.long SYMBOL_NAME(sys_vfork) /* 190 */

... ...

    而这两项在文件 /usr/src/linux/include/asm-386/unistd.h 中却申明如下:

... ...
#define __NR_sendfile 187
#define __NR_getpmsg 188 /* some people actually want streams */
#define __NR_putpmsg 189 /* some people actually want streams */
#define __NR_vfork 190

    由此可见,在此版本的内核源代码中,由于asmlinkage int sys_ni_syscall(void) 函数并不进行任何操作,所以包括 getpmsg, putpmsg 在内的好几个系统调用都是不进行任何操作的,即有待扩充的空调用; 但它们却仍然占用着sys_call_table表项,估计这是设计者们为了方便扩充系统调用而安排的; 所以只需增加相应服务例程(如增加服务例程getmsg或putpmsg),就可以达到增加系统调用的作用。

[目录]


一个简单程序的分析----深至内核

                A small trail through the Linux kernel

Andries Brouwer, [email protected] 2001-01-01

A program
---------------------------------------------------------------------------------------------------
#include <unistd.h>
#include <fcntl.h>
int main(){
        int fd;
        char buf[512];

        fd = open("/dev/hda", O_RDONLY);
        if (fd >= 0)
                read(fd, buf, sizeof(buf));
        return 0;
}
---------------------------------------------------------------------------------------------------

This little program opens the block special device referring to the first IDE disk, and if the open succeeded reads the first sector. What happens in the kernel? Let us read 2.4.0 source.

[目录]


open

The open system call is found in fs/open.c:

---------------------------------------------------------------------------------------------------
int sys_open(const char *filename, int flags, int mode) {
        char *tmp = getname(filename);
        int fd = get_unused_fd();
        struct file *f = filp_open(tmp, flags, mode);
        fd_install(fd, f);
        putname(tmp);
        return fd;
}
---------------------------------------------------------------------------------------------------

The routine getname() is found in fs/namei.c. It copies the file name from user space to kernel space:

---------------------------------------------------------------------------------------------------
#define __getname()     kmem_cache_alloc(names_cachep, SLAB_KERNEL)
#define putname(name)   kmem_cache_free(names_cachep, (void *)(name))

char *getname(const char *filename) {
        char *tmp = __getname();        /* allocate some memory */
        strncpy_from_user(tmp, filename, PATH_MAX + 1);
        return tmp;
}
---------------------------------------------------------------------------------------------------

The routine get_unused_fd() is found in fs/open.c again. It returns the first unused filedescriptor:

---------------------------------------------------------------------------------------------------
int get_unused_fd(void) {
        struct files_struct *files = current->files;
        int fd = find_next_zero_bit(files->open_fds,
                                    files->max_fdset, files->next_fd);
        FD_SET(fd, files->open_fds);    /* in use now */
        files->next_fd = fd + 1;
        return fd;
}
---------------------------------------------------------------------------------------------------

Here current is the pointer to the user task struct for the currently executing task.

The routine fd_install() is found in include/linux/file.h. It just stores the information returned by filp_open()

---------------------------------------------------------------------------------------------------
void fd_install(unsigned int fd, struct file *file) {
        struct files_struct *files = current->files;
        files->fd[fd] = file;
}
---------------------------------------------------------------------------------------------------

So all the interesting work of sys_open() is done in filp_open(). This routine is found in fs/open.c:

---------------------------------------------------------------------------------------------------
struct file *filp_open(const char *filename, int flags, int mode) {
        struct nameidata nd;
        open_namei(filename, flags, mode, &nd);
        return dentry_open(nd.dentry, nd.mnt, flags);
}
---------------------------------------------------------------------------------------------------

The struct nameidata is defined in include/linux/fs.h. It is used during lookups.

---------------------------------------------------------------------------------------------------
struct nameidata {
        struct dentry *dentry;
        struct vfsmount *mnt;
        struct qstr last;
};
---------------------------------------------------------------------------------------------------

The routine open_namei() is found in fs/namei.c:

---------------------------------------------------------------------------------------------------
open_namei(const char *pathname, int flag, int mode, struct nameidata *nd) {
        if (!(flag & O_CREAT)) {
                /* The simplest case - just a plain lookup. */
                if (*pathname == '/') {
                        nd->mnt = mntget(current->fs->rootmnt);
                        nd->dentry = dget(current->fs->root);
                } else {
                        nd->mnt = mntget(current->fs->pwdmnt);
                        nd->dentry = dget(current->fs->pwd);
                }
                path_walk(pathname, nd);
                /* Check permissions etc. */
                ...
                return 0;
        }
        ...
}
---------------------------------------------------------------------------------------------------

An inode (index node) describes a file. A file can have several names (or no name at all), but it has a unique inode. A dentry (directory entry)describes a name of a file: the inode plus the pathname used to find it. Avfsmount describes the filesystem we are in.

So, essentially, the lookup part op open_namei() is found in path_walk():

---------------------------------------------------------------------------------------------------
path_walk(const char *name, struct nameidata *nd) {
        struct dentry *dentry;
        for(;;) {
                struct qstr this;
                this.name = next_part_of(name);
                this.len = length_of(this.name);
                this.hash = hash_fn(this.name);
                /* if . or .. then special, otherwise: */
                dentry = cached_lookup(nd->dentry, &this);
                if (!dentry)
                        dentry = real_lookup(nd->dentry, &this);
                nd->dentry = dentry;
                if (this_was_the_final_part)
                        return;
        }
}
---------------------------------------------------------------------------------------------------

Here the cached_lookup() tries to find the given dentry in a cache of recently used dentries. If it is not found, the real_lookup() goes to the filesystem, which probably goes to disk, and actually finds the thing.After path_walk() is done, the nd argument contains the required dentry,which in turn has the inode information on the file. Finally we do dentry_open() that initializes a file struct:

---------------------------------------------------------------------------------------------------
struct file *
dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) {
        struct file *f = get_empty_filp();
        f->f_dentry = dentry;
        f->f_vfsmnt = mnt;
        f->f_pos = 0;
        f->f_op = dentry->d_inode->i_fop;
        ...
        return f;
}
---------------------------------------------------------------------------------------------------

So far the open. In short: walk the tree, for each component hope the information is in cache, and if not ask the file system. How does this work? Each file system type provides structs super_operations,file_operations, inode_operations, address_space_operations that contain the addresses of the routines that can do stuff. And thus

---------------------------------------------------------------------------------------------------
struct dentry *real_lookup(struct dentry *parent, struct qstr *name, int flags) {
        struct dentry *dentry = d_alloc(parent, name);
        parent->d_inode->i_op->lookup(dir, dentry);
        return dentry;
}
---------------------------------------------------------------------------------------------------

calls on the lookup routine for the specific fiilesystem, as found in the struct inode_operations in the inode of the dentry for the directory in which we do the lookup.

And this file system specific routine must read the disk data and search the directory for the file we are looking for. Good examples of file systems are minix and romfs because they are simple and small. For example,in fs/romfs/inode.c:

---------------------------------------------------------------------------------------------------
romfs_lookup(struct inode *dir, struct dentry *dentry) {
        const char *name = dentry->d_name.name;
        int len = dentry->d_name.len;
        char fsname[ROMFS_MAXFN];
        struct romfs_inode ri;
        unsigned long offset = dir->i_ino & ROMFH_MASK;
        for (;;) {
                romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE);
                romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
                if (strncmp (name, fsname, len) == 0)
                        break;
                /* next entry */
                offset = ntohl(ri.next) & ROMFH_MASK;
        }
        inode = iget(dir->i_sb, offset);
        d_add (dentry, inode);
        return 0;
}

romfs_copyfrom(struct inode *i, void *dest,
               unsigned long offset, unsigned long count) {
        struct buffer_head *bh;

        bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
        memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), count);
        brelse(bh);
}
(All complications, all locking, and all error handling deleted.)
---------------------------------------------------------------------------------------------------

[目录]


read

Given a file descriptor (that keeps the inode and the file position of the file) we want to read. In fs/read_write.c we find:

---------------------------------------------------------------------------------------------------
ssize_t sys_read(unsigned int fd, char *buf, size_t count) {
        struct file *file = fget(fd);
        return file->f_op->read(file, buf, count, &file->f_pos);
}
---------------------------------------------------------------------------------------------------

That is, the read system call asks the file system to do the reading,starting at the current file position. The f_op field was filled in the dentry_open() routine above with the i_fop field of an inode.

For romfs the struct file_operations is assigned in romfs_read_inode(). For a regular file (case 2) it assigns generic_ro_fops. For a block special file (case 4) it calls init_special_inode() (see devices.c) which assigns
def_blk_fops.

How come romfs_read_inode() was ever called? When the filesystem was mounted, the routine romfs_read_super() was called, and it assigned romfs_ops to the s_op field of the superblock struct.

---------------------------------------------------------------------------------------------------
struct super_operations romfs_ops = {
        read_inode:     romfs_read_inode,
        statfs:         romfs_statfs,
};
---------------------------------------------------------------------------------------------------

And the iget() that was skipped over in the discussion above (in romfs_lookup()) finds the inode with given number ino in a cache, and if it cannot be found there creates a new inode struct by calling get_new_inode()(see fs/inode.c):

---------------------------------------------------------------------------------------------------

struct inode * iget(struct super_block *sb, unsigned long ino) {
        struct list_head * head = inode_hashtable + hash(sb,ino);
        struct inode *inode = find_inode(sb, ino, head);
        if (inode) {
                wait_on_inode(inode);
                return inode;
        }
        return get_new_inode(sb, ino, head);
}

struct inode *
get_new_inode(struct super_block *sb, unsigned long ino,
              struct list_head *head) {
        struct inode *inode = alloc_inode();
        inode->i_sb = sb;
        inode->i_dev = sb->s_dev;
        inode->i_ino = ino;
        ...
        sb->s_op->read_inode(inode);
}
---------------------------------------------------------------------------------------------------

So that is how the inode was filled, and we find that in our case (/dev/hda is a block special file) the routine that is called by sys_read is def_blk_fops.read, and inspection of block_dev.c shows that that is the routine block_read():
---------------------------------------------------------------------------------------------------

ssize_t block_read(struct file *filp, char *buf, size_t count, loff_t *ppos) {
        struct inode *inode = filp->f_dentry->d_inode;
        kdev_t dev = inode->i_rdev;
        ssize_t blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
        loff_t offset = *ppos;
        ssize_t read = 0;
        size_t left, block, blocks;
        struct buffer_head *bhreq[NBUF];
        struct buffer_head *buflist[NBUF];
        struct buffer_head **bh;

        left = count;                   /* bytes to read */
        block = offset / blocksize;     /* first block */
        offset &= (blocksize-1);    /* starting offset in block */
        blocks = (left + offset + blocksize - 1) / blocksize;

        bh = buflist;
        do {
                while (blocks) {
                        --blocks;
                        *bh = getblk(dev, block++, blocksize);
                        if (*bh && !buffer_uptodate(*bh))
                                bhreq[bhrequest++] = *bh;
                }
                if (bhrequest)
                        ll_rw_block(READ, bhrequest, bhreq);
                /* wait for I/O to complete,
                   copy result to user space,
                   increment read and *ppos, decrement left */
        } while (left > 0);
        return read;
}
---------------------------------------------------------------------------------------------------

So the building blocks here are getblk(), ll_rw_block(), and wait_on_buffer().

The first of these lives in fs/buffer.c. It finds the buffer that already contains the required data if we are lucky, and otherwise a buffer that is going to be used.

---------------------------------------------------------------------------------------------------
struct buffer_head * getblk(kdev_t dev, int block, int size) {
        struct buffer_head *bh;
        int isize;

try_again:
        bh = __get_hash_table(dev, block, size);
        if (bh)
                return bh;
        isize = BUFSIZE_INDEX(size);
        bh = free_list[isize].list;
        if (bh) {
                __remove_from_free_list(bh);
                init_buffer(bh);
                bh->b_dev = dev;
                bh->b_blocknr = block;
                ...
                return bh;
        }
        refill_freelist(size);
        goto try_again;
}
---------------------------------------------------------------------------------------------------

The real I/O is started by ll_rw_block(). It lives in drivers/block/ll_rw_blk.c.

---------------------------------------------------------------------------------------------------
ll_rw_block(int rw, int nr, struct buffer_head * bhs[]) {
        int i;

        for (i = 0; i < nr; i++) {
                struct buffer_head *bh = bhs[i];

                bh->b_end_io = end_buffer_io_sync;

                submit_bh(rw, bh);
        }
}
---------------------------------------------------------------------------------------------------

Here bh->b_end_io specifies what to do when I/O is finished. In this case:

---------------------------------------------------------------------------------------------------
end_buffer_io_sync(struct buffer_head *bh, int uptodate) {
        mark_buffer_uptodate(bh, uptodate);
        unlock_buffer(bh);
}
---------------------------------------------------------------------------------------------------

So, ll_rw_block() just feeds the requests it gets one by one to submit_bh():

---------------------------------------------------------------------------------------------------
submit_bh(int rw, struct buffer_head *bh) {
        bh->b_rdev = bh->b_dev;
        bh->b_rsector = bh->b_blocknr * (bh->b_size >> 9);

        generic_make_request(rw, bh);
}
---------------------------------------------------------------------------------------------------

So, submit_bh() just passes things along to generic_make_request(), the routine to send I/O requests to block devices:

---------------------------------------------------------------------------------------------------
generic_make_request (int rw, struct buffer_head *bh) {
        request_queue_t *q;

        q = blk_get_queue(bh->b_rdev);
        q->make_request_fn(q, rw, bh);
}
---------------------------------------------------------------------------------------------------

Thus, it finds the right queue and calls the request function for that queue.

---------------------------------------------------------------------------------------------------
struct blk_dev_struct {
        request_queue_t         request_queue;
        queue_proc              *queue;
        void                    *data;
} blk_dev[MAX_BLKDEV];

request_queue_t *blk_get_queue(kdev_t dev)
{
        return blk_dev[MAJOR(dev)].queue(dev);
}
---------------------------------------------------------------------------------------------------

In our case (/dev/hda), the blk_dev struct was filled by hwif_init (from drivers/ide/ide-probe.c):
and this ide_get_queue() is found in drivers/ide/ide.c:

---------------------------------------------------------------------------------------------------
blk_dev[hwif->major].data = hwif;
        blk_dev[hwif->major].queue = ide_get_queue;

#define DEVICE_NR(dev)       (MINOR(dev) >> PARTN_BITS)

request_queue_t *ide_get_queue (kdev_t dev) {
        ide_hwif_t *hwif = (ide_hwif_t *) blk_dev[MAJOR(dev)].data;
        return &hwif->drives[DEVICE_NR(dev) & 1].queue;
}
---------------------------------------------------------------------------------------------------

This .queue field was filled by ide_init_queue():
And blk_init_queue() (from ll_rw_blk.c again):

---------------------------------------------------------------------------------------------------
ide_init_queue(ide_drive_t *drive) {
        request_queue_t *q = &drive->queue;

        q->queuedata = HWGROUP(drive);
        blk_init_queue(q, do_ide_request);
}

blk_init_queue(request_queue_t *q, request_fn_proc *rfn) {
        ...
        q->request_fn           = rfn;
        q->make_request_fn      = __make_request;
        q->merge_requests_fn    = ll_merge_requests_fn;
        ...
}
---------------------------------------------------------------------------------------------------

Aha, so we found the q->make_request_fn. Here it is:

---------------------------------------------------------------------------------------------------
       __make_request(request_queue_t *q, int rw, struct buffer_head *bh) {
               /* try to merge request with adjacent ones */
               ...
               /* get a struct request and fill it with device, start,length, ... */
               ...
               add_request(q, req, insert_here);
               if (!q->plugged)
                       q->request_fn(q);
       }

       add_request(request_queue_t *q, struct request *req,
                   struct list_head *insert_here) {
               list_add(&req->queue, insert_here);
       }

---------------------------------------------------------------------------------------------------

When the request has been queued, q->request_fn is called. What is that? We can see it above - it is do_ide_request() and lives in ide.c.

---------------------------------------------------------------------------------------------------

       do_ide_request(request_queue_t *q) {
               ide_do_request(q->queuedata, 0);
       }

       ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq) {
               ide_startstop_t startstop;

               while (!hwgroup->busy) {
                       hwgroup->busy = 1;
                       drive = choose_drive(hwgroup);
                       startstop = start_request(drive);
                       if (startstop == ide_stopped)
                               hwgroup->busy = 0;
               }
       }

       ide_startstop_t
       start_request (ide_drive_t *drive) {
               unsigned long block, blockend;
               struct request *rq;

               rq = blkdev_entry_next_request(&drive->queue.queue_head);
               block = rq->sector;
               block += drive->part[minor & PARTN_MASK].start_sect;
               SELECT_DRIVE(hwif, drive);
               return (DRIVER(drive)->do_request(drive, rq, block));
       }
---------------------------------------------------------------------------------------------------

So, in the case of a partitioned disk it is only at this very low level that we add in the starting sector of the partition in order to get an absolute sector.
The first actual port access happened already:

---------------------------------------------------------------------------------------------------
       #define SELECT_DRIVE(hwif,drive) \
               OUT_BYTE((drive)->select.all,
hwif->io_ports[IDE_SELECT_OFFSET]);
---------------------------------------------------------------------------------------------------

but this do_request function must do the rest. For a disk it is defined in ide-disk.c, in the ide_driver_t idedisk_driver, and the function turns out to be do_rw_disk().

---------------------------------------------------------------------------------------------------
       ide_startstop_t
       do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long
block) {
               if (IDE_CONTROL_REG)
                       OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
               OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
               if (drive->select.b.lba) {
                       OUT_BYTE(block,IDE_SECTOR_REG);
                       OUT_BYTE(block>>=8,IDE_LCYL_REG);
                       OUT_BYTE(block>>=8,IDE_HCYL_REG);

OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
               } else {
                       unsigned int sect,head,cyl,track;
                       track = block / drive->sect;
                       sect  = block % drive->sect + 1;
                       OUT_BYTE(sect,IDE_SECTOR_REG);
                       head  = track % drive->head;
                       cyl   = track / drive->head;
                       OUT_BYTE(cyl,IDE_LCYL_REG);
                       OUT_BYTE(cyl>>8,IDE_HCYL_REG);
                       OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
               }
               if (rq->cmd == READ) {
                       ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
                       OUT_BYTE(WIN_READ, IDE_COMMAND_REG);
                       return ide_started;
               }
               ...
       }
---------------------------------------------------------------------------------------------------

This fills the remaining control registers of the interface and starts the actual I/O. Now ide_set_handler() sets up read_intr() to be called when we get an interrupt. This calls ide_end_request() when a request is done, which calls
end_that_request_first() (which calls bh->b_end_io() as promised earlier) and end_that_request_last() which calls
blkdev_release_request() which wakes up whoever waited for the block.


[目录]


lisolog文章检索

[目录]


index


索引的内容:
我比较喜欢的帖子. 和精华收藏多有冲突.


索引的使用:
支持分类, 从这里找帖子, 然后分类,要比在论坛里搜索方便. 有这一点作用, 也不枉我遍历一次论坛.( 折叠起来看,不然太乱了)



索引的更新:
我们记录了索引更新的时间. 下一次更新时, 比方说一个月后, 只需选择显示一个月内发表的文章. 这样就不会丢掉文章. 但是,有可能重复, 因为新的回应会使帖子位置前移.这个问题这样解决:凡是我收集的文章统统加入我的收藏夹, 这样,更新时我只向索引里加入那些可以加入我的收藏夹的文章.



索引的局限:
水平有限, 不免错漏. 我尽力保留有价值的帖子. 不敢说没有在索引中的帖子就没有价值.但我一直在努力.

编辑者: hyl (07/12/02 13:56)

[目录]


list1


关于faq----征求建议和合作者

请教linux内核版本2.0.35的进程切换

诚征版主,祝内核版越办越好!

编译内核时有很多东东不明白什么含义,哪位知道什么地方可以找到比较全面的资料

高手是怎么编译内核的啊?

编译内核之一

编译内核之三

编译内核之四

编译内核之五

编译内核之六(后记)

大家对NeXT,BeOS,Darwin这些变种如何看待,似乎国内很少有人谈及?

编译内核之二

提问:the STRUCTURE of Linux?

编译内核时,在哪部分把PCMIA卡编译掉?

微内核?进程调度?

linux的非微内核一直受竞争对手的非议,请问未来linux的发展在这方面有什么计划?(null)

一点题外话

书上说段页式内存管理是最好的内存管理方式,但LINUX的内存管理使用的是页式,为什莫?

1f是指什么,好象并没有1f的标签

编译内核是否只是简单的make config,若不是,请给我详细步骤。

那位大侠知道如何记录通过ipchains网关的数据报

要怎样建立放火墙

模块程序却出错如下:invalid parameter parm_a

块设备驱动程序的注册似乎都是通过调用register_blkdev(),

调用了netif_rx 函数。

请问tunable parameter

关于进程数

如何编写自己的设备驱动程序,又如何在C语言中调用

where the kernel start? why I can not find the function 'main()'?

请问可不可以在Linux下改网卡的硬件地址(将每个发送出去的数据包的硬件地址改为特定的值)?

编译的时候他说我的最后一行:missing seprator

lilo怎么改?image=? initrd

init在哪儿

__asm__是干什么的?

编译setup.S为什么有一大堆错误

想让内核将printk输出到messages文件

get_current(void)

在内核态,进程如何访问用户态空间的数据?

Bug大侠,该如何研究Linux的源代码

head.s中调用setup_paging时,内存0x1000起开始的几页全被清零,岂不是将内核代码head.s部分覆盖了,这是怎么回事?

内核首先读入内存0x10000处,但后又移至0x1000处,这样岂不是与内核页目录swapper_pg_dir地址冲突吗

inw()/outw()、inl()/outl(),其中b、w、l各是什么意思

如何安装3块网卡,每个网卡在一个网段

要写一个PCI卡(自制的)driver

加载modules时就提示有些目标文件中存在unresolved simbol

linux -- driver的编写 -- file_operations

装载lilo时会显示:“LILO:”,请问如何改变这个显示

关于BluePoint2.0的几个烂点:

include头文件modversions.h时,一般用什么条件?为什么我用的时候它总是和#include asm/uaccess.h冲突

用insmod装载模块时,出现了unresolved symbol

Linux的整体式结构决定了其内核的高效性

我用命令 mount -t vfat /dev/hda1 /mnt/c 发现,我的linux好象,不支持vfat

如何查看当前内核的配置参数

我们也做嵌入式,欢迎一起创业

kmalloc(),返回的地址不用设置页表,而vmalloc()需要。 这说明什么

__get_free_pages()返回的是物理地址还是虚拟地址

分析do_fork chldreg指针的赋值的问题小弟感觉很糊涂

linux -- driver -- __put_user

linux -- driver -- ioctl

How can I do a Linux Boot/Root Disk

编写驱动程序时,需要将硬件的物理地址为虚地址. LINUX内核如何保证这种影射对每个进程都是有效的.

如何在Windows或者DOS下编译内核

内核过程所允许使用的堆栈空间有多大

system.map到底有什么用

想利用时钟中断,想自己加入一些代码形成自己的中断服务程序.

用signal 的确可以做当异步地运行某个函数, 信号是否是以中断的形式运行的

起动盘为什么mount不上去

看不出head.s是如何调用start_kernal()函数的

start_kernel()中我怎么没有发现初始化网络部分的函数

head.s究竟是怎样调用start_kernel()函数的呢

Linux是不是对中断控制器重新编程过了

系统初始化

阅读setup.S原程序时,对下面进入保护模式程序段有点不理解

kernel_thread()是用来产生init进程的,然后由init全权处理进程,它怎么会初始化网络呢

段描述符高速缓存寄存器和描述符投影寄存器有什么区别

段描述符中有一位p用来区分此段是否在内存中,问题是若一段中部分叶在内存,部分不在内存,此位如何赋值

如何有效阅读内核代码?

init 进程启动之后,所有其他进程就由INIT进程全权处理。这时可以说系统内核已经完全启动起来吗?

在head.s-->start_kernel()-->启动init这个过程我还没有发现内核调用调度函数

能不能将内核的地址空间动态映射到用户空间的0xc0000000-0xffffffff 上去

已分配并不再使用的堆栈页面是对换到交换空间中还是直接被系统回收

Why I can't boot linux form fd(volume 1.722M) but fd(volume 1.44) can do?

调入系统模块到0x1000:0处时,为虾米还要判断es为64k对齐 为什么还要调用kill_motor

Pentium CPU CR0中的WP位是干什么用的?

CPU从用户的特权级3进入到内核的特权级0,请问这时是CPU如何完成这个中断指令的?

setup.S的bootsect_helper程序时,对于bios的15h中断的87号移低端内存到高端内存的参数有点不明白

do_mmap()函数 off&~PAGE_MASK的含义是什么?mm->map_count 是何含义?mm->locked_vm不是被锁定的vm的个数吗?

Oops是什么意思

为什么要去分析内核 我是菜鸟,但我是鹰的后代

setup.S中为什么需要置A20线

setup.S中移动剩余setup代码区的一个问题

那么当系统物理内存为最大值4G时,内核似乎只能管理它自已地址空间中的1G? 应用程序0-0x8048000有何作用?

pmd是指什麽

vfork的原意是什么

东东太多,我不知从哪下手!

8295A的断口号是怎么分配的

一篇ELF格式的详细说明

SYMBOL_NAME(...)和__asm__volatile(...)的功能是什么呢?

Linux中,局部描述符表LDT有何用呢?

__pa(x)是干什么用的

内核不使用虚拟内存,但是却把0xc0000000-4g的地址做为内核保留地址,这是怎么一会事

vmalloc分配的内存能否被swap out

ping的源代码

MEM_ALT_K是什么

__volatile__是什么功能

将一个极小的GUI机制引入内核可否?

Linux console font operation

在共享库定义共享段

Linux交换内存的一个缺陷

do_page_fault部分时,对里边提及的Pentium cpu缺陷(f00fc7c8冻结指令)很迷惑

解读vmlinux.lds.S

fixmap_init()函数是用来干虾米的?

lcall7 读trap_init()

paging_init()中的end_mem = (end_mem + ~mask) & mask;

static定义的变量放于哪个段

_edata和_end在哪儿定义

如何往内核加载模块

勇敢地蜕去你的陈年旧皮!

do_IRQ()中断号错误码的正负问题

build_irq"#"前缀

编译内核VFS:can't open root device 08:0a

mem_map结构数组中每个页面的age值是在什么时候更新的

GCC为2.96版,这是个非稳定的版本

说说用户进程的页面切换

kernel_threadregs.esp由从何而来呢?

Too many open files"问题

3g的虚拟内存到物理内存的映射是否都在task_struct中

为什么要有8M的隔离带?

为何总是报错VFS:unable to mount root fs on 08:01?

\linux\kernel\init\main.c开始的部分其偏移地址是否都被编译为从大于3G开始

lds定义了内核映象所有符号从PAGE_OFFSET(3G)处开始编址

Linux目前的体系只能管理2G物理内存?

编译好的内核为什么要要经过压缩?

系统状态保存在当前进程任务结构和内核堆栈中。进程间的切换点都在schedule()中

setup_arch()函数是如何得到命令行参数的

进程数据结构页面是如何保证不被换出内存的?

问_end的定义

mount和insmod一个模块的区别?网卡的混杂模式是什么意思?

mmap用于进程间文件共享,SYSV IPC用于进程间内存共享

当当前进程current的调度策略是FIFO时,其时间片current.counter为何没有重新赋值

加载模块时,想给参数赋值

开机后不要人干与就直接可起动我的应用

FIFO策略进程的时间片(counter)没有重新赋值

如何使linux进程的堆栈是不可运行的?

可重入 和 ret_from_intr

FIFO的进程其时间片在schedule()中被忽视而没有重新赋值.请问这是否会导致它被频繁的重新调度呢?

推荐内核分析风格

__asm__ __volitile__

我的看法

如何编程接收ppp0上的裸IP包?

PS/2鼠标工作原理和硬件编程的技术资料

请教如何读linux的Kernel

模块的版本相关性指什么?

当物理内存超过3G时就管理不了?

lcall7的入口处比system_call多压栈了一项pushfl

原代码看的工具lxr是怎么用的?

调用_free_page()后,该内存页真的被释放了吗

free_area_init()中bitmap_size 是否有问题?

对try_to_swap_out()的一点改进

Where is file for mapping kernel virtual address

有没有关于gunzip()的解压算法

gunzip()函数在解压缩piggy.o(真正的内核)时,是如何定位或寻址到piggy.o的内存地址的呢

堆中内存是如何分配的

这里的set_pte使我很疑惑,为什么用的不是物理地址,而是加上0xC0000000的虚拟地址呢

编译时决定virtual address吗?

boot.S, setup.S ... is running with real-mode?Need not mmu settings?

idle是内核线程,init线程已转变为普通进程

a paper about debugger

由于ELF中允许用.section自定义段

trampoline乱谈

SMP不太熟悉,有几个入门性的问题

如何在内核中获得键盘和鼠标的消息

引用内核中变量求助 EXPORT_SYMBOL()宏添加相应内核符号

outb_p :where define?what does "%w1" mean ?

Linux太难学了

读kmem_cache_estimate ()这个函数

kmem_cache_estimate 中的 L1_CACHE_ALIGN

slab分配器的设计思想

page aligned 是什么意思

用nice等命令设定的进程优先级有什么确切的含义

模块中的全局符号自动添加到系统符号表中,模块中不能使用EXPORT_SYMBOL(), 可用EXPORT

在内核态与用户态转变时,地址映射是相同的,堆栈区域不同

linux核心程序中怎样打开设备呢

怎样调试内核???gdb行吗?

硬件内存在系统内存空间中的映射问题

和malloc,calloc一样,一般是用sbrk系统调用实现的

Linux头文件中定义了进行串操作的函数,驱动程序可以使用它们来获得比C语言写的循环更好的性能

readahead使用的各个变量的意义

About kernel stack

section __ex_table,"a"和.previous 以及.fixed

请教各位如何使用SYSCTL的问题

System Call is a limitation??

About GET_CURRENT

Linux的TCP/IP协议栈阅读笔记(1)

Linux的TCP/IP协议栈阅读笔记(2)

Linux的TCP/IP协议栈阅读笔记(3)

Linux的TCP/IP协议栈阅读笔记(4)

Linux的TCP/IP协议栈阅读笔记(5)

Linux的TCP/IP协议栈阅读笔记(6)

Linux的TCP/IP协议栈阅读笔记(7)

About the Linux Kernel Analysis Book

Linux的TCP/IP协议栈阅读笔记(8)

请大虾们推柬一下读核工具

请问内核2.4版本的zone分配器的设计思想

可否在内核中进行截短文件操作?

在模块中调用系统调用

请问怎样正确查找函数原型?

MTRR是什么?

Linux-2.4.0网络部分改变

About multiple txt segment in an elf

建议阅读linux device drivers

模块化编程可否替换所有的系统调用

How to release a Module by itself ?

Linux设备驱动程序勘误表(部分)

内核中替代realloc()函数 的具体实现方法

什么是NR?

MAP_FIXED是什么固定映射?

正交持续性 自反系统

为何要保存flags? 只用cli()和sti()不可以么?

如何写直接读写硬盘的驱动程序

这种kmalloc,优先级参数应为GFP_AUTOMIC?

因为"Hello,World"在内核段中,使用段超越试试看

##是文本连接运算符

详细介绍slab的linux实现的文章连接

Linux防火墙程序设计

编译内核后,一定要重新链接System.map到新的System.map吗?

tty 到底是谁的abbreviation

Linux 模块调度问题和抢占

about EXPORT_SYMBOL

什么叫映象文件

mm/memory.c和mm/vmalloc.c各负责什么功能

怎样把自行编写的设备驱动程序添加到Linux核心中

谁知道netstat下的TIME_WAIT如何产生和避免

内核代码中经常使用固定数组而不是链表是为了编程简单?

用ioperm申请要存取的端口范围的访问权

TIME_WAIT状态有什么用

要先包含<linux/module.h> <linux/kernel.h>然后sleep_on_interruptible_timeout就正常了,不会崩掉了

函数init()的最后执行了 execve()函数,为什么内核代码没有被execve()所创建的新进程覆盖掉

dput()和dget()

个时间片大小是固定的吗?

kmem_cache_grow()开头的一串标志检测也让我不理解
[目录]


list2



Shaper是一个限制网络速率的虚拟网络设备

jmpi go,INITSEG 是什么意思?

关于进程的flag,以及调度的一些概念

为何在 console_init()之后还不能打开/dev/console呀

do_wp_page

内存管理--end_mem解读

Unable to handle kernel paging request at virtual address...是从哪个模块报出来的?

按照原来配置重新编译2.4.0,重启后,屏幕显示:Uncompressing Linux ...Ok,booting the kernel.后就死机了

如何研究内核

有关kdev_t结构与次设备号的问题?

内存管理--free_area结构解读

内核地址手工转换,多是在填写页表时用到

进程管理--在时钟中断处理中为什么没有调用schedule函数

内存管理--memmap解读

请教Gcc源码的阅读问题

Unable to handle kernel paging request at virtual address....

Linux源代码讨论专用线索

我所看到的switch过程以及我的理解

我实现了一个进程切换方法

VFS: can't mount root filessystem . 这是怎么回事儿

内核栈

head.s中的LGDT装入gdt_descr处的内容...

内核空间在0xc0000000之上,但是如何使内核程序中访问的变量等的线形地址能够达到这个范围之上呢(因为段描述符的基地址是0),这是怎么做到的?(lds?)

汇编语言的语法

增加系统调用

块设备驱动,DMA内存,IDE硬盘的预读

traceroute的问题

在用户空间编写驱动程序

内核中的高端内存选项是怎么回事

在内核空间访问用户空间的问题

在地址0000开始的作了个中断向量表,这个是bios引导后实现的,还是dos获得系统控制权利后才做的事情??

中国的操作系统为什么停滞不前

在文件系统中struct dentry 是用来描述什么?

在x86平台上,io空间跟内存空间是分别编址的吗

Heap and Internal Fragmentation

文档最新动向 3月5日

/dev/ram,/dev/tty1等等,这些i节点是如何被创建的

消息可以代替信号?

About brk value, malloc, and heap

About contigeous virtual memory

About brk explaination from book.

在linux中,是怎么实现动态连接库的共享?地址和重入

raw disk I/O 的资料

todo & 遗留问题 & 计划介绍

一些资源

专题认领

源代码学习专题认领

编译过多次,但还是有些问题不能理解

外部中断的驱动怎么知道自己的3.硬件产生哪个中断?

对bottom half概念的一点理解

进程切换的时机


http://www.xfree86.org/
ftp://metalab.unc.edu/
http://www.xfree.org/FAQ/
http://www.xfree.org/#resources/
http://www.kde.org/
http://www.qt.org/
http://www.gnome.org/
http://www.gtk.org/
http://www.enlightenment.org/
http://www.opengroup.org/openmotif/
http://www.lesstif.org/
http://www.windowmaker.org/
http://www.gnustep.org/
http://www.itresearch.com/
ftp://ftp.funet.fi/pub/Linux/PEOPLE/Linus/SillySounds/
http://members.xoom.com/gnulix_guy/geek-gourmet/
http://www.csustan.edu/bazaar/


init process

在理解linux虚存的时候,我确总有些绕不过来

希望解决:3.硬件产生哪个中断的判别问题

Makefile 初探

所有的进程都公用_LDT(0)?

BUILD_IRQ宏

虚存难绕

Makefile解读之二

进程陷入内核时CR3的内容会改变吗?如果不变,如何存取内核空间呢?

barrier()的作用

Makefile解读之三: 模块的版本化处理

LDT:有点眉目了

系统调用流程

LINUX的系统内核空间的保护

Makefile解读之四: Rules.make的注释

HELP! printk() does not work in device module

as86汇编语言的语法说明

中断嵌套的问题

问题犹在:BUILD_COMMON_IRQ的宏展开

中断的部分代码解读

内核初起时如何从核心态进入用户态?

ASM格式简介

增加系统调用时的问题,虽已解决,但有的地方不太明白。

8259A的工作原理

Linux下的jiffies是10ms吗?

fork进程的学习

netfilter.c剖析1

getpid()

netfilter各个HOOK的关系

分析sockfs套接字文件系统

netfilter剖析2

backlog field in sock struct

所有进程在内核态的地址空间是一致的,可以相互访问的么

外设中的目录项(以EXT2为例)和内存中的"目录项"的比较

关于文件系统的安装与访问

Kernel 2.4中bottom half好象已经演化到soft-interrupt了

从系统调用open看源码

分析内核对gzip压缩文件进行解压的方法

sock 中zapped成员表示什

ip_tables.h分析

initrd 是干什么用的?

内核对以太网设备的探测过程

ISA网卡驱动程序的探测过程

sock结构的链接问题

read_lock()和write_lock()的过程描述如下

UNIX系统技术内幕》的第七章,介绍了自旋锁,看不懂他的改进

ISA网卡驱动程序发送和接收过程

EXT2的超级块与组描述符

新兵笔记--ULK(C2) beta版 Segmentation in Linux

网络包的排队发送过程

Linux secret.... (maybe)[Cross post]

Export了怎么还是无法resolve?

新兵笔记--ULK(Understanding the Linux Kernel) 序

关于netfilter的一点问题

ip_tables.c中组织规则的方式

内核打印的限速函数 net_ratelimit()

Confirm SA_INTERRUPT

trap和中断有什么不同啊

假如我要把PLT映射到内存的低端

Linux的硬件地址解析过程

inode和block之间有什么联系和区别

ip_tables.c的防火墙规则处理

内核堆栈与GET_CURRENT

根文件系统指的是哪个? 如何安装?

进程映象的过约束方法(overcommit_memory)

新兵笔记--ULK(C2) beta版 Paging in Hardware

ip_tables.c中防火墙规则环的检测

建立双向链表的一种常见方法

将root文件系统也放在一张软盘上

伙伴(buddy)算法的页面释放过程

ERR_PTR PTR_ERR(ptr) IS_ERR(ptr)

路由缓冲表的基本结构

IP包的接收过程

ip_tables.c的面向应用程序的接口

关于目录文件对象操作的一致性问题

IP包碎片重组过程

缓冲区管理与块设备

IP包的本地分发过程

vm_struct vs. vm_area_struct

从一个函数返回时,做什么?

套接字的基本结构

AT&T汇编与Intel汇编的比较1

AT&T汇编与Intel汇编比较2

设备驱动几个数据结构关系

对数据报套接字文件的select过程

MODULE_PARM(var,type)的功能是什么

EXT2:超级块----外部结构

EXT2:超级块----读入

EXT2:组描述符----外部结构

EXT2:组描述符----读入

EXT2:组描述符----查找

EXT2:inode位图----外部结构

EXT2:inode位图----读入

EXT2:概述

新兵笔记--ULK(C2) Paging in Linux

EXT2:inode----外部结构

EXT2:inode----查询与读入

EXT2:inode----分配

包缓冲区操作的几个内嵌函数

包队列操作的一些内嵌函数(续)

About init_mm and processes' mm

包缓冲的分配操作

信号处理问题

ip_tables.c的各个注册函数解释

execve的疑问

FD_SETSIZE问题,我补充了一点说明

get_user的使用

AT&T汇编语言的帮助文件

扩展的行内汇编手册

IP包的生成和发送接口

Linux网络接口层分析(之一):netif_rx

Kernel Hacking

open系统调用中对用户指针的检测

movl $pg0-_PAGE_OFFSET,%edi 其中的$pg0-_PAGE_OFFSET是什么意思?

关于Linux的路由

Segments are as follows:ds=es=ss=cs-INITSEG?

__get_free_pages()和free_pages()是最低层的内存分配和释放函数

新兵笔记--ULK(C3) Process Switching

EXT2:块----外部结构
[目录]


list3



LOOPBACK网络回送设备

网络设备的IP地址结构

pci_bios_find_device

网络过滤器的基本结构

表驱动IP过滤器的基本工作过程

barrier()的疑问

open打开一个设备时,内核做了一些什么事情后才去调用相应的驱动的呀

新兵笔记--ULK(C3) Process Descriptor

核心中内存访问是否要进行页表转换

__init具体是什么作用

do_softirq() 初步探讨

内核semaphore初步

[rainfall]系统调用

时钟概述

init进程如何从内核态切换到用户态。

init进程从内核态切换到用户态(2)

我觉得现在的进度比较慢。

8259A的编程原理(2?)

分析rwlock的结构

分析rt_sigframe结构

tasklet_action()初步

分析access_ok的结构

请问2.4中实现net_bh功能的函数到哪里去了

分析test_wp_bit的过程

ptype_all结构的一点理解和网络初始化以后的组织图

set_call_gate以及其中的汇编

zone allocator

虚拟内存的管理,搞不懂!!

分析内核检测CPU时钟频率的过程

Process Scheduling灌水版

at&t汇编说明

分析几个页目录处理的宏

Solar Designer设计的stack不可运行的patch

外部中断的上半部分(一)

怎样取得skb结构中的内容!

Linux调试技术介绍

如何编程获得系统时间

(*(unsigned long *)&jiffies)与jiffies有何不同

谁能改变中断的优先级别

分页,自己瞎琢磨,想破头都搞不定

什么情况下需要使用volatile修饰符

硬件中断:8259部分

关于保护模式

不是linux的问题-一个切入保护模式的问题

内核重入

kernel和modules的区别

分析进入和退出VM86模式的过程

分析get_wchan()过程(调度)

分析两个用于进程链表的宏:SET_LINKS和REMOVE_LINK

中断门的设置

强烈推荐大家看看《Linux内核原代码分析》

硬件中断的入口

关于ptype_all链表和pypte_base的理解

什么是footprint,hotpath?

VM86模式下的保护异常处理

分析一下FPU状态的切换

2.4.1中lock_kernel()和unlock_kernel()的问题

在什么情况下会触发一般保护故障(GFP)

linux内核内存分配初探

足印footprint

分析__udelay()算法

分析进程信号队列的结构.

如何捕获所有的ip包

分析spinlock的结构

从系统调用中返回

在mm_struct中,active_mm 表示什么

内核只是将用户虚存区域描述成vm_area_struct链表

Page Table and Memory Cost

解释一下__attribute__

对semaphore初步分析

ret_from_intr 非抢占的判断

map_user_kiobuf 注释

解释一下memcmp

小课题 & 焦点(请跟贴)

十分的困惑--get_user?

软中断「一」

粗略分析一下Linux对进程调试的支持

谈一谈gcc编绎出的跳板(trampolines)代码。

_end在那儿定义的

使Linux堆栈不可执行的简易补丁

我的错误,大家可以借鉴(driver)

map_user_kiobuf的注释

[ jkl ]active_mm是什么

对BUILD_IRQ中 -256的解释

对重入的理解

分析内核模块的结构

内核空间偏移3G的疑问

VFS文件系统(1)

防火墙技术分析讲义( for aka lecture)

linux的Kernel映像可以存放在压缩文件系统中吗

VFS文件系统(2)

About swap_page_dir, kmalloc

分析内核模块的加载过程

内核变量声明问题 (switch)

free_area 中的map

About 2 pages of task_struct

About vm_area

About symbol and loadable module

定时器:实现机制:思想

Linux Kernel中,对线程的支持如下

os的对于进程的内存分配是基于线性地址还是物理的

About Copy On Write--do_wp_page()

分析用户对文件访问权的算法

分析任务切换时虚存的切换

关于kmalloc和vmalloc的问题

VFS文件系统(3)

分析路径名搜索过程

分析文件页缓冲结构

解读softirq

内核也好,用户进程也好都是线性0-4G地址

Linux内核网络参数的意义及应用

我对spinlock中rep nop的理解

请教有关ELF文件格式

网络设备的初始化是在何时做的

set_fs(get_fs())应改为set_fs(KERNEL_DS)

2.4的file_operations结构问题

Qdisc链的含义

请教关于GDT和LDT的问题?

为什么找不到__initcall__start变量的赋值语句(lds)

分析文件名检索的散列算法

软中断

dev_queue_xmit真不知道是怎么“蹦”出来的??

分析IDE硬盘驱动器读写过程

"paranoia"问题

__init free_area_init 中bitmap_size = (end_mem -PAGE_OFFSET) >> (PAGE_SHIFT + i); 的问题

讲讲notifier机制-看到register_netdevice_notifier是的困惑

定时器:介绍&使用

malloc是如何调用内核的服务来实现的

mmap 的问题

分析list_head结构

可以自定义内存区的操作函数吗

内核中网络部分问题

进程调度

应将__MODULE__改成MODULE。

硬件中断处理的三个对象

关于内核内存分配, 依然需要努力

想读内核,可是lxr始终不能用

看了二个多月的源代码很气馁!

分析RAMFS文件系统

讲解一下文件系统中主要的数据结构

虚拟文件系统的基本原理

#define MODVERSIONS(模块的问题真多)

我想恐怕可以提前结束了。<Linux的文件系统分析>

示例clone系统调用的使用方法

__builtin_constant_p()在哪里

页IO

请教一个模块编程键盘中断的例子

几个非阻塞socket连续向一个ip的不同端口connect 时

分析应用程序加载时堆栈中的参数结构

分析ELF的加载过程

请问“:”是什么意思? (fixup:)

Linux动态链接技术

setup.S的到head.S的跳转在那里

fib_lookup的疑问:fib表是如何构造以及在何时构造的呢?

vm_area_struct 的vm_flags 有一位VM_SHARED是什么意思

某些语句会导致gcc隐含地生成一个memset()调用

系统调用时的NR_syscall中是什么地址

netfilter各个HOOK的关系

将核心空间的内存映射到用户空间,或者将用户空间的内存映射到核心空间

netfilter.h的解释

有关ext2 super_block 的疑问

分析ext2文件系统物理块的分配过程

connect超时时间的一点探讨

关于TCP连接的应答问题

可以把整个ext2的分区的东西移植到reiserfs的分区?

内核是怎么得到网卡的硬件地址的

关于as86的一些资料

ip_input,ip_forward和ip_output

分析ext2文件系统文件块的分配过程

lvm技术

网卡混杂模式(promisc)

分析EXT2文件系统目?

主机端口

关于pagh_hash函数

关于framebuffer的问题

BUG()?

about tss?

ip_nat_hash的参数问题?

What's numa and CONFIG_DISCONTIGMEM

net_family_read_lock,为什么要lock,其中的原子操作是什么

tty终端的写入过程

Linux-2.4等待队列头的结构有了很大变化,应该用DECLEARE_WAIT_QUEUE_HEAD()定义等待队列头

I can't catch some codes in kernel/sched.c , why?

LKM中分配内存的问题。

When I read do_fork() in kernel/fork.c,??

新兵笔记--ULK(C4) Returning from Interrupts and Except

tty终端文件的异步操作

Why not find tss in task_struct

请教关于LINUX设备驱动编程的问题

Linux下IP――分片与重组

发送、接受、转发包处理结构框图--again:)

rt_priority?

PPP驱动程序的基本原理

有没有人对Ipsec感兴趣?

FIB的数据结构问题?

驱动程序与用户进程

谁能告诉我atomic_inc(Atomic.h)函数是什么意思?

BSD伪终端设备驱动程序

Help me check some codes about bootmem

uname的系统调用?

请教:tty.h 中的宏定义

What's meaning of "mapping" in struct page

a question about memory_pressure

local_table是什么?在哪里初始化的?inet_addr_inet函数有点看不懂

如何获得网络因CRC校验错丢失包的个数

struct page {...struct buffer_head *buffers...};

转发表的检索过程(fib_lookup)

ZERONET(x)和BADCLASS(x)是怎样确定广播地址的

哪位大侠帮忙解释一下fn_hash_lookup函数的作用,谢谢!

策略路由的NAT和netfilter的NAT的区别

请问get_fs和set_fs的作用

__attribute__ 是何意?

do_wp_page()函数的疑问??

PPP帧的发送

各位大虾,键盘如何来处理??

LinuxKernel的错误?--“console的问题

Linux设备驱动程序的中断

在中断处理中分配内存时没有使用GFP_ATOMIC标志

make modules" error on rh7.1

Booting:BIOS POST?

tcp_hashinfo结构里的那些hash table都是什么作用?

关于mmap的问题

Linux上gdb如何跟踪调试进dlopen内??

请问msg_name派什么用处?

分析ip_route_output_key( )中的一个疑惑?

为什么check_region不可解析?

对volatile的解释

套接字地址的绑定

关于2.4的内核初始化的问题

关于free_area空闲块数组?  

一个LKM的初级问题 (包含头文件)

关于Ext2文件系统

嵌入式Linux内核精解

freeswan中,谁在操纵SADB?

linux下几种地址的区别


http://www.sgi.com/processors/r10k/timing/perf_count.html


我们的CPU-胡伟武

一个关于通过/proc与内核通信的问题

关于BOOT引导的问题?

中断描述符分析小问.

分析块设备缓冲区结构

kmalloc等内核常用函数的文档有没有

关于buffer cache的问题

进程,轻量级进程,内核线程,库线程的切换分别是由谁来负责,切换时都分别保存了什么信息?

为什么 virt_to_ phys( )不能用

分析sigframe的结构

分析信号的执行过程

请教核心0.01中head.s的检测A20地址线代码的意义

我在Kernel里定义的函数,在用户空间里怎么才能用呢

关于内存布置的问题

lilo与bootsect.s,setup.s的关系

有谁知道这个结构多少字节

start_kernel(void)函数的分析

分析虚拟文件系统的结构

slab

在驱动程序中可以直接从I/O内存读数据到用户空间?

start_kernel()后是怎么工作的

内核程序中分配内存最大是多少?

有关gcc内嵌汇编代码的问题

请问.S和.s文件的不同?



关于汇编
http://www.linuxassembly.org
Linuxkernel推荐


关于AT&T格式:

http://www-106.ibm.com/developerworks/linux/library/l-ia.html
lisoleg推荐

关于汇编:
http://www-aig.jpl.nasa.gov/public/home/decoste/HTMLS/GNU/binutils/as_toc.html
http://www.linuxassembly.org/resources.html#tutorials
lucian推荐


别谢我,这都是从http://www2.linuxforum.net/ker_plan/index/main.htm
找到的,有空去看看。

///////////////////////////////////////////////////////////////
你可以下载bochs:http://sf.net/projects/bochs/

其中就有BIOS的汇编源程序,你认为是C也可以(C也内嵌着汇编嘛!).

不过,这个BIOS比较简单, 功能比较弱.
/////////////////////////////////////////////////////////////////


__builtin_constant_p 与移植

象CTRL~C和CTRL~V这样的中断信号内核中何时被初始化

A20可以通过两种方式进行控制



大概的意思是将代码段放到.text.init中去,至于为什么要这样做,这里有对于__section__的讨论。
http://gcc.gnu.org/ml/gcc/2000-05/msg00536.html
你可以去看看。




calibrate-delay

最大分配128K?

Makefile中$<是什么?

请教有关usb编程的问题

[ chstar ]linux内核内存分配初探



links:
http://developer.intel.com/design/pentium4/manuals/

documents in pdf is good and enough for intel asm


Linux内核源代码漫游

386硬件支持得任务切换如何进行

linux0.0.1的内存转换问题

请问在内核中 HZ 是干什么用的

哪里有2.2内核的写驱动程序的文档

Ramdisk 和initrd,有什么用处?

诸位高手,看内核,从哪个入手的

哪位高人了解此函数usb_submint_urb

do_page_fault( )的几点疑惑

硬盘启动时,setup存储在哪里

内存转换问题

再问有关存储的三个问题

为什么动态连接库一定需要一个符号连结文件

我在查找内存分配错误的时候,找到了这 (ld 的_end)

build kernel步骤

.text .data .bss不要了会怎么样?

漫谈linux ieee1394

linux下能不能用c++来开发驱动程序呢

kpiod进程是做什么用的

关于内核内存初始化

请教一个linux下分段与分页的问题

spinlock_t?

请教mmap()的问题

有关ramdisk的问题

关于在proc下创建一个新项源代码的疑问

一个关于通过/proc与内核通信的问题

proc 文件系统分析(二)

对proc文件系统的分析(三)

proc文件系统分析(四)

关于南桥82371AB中断

创建进程和添加进程代码的概念

那位朋友讲一讲Linux对GDT的布置吗


增加系统调用的资料
http://202.113.16.117/cgi-bin/bbscon?linux/M.1005336295.A=4959

锁的实现
http://groups.yahoo.com/group/lisoleg/files/MultiProcessing/





请教关于内存访问的问题

更改后的发送、接受、转发包处理结构框图

386 boot代码分析

请教关于assert

对 proc 文件系统的分析(一)

逻辑地址如何转换为线性地址

Linux启动问题

内核中无法睡眠?

一个内核网络代码的问题:skbuff->dst_entry跟rtable结构是什么关系?

pci_bios_read_config_byte

ODL-One Disk Linux

[jkl]宏#与##(例子见BUILD_IRQ)

bbs.zsu上讨论的一个关于arp的问题,至今没有好的答案

有关linux引导扇区的问题?

谁能介绍一下变量的属性是如何设置的?

内核程序无法睡眠(续?)

bbs.zsu上讨论的一个关于arp的问题,至今没有好的答案 (又贴了一遍?)

分析serial.c

HZ问题

就是为什么可以用send_sig_info使阻塞的函数返回的原因

为什么一执行lidt就重起

只知道可用宏put_user和get_user macrocs使内核能够存取用户内存缓冲区。但究竟怎么用呢

setjmp、longjmp的问题

在内核中如何得到当前系统的时间

给个内存管理的整体概念先

请问各位,printk()将调试信息写到哪去了

limit标志表示分配区内可分配页面的富余程度

双向规则是什么意思

What's diefference between vmlinux & bImage

rt cache的问题

用户程序可以自己将自己切换到内核态吗

keywords: execve,vfork,kernel_thread,vm

如何在系统加一个自己的ioctl命令号

Linux Notification 机制的分析

请问__builtin_return_address在哪儿定义的

打开A20地址线为什么要写0x60口?

[ Feiyun ]linux/arch/i386/kernel/head.S (2.4.9)

[ Feiyun ]linux/arch/i386/boot/bootsect.S (2.4.9)

[ Feiyun ]linux/arch/i386/boot/setup.S (2.4.9)

[ Feiyun ]linux/arch/i386/boot/compressed/head.S

[ Feiyun ]Linux makefiles (Linux 2.4.9)

在unix下可以加入自己的协议模块吗?......

如何将Module和整个内核分离开?

中断服务程序怎么没有运行

something about neighbour Table

关于netfilter 里nfcache字段

建议用module_init()宏来调用card_init

请教一个内存管理的问题

中断描述(todo用)

stack of kernel mode and user mode

交换缓冲

分析信号的发送过程

Signal 11 while compiling the kernel(转)

堆栈上的current指针

一大堆序列号呀?

关于local的含义

关于nmap的问题 (tcp)

LINUX TCP层分析之一-------------主动连接发送SYN包的tcp_connect

缺frame和缺page table是如何区分的?

C.O.W到底到那一级

do { } while (0)是什么意思

怎样得到kmalloc分配的内存地址的实际物理地址

asmlinkage

怎样获得系统的精确计时?越精确越好

这个bug是由于内核将用户进程在内核空间引起的页保护故障错误地当成了内核vmalloc区域的页不存在故障所致

Linux下IrDA方面的开发资料

关于smp的疑问

TLB到底存储了写什么

有关任务切换的问题

内核空间的预映射是怎么回事

我知道肯定是有些我没注意到的代码改动了skb->len

about improved long long code sequences by linus

内核和用户态怎么共享内存

网络部分的proto_ops的方法是如何重载的

裁减内核到多少K等等,那么具体是那个文件的大小呢?

请教如何精简LINUX到8M以下

有关等待队列的问题

内核方式是否需要三级页表转化
[目录]


list4



elf_check_arch是什么函数

请问#APP和#NOAPP是什么意思

什么是L1 cache aligned

d_cache 与 inode _cache的问题

linux网络部分重要结构体联系图

linux网络部分重要结构体联系图(?)

用\续行, NR

请问ENTRY( ... ) 语法

在中断例程中如何与其他进程通讯??(用信号量还是全局变量)

内核模块编译需要优化

About task->active_mm

驱动程序怎样锁定内存

linux 2.4.x initialization for IA-32 howto

Booting Linux for IA64

About task->active_mm (bnn 续)

About Our China's CPU-Fang Zhou

我在你的帖子中注释了一下,不一定对.

RTL8139 驱动程序解析(转)

Sun YongYao--发送、接受、转发包处理结构框图修正

Linux内核分析文档计划

windowsXP和linux在系统结构上谁更好?

TCP协议的核心守护进程echod

请教一个中断方面的问题

再谈page cache和buffer cache

稀疏文件是怎么的概念呢

请教frees/wan的原理问题

块设备文件和普通文件有什么区别?普通文件不也是放在块设备上的吗?

关于I/O请求优化

请教一个MBR的问题

page cache与buffer cache的关系。

请问系统中有几个针对磁盘读写的request队列?

TCP传输控制块的初始状态

net_rx()网络数据流向

这样的内核程序能阻塞吗

Linux设备驱动程序的中断

页故障的处理过程

网卡的初始化过程

2.4内核网络部分的初始化

2.4网络代码阅读笔记(1)

2.4网络代码阅读笔记(2)

2.4网络代码阅读笔(3)

2.4网络代码阅读笔(4)

2.4网络代码阅读笔记(5)

RAM DISK 和 DiskOnChip 有什么关系?

文件映射区域的分配方式

未经accept的socket是如何处理数据的?

如何对拷不同长度的socket缓冲区?

Linux内核地址空间的布局

netfilter一问

请教GDT

我非得用SMP吗?

关于LDT的疑问

请教一个tcp连接性能的问题?

能解释一下A20地址线是怎么回事吗?为何要打开?

如何显示GDT,IDT,LDT表的内容

物理内存的分配结构

重入的疑惑

高端页面的映射

2.4.1内核中页结构PG_refrenced标志什么时候置位?

怎样在内核中写文件?

一些问题(网络方面)

网卡驱动程序(inw,insw提问)

向所有做内核分析的朋友提个问题

netfilter这里还是不明白

交换页分配方法

在网卡驱动程序中如何实现包转发?

请教一个内核寻址的问题

非分页、连续内存?

FreeBSD内核文件的结构

FreeBSD内核的编绎方法

virtual address to physical address - HOWTO?

关于printk的一个问题:

关于内存管理的一个问题

如何加一个文件系统到内核中

如何实现临界区

关于引导程序问题

不让root process使用1024以下的端口

get_current()的问题

hard_smp_processor_id()与smp_processor_id()有什么区别?

新兵笔记--ULK(C3) Exception Handing

TCP包处理的后备队列(backlog)和预备队列(prequeue)

解释一下__wait_queue_head结构?还有wake_up

TCP包的校验和

内核日志和内核控制台

minix文件系统的物理结构

关于buddy system的几个问题?

void page_cache_init(u_long mempages)

信号能否重入?

static __inline__和extern __inline__有什么区别?

void __remove_shared_vm_struct(vma)

spinlock

TCP PAWS处理

About Major Number

linux内核启动到底做了什么的资料

listen和accept过程的简要描述

真正的信号高手请进

有关页寿命的疑问

完了,没耐心看了,大侠帮忙,给我讲一下tcp_connect就好!

linux中如何设置stack的大小?

关于struct

GET_CURRENT还有疑问

谁有ARM远程调试的stub的源代码

[精华] 辞职申明 和 学习内核的一些感想

分析calibrate_delay过程

请教内存初始化的问题(reserve mem)

详细解释一下Thread(线程)???

linux 支持内核线程吗


asmlinkage
http://www.kernelnewbies.org/faq/index.php3#asmlinkage.xml




linuxdoc.org的对于Linux线程的一个比较完整的回答

行内汇编中,"o"(var)(不是零)"o"是什麽寄存器,谢谢!

如何在模块中增加系统调用

red black tree

iptables内核数据结构关系图

[serial]中断问题?

console注册与tty注册的区别

make_pages_present(),不知它的具体作用是什么

宏mb()是什么意思?

这是我搜集的linuxkernel兄的大作

linuxkernel兄大作之2

TCP层向外发包时为什么总是将skb进行克隆?

内核防火墙iptables的一条规则(图)

struct dentry中各项意义

关于read_super_block 的一点疑问!

端口读硬盘信息疑问

asmlinkage这个关键字是什么意思

fork()与vfork()的区别

pg0 的问题?

Linux的启动盘与DOS中的启动盘不一样

__attribute__是什么东西,怎么用呀


800 种文件格式
http://www.wotsit.org/default.asp


在linux下读取、修改(如果可能)主板bios的内容

制作基于软盘的Linux系统

调试信息过多造成dmesg无法完全显示怎么办?

Ctrl-D被按下后,产生什么信号?或者根本不产生信号?

关于中断响应

linux内核概念问题,欢迎讨论

介绍一下e820 map的内容

新兵笔记--ULK(C4) Initializing the idt

_set_gate分析

Linux A-Z书中实例的源程序

关于end_mem的一点疑惑

新兵笔记--ULK(C2) beta版

防火墙分析总结

网络包复制

head.S 中的问题

现转载对我帮助很大的文件,十分感谢那位作者!!!

关于MBR的一个问题:)

ELF的 e_ident[EI_NIDENT]是什么意思?

Makefile中Config.in是什麽文件

virtual address space and physical address

time quantum

Lisoleg的昨天、今天和明天

System.map中的几个标志T,t,B,b,..的问题

内存初始化mem_init中的两个问题


ldd2
Chapter 9: Interrupt Handling
http://www.xml.com/ldd/chapter/book/pdf/ch09.pdf
C & C

mmap
http://kernelnewbies.org/code/mmap/



内核代码可以读取文件或共享内存吗?

内核镜像是如果映射到3G去的?

进程的页目录表中的768项之后的项是不是都是有效的?

内核页目录

关于进程内存空间的映射问题*

Special expression from linux-kernel mailing list

discussion about MaxNumber Of Threads From KernelT

核心态对用户态的数据访问

内核是怎么访问PCI配置空间的?

这么做行不?(请大伙抽空看看)内核中共享内存的如何使用

which Compiler to Use for kernel compiling

GLIBC被多个进程共享疑问

fork()一个子进程时,IPC资源是否可以被子进程继承

malloc()在内核中对应的函数是什么

支持热插拔的驱动程序的框架疑问

回答“关于页面映射计数”

关于内核中加入自己得代码得可行性

用户空间和内核空间之间的数据传输

为什么需要copy_from_user?(转载)

内核启动过程问题(rush老哥看过来,我知道你看过启动代码)

软中断图解

早期的任务切换

谁能说一下iptables.c 和 netfilter.c 的关系?

最近看mm的人很多啊,和大家分享我给自己写的教程

一个疑问 --《Understanding Linux Kernel》

请教linux源代码中的数组问题

skb_reserve - adjust headroom

UNIX核心介绍

分配虚存空间有了mmap和munmap为什么还需要sys_brk?

Write Your Own Operating System Tutorial && FAQ !

some valuable HowtoS for KernelStudy

To get intelX86 docs

如何修改IP流程中的数据包???

请问怎样让我的一个可执行程序在开机过程中自动执行

关于ctags的问题?

关于copy sk_buff的问题

Kernel2.4.18的交换机制分析

系统初启时中断向量表放在哪里

swap in (2.4.0)

handle mm fault (2.4.0)

swap out for Kernel2.4.0

实时进程的时间片赋值问题?

关于驱动程序与实际设备的关联问题

本论坛关于时钟的一个悬而未决的问题

A20是什么东西?

bootsect.s和setup.s问题集锦


bios ox15中断ah=e801
http://www.ctyme.com/intr/int.htm


gas 文档
http://www.gnu.org/manual/gas-2.9.1/


用什么办法可以将系统的gdt给显示出来?

这么生动的赋值方法

在0x104000处的硬件参数表的详细说明

转贴一篇关于mm的文章。

这不是自己覆盖自己吗

do_irq()的问题

misc两个head.s, end, __PAGE_OFFSET

关于interrup和exception

一个setup.S与head.S连接的问题?

终于独立看懂了一个内嵌汇编,兼问个问题

draft BIOS use document for the kernel(里面有讲e820)

start_arch中copy e820_map的问题

2.2的一些task数据结构在2.4里是什么?

关于bottom half

mem_map array

__init 在那儿定义的

有个prototype用了pgprot来做变量。刚好pgprot_t的数据结构定义里又用了pgprot

fixed_addresses 的排列

how to smp

空闲页面free_area结构初试话一问?

setup_arch中的reserve resource!

slab里的free buffer

list_entry的疑惑

linux内核中的同步和互斥专题 (1)

linux内核中的同步和互斥专题(2)

setup_arch()中PFN_UP,PFN_DOWN,PFN_PHYS这三个函数是什么意思

cache_cache里的slab的大小是不是一个page

几个变量的意思(page,mm)

remap_page_range()的疑问!!??

start_kernel 里的 init_modules()问题

linux 内核原代码汇编中 .align 伪指令的意思是什么?

关于task_union

多个内核问题提问

请问关于页计数和页的使用情况的关系的一个问题?

CR3问题?

Linux字符设备基础(转贴)

又是start_kernel里的变量定义问题

ioremap是干什么用的?

Makefile :这行为什麽要有( )

关于flash的一些问题!

100个最佳Linux站点(推荐)

判断process是在user mode还是在kernel mode的标准是什么?

关于ext2的几个问题

谁知道unistd.h里的这段解释是什么意思

关于skbuff得资料

bdata.last_offset和last_pos在何处初始化?

内存管理问题?空闲页的bitmap是干什么的?

noncontiguous memory究竟指的是什么?

再聊聊free_area的bitmap

关于gcc内嵌汇编的一个问题

对bh_base[32]和bh_task_vec[32]的一些猜测

明白了free_area的bitmap了!

软中断softirq

路由问题


http://linux-mm.org/



super block是用来干什么的

研读Linux内核主要是为了做些什么?

Linux内核抢占补丁的基本原理

page描述符起名page,确实不好

请教一个术语--”堆“

#if 1肯定是满足条件,else后面的有什么用

内存管理中常常见到“round up”,它具体是什么意思呢?

我也来问几个关于gcc内嵌汇编的问题

"aka inode"? that is "as know as inode"

谁知道”16M以上的低位物理内存“到底有多大?占了多少百分比物理内存

如何做源程序的补丁?

local_table是什么?在哪里初始化的

汇编call命令调用C函数时,参数是如何通过stack传递的

一段boot的测试代码,有问题,来看看……

读do_basic_setup时碰到的问题(pci)

start_kernel里面的prof_shift干什么用的

HA开发,增强内核,学习好方式

关于spin_lock

vfs的索引节点号i_ino是怎么管理的

ip_route_input

想听听大家是怎么读核的

我的分析是应该首先发送第一片

谈一谈我的内核精简计划,以及目前的进展情况

IP分片与重组的分析与整理

“Practical Unix & Internet Security"。号称是”圣经”式著作

Linux源代码中的几个缩写

几个网络有关的变量

为什么不立刻用长跳转指令重载CS段寄存器呢

当物理空间的的线性地址>1G的时候,虚拟空间如何解决

请教关于睡眠和唤醒进程的两个函数...

如何读取系统物理内存

freeswan是什么东西呢?

ip_rcv_finish函数一问

inflate.c文件的fill_inbuf()函数中insize的赋值的问题

裁减Linux内核的心得体会

linux下的一个文件的最大尺寸是多少?由什么决定?windows下呢?

请教一个Netfilter的应用问题

使用register_blkdev( ), 做完insmod后,为什么/proc/devices没有我的块设备

应用层->pci->disk,这种情况disk的驱动该如何写?

请教一个内存管理问题(挺典型的一个问题)

快速找到connect,socket函数实现的代码

致地讲一下IP_FORWARD那个函数

如何在内核中唤醒和睡眠用户进程??

如何打开.patch文件?

关于IP_FRAGMENT的问题

How It Works: DOS Floppy Disk Boot Sector

How It Works -- Master Boot Record

Minix代码分析之一:引导扇区代码

同一程序加载执行空间分布一样吗?

锁和信号量

内核与根文件系统

读kernel时容易漏掉的几个细节

推荐课题及"目的"

devfs_entry是干什么用的

2.2里的vfsmntlist,2.4改什么了

如何直接向网卡读写数据包?

如何在内核中加入PF_KEY套接字?

enable_irq(),disable_irq().和cli(),sti()之间有什么区别

关于slip模块中使用ioperm

gfp是什么的缩写阿?

关于中断例程中的子进程睡眠问题

连续空间分配的大小问题!

skb->data为物理地址。为何在第一句中又进行转换

可加载模块写文件出现的奇怪现象 (程序设计经验)

SYMBOL_NAME( )

zone_struct

关于物理地址和虚拟地址的问题

请问linux文件系统层次结构的问题

kmalloc(size)返回的是物理地址,还是虚拟地址

VFS初始化过程中的一些问题

how to use create_proc_info_entry

中断上下文,进程上下文和任务上下文之间的区别---需要进一步深入

混杂模式

video adapter's ROM

本论坛和mailing list的区别!

很高兴能为大家服务, 及一些想法,大伙来参谋参谋

mkfs和fdisk的根本区别在哪儿?



amp - bios 资料
http://spr2.net/up_loader.php


内核等待队列机制介绍
http://www.linuxaid.com.cn/engineer/ideal/kernel/wait_queue.htm
linuxaid的网友ideal竹叶收藏




bootsect.s把大内核复制到高端内存的时候,还是实模式.如何实现的

page-》list

OWNOS操作系统设计书

need_resched 是怎么使用的

请教高手一个关于网络全局变量dev_base的问题

关于页面映射计数的讨论

free_area_init_core对其中struct page的virtual赋值为当前struct page所描述的物理页框的内核虚地址

再和4-1的反做与运算得到的又是什么呢?

__init , __exit , module_init, module_exit是什么意

写个小的OS项目,如何?加之一些感想(完全兴趣与学习目的)

mem_map数组

怎么解决这个include头文件时出现的矛盾

内核命令行是何时在何处传递给内核的

A20是什么?

关于brk与mmap的问题--已解决见另外一个帖子

在mm中,vm_area_struct为什么只用了单链表?

为什么要copy_from_user?

关于网络中接收软中断(net_rx_action)的问题

对UDP包在对包不做任何改变的情况下用udp_check()函数重新计算,结果与原校验和不符?

怎么初始化一个PCI设备?
[目录]


list5



关于queue_task的一个问题


打造软盘上的linux
http://www.linuxfromscratch.org/ to define your own OS distribution.
www.linuxdoc.org to get howtos you want


可以参见下面链接处:
http://www.linuxeden.com/edu/doctext.php?docid=1582
Linux内核调试技术介绍 :)希望有帮助

我根据这个
从源代码打造一个最小化的Linux系统实作篇
http://www.linux.org.tw/CLDP/gb/mini/BuildMin.html#toc6
编linux,但不知道如何安装到指定路径


为你的Linux快速编译所需要的模块
by 刘军民 ([email protected])
http://www-900.ibm.com/developerWorks/linux/l-tip-prompt/tip13/index.shtml





copy_e820_map()问题

合并相邻区函数merge_segments()--2.0.33

关于module的问题

关于卖广告和我对linux的一点意见 :-)

timer_interrupt()函数看不明白,请指点

如何驱动键盘灯?

schedule(void)函数一定是可以重入的,对吗?

mmap (2.4.0)

Linux: Using Video RAM as System RAM

Linux: -aa VM in 2.5

一个linux驱动程序的问题

大侠指点一下get_pid的算法?

怎样修改PIPE_BUF的大小限制

操作系统理论的探索 (序言部分)

问题出在你新编译的内核不支持模块的版本识别

版本2.4.16下path_init和path_walk,link_path_walk分析报告

怎么理解记录块缓冲区的内容是一致的?

mark_bh(TIMER_BH)和timer_bh( )

当今日新月异,“对象”横飞,潜读LINUX源码除了提高自身素质外还有哪些令人兴奋的应用前景

一个困扰很久的问题--paging_init()

Linux核心调试环境的搭建

磁盘扇区与磁盘块如何定义以及如何区分使用?

也表处理宏的定义释疑

为什么学习LINUX源码、学到什么程度及怎样学习才能在最短的时间里取得最大的收获?

关于时间中断的一个问题

事物总是从陌生开始认识

关于/dev/shm

怎么在本机上配置一个lxf看源代码?在下对apache网络配置一窍不通

哪位知道哪有/proc的手册么

请问叶框0(0 ~ 4K物理地址空间)是否一直未使用?

剖析Linux任务切换的细节。

请问linux下有没有好的看源码的工具?

system call

低1G虚存是做什么的?

bootsect,setup,head的总结

增加系统调用的血汗经验 贡献给大家

How to use ramfs ???

能否将framebuffer的源码说一下

entry.S(续)

谁知道系统刚启动时BIOS建立的参数表

指点一下《Understanding the Linux Kernel》中的难点

Linux: O(1) Update For 2.4

links4RealtimeLinux

A new era: Carrier-grade Linux for Telecommunicati

lwIP - A Lightweight TCP/IP Stack

sys_arch interface for lwIP 0.5

Raw TCP/IP interface for lwIP 0.5

links4EmbededLinux

man slabinfo , 对其中的SMP部分不得其解

请教一个关于Linux处理外设中断的问题

异常处理函数的压栈

使用VIDEO_CHAR检查内核启动时在何处挂起

为什么用files->fd[fd]而不用files->fd_array[fd]

Linux 下网卡混杂模式

修改时间片DEFAUT_PRIORITY,为什么当系统

Lan网卡收到包后,应该比较mac地址。

如果手工触发request_irq登记的中断服务

请介绍关于cacheline相关知识,比方cacheline_pingpong

网络包的接收过程

关于pt_regs

How the PCI Hot Plug Driver Filesystem


iptable用户空间和内核关系的问题?

LINUX文件系统分析草稿--面向微内核的OS设计

希望大家讨论讨论?我们学源码要学到什么时候才能收手?

中国有多少人在做关于kernel的开发(真遗憾偶挖了坑没有人跳进来)

Linux内核中对硬盘控制的有关资料

head.s 中“jmp 1f”的1f是什么意思?

how to get size fo ram?

谈谈自已所尝试的内核分析方法

linux中进程与线程的问题

如何在LKM中执行一个用户空间的程序

为什么32位的系统不支持大于2G的文件?

Linux上是否有类似winsock2上的SPI相对应的接口层?

请问如何驱动这个怪物

用双网卡如何同时传输数据(?)

在内核中如何进行大空间内存的分配??

请帮忙修改程序(do you like this?)

关于Login的一个问题(basic)

offsetof怎么理解

为什么每个磁道有63个扇区,而不是64个

ldd2的Backward Compatibility章节

LDD第三章的一个问题

如何根据pid得到进程的名称

如何将内核编译成无版本检查的呢

about init section (vmlinux.lds)

udp报文转发不了,而icmp报文可以,为什么(undstand it?)

张贴一个别人推荐的很好的内核线程的例子

2.4.16软中断分析图 (again)

about interrupt number : 15 or 16

关于引用内核输出符号的问题

cache不命中的情况

请教/proc/pci(有个pci图)

连续内存与离散内存的问题

请问怎样能够得到一个进程的所有的通讯流量

如何让root不能修改其他用户的口令?

slab对象的地址可以不通过页表映射吗?

atomic_fool_gcc的作用是什么?如果不加gcc可能怎么处理?为什么2.4中没有了

关于缺叶中断的内存管理1

slab对象的地址可以不通过页表映射吗(...?)

about "jmp *%eax

insmod的模块放在哪里?

linux smp 求救

8259A可编程控制器在linux上的初始化

在网络层构造UDP包好像不太合理

如何将内核中printk的输出到屏幕上

Linux在i386处理器在PAE模式下寻址

shell系统流程(哈哈)

网上那有关于a20线的详细介绍

调度 schedule 概念

zone balance 是何意

多一些供献,少一些..

大家看看,是不是《情景分析》说错了?(文件读写)

欢迎calfe,garycao加入版面管理



裁减libc的两个工具
一个是Montavista公司的lib优化工具
基本思想是扫描你的应用程序的库函数调用.将你的应用程序没有用到的库函数删除.
一个是IBM组织的一个minilibc project
http://www-124.ibm.com/developer/opensource/linux/projects/minilibc/


这儿摘录了其中的几个非正式观点,共享.

Some write for the fun/eduction of it and want to to it all.
Some have ego problems and must do it all.
Some do not know how to adapt existing code so they do it all.
Some have really wierd systems with nothing close so must do it all.
Some have not searched enough to know of simulair systems so must do it all.
Some have ethical/comercial problems with licenceses and must do it all.
Some rename code and say he did it all.
Some DO use existing code.



uclinux 分析
http://hhcn.org/uclinux.html


gnu/hurd 介绍
http://www.gnu.org/software/hurd/


几个手册
http://www.refcards.org


netlink socket资料
http://qos.ittc.ukans.edu/netlink/html/


截包零拷贝能不能实现

收集内核FAQ了! 大家如在版面上发现适合的话题请转载一份贴在这儿

local_irq_disable()在smp下究竟是关了所有处理器的中断还是当前处理器中断

读setup.s中的几个问题(openarm的几个连接)

spinlock的设计和实现

在linux中一个进程如何修改其它进程的数据(关注)

Linux的页式内存内存管理

head.S 分析

内核信号量的简单演示(几个进程并发调用down的情况 ppt)

i386内存结构示意图(intel and mine)

内存观点(2.4.0)(精华收集和自己的一点理解)

如何关闭linux的单用户模式

flush_tlb_xxx()这类函数都是干什么用的

设备初始化问题(basic)

System.map有什么用处?

Linux 内核笔记 -- 信号

如何读取指定硬盘扇区号的扇区数据?

关于swapper的问题

linus关于debug tools的一篇旧帖子

What is the meaning of "volatile"?

重新编译内核后不支持VFAT的中文文件名?(内核?)

忙里偷闲:Slab简要分析(一张图)

volatile的含义

kgdb文档

引导与启动部分之head.s部分的注释

系统引导与启动(suzhe前辈的一次报告)-----by linux3-III

系统引导与启动之深入lilo-----(zz by linux3-III)

有那位大虾做过内核裁减

jiffies耗尽后会发生什么

ip层及tcp层的处理究竟谁在做

关于tcp/ip不明白的地方

请问在Linux下如何得知每个CPU的使用率等信息

[ jkl ]__attribute__

问几个很弱地问题...

Ext2 文件系统的硬盘布局

如何修改或去掉以framebuffer方式启动的内核的小企鹅的标志

__copy_user的实现原理

boot loader 的一些研究

ip数据包接收发送

Linux的存储管理(一)

内核启动时传入的参数存放在甚么位置

编译linux内核及注意事项(支持ipv6)(看起来步骤详细)

我是怎样精通了linux内核的?(??)

pci_read_config_byte

[精华] LDD2-CH12-块设备

<<Linux设备驱动程序第二版>>第八章 硬件访问

<<Linux设备驱动程序第二版>>第六章 时钟管理

《Linux设备驱动程序》第二版译稿 第九章中断处理 by kongming etc

<<Linux设备驱动程序第二版>>第九章 中断处理

<<Linux设备驱动程序第二版>>导读计划

Linux 设备驱动程序》第二版翻译稿样章(前言)

《Linux 设备驱动程序》第二版中文翻译稿样章(第 6 章 时间流)

《Linux 设备驱动程序》第二版中文译稿样章(第 4 章 调试技术)

Linux设备驱动程序(第一版)完整勘误表

编译内核出错(为什么这种话题很多?)

gdtr,idtr,cr3乃至页目录表,他们之中的地址都是物理地址吗?

vm area的地址映射(多看些讨论有好处)

difference between "return" and "exit"?

关于booting部分的一些链接

[精华] Linux 内核笔记2 - 进程调度

Linux的存储管理(二)

linux内核调试(讨论)

推荐一本浅显易懂的书

此图如何看(e820)

论文“Linux内核进程调度与控制 --源代码分析与研究”

怎样用bochs和vmware测试内核

关于使用kgdb调试内核

Linux中的NAT能同时支持多少连接?

为什么会kernel panic(多多讨论)

task_struct结构中的comm成员是代表什么意思啊

《Linux 设备驱动程序》第二版中文译稿样章(第 15 章 外设总线概述)

注册设备文件处理函数的方法有问题(算个经验)

根文件在那里初始

关于in_interrupt的问题

为你的Linux快速编译所需要的模块 (zz)

关于《Linux 设备驱动程序》第二版中文译稿样章的说明

MODR/M and SIB,whar's the means?

What's the maximum preemption latency

LINUX下的设备驱动程序

[转贴]如何编写Linux操作系统下的设备驱动程序

关于vmalloc的疑问!!!

into entry-armv.s

设备驱动程序如何在启动时加载(faq)

bonding.c的原码分析

驱动开发的基础问题(硬件厂商提供驱动吗?)

LINUX的网络设备中,利用master 和slave来实现负载平衡

学习内核和驱动需要多少硬件知识

Linux 内核笔记3 --内核同步-- 第一部分

uClinux - BFLT Binary Flat Format

[精华] LINUX实时调度研讨。

mmap函数问题

Setup Terminal Systems

首席培训官教新手使用最新的内核

PG_active和PG_referenced

内核中的所有struct semaphore型变量都共享一个spinlock_t变量

进程互斥,PV操作

ldd2第十五章PCI总线阅读随记

Write Once protocol for multi processor

一个pci插槽的pci设备的中断是不是固定的

do_IRQ()问题

page->mapping和page->buffer分别是做什么用的?

do_softirq()问题

Linux内核进程调度与控制 --源代码分析与研究 by minipuss

可否核态和用户态直接进行通讯

Linux 内核笔记3 --内核同步-- 第三部分

Linux 内核笔记3 --内核同步-- 第二部分

How to open/read a file in interrupt_context?

Why "scheduling in interrupt" ?

when should i define a variable with "volatile" ?

Kernel/Device Driver questions?(mmap,kmalloc,irq)

"gdb-serial.txt" from "gdbstub.tar.gz"

gdtr中为什么放线形地址 ,而不是物理地址?

代表设备文件ext2_inode节点保存了什么信息保证找到设备的?

[精华] Linux 内核笔记3 - 内核同步(完整版)

内核与用户进程通讯的几种方式比较

我把内存管理FAQ重新整理了一下。

“逻辑地址”、“线性地址”、“物理地址”的区别和联系

关于在内核中向应用程序发送信号的问题

实际一个程序同时可能有多个进程,所以一般由pid找程序名较合理

refill_inactive()函数

又关于page->buffers和page->mapping

关于video.s的疑问

关于内存的几个问题

怎么添加上ip伪装和ip端口转发

Running Linux on Linux(User-Mode Linux )?

如何生成可被lilo引导的image?

同志们,为什么浪费0~08048000的进程地址空间不用

关于添加系统调用的问题

作linux向另一个硬件平台的移植工作应该如何入手?要做哪些工作(链接)

kernel精华!(文档包)已经上传到我公司的服务器上了! (有钱出钱)

Kongming给出了ldd2中文版的部分章节,大家抽空读读,对其中细节给点建议

请问如何使网关支持内网上任意IP的上网通讯?

那么MAIN这个进程是什么时候fork的呢?

怎样在内核中读写文件?

LINUX ALLOCATED DEVICES

Interview with Robert Love

请xshell结合这段代码对于cacheline做些解释

HZ 表示每两次时钟中断相隔的微秒数

head.S 中的__PAGE_OFFSET 和其他

谁能讲一讲Console和tty之间的关系?

wmb是干吗的函数 (zz)

这是我在学习设备驱动程序的一点问题?希望大家赐教!

2.4.16下网络结构全景图1.0 (包含模块接口变量名称)

谁有真正能用的Linux启动盘的资料?

Encrypted Ethernet Bridging

More Explicative Kernel Tainting

setup.S中"in al #0x92" 0x92端口的含义

BIOS 数据区域(BDA)位置在哪里?

Why pay someone to port Linux for you?

中断上下文中如何实现文件读写?

wait和completion数据结构的解释

user_struct

谁能解释一下tty,tty0,ttyS0,console,终端,控制台这几个词

Linux MTD源代码分析

请问内核中读写配置文件的源码在哪个目录的哪个文件中

写时复制中关于empty_zero_page的问题

Linux 内核使用的 GNU C 扩展

我的一个Object-oriented operating environment 设想.

是不是所有的包都要用到这里的match,target,在哪个hook用,看得糊涂了

为什么在转发之前需要COPY一下SK_BUFF???

转载一篇文章"泛系、自由与“一、百、万”工程"

2.4.16内核icmp报文处理分析

请问各hook函数之间怎么传递信息:在同一个hook点上的和不同hook点上的函数各自如何传递

关于中断问题的请教

大家有否考虑在Linux上实现一个硬件抽象层?(空)

Linux Kernel Module Programming Guide

可动态编译的嵌入式操作系统

CMOS里的东西在启动后是不是COPY到内存中去了

问个基础的问题

vmalloc的问题

超级宝书http://suse.chinasec.com/ulk.pdf

哪有LINUX内核分析(进程部分)的文章?

写时复制的问题

守护进程里如何发声?

关于swapper_pg_dir初始化的问题

linux 与 minix的共存

我编驱动程序的时候的问题(PCI)

如何实现内存映射?

关于在转发前sk_buff结构copy的问题

Are you familiar with Architecture or OS ?

为什么谁都可?
[目录]


AKA推荐书籍

====================================================================
《The Design of The UNIX Operating System》 Author: Maurice J. Bach
中译本:《UNIX 操作系统设计》
---------------------
翻译者:陈葆钰等
北大版: 32开,黄皮,9.40¥
机工版: 16开,褐皮,30¥
---------------------
一本老书,但绝对是bible级的。其内容覆盖了经典UNIX操作系统中的所有基本概念
以及设计思想。阅读者最好能懂一些基本的操作系统理论知识。
====================================================================

====================================================================
《Unix Internals - The New Frontiers》 Author: Uresh Vahalia
中译本:《UNIX 高级教程:系统技术内幕》
---------------------
翻译者:聊鸿斌等
清华大学出版社出版,16开,桔皮,58¥
---------------------
一本新书,也是bible级的。其主要特点是80%的内容都是现代UNIX操作系统的新思想
和新概念。对各UNIX大家的精彩设计点都有很详尽的阐述。阅读者最好先看看贝奇那
本书。
====================================================================

====================================================================
《Linux Core Kernel Commentary》
---------------------
Author: Scott Maxwell
---------------------
有中译本,但具体信息不详 对照源码讲Linux内核,版本还比较高,2.2.X。有些新
东西可看看。可惜没讲网络部分的实现。
====================================================================

====================================================================
《Linux Kernel Internals: 2nd version》
---------------------
Author: M Beck ...
---------------------
目前无中译本 以数据结构为主体来介绍内核,内容很丰富,但版本太低(2.0.x),有
些陈旧的东西容易令人误入歧途。
====================================================================

====================================================================
《The Design and Implementation of the 4.4BSD Operating System》
---------------------
Author: McKusick, Bostic, Karels, Quarterman
---------------------
目前无中译本  讲述BSD操作系统最权威的书。书的作者亦即BSD最早的几名开发者。
====================================================================

====================================================================
《Linux 操作系统及实验教程》
---------------------
作者:李善平,郑扣根(浙大教师)
机工出版
---------------------
有些内容不错,讲得还比较深入。也缺少网络部分的描述。
====================================================================

====================================================================
《UNIX 系统下的80386》
---------------------
作者:周明德, 张淑玲
清华出版
---------------------
讲X86的体系结构。要想看懂/arch/i386下的辕马,最好先看看这本书。
====================================================================

====================================================================
《UNIX Systems for Modern Architectures》
---------------------
Author: Curt Schimmel
---------------------
目前无中译本  如果想了解辕马中SMP的实现。可参考看这本书。
====================================================================

====================================================================
《保护模式下的80386及其编程》
---------------------
出版社:清华大学出版社
---------------------
本书全面地介绍了80386的结构。首先是80386实模式和保护模式下的寄存器集和
指令集,接着从保护模式下的虚存管理、保护级、多任务支持、中断和异常等方
面深入地剖析386的强大功能,再接着提供几个典型的编程实例以及一个完整的从
386加电初始化开始到形成一个有基本的多任务和虚拟存储能力的系统的例子并作
了详细解释,最后还清楚地说明了80386与8086/80286的兼容性和差别。本书的特
点是严谨深入,对CPU各种条件下的动作和反应用形式化的微程序讲解得十分清楚,
尤其适合系统程序员阅读。总之,这实在是一本不可多得的好书.

====================================================================

====================================================================
《Linux 操作系统的内核分析》 作者:陈莉君编著
---------------------
价格:40 元
出版社:人民邮电出版社
---------------------

====================================================================

[目录]


linux论坛推荐资源

~~~~~~~
一般文献
~~~~~~~
利索脚组织的网站:
http://www.lisoleg.net

linux kernel internals 2.4(不是特别全)

http://www.moses.uklinux.net/patches/lki.html
http://tzhang.xiloo.com/unix/kernel/

UNIX高级教程 系统技术内幕
Uresh Vahalia 清华大学出版社

书。这是一本非常好的书,思想性很强,对学习Linux以及操作系统非常有帮助。

Understanding the Linux Kernel
Daniel P. Bovet  Marco Cesati
O'Reilly出版

超级宝书!:-)2001年内会有中译本。
这里是第10章
Process Scheduling

Linux 操作系统及实验教程
李善平 郑扣根 编著
机械工业出版社

Linux 操作系统的内核分析
陈莉君编著
人民邮电出版社

Linux Device Driver

书。有中译本。帮助理解中断,任务队列等内核机制,帮助掌握驱动编程。

Operating Systems
resource center
jkl 推荐

关于操作系统的介绍,有许多专题文章

http://www.nondot.org/sabre//os/

关于Linux的重要链接
Pengcheng Zou 推荐

给出了许多重要的链接

http://www.linuxlinks.com/Kernel/

非常完备的CPU开发手册

Intel Architecture Software Developer’s Manual
Volume 3:
System Programming

" target="_new">http://developer.intel.com/design/pentiumii/manuals/24319202.pdf

对i386的机制讲得非常详细,可以帮助深入了解i386保护模式机制,对理解Linux的相关实现非常有帮助

~~~~~~~
关于内存
~~~~~~~

Outline of the Linux Memory Management System
黑饭 推荐

http://home.earthlink.net/~jknapka/linux-mm/vmoutline.html

已经是一个比较细致的介绍内存(包括内核的和用户空间的)了。

SGI公司的公开项目

http://oss.sgi.com/projects/

SGI公司的关于伸缩性的技术非常棒!Linux的NUMA就是由他们做的,这是他们的网页

Linux内存开发小组的网站
jkl 推荐

http://www.linux-mm.org

这是Linux负责开发内存管理的核心成员的网站
下面有他们关于zone设计思想的描述:

http://surriel.com/zone-alloc.html

~~~~~~~
关于中断
~~~~~~~

关于中断上锁的专题文档

http://netfilter.kernelnotes.org/unreliable-guides/kernel-locking/lklockingguide.html

我们知道在中断中写程序要小心,不小心会发生race condition.这篇文献对这方面做了介绍

~~~~~~~~~~
关于文件系统
~~~~~~~~~~

Linux Commentary
dream_seeker 推荐

http://www.cse.unsw.edu.au/~neilb/oss/linux-commentary/

主要介绍文件系统, 另外,下面的

linux-vm-readme

主要介绍VM和交换,也很值得一看


关于汇编
[http://www.linuxassembly.org http://www.linuxassembly.org ]
Linuxkernel推荐


关于AT

http://www-106.ibm.com/developerworks/linux/library/l-ia.html
lisoleg推荐

关于汇编:
http://www-aig.jpl.nasa.gov/public/home/decoste/HTMLS/GNU/binutils/as_toc.html
http://www.linuxassembly.org/resources.html#tutorials
lucian推荐

关于驱动,专门研究linux驱动设计的bbs
http://short.xilubbs.com
joyfire推荐


Edited by lucian_yao on 05/20/01 08:05 PM.


[目录]


数据结构

以下是linux操作系统核心常用的数据结构

block_dev_struct
此结构用于向核心登记块设备,它还被buffer
cache实用。所有此类结构都位于blk_dev数组中。
struct blk_dev_struct {
    void (*request_fn)(void);
    struct request * current_request;
    struct request   plug;
    struct tq_struct plug_tq;
};
buffer_head
此结构包含关于buffer cache中一块缓存的信息。
/* bh state bits */
#define BH_Uptodate  0   /* 1 if the buffer contains valid data      */
#define BH_Dirty     1   /* 1 if the buffer is dirty                 */
#define BH_Lock      2   /* 1 if the buffer is locked                */
#define BH_Req       3   /* 0 if the buffer has been invalidated     */
#define BH_Touched   4   /* 1 if the buffer has been touched (aging) */
#define BH_Has_aged  5   /* 1 if the buffer has been aged (aging)    */
#define BH_Protected 6   /* 1 if the buffer is protected             */
#define BH_FreeOnIO  7   /* 1 to discard the buffer_head after IO    */
struct buffer_head {
  /* First cache line: */
  unsigned long      b_blocknr;    /* block number                   */
  kdev_t             b_dev;        /* device (B_FREE = free)         */
  kdev_t             b_rdev;       /* Real device                    */
  unsigned long      b_rsector;    /* Real buffer location on disk   */
  struct buffer_head *b_next;      /* Hash queue list                */
  struct buffer_head *b_this_page; /* circular list of buffers in one
                                      page                           */
  /* Second cache line: */
  unsigned long      b_state;      /* buffer state bitmap (above)    */
  struct buffer_head *b_next_free;
  unsigned int       b_count;      /* users using this block         */
  unsigned long      b_size;       /* block size                     */
  /* Non-performance-critical data follows. */
  char               *b_data;      /* pointer to data block          */
  unsigned int       b_list;       /* List that this buffer appears  */
  unsigned long      b_flushtime;  /* Time when this (dirty) buffer
                                    * should be written              */
  unsigned long      b_lru_time;   /* Time when this buffer was
                                    * last used.                     */
  struct wait_queue  *b_wait;
  struct buffer_head *b_prev;      /* doubly linked hash list        */
  struct buffer_head *b_prev_free; /* doubly linked list of buffers  */
  struct buffer_head *b_reqnext;   /* request queue                  */
};
device
系统中每个网络设备都用一个设备数据结构来表示。
struct device
{
  /*
   * This is the first field of the "visible" part of this structure
   * (i.e. as seen by users in the "Space.c" file).  It is the name
   * the interface.
   */
  char                    *name;
  /* I/O specific fields                                           */
  unsigned long           rmem_end;        /* shmem "recv" end     */
  unsigned long           rmem_start;      /* shmem "recv" start   */
  unsigned long           mem_end;         /* shared mem end       */
  unsigned long           mem_start;       /* shared mem start     */
  unsigned long           base_addr;       /* device I/O address   */
  unsigned char           irq;             /* device IRQ number    */
  /* Low-level status flags. */
  volatile unsigned char  start,           /* start an operation   */
                          interrupt;       /* interrupt arrived    */
  unsigned long           tbusy;           /* transmitter busy     */
  struct device           *next;
  /* The device initialization function. Called only once.         */
  int                     (*init)(struct device *dev);
  /* Some hardware also needs these fields, but they are not part of
     the usual set specified in Space.c. */
  unsigned char           if_port;         /* Selectable AUI,TP,   */
  unsigned char           dma;             /* DMA channel          */
  struct enet_statistics* (*get_stats)(struct device *dev);
  /*
   * This marks the end of the "visible" part of the structure. All
   * fields hereafter are internal to the system, and may change at
   * will (read: may be cleaned up at will).
   */
  /* These may be needed for future network-power-down code.       */
  unsigned long           trans_start;     /* Time (jiffies) of
                                              last transmit        */
  unsigned long           last_rx;         /* Time of last Rx      */
  unsigned short          flags;           /* interface flags (BSD)*/
  unsigned short          family;          /* address family ID    */
  unsigned short          metric;          /* routing metric       */
  unsigned short          mtu;             /* MTU value            */
  unsigned short          type;            /* hardware type        */
  unsigned short          hard_header_len; /* hardware hdr len     */
  void                    *priv;           /* private data         */
  /* Interface address info. */
  unsigned char           broadcast[MAX_ADDR_LEN];
  unsigned char           pad;
  unsigned char           dev_addr[MAX_ADDR_LEN];
  unsigned char           addr_len;        /* hardware addr len    */
  unsigned long           pa_addr;         /* protocol address     */
  unsigned long           pa_brdaddr;      /* protocol broadcast addr*/
  unsigned long           pa_dstaddr;      /* protocol P-P other addr*/
  unsigned long           pa_mask;         /* protocol netmask     */
  unsigned short          pa_alen;         /* protocol address len */
  struct dev_mc_list      *mc_list;        /* M'cast mac addrs     */
  int                     mc_count;        /* No installed mcasts  */
  struct ip_mc_list       *ip_mc_list;     /* IP m'cast filter chain */
  __u32                   tx_queue_len;    /* Max frames per queue   */
  /* For load balancing driver pair support */
  unsigned long           pkt_queue;       /* Packets queued       */
  struct device           *slave;          /* Slave device         */
  struct net_alias_info   *alias_info;     /* main dev alias info  */
  struct net_alias        *my_alias;       /* alias devs           */
  /* Pointer to the interface buffers. */
  struct sk_buff_head     buffs[DEV_NUMBUFFS];
  /* Pointers to interface service routines. */
  int                     (*open)(struct device *dev);
  int                     (*stop)(struct device *dev);
  int                     (*hard_start_xmit) (struct sk_buff *skb,
                                              struct device *dev);
  int                     (*hard_header) (struct sk_buff *skb,
                                          struct device *dev,
                                          unsigned short type,
                                          void *daddr,
                                          void *saddr,
                                          unsigned len);
  int                     (*rebuild_header)(void *eth,
                                          struct device *dev,
                                          unsigned long raddr,
                                          struct sk_buff *skb);
  void                    (*set_multicast_list)(struct device *dev);
  int                     (*set_mac_address)(struct device *dev,
                                          void *addr);
  int                     (*do_ioctl)(struct device *dev,
                                          struct ifreq *ifr,
                                          int cmd);
  int                     (*set_config)(struct device *dev,
                                          struct ifmap *map);
  void                    (*header_cache_bind)(struct hh_cache **hhp,
                                          struct device *dev,
                                          unsigned short htype,
                                          __u32 daddr);
  void                    (*header_cache_update)(struct hh_cache *hh,
                                          struct device *dev,
                                          unsigned char *  haddr);
  int                     (*change_mtu)(struct device *dev,
                                          int new_mtu);
  struct iw_statistics*   (*get_wireless_stats)(struct device *dev);
};
device_struct
此结构被块设备和字符设备用来向核心登记(包含设备名称以及可对此设备进行的
文件操作)。chrdevs和blkdevs中的每个有效分别表示一个字符设备和块设备。
struct device_struct {
    const char * name;
    struct file_operations * fops;
};
file
每个打开的文件、套接口都用此结构表示。
struct file {
  mode_t f_mode;
  loff_t f_pos;
  unsigned short f_flags;
  unsigned short f_count;
  unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
  struct file *f_next, *f_prev;
  int f_owner;         /* pid or -pgrp where SIGIO should be sent */
  struct inode * f_inode;
  struct file_operations * f_op;
  unsigned long f_version;
  void *private_data;  /* needed for tty driver, and maybe others */
};
files_struct
描叙被某进程打开的所有文件。
struct files_struct {
  int count;
  fd_set close_on_exec;
  fd_set open_fds;
  struct file * fd[NR_OPEN];
};
fs_struct
struct fs_struct {
  int count;
  unsigned short umask;
  struct inode * root, * pwd;
};
gendisk
包含关于某个硬盘的信息。用于磁盘初始化与分区检查时。
struct hd_struct {
    long start_sect;
    long nr_sects;
};
struct gendisk {
    int major;               /* major number of driver */
    const char *major_name;  /* name of major driver */
    int minor_shift;         /* number of times minor is shifted to
                                get real minor */
    int max_p;               /* maximum partitions per device */
    int max_nr;              /* maximum number of real devices */
    void (*init)(struct gendisk *);
                             /* Initialization called before we
                                do our thing */
    struct hd_struct *part;  /* partition table */
    int *sizes;              /* device size in blocks, copied to
                                blk_size[] */
    int nr_real;             /* number of real devices */
    void *real_devices;      /* internal use */
    struct gendisk *next;
};
inode
此VFS inode结构描叙磁盘上一个文件或目录的信息。
struct inode {
    kdev_t                       i_dev;
    unsigned long                i_ino;
    umode_t                      i_mode;
    nlink_t                      i_nlink;
    uid_t                        i_uid;
    gid_t                        i_gid;
    kdev_t                       i_rdev;
    off_t                        i_size;
    time_t                       i_atime;
    time_t                       i_mtime;
    time_t                       i_ctime;
    unsigned long                i_blksize;
    unsigned long                i_blocks;
    unsigned long                i_version;
    unsigned long                i_nrpages;
    struct semaphore             i_sem;
    struct inode_operations      *i_op;
    struct super_block           *i_sb;
    struct wait_queue            *i_wait;
    struct file_lock             *i_flock;
    struct vm_area_struct        *i_mmap;
    struct page                  *i_pages;
    struct dquot                 *i_dquot[MAXQUOTAS];
    struct inode                 *i_next, *i_prev;
    struct inode                 *i_hash_next, *i_hash_prev;
    struct inode                 *i_bound_to, *i_bound_by;
    struct inode                 *i_mount;
    unsigned short               i_count;
    unsigned short               i_flags;
    unsigned char                i_lock;
    unsigned char                i_dirt;
    unsigned char                i_pipe;
    unsigned char                i_sock;
    unsigned char                i_seek;
    unsigned char                i_update;
    unsigned short               i_writecount;
    union {
        struct pipe_inode_info   pipe_i;
        struct minix_inode_info  minix_i;
        struct ext_inode_info    ext_i;
        struct ext2_inode_info   ext2_i;
        struct hpfs_inode_info   hpfs_i;
        struct msdos_inode_info  msdos_i;
        struct umsdos_inode_info umsdos_i;
        struct iso_inode_info    isofs_i;
        struct nfs_inode_info    nfs_i;
        struct xiafs_inode_info  xiafs_i;
        struct sysv_inode_info   sysv_i;
        struct affs_inode_info   affs_i;
        struct ufs_inode_info    ufs_i;
        struct socket            socket_i;
        void                     *generic_ip;
    } u;
};
ipc_perm
此结构描叙对一个系统V IPC对象的存取权限。
struct ipc_perm
{
  key_t  key;
  ushort uid;   /* owner euid and egid */
  ushort gid;
  ushort cuid;  /* creator euid and egid */
  ushort cgid;
  ushort mode;  /* access modes see mode flags below */
  ushort seq;   /* sequence number */
};
irqaction
用来描叙系统的中断处理过程。
struct irqaction {
  void (*handler)(int, void *, struct pt_regs *);
  unsigned long flags;
  unsigned long mask;
  const char *name;
  void *dev_id;
  struct irqaction *next;
};
linux_binfmt
用来表示可被Linux理解的二进制文件格式。
struct linux_binfmt {
  struct linux_binfmt * next;
  long *use_count;
  int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
  int (*load_shlib)(int fd);
  int (*core_dump)(long signr, struct pt_regs * regs);
};
mem_map_t
用来保存每个物理页面的信息。
typedef struct page {
  /* these must be first (free area handling) */
  struct page        *next;
  struct page        *prev;
  struct inode       *inode;
  unsigned long      offset;
  struct page        *next_hash;
  atomic_t           count;
  unsigned           flags;     /* atomic flags, some possibly
                                   updated asynchronously */
  unsigned           dirty:16,
                     age:8;
  struct wait_queue  *wait;
  struct page        *prev_hash;
  struct buffer_head *buffers;
  unsigned long      swap_unlock_entry;
  unsigned long      map_nr;    /* page->map_nr == page - mem_map */
} mem_map_t;
mm_struct
用来描叙某任务或进程的虚拟内存。
struct mm_struct {
  int count;
  pgd_t * pgd;
  unsigned long context;
  unsigned long start_code, end_code, start_data, end_data;
  unsigned long start_brk, brk, start_stack, start_mmap;
  unsigned long arg_start, arg_end, env_start, env_end;
  unsigned long rss, total_vm, locked_vm;
  unsigned long def_flags;
  struct vm_area_struct * mmap;
  struct vm_area_struct * mmap_avl;
  struct semaphore mmap_sem;
};
pci_bus
表示系统中的一个PCI总线。
struct pci_bus {
  struct pci_bus  *parent;     /* parent bus this bridge is on */
  struct pci_bus  *children;   /* chain of P2P bridges on this bus */
  struct pci_bus  *next;       /* chain of all PCI buses */
  struct pci_dev  *self;       /* bridge device as seen by parent */
  struct pci_dev  *devices;    /* devices behind this bridge */
  void    *sysdata;            /* hook for sys-specific extension */
  unsigned char  number;       /* bus number */
  unsigned char  primary;      /* number of primary bridge */
  unsigned char  secondary;    /* number of secondary bridge */
  unsigned char  subordinate;  /* max number of subordinate buses */
};
pci_dev
表示系统中的每个PCI设备,包括PCI-PCI和PCI-PCI桥接器。
/*
* There is one pci_dev structure for each slot-number/function-number
* combination:
*/
struct pci_dev {
  struct pci_bus  *bus;      /* bus this device is on */
  struct pci_dev  *sibling;  /* next device on this bus */
  struct pci_dev  *next;     /* chain of all devices */
  void    *sysdata;          /* hook for sys-specific extension */
  unsigned int  devfn;       /* encoded device & function index */
  unsigned short  vendor;
  unsigned short  device;
  unsigned int  class;       /* 3 bytes: (base,sub,prog-if) */
  unsigned int  master : 1;  /* set if device is master capable */
  /*
   * In theory, the irq level can be read from configuration
   * space and all would be fine.  However, old PCI chips don't
   * support these registers and return 0 instead.  For example,
   * the Vision864-P rev 0 chip can uses INTA, but returns 0 in
   * the interrupt line and pin registers.  pci_init()
   * initializes this field with the value at PCI_INTERRUPT_LINE
   * and it is the job of pcibios_fixup() to change it if
   * necessary.  The field must not be 0 unless the device
   * cannot generate interrupts at all.
   */
  unsigned char  irq;        /* irq generated by this device */
};
request
被用来向系统的块设备发送请求。它总是向buffer cache读出或写入数据块。
struct request {
    volatile int rq_status;
#define RQ_INACTIVE            (-1)
#define RQ_ACTIVE              1
#define RQ_SCSI_BUSY           0xffff
#define RQ_SCSI_DONE           0xfffe
#define RQ_SCSI_DISCONNECTING  0xffe0
    kdev_t rq_dev;
    int cmd;        /* READ or WRITE */
    int errors;
    unsigned long sector;
    unsigned long nr_sectors;
    unsigned long current_nr_sectors;
    char * buffer;
    struct semaphore * sem;
    struct buffer_head * bh;
    struct buffer_head * bhtail;
    struct request * next;
};
rtable
用来描叙向某个IP主机发送包的路由信息。此结构在IP路由cache内部实用。
struct rtable
{
    struct rtable     *rt_next;
    __u32             rt_dst;
    __u32             rt_src;
    __u32             rt_gateway;
    atomic_t          rt_refcnt;
    atomic_t          rt_use;
    unsigned long     rt_window;
    atomic_t          rt_lastuse;
    struct hh_cache   *rt_hh;
    struct device     *rt_dev;
    unsigned short    rt_flags;
    unsigned short    rt_mtu;
    unsigned short    rt_irtt;
    unsigned char     rt_tos;
};
semaphore
保护临界区数据结构和代码信号灯。
struct semaphore {
    int count;
    int waking;
    int lock ;                /* to make waking testing atomic */
    struct wait_queue *wait;
};
sk_buff
用来描叙在协议层之间交换的网络数据。
struct sk_buff
{
  struct sk_buff      *next;       /* Next buffer in list
*/
  struct sk_buff      *prev;       /* Previous buffer in list
*/
  struct sk_buff_head *list;       /* List we are on
*/
  int                 magic_debug_cookie;
  struct sk_buff      *link3;      /* Link for IP protocol level buffer chai
ns *
  struct sock         *sk;         /* Socket we are owned by
*/
  unsigned long       when;        /* used to compute rtt's
*/
  struct timeval      stamp;       /* Time we arrived
*/
  struct device       *dev;        /* Device we arrived on/are leaving by
*/
  union
  {
      struct tcphdr   *th;
      struct ethhdr   *eth;
      struct iphdr    *iph;
      struct udphdr   *uh;
      unsigned char   *raw;
      /* for passing file handles in a unix domain socket */
      void            *filp;
  } h;
  union
  {
      /* As yet incomplete physical layer views */
      unsigned char   *raw;
      struct ethhdr   *ethernet;
  } mac;
  struct iphdr        *ip_hdr;     /* For IPPROTO_RAW
*/
  unsigned long       len;         /* Length of actual data
*/
  unsigned long       csum;        /* Checksum
*/
  __u32               saddr;       /* IP source address
*/
  __u32               daddr;       /* IP target address
*/
  __u32               raddr;       /* IP next hop address
*/
  __u32               seq;         /* TCP sequence number
*/
  __u32               end_seq;     /* seq [+ fin] [+ syn] + datalen
*/
  __u32               ack_seq;     /* TCP ack sequence number
*/
  unsigned char       proto_priv[16];
  volatile char       acked,       /* Are we acked ?
*/
                      used,        /* Are we in use ?
*/
                      free,        /* How to free this buffer
*/
                      arp;         /* Has IP/ARP resolution finished
*/
  unsigned char       tries,       /* Times tried
*/
                      lock,        /* Are we locked ?
*/
                      localroute,  /* Local routing asserted for this frame
*/
                      pkt_type,    /* Packet class
*/
                      pkt_bridged, /* Tracker for bridging
*/
                      ip_summed;   /* Driver fed us an IP checksum
*/
#define PACKET_HOST         0        /* To us
  */
#define PACKET_BROADCAST    1        /* To all
  */
#define PACKET_MULTICAST    2        /* To group
  */
#define PACKET_OTHERHOST    3        /* To someone else
  */
  unsigned short      users;       /* User count - see datagram.c,tcp.c
*/
  unsigned short      protocol;    /* Packet protocol from driver.
*/
  unsigned int        truesize;    /* Buffer size
*/
  atomic_t            count;       /* reference count
*/
  struct sk_buff      *data_skb;   /* Link to the actual data skb
*/
  unsigned char       *head;       /* Head of buffer
*/
  unsigned char       *data;       /* Data head pointer
*/
  unsigned char       *tail;       /* Tail pointer
*/
  unsigned char       *end;        /* End pointer
*/
  void                (*destructor)(struct sk_buff *); /* Destruct function
*/
  __u16               redirport;   /* Redirect port
*/
};
sock
包含BSD套接口的协议相关信息。例如对于一个INET(Internet AddressDomain)套接口

此数据结构 包含TCP/IP和UDP/IP信息。
struct sock
{
    /* This must be first. */
    struct sock             *sklist_next;
    struct sock             *sklist_prev;
    struct options          *opt;
    atomic_t                wmem_alloc;
    atomic_t                rmem_alloc;
    unsigned long           allocation;       /* Allocation mode */
    __u32                   write_seq;
    __u32                   sent_seq;
    __u32                   acked_seq;
    __u32                   copied_seq;
    __u32                   rcv_ack_seq;
    unsigned short          rcv_ack_cnt;      /* count of same ack */
    __u32                   window_seq;
    __u32                   fin_seq;
    __u32                   urg_seq;
    __u32                   urg_data;
    __u32                   syn_seq;
    int                     users;            /* user count */
  /*
   *    Not all are volatile, but some are, so we
   *     might as well say they all are.
   */
    volatile char           dead,
                            urginline,
                            intr,
                            blog,
                            done,
                            reuse,
                            keepopen,
                            linger,
                            delay_acks,
                            destroy,
                            ack_timed,
                            no_check,
                            zapped,
                            broadcast,
                            nonagle,
                            bsdism;
    unsigned long           lingertime;
    int                     proc;
    struct sock             *next;
    struct sock             **pprev;
    struct sock             *bind_next;
    struct sock             **bind_pprev;
    struct sock             *pair;
    int                     hashent;
    struct sock             *prev;
    struct sk_buff          *volatile send_head;
    struct sk_buff          *volatile send_next;
    struct sk_buff          *volatile send_tail;
    struct sk_buff_head     back_log;
    struct sk_buff          *partial;
    struct timer_list       partial_timer;
    long                    retransmits;
    struct sk_buff_head     write_queue,
                            receive_queue;
    struct proto            *prot;
    struct wait_queue       **sleep;
    __u32                   daddr;
    __u32                   saddr;            /* Sending source */
    __u32                   rcv_saddr;        /* Bound address */
    unsigned short          max_unacked;
    unsigned short          window;
    __u32                   lastwin_seq;      /* sequence number when we las
t
                                                 updated the window we offer
*/
    __u32                   high_seq;         /* sequence number when we did

                                                 current fast retransmit */
    volatile unsigned long  ato;              /* ack timeout */
    volatile unsigned long  lrcvtime;         /* jiffies at last data rcv */

[目录]


重新编译

1.核心的源程序:
    我现在在用TLC, REDHAT也用过, SLACKWARE也用过. 无论哪一种, 都是把核心源程
序放到 /usr/src/linux 下, 因为有些别的应用程序在编译时好像也会从这个路径来引
用一些头文件之类. 一般来说这个 linux 目录都只是个符号连接, 有一点点像WIN下的
Shortcut, 而实际上它对应的目录可能是 /usr/src/linux-2.0.35 之类. RedHat的缺省
安装好像并不装源程序, 只有些头文件.
    以现在的2.2.5 核心为例, 我装的时候就是这样(其实什么版本都一样 :)
    cd /usr/src
    rm linux
    # 这个linux只是个符号连接, 删掉它没事的. 可以 ls -l 看看, 如果看到这个:
    # linux -> linux-XXXXX, 就表示它是个连接而已. 原来的源程序在箭头后的目录.
    tar zxvf XXXXXXX/linux-2.2.5.tar.gz
    # 这个包解开后, 新核心的源程序就放在了新建立的linux目录下, 这可是个货真价
    # 实的目录.
    mv linux linux-2.2.5
    ln -s linux-2.2.5 linux
    # 按照惯例, 还是把目录另命个名, 再重新做个linux的符号连接

2.准备编译:
    现在要做一些准备工作. 对于新释放出来的核心源程序也没啥好做的, 就打一个:
    cd /usr/src/linux
    make menuconfig

    然后就会看到一个很友好的界面(在LINUX下...已经是很友好的了), 大致上有点像
WIN 9X安装时的选择安装项目. 这就是在配置核心, 选择哪些内容要, 哪些不要.
然后选EXIT退出来, 问是否保存修改时答YES. 然后会有一些提示. 如果看到了有叫你
"make dep", 就要打"make dep"先. 完了后就打 make bzImage. 如果提示信息中没有
叫你"make dep", 只有叫你 "make zImage", "make zdisk" 或 "make zlilo" 的,
就直接打 make bzImage 就行了.

一点说明: make dep 是作一些准备工作, make bzImage 则是开始编译生成核心. 而
          make bzImage与make zImage的区别在于, 作成bzImage的核心压缩率比zImage
          高, 核心就更小一些. make zdisk 与 make zlilo 是做别的用处的核心的.

    然后就等吧(有得你等的). 一般从5分钟到半个钟头不等, 看你的机器了. 第一次编
译会 比较慢. 以后再改了配置后make就会快很多了.

    等这个完了后一定还要 make modules 和 make modules_install.

make bzImage 完后会显示核心放在什么地方, 一般是/usr/src/linux/arch/i386/boot/
下. 把bzImage拷到根下. 然后修改 /etc/lilo.conf, 照着原来的image = XXXXX来加上
image = /bzImage
  root = /dev/hda1 (这里视你的LINUX安装而定, 照你原有的改)
  label = linux
  read-only
    把原来的 label = linux 改一下, 如 label = oldlinux.
    把image = /bzImage 这一节加在原来的前面, 这样会自动作为缺省的核心. 你也可以在
LILO时打linux或oldlinux来启动不同的核心. 关于这一段, 也可以参考俺前面的"ALS007
发声经过". 最后, 切记切记, 一定要打个lilo来重新生成LILO程序.

好了, 重启...


[目录]


重建内核选项

       prompt for development and/or incomplete code/drivers
             很多参考书上说这是那些开发人员认为还不是很稳定的功能,但
             是根据我的经验,这个是应该选的一个选项,因为现代的LINUX是
             建立在这些基础上的,所以应该可以回答Y,除非你只是想使用
             LINUX中已经完全定型的东西,但性能肯定不会好到哪,而且对系
             统特性的支持也不会好。

       processor family (386,486/cx486,586/k5/5x86/6x86,pentinum/k6/tsc,ppro/6x86)
             这应该没有太多可说的吧,选择你的CPU的种类,BIOS可以自检得
             到,注意系统的启动信息。需要注意的是不能选择比你的CPU类型
             还高级的CPU,否则可能不能正常工作。

       math emulation
             模拟数学协处理器,如果你的机器没有数学协处理器,那就选上
             以提高性能,但486以后数学协处理器就集成到CPU内部了,应该是
             用不上的,所以一般的选择是N。当然选上也不会有什么问题,除
             了内 松陨 变大外。

       mttr(memory type range register) support
             这个选项是用来启动pentinum pro和pentinum II 的特殊功能,如果你用
             的不是这类CPU就选N,否则也仅仅是使内核变大而已。

       symmetric multi-processing support
             同步处理器支持,如果你有多个CPU就选上吧。

       enable loadable module support
             这会启动动态载入额外模块的功能,所以一定选上。

       set version information on all symbols for modules
             这个选项可以为某个版本的内核而编译的模块在另一个版本的内
             核下使用,但一般用不上。

        kernel module loader
             如果你启用这个选项,你可以通过kerneld程序的帮助在需要的时候
             自动载入或卸载那些可载入式的模块。建议选上。

         networking support
             如果你用到任何网络就应该选上

         pci bios support
             这个一般是应该选上的,除非你用没有任何PCI设备的机器。PCI
             BIOS是用来侦测并启用PCI设备的。

         pci bridge optimization(v1.3)
             当这个选项被启动时,操作系统会对从CPU和系统内存在PCI总线
             来往的数据做最佳化,这个功能已经完成实验阶段,使用起来应
             该很安全,而且还可增进系统的效率。

         system v ipc
             起用这个选项可以使内核支持System V 的进程间通信的功能
             (IPC),有些从System V转移过来的程序会需要这个功能,建议启
             用该功能。

         sysctl support
             除非你的内存少的可怜,否则你应该启动这个功能,启用该选项
             后内核会大8K,但能让你直接改变内核的参数而不必重新开机。

         kernel support for elf binaries
             该选项让你的系统得以执行用ELF格式存储的可执行文件,而ELF
             是现代LINUX的可执行文件、目标文件和系统函数库的标准格式。
             当操作系统要和编译器以及连接器合作时会需要这些标准,所以
             应该回答Y。

         compile kernel as elf
             这选项让你的内核本身以ELF的格式编译,如果你的系统上的过程
             gcc默认产生ELF格式的可执行文件,那么你就应该启动这个选项。
             先看看你的编译器的版本再决定。

         parallel port support
             如果你有任何并行口的设备并且想让LINUX使用,那么就可以启用
             这个选项。LINUX不仅可以使用并口的打印机,还可以支持PLIP
             (一种为并行口而设计的网络通讯协定),ZIP磁盘驱动器、扫描
             仪等。在大多情况下,你需要额外的驱动程序才能使用外接的并
             口设备。

         plug and play support
             支持PNP设备并非Microsoft的专利,如果你要让LINUX也支持PNP设
             备,只要启用该选项就可以,但有些情况下会和其他设备产生冲
             突(I/O,DMA,IRQ等)。这个选项对PCI设备没有影响,因为他们
             天生就是PNP设备。

         normal floppy disk support
             除非你不想在LINUX下使用软盘,否则就应该回答Y。但对于一些
             不需要支持软盘驱动器的系统而言,这个选项可以节省一些内
             存。

         enhanced ide/mfm/dll disk support
             除非你不需要MFM/DLL/IDE硬盘的的支持,否则就应该回答Y,但如
             果你只有SCSI的硬盘,关掉这个选项会比较安全。

         enhanced ide/mfm/dll cdrom support
             和上面的类似,只不过是对CDROM的支持而已。

         enhanced ide/mfm/dll tape support
             一般没有多少人在用磁带机吧,所以回答N是比较好的答案。

        enhanced ide/mfm/dll floppy support
             这个设备好象一般也没有人用,所以也可以回答N。

        xt harddisk support
             如果你有这种石器时代的XT硬盘,那么恭喜你你可以用上他了。

        parallel port ide device support
             LINUX是支持这种很新的并口的IDE设备的,如果你有的话就用上
             吧。

        networking options
             如果你在前面选了支持网络的话,在这里会回答很多问题。除非
             你有特别的需求,否则使用默认的选项应该就足够了。

        scsi support
             如果你有任何一种SCSI控制卡,这个选项就应该回答Y。事先搞清
             楚你的硬件的类型,因为这些问题都是针对特定的SCSI控制芯片和
             控制卡的,如果你不确定自己使用的是哪一种,查看你的硬件的
             说明文件或者LINUX的HOWTO文档。同样也会让你回答很多SCSI设
             备的支持(硬盘、CDROM、Tape、floppy等),依据你的情况选择。
             如果你没有SCSI设备的话,建议不要支持,因为这会节约很多内核
             空间。

         network device support
             这里面有很多关于网络控制卡的问题,如果你无法确定如何正确
             选择,查看硬件文档或LINUX HOWTO文档。

         amateur radio support
             这个选项可以用来启动无线网络的基本支持,目前的无线网络可
             以通过公众频率传输数据,如果你有此类设备就可以启用,具体
             请参考AX25和HAM HOWTO 文档。
             isdn subsystem
             如果你有ISDN硬件就应该启用该选项并安装合适的硬件驱动程
             序,你应该还会需要启用Support synchronous PPP选项(参考PPP over
             ISDN)。

         old cd-rom drivers
             这是针对一些特殊光盘驱动器程序的问题,如果你有IDE或SCSI的
             CDROM控制卡,那么就不用启用该选项了。

         character devices
             LINUX支持很多特殊的字符设备,例如并口、串口控制卡、QIC02磁
             带驱动器以及特定界面的鼠标,此外对于游戏杆和影象摄取和麦
             克等也在这里面,依据你自己的情况选者吧。

         filesystems
             这是一系列内核所支持的各文件系统的问题,对ext2 /proc文件系统
             是一定应该支持的,有光驱还应该支持ISO9660(或模块支持),
             有WINDOWS或DOS分区并且想在LINUX下访问他们也可以进行支
             持。

         console drivers
             你至少应该支持VGA text console,否则你无法从控制台使用LINUX。

         sound card support
             在这里回答Y会出现很多关于声卡的问题,根据你自己的情况来配
             置。

         kernel profiling support(v1.3)
             这个选项可以开启内核做效率统计的功能,并且会提供其他在为
             系统侦错时有用的信息。这些功能会需要付出一些代价并造成系
             统执行得较为缓慢,除非你正在研究内核的某个问题,否则你应
             该回答N。

          kernel hacking
             如果你正打算深入研究自己系统上运行的LINUX如何运作,这里有
             很多选项,但一般没有必要的话可以全部关掉。


[目录]


调试技术

本章介绍你可以用来监视内核代码和跟踪错误的技术。

用打印信息调试

    最一般的调试技术就是监视,就是在应用内部合适的点加上printf调用。当你调试内核代码的时候,你可以用printk完成这个任务。

Printk

    一般简单介绍的时候,都假设printk工作起来和printf很类似。现在是的它们不同。

    其中一个不同点就是,printk允许你根据它们的严重程度,通过附加不同的“记录级”来对消息分类,或赋予消息优先级。你可以用宏来指示记录级。例如,KERN_INFO,我们前面已经看到它被加在打印语句的前面,它就是一种可能的消息记录级。记录级宏展开为一个字串,在编译时和消息文本拼接在一起。

    在<linux/kernel.h>中定义了8种记录级别串。没有指定优先级的printk语句默认使用DEFAULT_MESSAGE_LOGLEVEL优先级,它是一个在kernel/printk.c中定义的整数。默认记录级的具体数值在Linux的开发期间曾变化过若干次,所以我建议你最好总是指定一个合适的记录级。

    根据记录级,内核将消息打印到当前文本控制台上:如果优先级低于console_loglevel这个数值的话,该消息就显示在控制台上。如果系统同时运行了klogd和syslogd,无论console_loglevel为何值,内核都将消息追加到/var/log/messages中。

    变量console_loglevel最初初始化为DEFAULT_CONSOLE_LOGLEVEL,但可以通过sys_syslog系统调用修改。如klogd的手册所示,可以在启动klogd时指定-c开关来修改这个变量。此外,你还可以写个程序来改变控制台记录级。你可以在O’Reilly站点上的源文件中找到我写的一个这种功能的程序,miscprogs/setlevel.c。新优先级是通过一个1到8之间的整数值指定的。

    你也许需要在内核失效后降低记录级(见“调试系统故障”),这是因为失效处理代码会将console_loglevel提升到15,之后所有的消息都会出现在控制台上。为看到你的调试信息,如果你运行的是内核2.0.x话,你需要提升记录级。内核2.0发行降低了MINIMUM_CONSOLE_LOGLEVEL,而旧版本的klogd默认情况下要打印很多控制消息。如果你碰巧使用了这个旧版本的守护进程,除非你提升记录级,内核2.0会比你预期的打印出更少的消息。这就是为什么hello.c中使用了<1>标记,这样可以保证消息显示在控制台上。

    从1.3.43后的内核版本通过允许你向指定虚控制台发送消息,藉此提供一个灵活的记录策略。默认情况下,“控制台”是当前虚终端。也可以选择不同的虚终端接收消息,你只需向所选的虚终端调用ioctl(TIOCLINUX)。setconsole可以用来选择哪个虚终端接收内核消息;它必须以超级用户身份运行。如果你对ioctl还不有把握,你可以跳过这至下一节,等到读完第5章“字符设备驱动程序的扩展操作”的“ioctl”一节后,再回到这里。

    setconsole使用了用于Linux专用功能的特殊的ioctl命令TIOCLINUX。为了使用TIOCLINUX,你要传递给它一个指向字节数组的指针。数组的第一个字节是所请求的子命令的编码,随后的字节依命令而不同。在setconsole中使用了子命令11,后一个字节(存放在bytes[1]中)标别虚拟控制台。TIOCLINUX的完成介绍可以在内核源码drivers/char/tty_io.c中找到。

消息是如何记录的

    printk函数将消息写到一个长度为LOG_BUF_LEN个字节的循环缓冲区中。然后唤醒任何等待消息的进程,即那些在调用syslog系统调用或读取/proc/kmesg过程中睡眠的进程。这两个访问记录引擎的接口是等价的。不过/proc/kmesg文件更象一个FIFO文件,从中读取数据更容易些。一条简单的cat命令就可以读取消息。

    如果循环缓冲区填满了,printk就绕到缓冲区的开始处填写新数据,覆盖旧数据。于是记录进程就丢失了最旧的数据。这个问题与利用循环缓冲区所获得的好处相比可以忽略不计。例如,循环缓冲区可以使系统在没有记录进程的情况下照样运行,同时又不浪费内存。Linux处理消息的方法的另一个特点是,可以在任何地方调用printk,甚至在中断处理函数里也可以调用,而且对数据量的大小没有限制。这个方法的唯一缺点就是可能丢失某些数据。

    如果klogd正在运行,它读取内核消息并将它们分派到syslogd,它随后检查/etc/syslog.conf找到处理这些数据的方式。syslogd根据一个“设施”和“优先级”切分消息;可以使用的值定义在<sys/syslog.h>中。内核消息根据相应printk中指定的优先级记录到LOG_KERN设施中。如果klogd没有运行,数据将保存在循环缓冲区中直到有进程来读取数据或数据溢出。

    如果你不希望因监视你的驱动程序的消息而把你的系统记录搞乱,你给klogd指定-f(文件)选项或修改/etc/syslog.conf将记录写到另一个文件中。另一种方法是一种强硬方法:杀掉klogd,将消息打印到不用的虚终端上*,或者在一个不用的xterm上执行cat /proc/kmesg显示消息。

使用预处理方便监视处理

    在驱动程序开发早期,printk可以对调试和测试新代码都非常有帮助。然而当你正式发行驱动程序时,你应该去掉,或者至少关闭,这些打印语句。很不幸,你可能很快就发现,随着你想不再需要那些消息并去掉它们时,你可能又要加新功能,你又需要这些消息了。解决这些问题有几种方法――如何从全局打开和关闭消息以及如何打开和关闭个别消息。

通过查询调试

上一节谈到了printk是如何工作的以及如何使用它。但没有谈及它的缺点。

    由于syslogd会一直保持刷新它的输出文件,每打印一行都会引起一次磁盘操作,因此过量使用printk会严重降低系统性能。至少从syslogd的角度看是这样的。它会将所有的数据都一股脑地写到磁盘上,以防在打印消息后系统崩溃;然而,你不想因为调试信息的缘故而降低系统性能。这个问题可以通过在/etc/syslogd.conf中记录文件的名字前加一个波折号解决,但有时你不想修改你的配置文件。如果不这样,你还可以运行一个非klogd的程序(如前面介绍的cat /proc/kmesg),但这样并不能为正常操作提供一个合适的环境。

    与这相比,最好的方法就是在你需要信息的时候,通过查询系统获得相关信息,而不是持续不断地产生数据。事实上,每一个Unix系统都提供了很多工具用来获得系统信息:ps,netstat,vmstat等等。

    有许多技术适合与驱动程序开发人员查询系统,简而言之就是,在/proc下创建文件和使用ioctl驱动程序方法。

使用/proc文件系统

    Linux中的/proc文件系统与任何设备都没有关系――/proc中的文件都在被读取时有核心创建的。这些文件都是普通的文本文件,它们基本上可由普通人理解,也可被工具程序理解。例如,对于大多数Linux的ps实现而言,它都通过读取/proc文件系统获得进程表信息的。/proc虚拟文件的创意已由若干现代操作系统使用,且非常成功。

    /proc的当前实现可以动态创建inode节点,允许用户模块为方便信息检索创建如何入口点。

    为了在/proc中创建一个健全的文件节点(可以read,write,seek等等),你需要定义file_operations结构和inode_operations结构,后者与前者有类似的作用和尺寸。创建这样一个i节点比起创建整个字符设备并没有什么不同。我们这里不讨论这个问题,如果你感兴趣,你可以在源码树fs/proc中获得进一步细节。

    与大多数/proc文件一样,如果文件节点仅仅用来读,创建它们是比较容易的,我将这里介绍这一技术。这一技术只能在2.0及其后续版本中使用。

    填写/proc文件非常容易。你的函数获取一个空闲页面填写数据;它将数据写进缓冲区并返回所写数据的长度。其他事情都由/proc文件系统处理。唯一的限制就是所写的数据不能超过PAGE_SIZE个字节(宏PAGE_SIZE定义在头文件<asm/page.h>中;它是与体系结构相关的,但你至少可以它有4KB大小)。

如果你需要写多于一个页面的数据,你必须实现功能健全的文件。

    注意,如果一个正在读你的/proc文件的进程发出了若干read调用,每一个都获取新数据,尽管只有少量数据被读取,你的驱动程序每次都要重写整个缓冲区。这些额外的工作会使系统性能下降,而且如果文件产生的数据与下一次的不同,以后的read调用要重新装配不相关的部分,这一会造成数据错位。事实上,由于每个使用C库的应用程序都大块地读取数据,性能并不是什么问题。然而,由于错位时有发生,它倒是一个值得考虑的问题。在获取数据后,库调用至少要调用1次read――只有当read返回0时才报告文件尾。如果驱动程序碰巧比前面产生了更多的数据,系统就返回到用户空间额外的字节并且与前面的数据块是错位的。我们将在第6章“时间流”的“任务队列”一节中涉及/proc/jiq*,那时我们还会遇到错位问题。
    cleanup_module中应该注销/proc节点
    传递给函数的参数是包含要撤销文件的目录名和文件的i节点号。由于i节点号是自动分配的,在编译时是无法知道的,必须从数据结构中读取。

ioctl方法

    ioctl是一个系统调用,它可以操做在文件描述符上;它接收一个“命令”号和(可选的)一个参数,通常这是一个指针。

    做为替代/proc文件系统的方法,你可以为调试实现若干ioctl命令。这些命令从驱动程序空间复制相关数据到进程空间,在进程空间里检查这些数据。

    只有使用ioctl获取信息比起/proc来要困难一些,因为你一个程序调用ioctl并显示结果。必须编写这样的程序,还要编译,保持与你测试的模块间的一致性等。

    不过有时候这是最好的获取信息的方法,因为它比起读/proc来要快得多。如果在数据写到屏幕前必须完成某些处理工作,以二进制获取数据要比读取文本文件有效得多。此外,ioctl不限制返回数据的大小。

    ioctl方法的一个优点是,当调试关闭后调试命令仍然可以保留在驱动程序中。/proc文件对任何查看这个目录的人都是可见的,然而与/proc文件不同,未公开的ioctl命令通常都不会被注意到。此外,如果驱动程序有什么异常,它们仍然可以用来调试。唯一的缺点就是模块会稍微大一些。

通过监视调试

    有时你遇到的问题并不特别糟,通过在用户空间运行应用程序来查看驱动程序与系统之间的交互过程可以帮助你捕捉到一些小问题,并可以验证驱动程序确实工作正常。例如,看到scull的read实现如何处理不同数据量的read请求后,我对scull更有信心。

    有许多方法监视一个用户态程序的工作情况。你可以用调试器一步步跟踪它的函数,插入打印语句,或者用strace运行程序。在实际目的是查看内核代码时,最后一项技术非常有用。

    strace命令是一个功能非常强大的工具,它可以现实程序所调用的所有系统调用。它不仅可以显示调用,而且还能显示调用的参数,以符号方式显示返回值。当系统调用失败时,错误的符号值(如,ENOMEM)和对应的字串(Out of memory)同时显示。strace还有许多命令行选项;最常用的是-t,它用来显示调用发生的时间,-T,显示调用所花费的时间,以及-o,将输出重定向到一个文件中。默认情况下,strace将所有跟踪信息打印到stderr上。

    strace从内核接收信息。这意味着一个程序无论是否按调试方式编译(用gcc的-g选项)或是被去掉了符号信息都可以被跟踪。与调试器可以连接到一个运行进程并控制它类似,你还可以跟踪一个已经运行的进程。

    跟踪信息通常用来生成错误报告报告给应用开发人员,但是对内核编程人员来说也一样非常有用。我们可以看到系统调用是如何执行驱动程序代码的;strace允许我们检查每一次调用输入输出的一致性。

    很明显,在ls完成目标目录的检索后首次对write的调用中,它试图写4KB。很奇怪,只写了4000个字节,接着重试这一操作。然而,我们知道scull的write实现每次只写一个量子,我在这里看到了部分写。经过若干步骤之后,所有的东西都清空了,程序正常退出。

    正如所料,read每次只能读到4000个字节,但是数据总量是不变的。注意本例中重试工作是如何组织的,注意它与上面写跟踪的对比。wc专门为快速读数据进行了优化,它绕过了标准库,以便每次用一个系统调用读取更多的数据。你可以从跟踪的read行中看到wc每次要读16KB。

    Unix专家可以在strace的输出中找到很多有用信息。如果你被这些符号搞得满头雾水,我可以只看文件方法(open,read等等)是如何工作的。

    个人认为,跟踪工具在查明系统调用的运行时错误过程中最有用。通常应用或演示程序中的perror调用不足以用来调试,而且对于查明到底是什么样的参数触发了系统调用的错误也很有帮助。

调试系统故障

    即便你用了所有监视和调试技术,有时候驱动程序中依然有错误,当这样的驱动程序执行会造成系统故障。当这种情况发生时,获取足够多的信息来解决问题是至关重要的。

    注意,“故障”不意味着“panic”。Linux代码非常鲁棒,可以很好地响应大部分错误:故障通常会导致当前进程的终止,但系统继续运行。如果在进程上下文之外发生故障,或是组成系统的重要部件发生故障时,系统可能panic。但问题出在驱动程序时,通常只会导致产生故障的进程终止――即那个使用驱动程序的进程。唯一不可恢复的损失就是当进程被终止时,进程上下文分配的内存丢失了;例如,由驱动程序通过kmalloc分配的动态链表可能丢失。然而,由于内核会对尚是打开的设备调用close,你的驱动程序可以释放任何有open方法分配的资源。

    我们已经说过,当内核行为异常时会在控制台上显示一些有用的信息。下一节将解释如何解码和使用这些消息。尽管它们对于初学者来说相当晦涩,处理器的给出数据都是些很有意思的信息,通常无需额外测试就可以查明程序错误。

Oops消息

    大部分错误都是NULL指针引用或使用其他不正确的指针数值。这些错误通常会导致一个oops消息。

    由处理器使用的地址都是“虚”地址,而且通过一个复杂的称为页表(见第13章“Mmap和DMA”中的“页表”一节)的结构映射为物理地址。当引用一个非法指针时,页面映射机制就不能将地址映射到物理地址,并且处理器向操作系统发出一个“页面失效”。如果地址确实是非法的,内核就无法从失效地址上“换页”;如果此时处理在超级用户太,系统于是就产生一个“oops”。值得注意的是,在版本2.1中内核处理失效的方式有所变化,它可以处理在超级用户态的非法地址引用了。新实现将在第17章“最近发展”的“处理内核空间失效”中介绍。

    oops显示故障时的处理器状态,模块CPU寄存器内容,页描述符表的位置,以及其他似乎不能理解的信息。这些是由失效处理函数(arch/*/kernel/traps.c)中的printk语句产生的,而且象前面“Printk”一节介绍的那样进行分派。

    让我们看看这样一个消息。这里给出的是传统个人电脑(x86平台),运行Linux 2.0或更新版本的oops――版本1.2的输出稍有不同。

(代码)

    上面的消息是在一个有意加入错误的失效模块上运行cat所至。fault.c崩溃如下代码:

(代码)

由于read从它的小缓冲区(faulty_buf)复制数据到用户空间,我们希望读一小块文件能够工作。然而,每次读出多于1KB的数据会跨越页面边界,如果访问了非法页面read就会失败。事实上,前面给出的oops是在请求一个4KB大小的read时发生的,这条消息在/var/log/messages(syslogd默认存放内核消息的文件)的oops消息前给出了:

(代码)

同样的cat命令却不能在Alpha上产生oops,这是因为从faulty_buf读取4KB字节没有超出页边界(Alpha上的页面大小是8KB,缓冲区正好在页面的起始位置附近)。如果在你的系统上读取faulty没有产生oops,试试wc,或者给dd显式地指定块大小。

使用ksymoops

oops消息的最大问题就是十六进制数值对于程序员来说没什么意义;需要将它们解析为符号。

    内核源码通过其所包含的ksymoops工具帮助开发人员――但是注意,版本1.2的源码中没有这个程序。该工具将oops消息中的数值地址解析为内核符号,但只限于PC机产生的oops消息。由于消息本身就是处理器相关的,每一体系结构都有其自身的消息格式。

    ksymoops从标准输入获得oops消息,并从命令行内核符号表的名字。符号表通常就是/usr/src/linux/System.map。程序以更可读的方式打印调用轨迹和程序代码,而不是最原始的oops消息。下面的片断就是用上一节的oops消息得出的结果:

(代码)

    由ksymoops反汇编出的代码给出了失效的指令和其后的指令。很明显――对于那些知道一点汇编的人――repz movsl指令(REPeat till cx is Zero, MOVe a String of Longs)用源索引(esi,是0x202e000)访问了一个未映射页面。用来获得模块信息的ksymoops -m命令给出,模块映射到一个在0x0202dxxx的页面上,这也确认乐esi确实超出了范围。

    由于faulty模块所占用的内存不在系统表中,被解码的调用轨迹还给出了两个数值地址。这些值可以手动补充,或是通过ksyms命令的输出,或是在/proc/ksyms中查询模块的名字。

    然而对于这个失效,这两个地址并不对应与代码地址。如果你看了arch/i386/kernel/traps.c,你就发现,调用轨迹是从整个堆栈并利用一些启发式方法区分数据值(本地变量和函数参数)和返回地址获得的。调用轨迹中只给出了引用内核代码的地址和引用模块的地址。由于模块所占页面既有代码也有数据,错综复杂的栈可能会漏掉启发式信息,这就是上面两个0x202xxxx地址的情况。

    如果你不愿手动查看模块地址,下面这组管道可以用来创建一个既有内核又有模块符号的符号表。无论何时你加载模块,你都必须重新创建这个符号表。

(代码)

    这个管道将完整的系统表与/proc/ksyms中的公开内核符号混合在一起,后者除了内核符号外,还包括了当前内核里的模块符号。这些地址在insmod重定位代码后就出现在/proc/ksyms中。由于这两个文件的格式不同,使用了sed和awk将所有的文本行转换为一种合适的格式。然后对这张表排序,去除重复部分,这样ksymoops就可以用了。

    如果我们重新运行ksymoops,它从新的符号表中截取出如下信息:

(代码)

    正如你所见到的,当跟踪与模块有关的oops消息时,创建一个修订的系统表是很有助益的:现在ksymoops能够对指令指针解码并完成整个调用轨迹了。还要注意,显式反汇编码的格式和objdump所使用的格式一样。objdump也是一个功能强大的工具;如果你需要查看失败前的指令,你调用命令objdump &#0;d faulty.o。

    在文件的汇编列表中,字串faulty_read+45/60标记为失效行。有关objdump的更多的信息和它的命令行选项可以参见该命令的手册。

    即便你构建了你自己的修订版符号表,上面提到的有关调用轨迹的问题仍然存在:虽然0x202xxxx指针被解码了,但仍然是假的。

    学会解码oops消息需要一定的经验,但是确实值得一做。用来学习的时间很快就会有所回报。不过由于机器指令的Unix语法与Intel语法不同,唯一的问题在于从哪获得有关汇编语言的文档;尽管你了解PC汇编语言,但你的经验都是用Intel语法的编程获得的。在参考书目中,我给一些有所补益的书籍。

使用oops

    使用ksymoops有些繁琐。你需要C++编译器编译它,你还要构建你自己的符号表来充分发挥程序的能力,你还要将原始消息和ksymoops输出合在一起组成可用的信息。

    如果你不想找这么多麻烦,你可以使用oops程序。oops在本书的O’Reilly FTP站点给出的源码中。它源自最初的ksymoops工具,现在它的作者已经不维护这个工具了。oops是用C语言写成的,而且直接查看/proc/ksyms而无需用户每次加载模块后构建新的符号表。

    该程序试图解码所有的处理器寄存器并堆栈轨迹解析为符号值。它的缺点是,它要比ksymoops罗嗦些,但通常你所有的信息越多,你发现错误也就越快。oops的另一个优点是,它可以解析x86,Alpha和Sparc的oops消息。与内核源码相同,这个程序也按GPL发行。

    oops产生的输出与ksymoops的类似,但是更完全。这里给出前一个oops输出的开始部分&#0;由于在这个oops消息中堆栈没保存什么有用的东西,我不认为应该显示整个堆栈轨迹:

(代码)

    当你调试“真正的”模块(faulty太短了,没有什么意义)时,将寄存器和堆栈解码是非常有益的,而且如果被调试的所有模块符号都开放出来时更有帮助。在失效时,处理器寄存器一般不会指向模块的符号,只有当符号表开放给/proc/ksyms时,你才能输出中标别它们。

    我们可以用一下步骤制作一张更完整的符号表。首先,我们不应在模块中声明静态变量,否则我们就无法用insmod开放它们了。第二,如下面的截取自scull的init_module函数的代码所示,我们可以用#ifdef SCULL_DEBUG或类似的宏屏蔽register_symtab调用。

(代码)

    我们在第2章“编写和运行模块”的“注册符号表”一节中已经看到了类似内容,那里说,如果模块不注册符号表,所有的全局符号就都开放。尽管这一功能仅在SCULL_DEBUG被激活时才有效,为了避免内核中的名字空间污染,所有的全局符号有合适的前缀(参见第2章的“模块与应用程序”一节)。

使用klogd

    klogd守护进程的近期版本可以在oops存放到记录文件前对oops消息解码。解码过程只由版本1.3或更新版本的守护进程完成,而且只有将-k /usr/src/linux/System.map做为参数传递给守护进程时才解码。(你可以用其他符号表文件代替System.map)

    有新的klogd给出的faulty的oops如下所示,它写到了系统记录中:

(代码)

    我想能解码的klogd对于调试一般的Linux安装的核心来说是很好的工具。由klogd解码的消息包括大部分ksymoops的功能,而且也要求用户编译额外的工具,或是,当系统出现故障时,为了给出完整的错误报告而合并两个输出。当oops发生在内核时,守护进程还会正确地解码指令指针。它并不反汇编代码,但这不是问题,当错误报告给出消息时,二进制数据仍然存在,可以离线反汇编代码。

    守护进程的另一个功能就是,如果符号表版本与当前内核不匹配,它会拒绝解析符号。如果在系统记录中解析出了符号,你可以确信它是正确的解码。

    然而,尽管它对Linux用户很有帮助,这个工具在调试模块时没有什么帮助。我个人没有在开放软件的电脑里使用解码选项。klogd的问题是它不解析模块中的符号;因为守护进程在程序员加载模块前就已经运行了,即使读了/proc/ksyms也不会有什么帮助。记录文件中存在解析后的符号会使oops和ksymoops混淆,造成进一步解析的困难。

    如果你需要使用klogd调试你的模块,最新版本的守护进程需要加入一些新的特殊支持,我期待它的完成,只要给内核打一个小补丁就可以了。

系统挂起

    尽管内核代码中的大多数错误仅会导致一个oops消息,有时它们困难完全将系统挂起。如果系统挂起了,没有消息能够打印出来。例如,如果代码遇到一个死循环,内核停止了调度过程,系统不会再响应任何动作,包括魔法键Ctrl-Alt-Del组合。

    处理系统挂起有两个选择――一个是防范与未然,另一个就是亡羊补牢,在发生挂起后调试代码。

    通过在策略点上插入schedule调用可以防止死循环。schedule调用(正如你所猜想到的)调用调度器,因此允许其他进程偷取当然进程的CPU时间。如果进程因你的驱动程序中的错误而在内核空间循环,你可以在跟踪到这种情况后杀掉这个进程。

    在驱动程序代码中插入schedule调用会给程序员带来新的“问题”:函数,,以及调用轨迹中的所有函数,必须是可重入的。在正常环境下,由于不同的进程可能并发地访问设备,驱动程序做为整体是可重入的,但由于Linux内核是不可抢占的,不必每个函数都是可重入的。但如果驱动程序函数允许调度器中断当前进程,另一个不同的进程可能会进入同一个函数。如果schedule调用仅在调试期间打开,如果你不允许,你可以避免两个并发进程访问驱动程序,所以并发性倒不是什么非常重要的问题。在介绍阻塞型操作时(第5章的“写可重入代码”)我们再详细介绍并发性问题。

    如果要调试死循环,你可以利用Linux键盘的特殊键。默认情况下,如果和修饰键一起按了PrScr键(键码是70),系统会向当前控制台打印有关机器状态的有用信息。这一功能在x86和Alpha系统都有。Linux的Sparc移植也有同样的功能,但它使用了标记为“Break/Scroll Lock”的键(键码是30)。

    每一个特殊函数都有一个名字,并如下面所示都有一个按键事件与之对应。组合键之后的括号里是函数名。

Shift-PrScr(Show_Memory)

    打印若干行关于内存使用的信息,尤其是有关缓冲区高速缓存的使用情况。

Control-PrScr(Show_State)

    针对系统里的每一个处理器打印一行信息,同时还打印内部进程树。对当前进程进行标记。

RightAlt-PrScr(Show_Registers)

    由于它可以打印按键时的处理器寄存器内容,它是系统挂起时最重要的一个键了。如果有当前内核的系统表的话,查看指令计数器以及它如何随时间变化,对了解代码在何处循环非常有帮助。

    如果想将这些函数映射到不同的键上,每一个函数名都可以做为参数传递给loadkeys。键盘映射表可以任意修改(这是“策略无关的”)。

    如果console_loglevel足够到的话,这些函数打印的消息会出现在控制台上。如果不是你运行了一个旧klogd和一个新内核的话,默认记录级应该足够了。如果没有出现消息,你可以象以前说的那样提升记录级。“足够高”的具体值与你使用的内核版本有关。对于Linux 2.0或更新的版本来说是5。

    即便当系统挂起时,消息也会打印到控制台上,确认记录级足够高是非常重要的。消息是在产生中断时生成的,因此即便有错的进程不释放CPU也可以运行――当然,除非中断被屏蔽了,不过如果发生这种情况既不太可能也非常不幸。

    有时系统看起来象是挂起了,但其实不是。例如,如果键盘因某种奇怪的原因被锁住了就会发生这种情况。这种假挂起可以通过查看你为探明此种情况而运行的程序输出来判断。我有一个程序会不断地更新LED显示器上的时钟,我发现这个对于验证调度器尚在运行非常有用。你可以不必依赖外部设备就可以检查调度器,你可以实现一个程序让键盘LED闪烁,或是不断地打开关闭软盘马达,或是不断触动扬声器――不过我个人认为,通常的蜂鸣声很烦人,应该尽量避免。看看ioctl命令KDMKTONE。O’Reilly FTP站点上的例子程序(misc-progs/heartbeat.c)中有一个是让键盘LED不断闪烁的。

    如果键盘不接收输入了,最佳的处理手段是从网络登录在系统中,杀掉任何违例的进程,或是重新设置键盘(用kdb_mode -a)。然而,如果你没有网络可用来恢复的话,发现系统挂起是由键盘锁死造成的一点儿用也没有。如果情况确实是这样,你应该配置一种替代输入设备,至少可以保证正常地重启系统。对于你的计算机来说,关闭系统或重启比起所谓的按“大红钮”要更方便一些,至少它可以免去长时间地fsck扫描磁盘。

    这种替代输入设备可以是游戏杆或是鼠标。在sunsite.edu.cn上有一个游戏杆重启守护进程,gpm-1.10或更新的鼠标服务器可以通过命令行选项支持类似的功能。如果键盘没有锁死,但是却误入“原始”模式,你可以看看kdb包中文档介绍的一些小技巧。我建议最好在问题出现以前就看看这些文档,否则就太晚了。另一种可能是配置gpm-root菜单,增添一个“reboot”或“reset keyboard”菜单项;gpm-root一个响应控制鼠标事件的守护进程,它用来在屏幕上显示菜单和执行所配置的动作。

    最好,你会可以按“留意安全键”(SAK),一个用于将系统恢复为可用状态的特殊键。由于不是所有的实现都能用,当前Linux版本的默认键盘表中没有为此键特设一项。不过你还是可以用loadkeys将你的键盘上的一个键映射为SAK。你应该看看drivers/char目录中的SAK实现。代码中的注释解释了为什么这个键在Linux 2.0中不是总能工作,这里我就不多说了。

    不过,如果你运行版本2.1.9或是更新的版本,你就可以使用非常可靠地留意安全键了。此外,2.1.43及后续版本内核还有一个编译选项选择是否打开“SysRq魔法键”;我建议你看一看drivers/char/sysrq.c中的代码并使用这项新技术。

    如果你的驱动程序真的将系统挂起了,而且你有不知道在哪插入schedule调用,最佳的处理方法就是加一些打印消息,并将它们打印到控制台上(通过修改console_loglevel变量值)。在重演挂起过程时,最好将所有的磁盘都以只读方式安装在系统上。如果磁盘是只读的或没有安装,就不会存在破坏文件系统或使其进入不一致状态的危险。至少你可以避免在复位系统后运行fsck。另一中方法就是使用NFS根计算机来测试模块。在这种情况下,由于NFS服务器管理文件系统的一致性,而它又不会受你的驱动程序的影响,你可以避免任何的文件系统崩溃。

使用调试器

    最后一种调试模块的方法就是使用调试器来一步步地跟踪代码,查看变量和机器寄存器的值。这种方法非常耗时,应该尽可能地避免。不过,某些情况下通过调试器对代码进行细粒度的分析是非常有益的。在这里,我们所说的被调试的代码运行在内核空间――除非你远程控制内核,否则不可能一步步跟踪内核,这会使很多事情变得更加困难。由于远程控制很少用到,我们最后介绍这项技术。所幸的是,在当前版本的内核中可以查看和修改变量。

    在这一级上熟练地使用调试器需要精通gdb命令,对汇编码有一定了解,并且有能够将源码与优化后的汇编码对应起来的能力。

    不幸的是,gdb更适合与调试核心而不是模块,调试模块化的代码需要更多的技术。这更多的技术就是kdebug包,它利用gdb的“远程调试”接口控制本地内核。我将在介绍普通调试器后介绍kdebug。

使用gdb

    gdb在探究系统内部行为时非常有用。启动调试器时必须假想内核就是一个应用程序。除了指定内核文件名外,你还应该在命令行中提供内存镜象文件的名字。典型的gdb调用如下所示:

(代码)

    第一个参数是未经压缩的内核可执行文件(在你编译完内核后,这个文件在/usr/src/linux目录中)的名字。只有x86体系结构有zImage文件(有时称为vmlinuz),它是一种解决Intel处理器实模式下只有640KB限制的一种技巧;而无论在哪个平台上,vmlinux都是你所编译的未经压缩的内核。

    gdb命令行的第二个参数是是内存镜象文件的名字。与其他在/proc下的文件类似,/proc/kcore也是在被读取时产生的。当read系统调用在/proc文件系统执行时,它映射到一个用于数据生成而不是数据读取的函数上;我们已在“使用/proc文件系统”一节中介绍了这个功能。系统用kcore来表示按内存镜象文件格式存储的内核“可执行文件”;由于它要表示整个内核地址空间,它是一个非常巨大的文件,对应所有的物理内存。利用gdb,你可以通过标准gdb命令查看内核标量。例如,p jiffies可以打印从系统启动到当前时刻的时钟滴答数。

    当你从gdb打印数据时,内核还在运行,不同数据项会在不同时刻有不同的数值;然而,gdb为了优化对内存镜象文件的访问会将已经读到的数据缓存起来。如果你再次查看jiffies变量,你会得到和以前相同的值。缓存变量值防止额外的磁盘操作对普通内存镜象文件来说是对的,但对“动态”内存镜象文件来说就不是很方便了。解决方法是在你想刷新gdb缓存的时候执行core-file /proc/kcore命令;调试器将使用新的内存镜象文件并废弃旧信息。但是,读新数据时你并不总是需要执行core-file命令;gdb以1KB的尺度读取内存镜象文件,仅仅缓存它所引用的若干块。

    你不能用普通gdb做的是修改内核数据;由于调试器需要在访问内存镜象前运行被调试程序,它是不会去修改内存镜象文件的。当调试内核镜象时,执行run命令会导致在执行若干指令后导致段违例。出于这个原因,/proc/kcore都没有实现write方法。

    如果你用调试选项(-g)编译了内核,结果产生的vmlinux比没有用-g选项的更适合于gdb。不过要注意,用-g选项编译内核需要大量的磁盘空间――支持网络和很少几个设备和文件系统的2.0内核在PC上需要11KB。不过不管怎样,你都可以生成zImage文件并用它来其他系统:在生成可启动镜象时由于选项-g而加入的调试信息最终都被去掉了。如果我有足够的磁盘空间,我会一致打开-g选项的。

    在非PC计算机上则有不同的方法。在Alpha上,make boot会在生成可启动镜象前将调试信息去掉,所以你最终会获得vmlinux和vmlinux.gz两个文件。gdb可以使用前者,但你只能用后者启动。在Sparc上,默认情况下内核(至少是2.0内核)不会被去掉调试信息,所以你需要在将其传递给silo(Sparc的内核加载器)前将调试信息去掉,这样才能启动。由于尺寸的问题,无论milo(Alpha的内核加载器)还是silo都不能启动未去掉调试信息的内核。

    当你用-g选项编译内核并且用vmlinux和/proc/kcore一起使用调试器,gdb可以返回很多有关内核内部结构的信息。例如,你可以使用类似于这样的命令,p *module_list,p *module_list->next和p *chrdevs[4]->fops等显示这些结构的内容。如果你手头有内核映射表和源码的话,这些探测命令是非常有用的。

    另一个gdb可以在当前内核上执行的有用任务是,通过disassemble命令(它可以缩写)或是“检查指令”(x/i)命令反汇编函数。disassemble命令的参数可以是函数名或是内存区范围,而x/i则使用一个内存地址做为参数,也可以用符号名。例如,你可以用x/20i反汇编20条指令。注意,你不能反汇编一个模块的函数,这是因为调试器处理vmlinux,它并不知道你的模块的信息。如果你试图用模块的地址反汇编代码,gdb很有可能会报告“不能访问xxxx处的内存(Cannot access memory at xxxx)”。基于同样的原因,你不查看属于模块的数据项。如果你知道你的变量的地址,你可以从/dev/mem中读出它的值,但很难弄明白从系统内存中分解出的数据是什么含义。

    如果你需要反汇编模块函数,你最好对用objdump工具处理你的模块文件。很不幸,该工具只能对磁盘上的文件进行处理,而不能对运行中的模块进行处理;因此,objdump中给出的地址都是未经重定位的地址,与模块的运行环境无关。

    如你所见,当你的目的是查看内核的运行情况时,gdb是一个非常有用的工具,但它缺少某些功能,最重要的一些功能就是修改内核项和访问模块的功能。这些空白将由kdebug包填补。

使用kdebug

    你可用从一般的FTP站点下的pcmcia/extras目录下拿到kdebug,但是如果你想确保拿到的是最新的版本,你最好到ftp://hyper.stanford.edu/pub/pcmcia/extras/去找。该工具与pcmcia没有什么关系,但是这两个包是同一个作者写的。

    kdebug是一个使用gdb“远程调试”接口与内核通信的小工具。使用时首先向内核加载一个模块,调试器通过/dev/kdebug访问内核数据。gdb将该设备当成一个与被调试“应用”通信的串口设备,但它仅仅是一个用于访问内核空间的通信通道。由于模块本身运行在内核空间,它可以看到普通调试器无法访问的内核空间地址。正如你所猜想到的,模块是一个字符设备驱动程序,并且使用了主设备号动态分配技术。

    kdebug的优点在于,你无需打补丁或重新编译:无论是内核还是调试器都无需修改。你所需要做的就是编译和安装软件包,然后调用kgdb,kgdb是一个完成某些配置并调用gdb,通过新接口访问内核部件结构的脚本程序。

    但是,即便是kdebug也没有提供单步跟踪内核代码和设置断点的功能。这几乎是不可避免的,因为内核必须保持运行状态以保证系统的出于运行状态,跟踪内核代码的唯一方法就是后面将要谈到的从另外一台计算机上通过串口控制系统。不过kgdb的实现允许用户修改被调试应用(即当前内核)的数据项,可以传递给内核任意数目的参数,并以读写方式访问模块所属的内存区。

    最后一个功能就是通过gdb命令将模块符号表增加到调试器内部的符号表中。这个工作是由kgdb完成的。然后当用户请求访问某个符号时,gdb就知道它的地址是哪了。最终的访问是由模块里的内核代码完成的。不过要注意,kdebug的当前版本(1.6)在映射模块化代码地址方面还有些问题。你最好通过打印一些符号并与/proc/ksyms中的值进行比较来做些检查。如果地址没有匹配,你可以使用数值,但必须将它们强行转换为正确的类型。下面就是一个强制类型转换的例子:

(代码)

    kdebug的另一个强于gdb的优点是,它允许你在数据结构被修改后读取到最新的值,而不必刷新调试器的缓存;gdb命令set remotecache 0可以用来关闭数据缓存。

    由于kdebug与gdb使用起来很相似,这里我就不过多地罗列使用这个工具的例子了。对于知道如何使用调试器的人来说,这种例子很简单,但对于那些对调试器一无所知的人来说就很晦涩了。能够熟练地使用调试器需要时间和经验,我不准备在这里承担老师的责任。

    总而言之,kdebug是一个非常好的程序。在线修改数据结构对于开发人员来说是一个非常大的进步(而且一种将系统挂起的最简单方法)。现在有许多工具可以使你的开发工作更轻松――例如,在开发scull期间,当模块的使用计数器增长后*,我可以使用kdebug来将其复位为0。这就不必每次都麻烦我重启机器,登录,再次启动我的应用程序等等。

远程调试

    调试内核镜象的最后一个方法是使用gdb的远程调试能力。

    当执行远程调试的时候,你需要两台计算机:一台运行gdb;另一台运行你要调试的内核。这两台计算机间用普通串口连接起来。如你所料,控制gdb必须能够理解它所控制的内核的二进制格式。如果这两台计算机是不同的体系结构,必须将调试器编译为可以支持目标平台的。

    在2.0中,Linux内核的Intel版本不支持远程调试,但是Alpha和Sparc版本都支持。在Alpha版本中,你必须在编译时包含对远程调试的支持,并在启动时通过传递给内核命令行参数kgdb=1或只有kgdb打开这个功能。在Sparc上,始终包含了对远程调试的支持。启动选项kgdb=ttyx可以用来选择在哪个串口上控制内核,x可以是a或b。如果没有使用kgdb=选项,内核就按正常方式启动。

    如果在内核中打开了远程调试功能,系统在启动时就会调用一个特殊的初始化函数,配置被调试内核处理它自己的断点,并且跳转到一个编译自程序中的断点。这会暂停内核的正常执行,并将控制转移给断点服务例程。这一处理函数在串口线上等待来自于gdb的命令,当它获得gdb的命令后,就执行相应的功能。通过这一配置,程序员可以单步跟踪内核代码,设置断点,并且完成gdb所允许的其他任务。

    在控制端,需要一个目标镜象的副本(我们假设它是linux.img),还需要一个你要调试的模块副本。如下命令必须传递给gdb:

file linux.img

    file命令告诉gdb哪个二进制文件需要调试。另一种方法是在命令行中传递镜象文件名。这个文件本身必须和运行在另一端的内核一模一样。

target remote /dev/ttyS1

    这条命令通知gdb使用远程计算机做为调试过程的目标。/dev/ttyS1是用来通信的本地串口,你可以指定任一设备。例如,前面介绍的kdebug软件包中的kgdb脚本使用target remote /dev/kdebug。

add-symbol-file module.o address

    如果你要调试已经加载到被控内核的模块的话,在控制系统上你需要一个模块目标文件的副本。add-symbol-file通知gdb处理模块文件,假定模块代码被定位在地址address上了。

    尽管远程调试可以用于调试模块,但你还是要加载模块,并且在模块上插入断点前还需要触发另一个断点,调试模块还是需要很多技巧的。我个人不会使用远程调试去跟踪模块,除非异步运行的代码,如中断处理函数,出了问题。


[目录]


ptrace进程跟踪

函数使用说明
名字
ptrace - 进程跟踪

形式
#include <sys/ptrace.h>
int ptrace(int request, int pid, int addr, int data);

描述
    Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。

Request参数决定了系统调用的功能:
    PTRACE_TRACEME
        本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。
    PTRACE_PEEKTEXT, PTRACE_PEEKDATA
        从内存地址中读取一个字节,内存地址由addr给出。
    PTRACE_PEEKUSR
        从USER区域中读取一个字节,偏移量为addr。
    PTRACE_POKETEXT, PTRACE_POKEDATA
        往内存地址中写入一个字节。内存地址由addr给出。
    PTRACE_POKEUSR
        往USER区域中写入一个字节。偏移量为addr。
    PTRACE_SYSCALL, PTRACE_CONT
        重新运行。
    PTRACE_KILL
        杀掉子进程,使它退出。
    PTRACE_SINGLESTEP
        设置单步执行标志
    PTRACE_ATTACH
        跟踪指定pid 进程。
    PTRACE_DETACH
        结束跟踪

Intel386特有:
    PTRACE_GETREGS
        读取寄存器
    PTRACE_SETREGS
        设置寄存器
    PTRACE_GETFPREGS
        读取浮点寄存器
    PTRACE_SETFPREGS
        设置浮点寄存器
    init进程不可以使用此函数

返回值
成功返回0。错误返回-1。errno被设置。

错误
    EPERM
        特殊进程不可以被跟踪或进程已经被跟踪。
    ESRCH
        指定的进程不存在
    EIO
        请求非法

功能详细描述
    PTRACE_TRACEME
    形式:ptrace(PTRACE_TRACEME,0 ,0 ,0)
    描述:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。

    PTRACE_PEEKTEXT, PTRACE_PEEKDATA
    形式:ptrace(PTRACE_PEEKTEXT, pid, addr, data)
    描述:从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。

    PTRACE_POKETEXT, PTRACE_POKEDATA
    形式:ptrace(PTRACE_POKETEXT, pid, addr, data)
    描述:往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。

    PTRACE_PEEKUSR
    形式:ptrace(PTRACE_PEEKUSR, pid, addr, data)
    描述:从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。

    PTRACE_POKEUSR
    形式:ptrace(PTRACE_POKEUSR, pid, addr, data)
    描述:往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。

    PTRACE_CONT
    形式:ptrace(PTRACE_CONT, pid, 0, signal)
    描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。

    PTRACE_SYSCALL
    形式:ptrace(PTRACE_SYS, pid, 0, signal)
描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。

    PTRACE_KILL
    形式:ptrace(PTRACE_KILL,pid)
描述:杀掉子进程,使它退出。pid表示被跟踪的子进程。

    PTRACE_SINGLESTEP
    形式:ptrace(PTRACE_KILL, pid, 0, signle)
描述:设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。

    PTRACE_ATTACH
    形式:ptrace(PTRACE_ATTACH,pid)
描述:跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。

    PTRACE_DETACH
    形式:ptrace(PTRACE_DETACH,pid)
描述:结束跟踪。 pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。

    PTRACE_GETREGS
    形式:ptrace(PTRACE_GETREGS, pid, 0, data)
描述:读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。

    PTRACE_SETREGS
    形式:ptrace(PTRACE_SETREGS, pid, 0, data)
描述:设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。

    PTRACE_GETFPREGS
    形式:ptrace(PTRACE_GETFPREGS, pid, 0, data)
    描述:读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有浮点协处理器387的所有寄存器的值。

    PTRACE_SETFPREGS
    形式:ptrace(PTRACE_SETREGS, pid, 0, data)
    描述:设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有浮点协处理器387的所有寄存器的值。


[目录]


宏#与##

宏定义中的"#"前缀将参数替换成字符串,例如
#define test(x) #x
test(123) 被替换成字符串"123";
"##"用于连接参数,例如
#define test(x,y) ##x##y
test(123,456) 替换成123456;
因此BI(0x0,0)替换成BUILD_IRQ(0x00),"pushl $"#nr"-256\n\t"替换成"pushl $""0x00""-256\n\t"

cpp.info手册对此有详细说明。

[目录]


lxr和glimpse

读linux源代码的工具---lxr和glimpse简介

    我们在阅读linux源代码时都有这样的体会:核心的组织相对松散,在看一个文件时往往要牵涉到其他的头文件、源代码文件。如此来回跳转寻找变量、常量、函数的定义十分不方便,这样折腾几次,便使读代码的心情降到了低点。

    lxr(linux cross reference)就是一个解决这个问题的工具:他对你指定的源代码文件建立索引数据库,利用perl脚本CGI动态生成包含源码的web页面,你可以用任何一种浏览器查阅。在此web页中,所有的变量、常量、函数都以超连接的形式给出,十分方便查阅。比如你在阅读/usr/src/linux/net/socket.c的源代码,发现函数get_empty_inode不知道是如何以及在哪里定义的,这时候你只要点击 get_empty_inode,lxr将返回此函数的定义、实现以及各次引用是在什么文件的哪一行,注意,这些信息也是超连接,点击将直接跳转到相应的文件相应的行。另外lxr还提供标识符搜索、文件搜索,结合程序glimpse还可以提供对所有的源码文件进行全文检索,甚至包括注释!

    下面将结合实例介绍一下lxr和glimpse的基本安装和使用,由于glimpse比较简单,就从它开始:
    首先访问站点: http://glimpse.cs.arizona.edu/ 得到glimpse 的源码,比如我得到的是glimpse-4.12.5.tar.gz .

    用root登录,在: 任一目录下用tar zxvf glimpse-4.12.5.tar.gz解开压缩包,在当前目录下出现新目录glimpse-4.12.5 .进入该目录,执行make即可。进入bin目录,将文件glimpse和glimpseindex拷贝到/bin或/usr/bin下即可。如果单独使用glimpse,那么只要简单的执行glimpseindex foo 即可,其中foo是你想要索引的目录,比如说是/usr/src/linux .glimpseindex 的执行结果是在你的起始目录下产生若干.glimpse*的索引文件。然后你只要执行glimpse yourstring即可查找/usr/src/linux下所有包含字符串yourstring的文件。

    对于lxr,你可以访问 http://lxr.linux.no/得到它的源代码。解包后,遵循如下步骤:

/*下面的文字来源于lxr的帮助文档以及本人的安装体会*/
    1)修改Makefile中的变量PERLBIN和INSTALLPREFIX,使它们分别为 perl程序的位置和你想lxr安装的位置.在我的机器上,PERLBIN的值为 /usr/bin/perl .至于INSTALLPREFIX,有如下原则,lxr的安装路径必须是web服务器能有权限访问。因此它的值简单一点可取 /home/httpd/html/lxr (对于Apache web server)。

    2)执行 make install

    3)修改$INSTALLPREFIX/http/lxr.conf :
baseurl : http://yourIP/lxr/http/
htmlhead: /home/httpd/html/lxr/http/template-head
htmltail: /home/httpd/html/lxr/http/template-tail
htmldir: /home/httpd/html/lxr/http/template-dir
sourceroot : /usr/src/linux # 假如对linux核心代码索引
dbdir : /home/httpd/html/lxr/dbdir/ #dbdirk可任意起名,且位置任意
glimpsebin: /usr/bin/glimpse #可执行程序glimpse的位置

    4)在$INSTALLPREFIX/http/下增加一个文件.htaccess 内容:
SetHandler cgi-script
上面这个文件保证Apache server将几个perl文件作为cgi-script.

    5)按照lxr.conf中的设置建立dbdir ,按照上例,建立目录 /home/httpd/html/lxr/dbdir
进入这个目录执行$INSTALLPREFIX/bin/genxref yourdir
其中yourdir是源码目录,比如/usr/src/linux
如果要结合glimpse,则执行glimpseindex -H . yourdir

    6)修改 /etc/httpd/conf/access.conf ,加入
《Directory /home/httpd/html/lxr/http》 Options All AllowOverride All order allow,deny allow from all
《/Directory》

    7)进入/etc/rc.d/init.d/ 执行
killall httpd
./httpd start
进入X ,用浏览器 http://yourIP/lxr/http/blurb.html
大功告成 ,这下你可以舒心的读源码了。

    注意:以上只是lxr和glimpse的基本用法,进一步的说明可以参考连机文档。
***文中的“《”“》”,实际为“<”“>”,sorry,不这么写就不显示了:(

[目录]


内核阅读介绍

  随着linux的逐步普及,现在有不少人对于Linux的安装及设置已经比较熟悉了。与Linux 的蓬勃发展相适应,想深入了解Linux的也越来越多。而要想深入了解Linux,就需要阅读和分析linux内核的源代码。

  Linux的内核源代码可以从很多途径得到。一般来讲,在安装的linux系统下,/usr/src/linux目录下的东西就是内核源代码。另外还可以从互连网上下载,解压缩后文件一般也都位于linux目录下。内核源代码有很多版本,目前最新的稳定版是2.2.14。

  许多人对于阅读Linux内核有一种恐惧感,其实大可不必。当然,象Linux内核这样大而复杂的系统代码,阅读起来确实有很多困难,但是也不象想象的那么高不可攀。只要有恒心,困难都是可以克服的。也不用担心水平不够的问题,事实上,有很多事情我们不都是从不会到会,边干边学的吗?

  任何事情做起来都需要有方法和工具。正确的方法可以指导工作,良好的工具可以事半功倍。对于Linux 内核源代码的阅读也同样如此。下面我就把自己阅读内核源代码的一点经验介绍一下,最后介绍Window平台下的一种阅读工具。

  对于源代码的阅读,要想比较顺利,事先最好对源代码的知识背景有一定的了解。对于linux内核源代码来讲,我认为,基本要求是:1、操作系统的基本知识;2、对C语言比较熟悉,最好要有汇编语言的知识和GNU C对标准C的扩展的知识的了解。另外在阅读之前,还应该知道Linux内核源代码的整体分布情况。我们知道现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序、网络等组成。看一下Linux内  核源代码就可看出,各个目录大致对应了这些方面。Linux内核源代码的组成如下(假设相对于linux目录):

  arch 这个子目录包含了此核心源代码所支持的硬件体系结构相关的核心代码。如对于X86平台就是i386。

  include 这个目录包括了核心的大多数include文件。另外对于每种支持的体系结构分别有一个子目录。

  init 此目录包含核心启动代码。

  mm 此目录包含了所有的内存管理代码。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/i386/mm/fault.c 。

  drivers 系统中所有的设备驱动都位于此目录中。它又进一步划分成几类设备驱动,每一种也有对应的子目录,如声卡的驱动对应于drivers/sound。

  ipc 此目录包含了核心的进程间通讯代码。

  modules 此目录包含已建好可动态加载的模块。

  fs Linux支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext2文件系统对应的就是ext2子目录。

  kernel 主要核心代码。同时与处理器结构相关代码都放在arch/*/kernel目录下。

  net 核心的网络部分代码。里面的每个子目录对应于网络的一个方面。

  lib 此目录包含了核心的库代码。与处理器结构相关库代码被放在arch/*/lib/目录下。

  scripts此目录包含用于配置核心的脚本文件。

  Documentation 此目录是一些文档,起参考作用。

  清楚了源代码的结构组成后就可以着手阅读。对于阅读方法或者说顺序,有所谓的纵向与横向之分。所谓纵向就是顺着程序的执行顺序逐步进行;所谓横向,就是分模块进行。其实他们之间不是绝对的,而是经常结合在一起进行。对于Linux源代码来讲,启动的代码就可以顺着linux的启动顺序一步一步来,它的大致流程如下(以X86平台为例):

  ./larch/i386/boot/bootSect.S-->./larch/i386/boot/setup.S-->./larch/i386/kernel/head.S-->./init/main.c中的start_kernel()。而对于象内存管理等部分,则可以单独拿出来进行阅读分析。我的体会是:开始最好按顺序阅读启动代码,然后进行专题阅读,如进程部分,内存管理部分等。在每个功能函数内部应该一步步来。实际上这是一个反复的过程,不可能读一遍就理解。

  俗话说:“工欲善其事,必先利其器”。 阅读象Linux核心代码这样的复杂程序令人望而生畏。它象一个越滚越大的雪球,阅读核心某个部分经常要用到好几个其他的相关文件,不久你将会忘记你原来在干什么。所以没有一个好的工具是不行的。由于大部分爱好者对于Window平台比较熟悉,并且还是常用Window系列平台,所以在此我介绍一个Window下的一个工具软件:Source Insight。这是一个有30天免费期的软件,可以从www.sourcedyn.com下载。安装非常简单,和别的安装一样,双击安装文件名,然后按提示进行就可以了。安装完成后,就可启动该程序。这个软件使用起来非常简单,是一个阅读源代码的好工具。它的使用简单介绍如下:先选择Project菜单下的new,新建一个工程,输入工程名,接着要求你把欲读的源代码加入(可以整个目录加)后,该软件就分析你所加的源代码。分析完后,就可以进行阅读了。对于打开的阅读文件,如果想看某一变量的定义,先把光标定位于该变量,然后点击工具条上的相应选项,该变量的定义就显示出来。对于函数的定义与实现也可以同样操作。别的功能在这里就不说了,有兴趣的朋友可以装一个Source Insight,那样你阅读源代码的效率会有很大提高的。怎么样,试试吧!


[目录]


内核重编译常见故障

内核重编译常见故障

    内核重编译对很多Linux爱好者来说是一个不小的挑战。笔者认为,很多Linux用户对内核通常有一种误解,他们认为普通用户是不能调整内核的。其实,就实际情况而言,这种认识是不全面的。应该说,内核重编译是具有一定深度和复杂性的,同时也是易失败的配置工作。

    如果大家留意那些比较权威的Linux参考工具书的话,就会发现很多原版书籍都把内核重编译作为一个很重要的章节进行介绍。本文将要向读者介绍的并不是如何去一步步的对Linux内核进行重编译,而是收集整理了几个在Linux内核重编译中最常见的故障及其解决方法,如果您在编译内核过程中遇到了类似的故障,那么本文将会对您有所帮助。

Linux内核重编译常见故障介绍
    在介绍Linux内核重编译常见故障前,假设我们已经按照参考工具书上的步骤对Linux内核进行了相应的配置。

    Linux内核重编译通常包括了许多步骤。如果“幸运”的话,Linux内核重编译是可以一次性编译成功的。如果在编译完成后,启动计算机或者内核的时候系统有错误信息的提示,那么最有可能出现的是以下5个:1.内核不能启动;2.异常I/O错误;3.内核反映缓慢;4.内核不能正确编译;5.系统重复启动。

故障分析及其解决方法

·内核不能启动

    当我们重新创建Linux内核时,主要是选择用户需要或不需要在系统中使用的设备及服务。从2.0版内核开始,Linux引入了一个全新的设计特征到内核中并提供了折中方案:组件可以动态的、随时的调入和调出内核。例如我们在修改/etc/lilo.conf之后都要重新启动系统,如果你的内核不能启动,并且在屏幕上看到了下面的信息:

Warning: unable to open an initial console Kernel panic: no init found. Try passing init= option to kernel

    这个错误最大的可能就是我们没有正确的给/etc/lilo.conf 中的“root=”提供参数。例如,在一个Linux系统中有root=dev/hdc5这样的配置方式,那么这是错误的,正确的应该是root=/dev/hdc5,不要小看只是多了一个 “/”,这是给root提供的重要参数。没有“/”则Linux内核无法确认root到底该从哪里启动。很多朋友往往忽略了这个小细节而造成内核引导失败。下面给出/etc/lilo.conf的一个正确的配置清单,仅供参考。

/etc/lilo.conf示例
―――――――――――
boot=/dev/hdc5
map=/boot/map
prompt
timeout=50
image=/boot/vmlinuz-2.2.2-1
label=Linux
root=/dev/hdc5
inirtd=/boot/initrd-2.2.2-1.img
read-only

other=/dev/hda1
label=dos
table=/dev/hda

·异常I/O错误

    如果您重新创建了一个Linux内核,并且能正确启动,但在使用新内核过程中,系统经常出现崩溃、死机等异常现象。那么很可能是I/O出现了问题。I/O异常除了使得系统频繁出现莫名其妙的故障之外,更重要的是会使Linux内核降级,导致整个系统系能严重下降。

    究其异常I/O错误的原因,是用户在编译Linux内核结束的时候没有执行“make dep clean”这一步骤。一般来说,我们在保存Kernel configuration选项中的“menuconfig”或“xconfig”时并不包含“make dep clean”这个步骤。因此,这里建议用户在保存配置后的Kernel configuration选项时注意确认是否已经进行了“make dep clean”这一步。

    另外,请注意硬件问题,很多时候编译内核是因为系统新增加了一些硬件设施。在硬件安装的时候有可能造成插槽松动等问题。

·内核反映缓慢

    目前很多计算机都采用了高速的CPU和大容量内存。可有时候在创建新内核后系统显得比没有配置内核之前的反映速度慢得多。出现这个情况,很可能是用户在修改Kernel configuration options的时候,在“menuconfig”或者“xconfig”选择了过多的选项。这样不仅使得计算机在编译新内核的时候要花费更多的时间,也使得系统在工作的时候占用了太多的内存。由于很多内存都是被无用的选项所占用,这就导致了系统运行的缓慢。解决方法很简单,尽量选择我们需要的选项,那些无用的,太过于复杂的选项就无需去修改了,有时候使用默认的参数效果会显得更好。
    进一步,例如redhat这样的发行版,为了支持尽可能多的硬件,所以选择支持了几乎所有的板卡驱动程序。在自己编译的时候,就可以不客气地“排头砍去”,只保留自己系统特有的硬件驱动。这样你才会得到比一般系统更好的性能。

·内核不能编译

    当用户输入“make bzImage modules”并按下输入键的时候,出现了内核不能编译或者其他的奇怪现象。此时最好的方法就是重新启动系统,然后使用“rpm -e”命令移除Kernel configuration tools这个内核配置工具。接着再重新安装这个工具(请使用“rpm -I”或“rpm -Uvh”命令来安装),如果能正确安装,那么问题就简单多了,此时再重新配置内核和再编译应该就能成功。如果还是不能编译内核,请检查硬件设备是不是有问题。

·系统重复启动

    出现这种情况,十有八九是因为在对内核做完修改之后忘记使用LILO来注册新的映象到启动加载程序。LILO需要内核的扇区位置,因此即使是拷贝映象也会将其迁移到新的扇区中,这将使得LILO存储的老指针挂在一个深渊上。

    为了解决这个问题,请从软驱启动并运行LILO命令,或使用挽救磁盘启动并安装启动分区到“/mnt”,同时使用如下命令:lilo -r /mnt。

[目录]


[ 本文件由良友·收藏家自动生成 ]