I Am Moving
Hello everyone!
Since the service of http://wordpress.com.cn/ is so unstable, I decided to move my blog to http://www.ylxiong.cn/. All the articles here will be moved there soon. Thanks for your concern about this blog and please spare some time to update your bookmarks.
SUSE中修改GRUB的背景图案的步骤
1、建一新目录:
cd
mkdir new
2、拷贝要修改的文件:
cp /boot/message ~/new
3、解开该文件:
cd ~/new
cpio -i < message
4、清除旧文件:
rm message
5、找一张自己满意的图片,用GIMP打开,裁剪缩放成800×600,保存为jpg格式的图片
要注意两点:1)保存时要调节图片质量以缩减jpg文件大小,使最终的文件尺寸不超过37KB。从GIMP的保存对话框里很容易知道最终的文件尺寸。2)保存时将文件名指向~/new/back.jpg,即覆盖掉~/new/back.jpg
6、重新生成文件:
cd ~/new
ls | cpio -o>../message
7、备份原GRUB信息文件:
su
cd /boot
mv message message.orig
exit
8、使用包含新背景图案的GRUB信息文件:
cd
su
cp message /boot
exit
9、下次重启就会看到你的新的GRUB背景图案。
C++中extern "C"浅析
extern "C" 包含双重含义:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。
(1)被extern "C"限定的函数或变量是extern类型的;
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。下列语句:
extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
(2)被extern "C"修饰的变量和函数是按照C语言方式编译和连接的
可以用一句话概括extern “C”这个声明的真实目的:实现C++与C及其它语言的混合编程。在C++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等。而在C中,只是简单的函数名字而已,不会加入其他的信息。也就是说:C++和C对产生的函数名字的处理是不一样的。
比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:
int f(void)
{
return 1;
}
在加入extern "C"的时候产生的汇编代码是:
.file "test.cxx"
.text
.align 2
.globl _f
.def _f; .scl 2; .type 32; .endef
_f:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret
但是不加入了extern "C"时:
.file "test.cxx"
.text
.align 2
.globl __Z1fv
.def __Z1fv; .scl 2; .type 32; .endef
__Z1fv:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret
两段汇编代码同样都是使用gcc -S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。如果在C++代码中引用了C写的库,这种不一致性将导致在链接的时候出现链接器找不到函数的错误。因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。
(3)extern "C"的惯用法
因为C语言不支持extern "C"声明,所以:在C++中引用C语言中的函数和变量,在包含C语言头文件时需进行extern "C"声明;在C语言的头文件中,其外部函数只能指定为extern类型;在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
高斯分布随机数源代码
以下函数用来生成服从高斯分布的随机数:
#include <stdlib.h>
#include <math.h>double gaussrand()
{
static double V1, V2, S;
static int phase = 0;
double X;
if ( phase == 0 ) {
do {
double U1 = (double)rand() / RAND_MAX;
double U2 = (double)rand() / RAND_MAX;
V1 = 2 * U1 – 1;
V2 = 2 * U2 – 1;
S = V1 * V1 + V2 * V2;
} while(S >= 1 || S == 0);
X = V1 * sqrt(-2 * log(S) / S);
} else
X = V2 * sqrt(-2 * log(S) / S);
phase = 1 – phase;return X;
}
最简单的Iframe自适应高度代码
<iframe id=”myframe” name=”myframe”
scrolling=”no” style=”width:100%;” frameborder=”0″
onload=”this.height=myframe.document.body.scrollHeight”>
</iframe>
在HttpHandlers中使用Session
当你希望从ashx或HttpHandler里访问你的Session时,你必须实现IReadOnlySessionState接口。
using System;
using System.Web;
using System.Web.SessionState;![]()
public class MyHandler : IHttpHandler, IReadOnlySessionState
{
public bool IsReusable{get { return true; }}
public void ProcessRequest(HttpContext ctx)
{
ctx.Response.Write(ctx.Session["UserName"]);
}
}
Linux内核中的list_head结构
在 Linux 内核链表中,需要用链表组织起来的数据通常会包含一个struct list_head 成员。这种通用的链表结构避免了为每个数据项类型定义自己的链表的麻烦。
include/linux/list.h
struct list_head {
struct list_head *next, *prev;
};
在该头文件海定义了list_head的若干常用操作:
- 初始化:宏INIT_LIST_HEAD;
- 链表的插入删除合并操作:函数list_add(),list_add_tail(),list_del(),list_move(),list_move_tail(),list_empty(),list_splice();
- 遍历链表用的迭代器:宏list_for_each等等;
这些操作的具体实现我就不多说了,这是任何一本数据结构的书中最基础的内容。值得注意的是list_head的使用方式。在Linux内核里有相当多的队列,其操作都是通过list_head进行的,但是list_head只是个连接件,如果通过该连接件找到其宿主结构呢?以struct page为例:
include/linux/mm.h
typedef struct page {
struct list_head list; /* ->mapping has some page lists. */
….
struct list_head lru; /* Pageout list, eg. active_list;
protected by pagemap_lru_lock !! */
….
} mem_map_t;
此时,struct page是list_head结构体的变量list和lru的宿主结构。我们直接看看代码是如何解决寻找宿主结构这个问题的,在mm/page_alloc.c定义的函数rmqueue()中有如下的代码:
page = list_entry(curr, struct page, list);
而list_entry定义在文件include/linux/list.h中:
/**
* list_entry – get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
用C语言宏替换的规则,可知上面page = list_entry(…)将会被替换为:
page = ((struct page*)((char*)(curr) – (unsigned long)(&((struct page*)0)->list)));
这里的curr是一个page结构内部的成分list的地址,而我们需要的是那个page结构本身的地址,所以要从地址curr减去一个位移量,即成分list在page内部的位移量,才能达到要求。这里使用了一个很巧妙的方法:&((struct page*)0)->list就表示当结构page正好在地址0上时其成分list的地址,这刚好等于位移。
更详细的解读请参考《Linux内核情景分析》。
Goodbye FlashGet
This is the Unix philosophy: Write programs that do one thing and do it well.
Peter H. Salus. A Quarter-Century of Unix. Addison-Wesley. 1994.
昨天,“FlashGet 2.0 Beta4 隆重发布”,我,作为一个自从接触电脑起就一直使用FlashGet的铁杆拥护者和迅雷的坚定反对者,当然立即升级。可惜的是,可爱的FlashGet这次给我的失望远远大于惊喜。
过去一直使用FlashGet并且鄙视迅雷的原因无非如下:
- FlashGet界面简洁清爽,迅雷的界面太杂乱;
- FlashGet不内嵌广告(当然这是以前的事情,最近的版本也嵌入了少量文字广告),迅雷的界面有一半为广告所占据;
- 迅雷太缺乏社会责任感。在利益的驱使下,迅雷社区的页面,就如同我们所谓的门户网站一样,充斥着大量色情挑逗的信息;
- 迅雷所使用的技术。这个我不好评论,其实所有的争吵都和用户无关,有关的是各个网站的利益关系;
- FlashGet的下载管理做得很好,长期来我都用FlashGet直接管理我Download分区的文件,甚至已经抛弃了Windows资源管理器;
- 迅雷正在试图把自己做成一个All-in-One的软件,http、ftp、bt、eMule下载无所不包,我怀疑迅雷的团队是否真的有能力把这一切功能做到完美。
FlashGet 2.0 Beta4横空出世,这下可好,原先我认为的它的优点全部荡然无存。安装的时候,它提示我“Windows Vista用户建议安装到非系统盘”,低级;安装后,把我以前的下载数据库搞得一团糟;每次启动FlashGet的时候都会运行它的自动更新程序,并且无法禁止,于是每次在启动FlashGet的时候都会弹出UAC的提示;BT下载功能现在已经无法关闭,运行FlashGet它就会强行与.torrent文件关联。等等等等。界面的更新?现在FlashGet的界面与其说是更modern了,还不如说是更迅雷了。图标的风格,大幅的banner,三栏的布局,都充满了迅雷的色彩。你有“雷区”?好,我就做个“车库”。你有“资源推荐”?那我当然也可以推荐啊。下图便是FlashGet推荐的内容:
啥都别说了。
曾经以为FlashGet是一个do one thing and do it well的软件,现在看来,它已经不再是了。突然有冲动想建立一个开源的项目,打造一个完美无公害的http下载工具,就像uTorrent、FileZilla、eMule一样,专心地把一个功能做到尽善尽美。
fork()用法小结
1、Linux下进程的结构
一个进程,主要包含三个元素: 一个可以执行的程序;和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等); 程序的执行上下文(execution context)。
Linux下一个进程在内存里有三部份的数据,就是“数据段”,“堆栈段”和“代码段。“代码段”,顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。堆栈段存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。系统如果同时运行数个相同的程序,它们之间就不能使用同一个堆栈段和数据段。
2、fork()的使用
fork() creates a child process that differs from the parent process only in its PID and PPID, and in the fact that resource utilizations are set to 0. File locks and pending signals are not inherited.
一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。
典型的代码如下:
void main(){
if ( fork() == 0 ) {
/* 子进程程序 */
printf("This is child process\n");
}
else {
/* 父进程程序*/
printf("This is process process\n");
}
}
3、关于fork()性能的疑问
如果一个大程序在运行中,它的数据段和堆栈都很大,如果fork只是简单地复制所有的数据,那系统开销会变得很大。在Linux系统里面,无论是数据段还是堆栈段都是由许多页构成的,fork函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的“页”从物理上也分开。这就是Linux系统的COW(Copy-On-Write)机制,这样系统在空间上的开销就可以达到最小。
在fork()的man page里有关于此的说明:
Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child.
4、fork()后父进程与子进程的调度
依赖于具体系统的调度算法。如果需要父子进程协同,需要通过原语来解决。
RHEL4内核升级简单步骤
2.6系列内核的配置安装较以前的版本已经相当容易,在RHEL4下面,我们可以通过如下几个简单的步骤来实现。
1、到http://www.kernel.org/下载源码包 在这里我们假设下到的源码包为linux-2.6.10.tar.gz
2、解压压缩包,并移动到/usr/src下
#tar -zxvf linux-2.6.10.tar.gz
#mv linux-2.6.10 /usr/src
3、删除以前可能失败的编译配置信息
#cd /usr/src/linux-2.6.10
#make mrproper
4、内核模块配置,可以选择新的功能 –例如支持NTFS ,无线网络,蓝牙等等
#make xconfig 或 make gconfig 或 make menuconfig –我认为第一个比较方便
5、创建压缩内核镜像
#make bzImage
6、编译模块
#make modules
7、安装模块
#make modules_install
8、安装内核
#make install
9、修改/boot/grub/grub.conf 将新内核改为默认启动项
#vi /boot/grub/grub.conf
改 default=0 –>default=1
10、重新启动机器
Calendar
February 2010 M T W T F S S « Apr 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28