存档在 2012年6月

miniLZO的基本使用

2012年6月19日

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编译多文件的内核模块

2012年6月10日

之前对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进行外部即可。

配置ubuntu

2012年6月9日

对于一个刚刚安装的ubuntu系统,为了更好的使用体验,我们需要对它进行一些配置。下面命令均在ubuntu 10.04上测试过。

1.更新系统源

打开源所在文件后,复制速度比较快的源保存。然后再对源进行刷新:

$ sudo gedit /etc/apt/sources.list
$ sudo apt-get update

2.安装中文环境

先安装中文ibus输入法:

$ sudo apt-get install ibus ibus-pinyin ibus-table

即便安装时选择了中文系统,安装后也不完全是中文环境。通过下面的命令安装中文环境:

$ sudo apt-get install language-support-zh language-pack-zh

3.安装显卡驱动

首先将系统切换到字符界面,按Ctr+Alt+F1即可。然后关闭X-server:

$ sudo /etc/init.d/gdm stop

接着用root身份安装驱动:

# ./NVIDIA-Linux-x86-285.05.09.run

安装完毕后重启系统。

4.安装常用软件

对于我来说,我需要一个完成的vim,内核源码阅读工具,别人可远程登录到我的系统,英汉词典,下载工具,C++编译环境。因此依次下载如下:

$ sudo apt-get install -y vim cscope ctags openssh-server stardict multiget g++

另外,可以进一步配置一下vim。先打开vim配置文件:

$ sudo gedit /etc/vim/vimrc

再在文件末尾输入下面两行,代码可以自动缩进对齐:

set autoindent
set cindent

对于我来说,这两项足够了。

Linux内核中的时间

2012年6月6日

时间在内核中占有重要地位,操作系统必须随时都能获得当前时间,其次操作系统必须提供一种计时器可以通知内核某一段时间已经过去了。时间在内核中最常见的应用就是进程调度,内核不但要为每个进程分配时间片,而且要周期性的对可运行队列中的进程进行调整。Linux内核中的时间由两种设备同时进行计时:实时时钟和系统定时器。

实时时钟

实时时钟(Real Time Clock)用来永久存放系统时间,即便系统关闭也可以靠主板上的电池继续进行计时。由于RTC通常和CMOS被集成在一起,因此RTC也称为CMOS时钟。虽然可以通过操作/dev/rtc对RTC进行编程,但是一般Linux只用RTC来获取当前的时间和日期。当系统启动时,内核通过读取RTC来初始化墙上时间,该时间存放在xtime变量中。所谓墙上时间也就是当前的实际时间。

系统定时器

系统定时器是内核时间机制中最重要的一部分,它提供了一种周期性触发中断机制,即系统定时器以HZ(时钟节拍率)为频率自行触发时钟中断。当时钟中断发生时,内核就通过时钟中断处理程序timer_interrupt()对其进行处理。
系统定时器完全由操作系统管理,因此也成为系统时钟或者软件时钟。当系统启动时,内核通过RTC初始化系统定时器,系统定时器接着由操作系统共掌管,进行固定频率的定时。可以看到,系统时间并不是传统意义上的那种计时时钟,而是通过定时这种特殊的方式来表现时间。在x86架构下,系统时钟通过可编程间隔定时器(PIT)这种设备产生定时。

内核定时器

内核定时器也称为动态定时器,它可以使任务能在指定的时间点上执行。要使用定时器,必须先设置好定时器超时的时间,指定超时发生后应该执行的内核函数,最后激活这个定时器实例。当定时器超时的时候,该内核函数将被自动执行(但不周期执行),执行完毕后自行销毁,这也是内核定时器被称为动态定时器的原因。

获得时间

内核通过xtime变量保存墙上时间,该变量是timespec类型的,在linux/time.h中定义如下:

struct timespec {
        __kernel_time_t tv_sec;                 /* seconds */
        long            tv_nsec;                /* nanoseconds */
};

其中,tv_sec是以秒为单位时间,它保存着从1970年7月1日以来经过的时间,而tv_nsec记录自上一秒开始经过的纳秒数。

在最新的内核中,xtime未导出因此不能在内核模块中使用。不过内核提供了内核函数current_kernel_time()来获取当前时间,该函数返回timespec类型的时间。

libc库和系统调用

2012年6月2日

Linux系统调用这部分经常出现两个词:libc库和封装函数,不知道你是否清楚它们的含义?

libc

libc是Standard C library的简称,它是符合ANSI C标准的一个标准函数库。libc库提供C语言中所使用的宏,类型定义,字符串操作函数,数学计算函数以及输入输出函数等。正如ANSI C是C语言的标准一样,libc只是一种函数库标准,每个操作系统都会按照该标准对标准库进行具体实现。通常我们所说的libc是特指某个操作系统的标准库,比如我们在Linux操作系统下所说的libc即glibc。glibc是类Unix操作系统中使用最广泛的libc库,它的全称是GNU C Library。

类Unix操作系统通常将libc库作为操作系统的一部分,它被视为操作系统与用户程序之间的接口。libc库不仅实现标准C语言中的函数,而且也包含自己所属的函数接口。比如在glibc库中,既包含标准C中的fopen(),又包含类Unix系统中的open()。在类Unix操作系统中,如果缺失了标准库,那么整个操作系统将不能正常运转。

与类Unix操作系统不同的是,Windows系统并不将libc库作为整个核心操作系统的一部分。通常每个编译器都附属自己的libc库,这些libc既可以静态编译到程序中,又可以动态编译到程序中。也就是说应用程序依赖编译器而不是操作系统。

封装函数

在Linux系统中,glibc库中包含许多API,大多数API都对应一个系统调用,比如应用程序中使用的接口open()就对应同名的系统调用open()。在glibc库中通过封装例程(Wrapper Routine)将API和系统调用关联起来。API是头文件中所定义的函数接口,而位于glibc中的封装例程则是对该API对应功能的具体实现。事实上,我们知道接口open()所要完成的功能是通过系统调用open()完成的,因此封装例程要做的工作就是先将接口open()中的参数复制到相应寄存器中,然后引发一个异常,从而系统进入内核去执行sys_open(),最后当系统调用执行完毕后,封装例程还要将错误码返回到应用程序中。

需要注意的是,函数库中的API和系统调用并没有一一对应的关系。应用程序借助系统调用可以获得内核所提供的服务,像字符串操作这样的函数并不需要借助内核来实现,因此也就不必与某个系统调用关联。

不过,我们并不是必须通过封装例程才能使用系统调用,syscall()和_syscallx()两个函数可以直接调用系统调用。具体使用方法man手册中已经说明的很清楚了。

参考:

1. http://en.wikipedia.org/wiki/Libc

2. man syscalls

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