| 最近工作比较闲散,因此抽空看了看汇编,基本上从头开始,看了前面几章不知不觉也学会了“Hello world”,刚好看的这本书基本上都是以GNU的开发工具为开发环境的,这比较适合我的兴趣。其实看的这几章也就只有一章(这一章还没看完^_^)才是汇编语言的基础,刚学会了点AT&T的汇编语法,知道了跟Intel的汇编语法的区别,另外这一章用不同的方式实现了cpuid的调用,学会了如何调用系统调用以及如何调用C函数库中的函数。
先看看下面通过系统调用实现的hello world代码:
.section .data
msg:
.ascii "Hello world!\n"
.section .text
.globl _start
_start:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $13, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
系统调用是通过int 0x80来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数,从/usr/include/asm/unistd.h中可以看到exit的功能号_NR_exit为1,write(_NR_write)功能号为4,因此第一个int $0x80调用之前eax寄存器值为4,ebx为文件描述符,stdout的文件描述符为1,ecx则为buffer的内存地址,edx为buffer长度。第二个int $0x80之前eax为1表示调用exit,ebx为0表示返回0。编译链接步骤如下所示: as -o helloworld.o helloworld.s ld -o helloworld helloworld.o
再看看调用C函数的代码:
.section .data
output:
.asciz "Hello world!\n"
.section .text
.globl _start
_start:
pushl $output
call printf
addl $8, %esp
pushl $0
call exit
这个例子相对来说看起来简单得多,将参数压入堆栈调用相应的函数即可,不过要注意的是:1、C函数需要调用的字符串参数必须以asciz声明,而不是ascii,这样才会给字符串后面加'\0'。2、压栈顺序刚好与C函数顺序相反,最后的参数应最先入栈。3、链接的时候需要链接libc.so库并指定动态链接库加载器/lib/ld-linux.so.2,步骤如下: as -o helloworld2.o helloworld2.s ld -dynamic-linker /lib/ld-linux.so.2 -lc -o helloworld2 helloworld2.o | comments: 1 comment or Leave a comment  |
| 一个简单的应用:我的机器IP是192.168.1.240,期望访问另一个局域网的web服务器其IP为192.168.0.109,中间有台双网卡的服务器用来做透明代理,该服务器eth0为192.168.1.2,eth1为192.168.0.2。
首先需要将到达192.168.1.2:80的包顺利转给192.168.0.109,因此有了下面的DNAT: iptables -t nat -A PREROUTING -s 192.168.1.0/24 -d 192.168.1.2 -p tcp --dport 80 -j DNAT --to 192.168.0.109:80
乍一看这应该没什么问题了,可是接下来问题是发现我的包是到0.109了,可是我的机器与0.109仍然无法正常通信,原来是到达0.109的源地址仍然是1.240,0.109回包的时候当然无法直接给1.240了,接下来就是SNAT的事了: iptables -t nat -A POSTROUTING -d 192.168.0.109 --dport 80 -o eth1 -j SNAT --to 192.168.0.2
当然别忘了echo 1 > /proc/sys/net/ipv4/ip_forward,最好连/etc/sysctl.conf里的net.ipv4.ip_forward=1一并加上。。。 | comments: Leave a comment  |
| inotify是什么?用它能干些什么?这个问题我们还是首先从内核的文档开始吧--Documentation/filesystems/inotify.txt(说点题外话,内核文档虽然是没有任何格式的txt文档,给人的感觉却非常好,而且作者总是以最精炼的语言清楚地描述了相关的内容): a powerful yet simple file change notification system,通俗点说它是一个内核用于通知用户空间客户程序文件系统变化的系统,并且它是powerful yet simple的。 ( Read more... ) | comments: Leave a comment  |
| 很久没有更新blog了,就跟抽烟一样,如果长时间不抽的话也就不想抽了,看来这是一个不好的现象,不过长时间的沉默也说明了自己这段时间在技术没有什么积累。罢了,今天的blog就记录这段时间来比较郁闷的事情。
这个项目的开发就像当年初中的长跑一样,前几圈劲头十足信心满怀,接下来的情况我想大部分人跟我一样,慢慢地感觉有点累,再后来压力越来越大,越来越感觉大脑缺氧呼吸困难,到后来已经身心疲累。人这一生从出生开始就背负了责任,要么为责任活着要么超越责任为理想活着,所以有人活得辛苦而无趣,有人轻松而超然。有点跑题了,还是说一下前几天遇到的技术问题。 ( Read more... ) | comments: 3 comments or Leave a comment  |
| 在软件项目的开发过程中,我们总是试图让程序能够适应更多的应用环境以及业务流程,不管你对于需求的了解如何准确,也不管你做了有多么充分的估计,但有很多情况仍然让你无法应付,比如:需求是会变化的;维护项目的人不一定都能用C/C++写出没有内存问题的代码等等。
让软件系统能够适应更多变化的方法有很多种,高度的抽象、动态链接技术等等都是一直以来被大家采用较广的方法,那么今天我们要讨论的是脚本引擎的嵌入问题。通常我更喜欢c代码加上python脚本引擎的结构,python的好处就不多说了。不过在C中嵌入python脚本引擎调用之后也有一些非常麻烦的地方,比如不便于调试,因为我们的宿主应用程序必然会为python脚本wrap一些module、class、function等等,因此这带来了调试的难度,有时候仅仅依赖print是一件非常低效的事,那么我们如何得到python脚本在异常时的traceback情况呢?比如代码出现异常到底是在哪个脚本文件中,到底是哪行代码出了问题?错误又是什么呢?我们先来看看下面的函数:
#include <Python.h>
#include <compile.h>
#include <frameobject.h>
#include <traceback.h>
void
process_python_exception(void)
{
char buf[512], *buf_p = buf;
PyObject *type_obj, *value_obj, *traceback_obj;
PyErr_Fetch(&type_obj, &value_obj, &traceback_obj);
if (value_obj == NULL)
return;
if (!PyString_Check(value_obj))
return;
char *value = PyString_AsString(value_obj);
size_t szbuf = sizeof(buf);
int l;
PyCodeObject *codeobj;
l = snprintf(buf_p, szbuf, _("Error Message:\n%s"), value);
buf_p += l;
szbuf -= l;
if (traceback_obj != NULL) {
l = snprintf(buf_p, szbuf, _("\n\nTraceback:\n"));
buf_p += l;
szbuf -= l;
PyTracebackObject *traceback = (PyTracebackObject *)traceback_obj;
for (;traceback && szbuf > 0; traceback = traceback->tb_next) {
codeobj = traceback->tb_frame->f_code;
l = snprintf(buf_p, szbuf, "%s: %s(# %d)\n",
PyString_AsString(codeobj->co_name),
PyString_AsString(codeobj->co_filename),
traceback->tb_lineno);
buf_p += l;
szbuf -= l;
}
}
message_error_dialog_show(buf);
Py_XDECREF(type_obj);
Py_XDECREF(value_obj);
Py_XDECREF(traceback_obj);
}
PyErr_Fetch用来获取异常对象,并且同时可以得到traceback对象,traceback其实是一个PyTracebackObject结构体,可以在python的头文件traceback.h中找到,PyTracebackObject其实也是一个单向链表,可以通过其tb_next成员来枚举,其tb_frame则是一个_frame结构体,在frameobject.h头文件中可以看到,其中f_code就是我们需要的PyCodeObject结构体,PyCodeObject中就可以得到co_name和co_filename这两个关键的描述,一个是错误信息另一个是文件名称,PyTracebackObject的tb_lineno就是出错的行号,有了这些数据我们调试还有困难吗? | comments: Leave a comment  |
| 昨天晚上给客户一个应用定制裁减linux系统的时候,发生了一件非常不幸的事,我的data目录被我无意给rm -rf删除掉了,那里面存放着我的所有项目的代码和文档,还有很多其它重要的东西。看见那个目录突然什么都没有的时候那种感觉简直无法形容,由于长期出差一直没有机会给公司保留备份,还好今天恢复了大部分重要的文档和代码。希望跟我一样倒霉的朋友能以此为鉴,删除文件时一定要小心,并且经常备份重要的代码和文档。
网上有两篇文章对reiserfs文件系统的数据恢复做了详细的介绍,ReiserFS undelete/data recovery HOWTO和Reiserfs filesystem recovery,另外提供几点建议:
- 在误删除之后切记不要再进行任何操作,立即关机,做好准备再来做数据恢复!
- 准备好一块大硬盘或移动硬盘(最好使用usb 2.0的硬盘盒子),第一时间用dd把误删除数据的分区完整备份一份。恢复也最好针对这个image文件,直到重要的数据完全找到再处理该分区,如果失败了还可以借助专业数据恢复公司来进行数据恢复。
- 您在恢复时一定记得给reiserfsck加上-l参数,生成日志文件是非常重要的,如果文件太多reiserfsck并不能100%地恢复您的数据,日志文件里会告诉您哪些文件没有被正确地恢复。再次提醒您经常备份自己重要的数据!!!
顺便赞叹一下reiserfs作者,reiserfs确实是一个非常优秀的文件系统,我54G容量的分区dd到移动硬盘时间不超过1小时,数据恢复时间不超过30分钟,并且最终重要的数据基本上都得以恢复! | comments: 1 comment or Leave a comment  |
| 在使用GtkTreeView时,用GtkScrolledWindow来添加滚动条,但列表(或树)中的数据行在不断得更新(增加),而垂直滚动条默认情况下不能自动往下滚动,以显示最新的数据行,而是始终停在top的位置,针对此问题有什么方法可以解决呢? 每次增加数据行之后,得到最后一行的path,需要手动调用gtk_tree_view_scroll_to_cell,滚动到最后的地方。如果不想手动 调用,可以监听 model 的 row-inserted,在该signal callback中gtk_tree_view_scroll_to_cell,如果大量迅速的增加新行的话,速度会受影响,可以跟延时结合减少scroll的频率。 | comments: Leave a comment  |
| 以下是我在开发一个应用中集成python脚本引擎的过程中所遇到的一些入门级问题,稍后再整理一些比较完整的关于python扩展与嵌入的日志。
1、如何在宿主应用中注册module?可在Py_Initialize之前调用PyImport_AppendInittab来注册,也可在Py_Initialize之后直接调用注册函数。
2、如何添加宿主应用脚本的路径到python搜索路径?在Py_Initialize之后通过PySys_GetObject("path")得到sys.path,然后再通过PyList_Insert加入宿主应用的脚本路径。
3、如何调用python模块中的函数?通过PyImport_ImportModule得到module,然后再通过PyModule_GetDict从module中得到dict,再通过PyDict_GetItemString得到函数对象,最后通过PyObject_CallObject或其它PyObject_Call系列函数调用,当然调用之前最好用PyCallable_Check检查,另外就是dict、func都属于Borrowed reference,因此不需要Py_DECREF或Py_XDECREF。
4、所有的wrapper函数都应该返回PyObject实例指针,即使在python中调用的时候并不需要返回值,但仍需要返回Py_None,相当于C/C++中的void,Py_INCREF(Py_None); return Py_None;当然也可以用宏Py_RETURN_NONE来代替。 | comments: Leave a comment  |
| 1、在gtk+开发过程中,我们经常需要在event handler中来访问该窗口中的某些控件的信息,通常习惯于整一大堆全局变量,其实还有更好的方式,在glib的实现中我们可以通过g_object_set_data和g_object_get_data给一个GObject对象绑定自定义的数据及属性,g_object_set_data_full还可以在对象释放资源时通知你的回调函数来做资源释放处理,这样我们就可以给event handler传递一个窗口对象指针就行了,其它的数据及控件信息可以通过窗口对象得到我们想要的东西。
2、某些对象并未提供所有属性的存取方法,但只要是通过GObject继承下来的对象均可采用g_object_set、 g_object_set_property及g_object_get、g_object_get_property来存取这些属性,如给 TreeView中某一列的值要求居右则可以对gtk_cell_renderer_text_new()产生的GtkCellRenderer对象调用 g_object_set(renderer, "xalign", 1.0, NULL)来达到居右的效果。
3、使用g_idle_add及g_timeout_add等函数中的时候一定要小心,由于代码延后执行,因此一定要保证其数据的生命周期,如下面的例子是我在开发一个项目应用中遇到的问题(注释掉的代码即为出现问题之后的修正代码),传递的Python对象到代码执行的时候已经被释放掉了,因此出现了比较奇怪的现象:如果连续调用这个函数两次,结果加进去的两行信息全部都是最后一次的数据,当然可能还会有更奇特的现象发生。 ( Read more... ) | comments: Leave a comment  |
| 在网上发现一篇讲解利用mplayer的mencoder转换视频的文章,刚好适用我简陋的mp4转贴一下^_^,原文网址:Linux下的视频转换
制作适合在智能手机和PDA上观看的mpeg4视频。 Linux上有一个很强大的视频音频转换软件,就是Mplayer自带的mencoder (MPlayer's Movie Encoder)。mencoder就象是一台全手动的照相机,可调整的选项非常多,不过这儿只是把最常见的情况说一下。 ( Read more... ) | comments: Leave a comment  |
| 已经有接近一个月了无法享受U盘、移动硬盘插入时自动挂载的功能了,这段时间里一直非常郁闷,不知道为什么就成这样了,心想着可能是当前我的hal、dbus或gnome-volume-manager或gnome-mount之类的软件有问题吧,抱着侥幸的心理一直在等待新版本中是否可以解决这个问题,可是升了几次级也不见得有什么好转,尝试过无数次都以失望告终。。。
昨晚进/usr/share/hal/fdi/policy目录看了一下,差点当场晕倒,原来前段时间使用gparted产生了一个gparted-disable-automount.fdi,qfile了一下这个文件不属于任何一个包,看来是gparted产生的啦,为了在管理分区的时候禁止自动挂载让我痛苦这么久着实也太BT了。。。 | comments: Leave a comment  |
| 笔记本的鼠标触摸板我基本上很少用,比起外接鼠标还是没那么方便灵活,何况写程序的时候基本上很少用鼠标,但是笔记本的触摸板却在写程序的时候经常搞得鼠标指针胡乱飘移,非常影响键盘输入的准确性。
在linux下控制触摸板必须得使用2.6.x内核,大致有几件事要做:正确配置内核、安装synaptics驱动、正确配置xorg.conf。 ( Read more... ) | comments: 1 comment or Leave a comment  |
| 自从用了autotools很久不曾有过编写Makefile的经历了,这两天维护客户以前让别的公司给做的一个项目中的烂代码,写了个Makefile感觉挺不错。。。
CC = g++
GTK_CFLAGS = $(shell gdk-pixbuf-config --cflags)
GTK_LIBS = $(shell gdk-pixbuf-config --libs) $(shell pkg-config --libs gthread)
MYSQL_CFLAGS = $(shell mysql_config --cflags)
MYSQL_LIBS = $(shell mysql_config --libmysqld-libs)
CFLAGS = $(GTK_CFLAGS) $(MYSQL_CFLAGS) -I/usr/include/lclibs -DMYSQL_EMBED -DETC_THREAD
LIBS = $(GTK_LIBS) $(MYSQL_LIBS) -L./libs -ldl -rdynamic -llclibs -ldsrc -lgts
SRC_PATH = src
OBJ_PATH = obj
SOURCES = $(wildcard $(SRC_PATH)/*.c)
OBJECTS = $(patsubst $(SRC_PATH)/%.c, $(OBJ_PATH)/%.o, $(SOURCES))
DEPENDS = $(patsubst $(SRC_PATH)/%.c, $(OBJ_PATH)/%.d, $(SOURCES))
all: hwetc
$(DEPENDS): $(OBJ_PATH)/%.d: $(SRC_PATH)/%.c
$(CC) -MM $(CFLAGS) $< > $@; \
sed -i 's,\($*\)\.o[ :]*,$(OBJ_PATH)/\1.o $@ : ,g' $@
include $(DEPENDS)
hwetc: $(OBJECTS)
$(CC) $(OBJECTS) $(LIBS) -o $@
$(OBJECTS):
$(CC) -c $(CFLAGS) $< -o $@
.PHONY: clean
clean:
rm -f hwetc $(OBJECTS) $(DEPENDS)
ps: 之所以CC = g++是因为其中用到gts等C++写就的library,而且头文件里就包含有C++语法,因此将就着应付一下。。。 | comments: Leave a comment  |
| 以前一个web项目采用mysql 4.0.28,公司大部分开发人员用的是windows,java程序里存取数据采用的是GBK编码,当时建库的时候也没有指定字符集编码,因此升级到4.1之后原来的程序取出来的数据中文全成了乱码,mysql 4.1变化比较大,要兼容原来的程序及数据库服务器端设置/etc/mysql/my.cnf须做以下修改:
[mysqld] default-character-set = gbk skip-character-set-client-handshake | comments: Leave a comment  |
|
#!/bin/sh
title="Select the window manager"
text="Window Manager List"
rows="TRUE compiz FALSE metacity"
result=`zenity --list --radiolist --title "$title" --text "$text" --column "" --column "WM" $rows`
if [ "$result" == "compiz" ]
then
compiz-aiglx
elif [ "$result" == "metacity" ]
then
metacity --replace
fi
运行效果如下:
![]() | comments: 5 comments or Leave a comment  |
| 下午花了半个多小时hack了一把gentoo livecd 2006.1,给一个项目裁减好的linux系统制作安装光盘,省了不少时间,gentoo的livecd里关键的就两个文件:image.squashfs和isolinux/gentoo.igz,前者基本上就是一个完整的gentoo base system,后者是一个initramfs,临时的根文件系统,用于加载光盘并让前者正确地接管系统,下面开始说正题吧。。。
1、挂载光盘文件mount -o loop /data/soft/livecd/livecd.iso /mnt/cdrom,/mnt/cdrom下现在就是livecd光盘的完整内容。另外建立一个目录/mnt/mycd把光盘里的文件全部复制过来用于制作光盘。
2、挂载image.squashfs:mount -o loop /mnt/cdrom/image.squashfs /mnt/gentoo(需要你的内核支持squashfs文件系统),全部复制到另一个目录进行hack吧,复制的时候记得加上-pr参数。这个是重头戏,进去好好研究研究,放手去修改配置添加东西吧,你甚至可以把你的开发环境、portage等等全搞进去。搞好了直接mksquashfs * /mnt/mycd/image.squashfs就可以了,不过在mksquashfs之前你得把原文件删掉。
3、复制isolinux/gentoo.igz到你另外一个目录,mv gentoo.igz gentoo.gz && gunzip gentoo.gz,然后正确地解压gentoo文件,得到一个简单的临时用根文件系统,下面这个脚本可以很方便地进行解压,用下面的脚本./unigz.sh gentoo就生成了gentoo.dir目录,执行脚本之前确认一下你的系统里有没有安装File-Slurp包:
#!/usr/bin/perl -w
#
use strict;
use File::Slurp qw(slurp);
my $ifile = slurp($ARGV[0], binmode=> ':raw');
my $newfile;
my $i=1;
my @newfiles = split(/TRAILER!!!/,$ifile);
`mkdir $ARGV[0].dir`;
foreach $newfile (@newfiles)
{
open F, "> ./$ARGV[0].dir/$i";
print F $newfile;
print F "TRAILER!!!\0";
close F;
`cd $ARGV[0].dir; cpio -i -H newc < $i; rm $i; cd ..`;
$i++;
}
进入gentoo.dir目录主要看看init和etc下两个脚本文件initrd.defaults和initrd.scripts,加上自己想要执行的东西即可,当然如果你想大刀阔斧地hack也可以,完了之后在gentoo.dir目录下执行find . -print | cpio --quiet -o -H newc | gzip -9 > ../gentoo.igz。
4、一切就绪之后制作iso文件: cd /mnt/mycd mkisofs -v -R -J -P "cd" -p "cd" -V "xxx_installer" -A "xxx-install-cd" -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o /data/download/etc.iso . | comments: 1 comment or Leave a comment  |
| 不可避免地,不管是新手老手,在稳定之前都会有痛苦的测试及调试阶段,也许你很幸运,一开始就抓到了最难捕捉的bug,正确地解决了。
但不管怎么样,你的程序总是很容易地就会得到一个Segmentation fault这样的错误信息并且程序就这样毫无保留地跳出来了,遇到这样的问题会让你很痛苦的,查找问题不亚于你N多天辛苦劳累编写代码的难度。那么有没有更好的方法可以在产生SIGSEGV信号的时候得到调试可用的信息呢?看看下面的例程吧!
( Read more... ) | comments: Leave a comment  |
| |