网络协议栈入门知识

9 8 月, 2012 by edsionte 2 comments »

Linux内核将一切都看作为文件,因此网络协议栈以文件系统sockfs的形式存在于内核中,该文件系统中的文件则是我们熟知的套接字。稍微熟知Linux文件系统的人都会知道内核中将所有文件系统进行抽象,通过虚拟文件系统(VFS)来统一管理。网络文件系统也是如此,也就是说超级块,索引节点,目录项等概念在网路文件系统中也存在。

在proc文件系统下,我们可以通过下面的方式得到内核当前所有文件系统的名称:

[root@TENCENT64 ~]# cat /proc/filesystems
nodev   sysfs
nodev   rootfs
nodev   bdev
nodev   proc
nodev   tmpfs
nodev   debugfs
nodev   sockfs
nodev   pipefs
nodev   anon_inodefs
nodev   rpc_pipefs
nodev   inotifyfs
…………
        ext3
        ext2
        ext4

网络文件系统和ext3这样的文件系统有什么区别?前者只存在于内存中,而后者则存在于磁盘中。比如上述信息中的sockfs和tmpfs,甚至是proc,在第一列均显示nodev,也就是说这些文件系统并没有存储在磁盘这样的块设备中。相反,它们存储在内存中,即采用的是一种非持久性的存储方式。

这里应该注意的是sockfs的“虚拟”和VFS的“虚拟”是两个不同的概念。前者表示sockfs这种虚拟文件系统不同于ext2这样的磁盘文件系统,它们存储在内存中,是一种非持久的存储,只要系统重启信息全部丢失。而虚拟文件系统中的“虚拟”表示的是对所有文件系统的一种抽象,它将所有不同种类文件系统的共同信息放入同一组数据结构当中,为上层用户提供了统一的接口。

VFS中用于描述文件系统类型的数据结构为file_system_type,所有文件系统都对应该类型的变量:

static struct file_system_type sock_fs_type = {
        .name =         "sockfs",
        .mount =        sockfs_mount,
        .kill_sb =      kill_anon_super,
};

static struct file_system_type proc_fs_type = {
        .name           = "proc",
        .mount          = proc_mount,
        .kill_sb        = proc_kill_sb,
};

可以看到,/proc/filesystem中的文件系统名称即从file_system_type结构的实例中获得。

socket结构

内核中通过socket结构体来表示套接字。该结构中包含的字段比较少,比如描述套接字状态的state,套接字标志位flags,等待队列wp以及与该套接字关联的文件对象。

 struct socket {
         socket_state            state;
         kmemcheck_bitfield_begin(type);
         short                   type;
         kmemcheck_bitfield_end(type);
         unsigned long           flags;
         struct socket_wq __rcu  *wq;
         struct file             *file;
         struct sock             *sk;
         const struct proto_ops  *ops;
 };

在socket结构中包含了一个与它比较相似的结构sock,该结构也是对套接字进行描述的。

sock结构体

sock结构中包含大量的字段,这些字段更多的是用来描述协议的通用属性,而socket结构则是更多的描述与应用程序相关的部分。

sk_buff结构体

sk_buff用于对网络协议中的数据包进行描述,不管该数据包位于那一层协议,都会有一个sk_buff结构与之对应。而sk_buff结构通过其内部的几个指针字段就可以轻松完成加包头去包头的操作。

三者的关系

每个socket结构都有一个sk字段指向sock结构,而sock结构中通过sk_socket字段反指向socket结构。sock结构通过sk_receive_queue和sk_write_queue等字段指向数据包头结构(struct sk_buff_head),而这个结构内部又通过prev和next两个字段指向真正的数据包结构sk_buff。

调用inotify_init()出现function not implemented的解决办法

25 7 月, 2012 by edsionte 无评论 »

调用inotify_init()后返回-1,通过strerror(error)提示为“Function not implemented”,这个错误的排除方法如下:

inotify是从内核2.6.13开始引入的,而glibc为inotify机制提供用户态API是从2.4开始引入的,因此先确定内核以及glibc的版本是否满足上述条件。

内核的版本确定方法:uname -r

查看glibc的版本信息:ls -l /lib/libc.so.6

如果上述版本信息无误,那么接着查看当前内核是否打开了inotify的相关选项:

1.进入家目录:cd

2.将config.gz文件拷贝到家目录:cp /proc/config.gz  ./

3.解压这个文件到家目录:gzip -d config.gz

4.打开刚刚解压的文件(默认情况下文件名为config),查找CONFIG_INOTIFY关键字。如果CONFIG_INOTIFY和CONFIG_INOTIFY_USR没有打开,也就是说其值不是Y,那么就不能使用inotify功能。

块缓冲区和块缓冲区头

24 7 月, 2012 by edsionte 2 comments »

在详细说明块缓冲区和块缓冲区头之前,我们先来看一下块设备中的两个基本概念:扇区和块。

扇区是块设备传输数据的基本单元,也就是说它是块设备中最小的寻址单位,扇区通常的大小为512B。块是内核对文件系统的一种抽象,也就是说内核执行的所有磁盘操作都是以块为基本单位的。可以简单的将扇区和块理解为:扇区是硬件设备传输数据的最小单位,而块是操作系统传输数据的最小单位。一个块通常对应一个或多个相邻的扇区,由于内核将块作为对文件系统操作的最小单位,因此VFS将其看作是单一的数据单元。

当内核从磁盘读入数据后或者即将写数据到磁盘时,它需要将数据写入一个缓冲区。缓冲区其实就是物理页框的一部分,因此一个物理页框可能包含一个或多个块缓冲区。根据上述描述的关系,包含磁盘数据的物理页框构造如下图:

正如上面所说,块缓冲区是页框的一部分,因此不用特别描述块缓冲区中的数据。每个块缓冲区都对应一个块缓冲区头buffer_head,他们的关系如同物理页框和物理页框描述符,前者用来存储数据,后者是对前者的属性以及控制信息的描述。块缓冲区头、块缓冲区以及页框的关系如下:


内核中使用buffer_head结构来描述缓冲区头,该结构中的部分字段解释如下:

struct buffer_head {
         unsigned long b_state;          
         struct buffer_head *b_this_page;
         struct page *b_page;            
         atomic_t b_count;               
         u32 b_size;                     
         sector_t b_blocknr;             
         char *b_data;                  
         struct block_device *b_bdev;
         bh_end_io_t *b_end_io;         
         void *b_private;               
         struct list_head b_assoc_buffers; 

b_state:对块缓冲区状态的描述。
b_this_page:在一个页框中,可能包含多个块缓冲区。一个页框内的所有缓冲区形成循环链表,该字段指向下一个块缓冲区。
b_page:指向缓冲区所在页框的描述符。
b_size:块缓冲区大小。
b_data:当前块在作为缓冲的页框内的位置。
b_bdev:指向块设备的指针。

miniLZO的基本使用

19 6 月, 2012 by edsionte 无评论 »

miniLZO是一种轻量级的压缩和解压缩库,它是基于LZO压缩和解压缩算法实现的。LZO虽然功能强大,但是编译后的库文件较大,而minilzo编译后的库则小于5kb,因此miniLZO为那些仅需要简单压缩和解压缩功能的程序而设计。

实现miniLZO的源码很简单,包含如下文件:

edsionte@virtualPC ~/minilzo-2.06 $ tree
.
├── COPYING
├── lzoconf.h
├── lzodefs.h
├── Makefile
├── minilzo.c
├── minilzo.h
├── README.LZO
└── testmini.c

在程序中加入miniLZO压缩和解压缩功能的方法很简单,只需将源文件minilzo.c和头文件lzoconf.h,lzodefs.h和minilzo.h拷贝到源文件目录下,在源代码中加入头文件minilzo.h,并且在编译时加入miniloz.c。

如何在源程序中具体使用miniLZO的压缩和解压缩功能?下面将给出一个简单的实例程序来说明使用方法。该程序所演示的功能是:首先利用minilzo提供的压缩API将指定文件的前1024字节进行压缩,并将压缩后的内容保存在cfile文件中;然后将该压缩文件的内容再进行解压缩,并存入另一个文件dcfile中。

正如上面所描述的那样,要使用miniLZO,必须加入头文件minilzo.h。

#include "minilzo.h"

#define MAX_LEN 1024

#define HEAP_ALLOC(var,size) \
    lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]

//the wkmem is used for compressed
static HEAP_ALLOC(wkmem, LZO1X_1_MEM_COMPRESS);

除此之外,定义一个宏HEAP_ALLOC,它为压缩提供一个内存空间。要使用压缩或解压缩功能,必须先通过lzo_init()对miniLZO对应的库进行初始化。

int main()
{
	int fd;
	char *srcbuffer = NULL, *destbuffer = NULL;
	lzo_uint inlen = MAX_LEN;
	lzo_uint outlen = 0;
	lzo_uint newlen = 0;

	//init the lzo lib
	if (lzo_init() != LZO_E_OK) {
		printf("lzo_init error.\n");
		return -1;
	}

打开要压缩的文件,将数据读入内存的缓冲区中。

	if ((fd = open("./minilzo.c", O_RDONLY)) < 0) {
		printerror(__LINE__, errno, "open");
	}

	//alloc the src buffer and dest buffer
	if ((srcbuffer = malloc(MAX_LEN)) < 0) {
		printerror(__LINE__, errno, "malloc");
	}
	if ((destbuffer = malloc(MAX_LEN)) < 0) {
		printerror(__LINE__, errno, "malloc");
	}
	memset(srcbuffer, 0, MAX_LEN);
	memset(destbuffer, 0, MAX_LEN);

	if (read(fd, srcbuffer, MAX_LEN) < 0) {
		printerror(__LINE__, errno, "read");
	}

利用压缩接口lzo1x_1_compress()将srcbuffer中长度为inlen的数据进行压缩,并将压缩后的数据保存在destbuffer中,其压缩数据的长度返回到值结果参数outlen中。其中wkmem为压缩数据时所需要的内存空间。

	int ret;
	if ((ret = lzo1x_1_compress(srcbuffer, inlen, destbuffer, &outlen, wkmem)) != LZO_E_OK) {
		printf("compress error:%d.\n", ret);
		return -1;
	} else {
		printf("compress %lu bytes into %lu bytes.\n", (unsigned long)inlen, (unsigned long)outlen);
	}

	if (outlen >= inlen) {
		printf("This block contains incompressible data.\n");
		return 0;
	}

下面将压缩后的数据保存到新文件cfile中。

	int cfd;
	if ((cfd = open("./cfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
		printerror(__LINE__, errno, "open");
	}

	if (write(cfd, destbuffer, outlen) < 0) {
		printerror(__LINE__, errno, "write");
	}

接着进行解压缩过程,利用lzo1x_decompress()则可以完成。各个参数的意义与压缩时相同,只不过要调整源数据和目的数据之间的方向。

	memset(srcbuffer, 0, MAX_LEN);
	if ((ret = lzo1x_decompress(destbuffer, outlen, srcbuffer, &newlen, NULL)) != LZO_E_OK) {
		printf("decompress error:%d.\n", ret);
		return -1;
	} else if ( inlen == newlen) {
		printf("decompress %lu bytes into %lu bytes.\n", (unsigned long)outlen, (unsigned long)newlen);
	}

最后将解压缩的数据写入新文件对此dcfile中。

	int dcfd;
	if ((dcfd = open("./dcfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
		printerror(__LINE__, errno, "open");
	}

	if (write(dcfd, srcbuffer, newlen) < 0) {
		printerror(__LINE__, errno, "write");
	}

	close(fd);
	close(cfd);
	close(dcfd);
	printf("compress success!\n");
	return 0;
}

上述为源代码,如果使用make编译文件,则Makefile为:

edsionte@virtualPC ~/edsionte-code/lzo $ cat Makefile
target:=testlzo
source:=testlzo.c minilzo.c

default:
	gcc -o $(target) $(source)
clean:
	rm $(target)

执行后,可查看两个文件的大小差异:

edsionte@virtualPC ~/edsionte-code/lzo $ ls -l cfile dcfile
-rw------- 1 edsionte edsionte  265  6月 19 10:23 cfile
-rw------- 1 edsionte edsionte 1024  6月 19 10:23 dcfile

可以看出minilzo的压缩率大约为1:4。

通过make编译多文件的内核模块

10 6 月, 2012 by edsionte 无评论 »

之前对make的使用都仅局限于单个文件,比如用make编译单个文件的内核模块。我试着将一个程序分解成多个文件,然后再通过make进行多文件的编译,途中还是遇到一些列问题,因此总结成下文。

1.文件清单

下面的文件清单类出的是一个内核模块程序,我们这里不去深究这个模块的功能。procfs_mod.c主要包含内核模块程序的主要框架,比如加载函数和卸载函数等;procfs.c包含主要的函数的实现;而procfs.h是整个程序所用到的头文件,包含函数以及变量的声明等。

edsionte@edsionte-desktop:~/proc_vfs$ ls
Makefile  procfs.c  procfs.h  procfs_mod.c

2.Makefile的组成

我们可以将Makefile看作是编译规则集合,它告诉make应该如何进行编译,最终生成目标文件。

edsionte@edsionte-desktop:~/proc_vfs$ cat Makefile
obj-m += myprocfs.o
myprocfs-objs := procfs.o procfs_mod.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

在这个Makefile中,obj-m说明最终生成的内核模块目标文件是myprofs.ko,并且它必须依赖两个文件procfs.c和procfs_mod.c。因此,通过模块名加-objs的形式可以定义整个模块所包含的文件。注意,这里定义的都是与原文件同名的目标文件。

3.头文件的引用

在本文所举例的源程序中,procfs.h包含了整个程序所需要的函数以及变量,也即是说所有.c的源程序都会引用该头文件。为了防止头文件被多次重复引用,我们应该在头文件中使用条件编译。

edsionte@edsionte-desktop:~/proc_vfs$ cat procfs.h
#ifndef _MY_PROC_FS_H
#define _MY_PROC_FS_H

#include 
…………
#endif

也就是将整个头文件的内容用头文件包围起来。上面的条件编译说明:如果没有定义符号常量_MY_PROC_FS_H,就定义该变量并且包含该头文件。

但是,即便我在头文件中使用了条件编译,在编译时还是出现了下面的错误:

edsionte@edsionte-desktop:~/proc_vfs$ make
make -C /lib/modules/2.6.32-24-generic/build M=/home/edsionte/proc_vfs modules
make[1]: 正在进入目录 `/usr/src/linux-headers-2.6.32-24-generic'
  CC [M]  /home/edsionte/proc_vfs/procfs.o
  CC [M]  /home/edsionte/proc_vfs/procfs_mod.o
  LD [M]  /home/edsionte/proc_vfs/myprocfs.o
/home/edsionte/proc_vfs/procfs_mod.o:(.bss+0x820): multiple definition of `my_proc_file'
/home/edsionte/proc_vfs/procfs.o:(.bss+0x820): first defined here
/home/edsionte/proc_vfs/procfs_mod.o:(.data+0x80): multiple definition of `my_inode_ops'
/home/edsionte/proc_vfs/procfs.o:(.data+0x80): first defined here
make[2]: *** [/home/edsionte/proc_vfs/myprocfs.o] 错误 1
make[1]: *** [_module_/home/edsionte/proc_vfs] 错误 2
make[1]:正在离开目录 `/usr/src/linux-headers-2.6.32-24-generic'
make: *** [all] 错误 2

产生这种错误的原因是我之前一直在头文件对一些全局变量进行定义,这样导致进行了多次定义。正确的做法是在某一个.c中进行全局变量的定义,而在.h文件中使用extern进行外部即可。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key