在Windows下发现这个软件不错后,决定要尝试把它弄成我的ubuntu的“壁纸”
首先是安装,默认会出问题,原因是少了个gdiplus.dll, Dream Aquarium主页上关于Win2000版本的地方有说明。 不过我的XP似乎没有这个文件也能跑。 总之,下一个gdiplus.dll放wine的系统目录里就好了。
如果想装破解版,会麻烦些,我按照Windows的方法试了好久,没有成功,最后是胡搞了几次才成,可能有用的几点是:
1.破解包里有一个dai文件,用16进制编辑器查看发现其前三行是明文,之后是一个PE文件头,于是删掉前三行,果然可以运行,但是经常会crash。 这个文件应该就是1.1090完全版的主程序了。一般在鼓弄过程(最好还是按着破解包里面的说明弄)中可以试试这个能不能正常运行,即显示鱼缸。我这里最好结果是它可以运行,但是仍是使用版,似乎只能加2号鱼。
2.注册似乎一直都不成功,我最后是在上面一条成功后,认为注册表方面没问题了,然后把Windows下的安装文件拷过来了一份,于是能正常运行了,可加的鱼也没有限制了。
3.破解包里说要block这个屏保的网络连接,但是我不会弄,说不准哪一天屏保自己发现被破解了,又拒绝运行了。不过现在已经运行了几次,没有问题。
能运行以后,开始尝试把它弄成壁纸.
首先是要配置一下wine.设上虚拟桌面,并去掉Allow the window manager to control the windows。 我一开始没设好这个,总是不行。原因大概是屏保发现失去焦点后就会自动退出,可是我们把它设成了桌面以后,肯定要做别的事,于是切换到其他窗口时,屏保就退出了。 去掉Allow the window manager to control the windows 后, 该窗口就得不到失去焦点的信息了,也就不会自动退出了。 另外如果不选上虚拟桌面,后面用的devilspie会找不到这个窗口。
其次,配置一下屏保(在屏保里面按回车),去掉鼠标退出,然后把键盘退出的范围改小,我弄的是Esc only。
接着是装上devilspie, 在home里建目录.devilspie,并写一个文件DreamAquarium.ds
(if (is (application_name) "Wine desktop")
(begin
(below)
(undecorate)
(focus)
(wintype "desktop")
(opacity 60)
(pin)
(stick)
(skip_pager)
(geometry "1280x800+0+0")
))
再运行devilspie和屏保就成了 :)
此外可以参考Dream Aquarium as a Desktop Wallpaper with Beryl!
我这里是Compiz,一直没弄成功,也不知道是Compiz不兼容还是我没配好。
另外说一下这个方法的不足,从脚本中可以看出我是把Wine desktop设了stick, below之类,因此对其他在同一个wine环境下运行的程序也有效,特别地,屏保会退出。 当然可以设一下用其他环境,像我这的ie4linux就是如此。 当然我很少用wine程序, 而且ie4linux由于是单独的环境与其互不影响,所以不成大问题。
还有,opacity这个参数似乎不起作用,于是桌面图标看不到了
喜欢的话还可以把devilspie设成自启动。
2007-12-27
软件推荐: Dream Aquarium
最初听说Dream Aquarium是在一个介绍如何把它嵌入Beryl桌面的帖子,当时wine安装都没成功。
最近在Windows试了下, 确实不错。很有趣的屏保。
最新试用版是1.1040, 而最新正式版似乎是1.1090
破解不大好找,但确实能找到,我找到的链接给删掉了,好像是在www.leehare.cn.
最近在Windows试了下, 确实不错。很有趣的屏保。
最新试用版是1.1040, 而最新正式版似乎是1.1090
破解不大好找,但确实能找到,我找到的链接给删掉了,好像是在www.leehare.cn.
2007-12-22
gnome-blog: blog小工具
看full circle发现了一些blog工具, 后来自己又查了查,发现如下几个
gnome-blog //gnome-panel 的 applet
drivel //程序
blogtk //程序
scribefire //firefox 插件
其中除最后一个外均可以正常发帖,但都不支持tag。 感觉还是gnome-blog比较好,因为功能方面三者差不多,但它最轻巧。
gnome-blog //gnome-panel 的 applet
drivel //程序
blogtk //程序
scribefire //firefox 插件
其中除最后一个外均可以正常发帖,但都不支持tag。 感觉还是gnome-blog比较好,因为功能方面三者差不多,但它最轻巧。
xournal: 批注pdf的小工具
最近多人合写一个pdf, 需要批注功能。
网上搜了搜,发现了一个叫xournal的软件,试了试, 很不错。
非常推荐。 另外希望evince也能加入这个功能。
网上搜了搜,发现了一个叫xournal的软件,试了试, 很不错。
非常推荐。 另外希望evince也能加入这个功能。
2007-12-19
修改UBuntu登录后桌面显示的黄色
sudo gedit /etc/gdm/PreSession/Default
# Default value
if [ "x$BACKCOLOR" = "x" ]; then
BACKCOLOR="#dab082"
fi
#dab082 就是颜色 可以随便改
例如 #000000 为黑
#ffffff白色
# Default value
if [ "x$BACKCOLOR" = "x" ]; then
BACKCOLOR="#dab082"
fi
#dab082 就是颜色 可以随便改
例如 #000000 为黑
#ffffff白色
2007-12-18
wine下安装DirectX 9.0
参考http://feeds.feedburner.com/~r/winereview/~3/187817620/directx-90c-on-linux-with-wine.html
首先拷来mscoree.dll 和 streamci.dll
然后设置dll override
"d3d8"="builtin"
"d3d9"="builtin"
"d3dim"="native"
"d3drm"="native"
"d3dx8"="native"
"d3dxof"="native"
"dciman32"="native"
"ddrawex"="native"
"devenum"="native"
"dinput"="builtin"
"dinput8"="builtin"
"dmband"="native"
"dmcompos"="native"
"dmime"="native"
"dmloader"="native"
"dmscript"="native"
"dmstyle"="native"
"dmsynth"="native"
"dmusic"="native"
"dmusic32"="native"
"dnsapi"="native"
"dplay"="native"
"dplayx"="native"
"dpnaddr"="native"
"dpnet"="native"
"dpnhpast"="native"
"dpnlobby"="native"
"dsound"="builtin"
"dswave"="native"
"dxdiagn"="native"
"mscoree"="native"
"msdmo"="native"
"qcap"="native"
"quartz"="native"
"streamci"="native"
接着下载并安装DirectX9.0, 地址为http://filehippo.com/download_directx/,下载运行后指定解压路径,再运行解压出来的DXSETUP.exe
原文说可能需要装两次。
另外原文说为了Direct Music要安装gm.dls,位于windows/system32/drivers,不过我没有成功.
之后可以用dxdiag.exe看看效果, 还不错。
首先拷来mscoree.dll 和 streamci.dll
然后设置dll override
"d3d8"="builtin"
"d3d9"="builtin"
"d3dim"="native"
"d3drm"="native"
"d3dx8"="native"
"d3dxof"="native"
"dciman32"="native"
"ddrawex"="native"
"devenum"="native"
"dinput"="builtin"
"dinput8"="builtin"
"dmband"="native"
"dmcompos"="native"
"dmime"="native"
"dmloader"="native"
"dmscript"="native"
"dmstyle"="native"
"dmsynth"="native"
"dmusic"="native"
"dmusic32"="native"
"dnsapi"="native"
"dplay"="native"
"dplayx"="native"
"dpnaddr"="native"
"dpnet"="native"
"dpnhpast"="native"
"dpnlobby"="native"
"dsound"="builtin"
"dswave"="native"
"dxdiagn"="native"
"mscoree"="native"
"msdmo"="native"
"qcap"="native"
"quartz"="native"
"streamci"="native"
接着下载并安装DirectX9.0, 地址为http://filehippo.com/download_directx/,下载运行后指定解压路径,再运行解压出来的DXSETUP.exe
原文说可能需要装两次。
另外原文说为了Direct Music要安装gm.dls,位于windows/system32/drivers,不过我没有成功.
之后可以用dxdiag.exe看看效果, 还不错。
2007-12-15
Wolfenstein: Enemy Territory 在linux下不发声的解决
按网上的说法,原因是ET只支持oss,对ALSA支持得不够好。
解决办法为
echo "et.x86 0 0 direct" > /proc/asound/card0/pcm0p/oss (需要管理员权限)
解决办法为
echo "et.x86 0 0 direct" > /proc/asound/card0/pcm0p/oss (需要管理员权限)
gnome的彩蛋
按alt+f2调出Run Application对话框, 然后输入
free the fish
或
gegls from outer space
前者会出现一个小鱼,不过要想让它消失只能kill掉gnome-panel
后者出现一个鱼打牛的小游戏
free the fish
或
gegls from outer space
前者会出现一个小鱼,不过要想让它消失只能kill掉gnome-panel
后者出现一个鱼打牛的小游戏
Labels:
linux
2007-12-10
pidgin 2.3.1 支持QQ了
最近看到Pidgin升级到2.3.1了,Changelog里赫然写着改进了对QQ的支持, 于是我下载编译试了一下。(注意首先要卸了原来的)
开始时我的QQ号需要激活,还需要输入验证码,这些都是用Wine的QQ发现的。 前者上个网页就行了,后者则需要更改密码。 都还不算麻烦。
之后用pidgin连了一下,果然连上了。 只是现在不支持好友分组, 也不支持表情。
但是已经很高兴了。
开始时我的QQ号需要激活,还需要输入验证码,这些都是用Wine的QQ发现的。 前者上个网页就行了,后者则需要更改密码。 都还不算麻烦。
之后用pidgin连了一下,果然连上了。 只是现在不支持好友分组, 也不支持表情。
但是已经很高兴了。
2007-12-04
ubuntu vim-latexsuite默认变量的问题
ubuntu里装了vim-latexsuite后问题多多,除了上次说的找不到插件的问题外,还有一个问题,就是默认变量没有被设置,每次按\ll编译时都会提示没有设置initTarget. 以前就没有遇到这个问题。
今天研究了一下,原因是vim自己的目录下有一个关于tex文件compile的设置,优先级比latexsuite的高,所以latexsuite的那个就不能被运行了。
解决办法是把/usr/share/vim/vim71/compiler/tex.vim改名或删除,之后就正常了 :)
今天研究了一下,原因是vim自己的目录下有一个关于tex文件compile的设置,优先级比latexsuite的高,所以latexsuite的那个就不能被运行了。
解决办法是把/usr/share/vim/vim71/compiler/tex.vim改名或删除,之后就正常了 :)
2007-12-01
游戏推荐: Anika's Odyssey
见http://www.kongregate.com/games/Trickysheep/anikas-odyssey
point and click类游戏,难度不大,但是美工非常棒,情节也比较有趣。是我喜欢的类型。
point and click类游戏,难度不大,但是美工非常棒,情节也比较有趣。是我喜欢的类型。
游戏推荐: excit
见http://www.kongregate.com/games/Krystman/excit
puzzle类游戏,做得不错,一共30关,最后一关比较难,前面还好
我一口气玩完的,最后排名在558。
puzzle类游戏,做得不错,一共30关,最后一关比较难,前面还好
我一口气玩完的,最后排名在558。
2007-11-30
SharedObject 修改器
最近破解一个Flash时需要修改它的SharedObject, 找到一些修改器,其中最好用的一个叫做.sol
Editor, 主页是http://sourceforge.net/projects/soleditor/
只可惜只有Windows版本,但是在wine下跑得也不错。 另外就是不开源,也有点不爽。
但是它是我找到的几个修改器里最好用的一个。
Editor, 主页是http://sourceforge.net/projects/soleditor/
只可惜只有Windows版本,但是在wine下跑得也不错。 另外就是不开源,也有点不爽。
但是它是我找到的几个修改器里最好用的一个。
2007-11-25
beep media player mp3 信息乱码的解决
右键 -> Preferences -> Plugins -> Media -> MPEG Audio Plugin -> Preferences -> Title
选上Covert non-UTF8 ID3 tags to UTF8
并把ID3 encoding设为GBK
选上Covert non-UTF8 ID3 tags to UTF8
并把ID3 encoding设为GBK
2007-11-24
linux 连接 蓝牙 手机
参考:http://blog.flyingdream.net.cn/2007/%E5%9C%A8-ubuntu-linux-%E4%B8%8B%E5%88%A9%E7%94%A8%E8%93%9D%E7%89%99%E9%80%9A%E8%BF%87-gprs-%E6%97%A0%E7%BA%BF%E4%B8%8A%E7%BD%91/
http://www.chinalinuxpub.com/read.php?wid=1490
测试环境, ubuntu 7.10 + motorola A768i
网上搜这个能搜到很多, 大致是装上很多以bluez-开头和包含bluetooth的包(我差不多都装了), 之后启动bluetooh服务,以及运行gnome-obex-server(Applications->Accessories->Bluetooth File Sharing). 然后就能自动找到手机了(可能也需要修改hcid.conf,见下), 传文件是点右键, Send to, 里面有个Bluetooh的选项。
今天一直在搞rfcomm, 本来是试图连上蓝牙局域网的,但是一直没成功,到现在有点成果。记录如下。
注意:
要用到的命令:hcitool, hciconfig, sdptool, rfcomm, 最好都看一下文档。
另外每次修改配之后注意重启bluetooth服务:/etc/init.d/bluetooth restart
很多命令需要管理员权限,以下省略sudo
步骤:
1.修改/etc/bluetooth/hcid.conf, 关键是如下两行
security auto;
passkey "1234";
第一行很重要, 我一开始设的user,手机怎么都连不上电脑
第二行是手机连电脑时需要输入的验证码
2.hcitool scan 得到手机的地址
3.sdptool browse <地址> 得到手机服务列表
我这里这条命令结果为空,
我用的是spdtool search --bdaddr <地址>
常用的service有
DUN 拨号
LAN
HS headset
OPUSH obex push
得到列表后注意看Protocal Descriptor List里“RFCOMM”一行及下一行,
找到Channel: x, 记住这个x
4.编辑 /etc/bluetooth/rfcomm.conf,
rfcomm0 {
bind yes;
device <地址>;
channel;
comment “your comment here”;
}
之后/dev/rfcomm0就是这个channel的连接了
如果rfcomm0已经使用,那么直接改rfcomm.conf里的设备名
引文里提到用kermit可以通过/dev/rfcomm0实现与手机通信, 我这里没成功
另一引文里提到可以用/dev/rfcomm0通过手机上网, 我没有试
现在我这里主要就是能够手机电脑互传文件, 互相bond而已,很多功能还有待研究
http://www.chinalinuxpub.com/read.php?wid=1490
测试环境, ubuntu 7.10 + motorola A768i
网上搜这个能搜到很多, 大致是装上很多以bluez-开头和包含bluetooth的包(我差不多都装了), 之后启动bluetooh服务,以及运行gnome-obex-server(Applications->Accessories->Bluetooth File Sharing). 然后就能自动找到手机了(可能也需要修改hcid.conf,见下), 传文件是点右键, Send to, 里面有个Bluetooh的选项。
今天一直在搞rfcomm, 本来是试图连上蓝牙局域网的,但是一直没成功,到现在有点成果。记录如下。
注意:
要用到的命令:hcitool, hciconfig, sdptool, rfcomm, 最好都看一下文档。
另外每次修改配之后注意重启bluetooth服务:/etc/init.d/bluetooth restart
很多命令需要管理员权限,以下省略sudo
步骤:
1.修改/etc/bluetooth/hcid.conf, 关键是如下两行
security auto;
passkey "1234";
第一行很重要, 我一开始设的user,手机怎么都连不上电脑
第二行是手机连电脑时需要输入的验证码
2.hcitool scan 得到手机的地址
3.sdptool browse <地址> 得到手机服务列表
我这里这条命令结果为空,
我用的是spdtool search --bdaddr <地址>
常用的service有
DUN 拨号
LAN
HS headset
OPUSH obex push
得到列表后注意看Protocal Descriptor List里“RFCOMM”一行及下一行,
找到Channel: x, 记住这个x
4.编辑 /etc/bluetooth/rfcomm.conf,
rfcomm0 {
bind yes;
device <地址>;
channel
comment “your comment here”;
}
之后/dev/rfcomm0就是这个channel的连接了
如果rfcomm0已经使用,那么直接改rfcomm.conf里的设备名
引文里提到用kermit可以通过/dev/rfcomm0实现与手机通信, 我这里没成功
另一引文里提到可以用/dev/rfcomm0通过手机上网, 我没有试
现在我这里主要就是能够手机电脑互传文件, 互相bond而已,很多功能还有待研究
2007-11-19
ubuntu 7.10 下安装 windriver
最近要用Xilinx ISE, 装驱动时老装不上, 去Windriver官网上下的也没用。 configure和make都报错, 自己尝试修改未果。
几经查找, 在http://www.visionlab.uncc.edu/component/option,com_jd-wiki/Itemid,46/上找到解决办法:
首先去ftp://ftp.xilinx.com/pub/utilities/fpga/install_drivers.tar.gz下载并解压最新的驱动, 我下的跟上面那个网站说的一样, md5是d0aea515f1b4523eb2537df86bc6db41
创建一个diff文件
保存在源码顶层目录,用patch -p1 < filename打补丁
最后是安装,原文说的是直接运行./install_drivers即可,我这里不行,要进入linux_drivers/windriver32/windrvr
然后运行bash ./configure 和sudo make && sudo make install
那个configure挺诡异的。。。
不过最后终于编译成功了
几经查找, 在http://www.visionlab.uncc.edu/component/option,com_jd-wiki/Itemid,46/上找到解决办法:
首先去ftp://ftp.xilinx.com/pub/utilities/fpga/install_drivers.tar.gz下载并解压最新的驱动, 我下的跟上面那个网站说的一样, md5是d0aea515f1b4523eb2537df86bc6db41
创建一个diff文件
--- install_drivers/linux_drivers/windriver32/windrvr/linux_wrappers.c.orig 2007-03-27 22:35:36.000000000 +0200
+++ install_drivers/linux_drivers/windriver32/windrvr/linux_wrappers.c 2007-09-24 00:32:44.000000000 +0200
@@ -44,9 +44,6 @@
#endif
#include <asm/mman.h>
#include "linux_wrappers.h"
-#if defined(LINUX_26)
- #include <linux/ioctl32.h>
-#endif
#if defined(UDEV_SUPPORT)
#include <linux/devfs_fs_kernel.h>
#endif
@@ -91,7 +88,10 @@
#endif
#if defined(MODULE_LICENSE)
- MODULE_LICENSE("Proprietary");
+ // MODULE_LICENSE("Proprietary");
+ // Uh! Uh! We need to set this to GPL so we can talk to the USB API.
+ // Is it a copyright infringement if I do that for private use??
+ MODULE_LICENSE("GPL");
#endif
#if defined(MODULE_AUTHOR)
MODULE_AUTHOR("Jungo");
@@ -587,7 +587,7 @@
if (!bh)
return NULL;
memset(bh, 0, sizeof(*bh));
- bh->data = data;
+ atomic_long_set(&bh->data, data);
#if defined(LINUX_26)
bh->func = routine;
bh->entry.next = &bh->entry;
@@ -650,7 +650,7 @@
#else
void
#endif
-wrapper_handler(int irq, void *ctx, struct pt_regs *pt)
+wrapper_handler(int irq, void *ctx)
{
struct int_wrapper *context = (struct int_wrapper *)ctx;
int rc = 0;
@@ -1023,7 +1023,7 @@
}
#if defined(WINDRIVER_KERNEL)
- pci_module_init(&generic_pci_driver);
+ pci_register_driver(&generic_pci_driver);
#endif
return 0;
#endif
--- install_drivers/linux_drivers/xpc4drvr2_6/xpc4drvr/Makefile.orig 2007-09-24 00:36:20.000000000 +0200
+++ install_drivers/linux_drivers/xpc4drvr2_6/xpc4drvr/Makefile 2007-09-24 00:36:27.000000000 +0200
@@ -17,7 +17,7 @@
# Run ./configure first, to generate regparm_option
REG_PARM = $(shell grep -c "regparm=3" $(PWD)/regparm_option)
SYM_FILE_DIR = /lib/modules/$(shell uname -r)/build
-GET_USER_SIZE_SYM = $(shell grep -c "get_user_size" $(SYM_FILE_DIR)/Module.symvers)
+GET_USER_SIZE_SYM = 0
ARCH_64BIT = $(shell arch | grep -c "64")
ifeq ($(ARCH_64BIT),1)
CFLAGS += -mcmodel=kernel
保存在源码顶层目录,用patch -p1 < filename打补丁
最后是安装,原文说的是直接运行./install_drivers即可,我这里不行,要进入linux_drivers/windriver32/windrvr
然后运行bash ./configure 和sudo make && sudo make install
那个configure挺诡异的。。。
不过最后终于编译成功了
2007-11-13
Bonjour.exe 清除办法
刚装了Adobe CS3, 防火墙突然给我蹦出一堆Bonjour.exe的信息,很是烦人。
查了一下,不算病毒,是Adobe的某软件, 删之。
具体方法是:
运行“C:\Program Files\Bonjourm\DNSResponder.exe -remove”
打开 C:Program Files\Bonjour ,重命名 mdnsNSP.dll 为 mdnsNSP.old
重启电脑
删除 Program Files\Bonjour 文件夹
查了一下,不算病毒,是Adobe的某软件, 删之。
具体方法是:
运行“C:\Program Files\Bonjourm\DNSResponder.exe -remove”
打开 C:Program Files\Bonjour ,重命名 mdnsNSP.dll 为 mdnsNSP.old
重启电脑
删除 Program Files\Bonjour 文件夹
2007-11-03
linux 下 nvidia 显卡双屏幕设置
这个被nvidia称为TwinView, 其设置方法是在Section "Screen"里加入
Option "TwinView" "True"
之后是Option "MetaModes" "nvidia-auto-select, nvidia-auto-select" (这个也可以用nvidia-settings设置)
这样就启动了TwinView, 不过这时两个屏幕是拼接的关系。 我这次是要接投影仪, 因此需要两个屏幕显示一致,因此还要加上
Option "TwinVieworientation" "Clone"
之后重启X即可。
nvidia驱动的文档写得很清楚,我看了2分钟就搞明白了。
Option "TwinView" "True"
之后是Option "MetaModes" "nvidia-auto-select, nvidia-auto-select" (这个也可以用nvidia-settings设置)
这样就启动了TwinView, 不过这时两个屏幕是拼接的关系。 我这次是要接投影仪, 因此需要两个屏幕显示一致,因此还要加上
Option "TwinVieworientation" "Clone"
之后重启X即可。
nvidia驱动的文档写得很清楚,我看了2分钟就搞明白了。
Labels:
linux
2007-10-30
ubuntu 中文默认字体的设置
我一直希望能够分别设置中文和英文的字体, 但是一直都没成功。
之前一直是只设置喜欢的英文字体作为默认字体, 但是中文的话就非常难看,现象是很多文字字体,大小都不一致。
仔细想想,它(我一直用gnome)字体显示的工作原理大致是有个字体列表,然后要显示一个字的时候就依次查找,直到找到一个能够显示的为止。 比如如果我设的默认字体是个中文字体,那么英文字体一般就会跟着改变。但是现在默认字体是英文的,显示中文时自然找不到,于是它就从自己的默认列表里去找到了。
至于列表具体存放的位置, 我找了找,是在/etc/fonts/conf.d里,相关的有两个文件, 40-generic.conf 和 65-nonlatin.conf, 根据/etc/fonts/conf.avail里的说明,这些配置文件是按编号顺序依次加载的, 于是修改65-nonlatin.conf, 由于我想使用文鼎楷体,于是找到对应的, 并把它移到所在里的最前面,成为第一个。
之后重启X时没进去,卡住了,然后重启了一下问题解决。
现在爽眼多了。
之前一直是只设置喜欢的英文字体作为默认字体, 但是中文的话就非常难看,现象是很多文字字体,大小都不一致。
仔细想想,它(我一直用gnome)字体显示的工作原理大致是有个字体列表,然后要显示一个字的时候就依次查找,直到找到一个能够显示的为止。 比如如果我设的默认字体是个中文字体,那么英文字体一般就会跟着改变。但是现在默认字体是英文的,显示中文时自然找不到,于是它就从自己的默认列表里去找到了。
至于列表具体存放的位置, 我找了找,是在/etc/fonts/conf.d里,相关的有两个文件, 40-generic.conf 和 65-nonlatin.conf, 根据/etc/fonts/conf.avail里的说明,这些配置文件是按编号顺序依次加载的, 于是修改65-nonlatin.conf, 由于我想使用文鼎楷体,于是找到对应的
之后重启X时没进去,卡住了,然后重启了一下问题解决。
现在爽眼多了。
2007-10-29
linux下麦克风不工作
我这里麦克风的症状如之前声卡output的一样, 不报错但是不工作(偶尔好像也报错)。
弄了好几次不见起色。
今天偶然又看看了alsa网站里的详细安装说明,发现我这里设备文件的权限没设对。
于是运行chmod a+rw /dev/dsp /dev/mixer /dev/sequencer,之后问题解决(我这里没有midi)
不过似乎噪声很大,尚待解决。但是好歹可以用了。
弄了好几次不见起色。
今天偶然又看看了alsa网站里的详细安装说明,发现我这里设备文件的权限没设对。
于是运行chmod a+rw /dev/dsp /dev/mixer /dev/sequencer,之后问题解决(我这里没有midi)
不过似乎噪声很大,尚待解决。但是好歹可以用了。
2007-10-25
创建 ssh 信任连接
参考: http://www.powersite.cn/?q=node/62&p=9334c90a88934eac5df7c7710c149d&user=baidu
主要目的是让服务器给客户端建立信任关系,使得以后连接时不需要输入密码
具体步骤是
1.在客户端建立key, 如果没有创建过, 用ssh-keygen -t rsa创建一个
2.把生成的id文件(一般是~/.ssh/id_rsa)的内容追加到服务器的~/.ssh/authorized_keys文件中
就是这么简单。
主要目的是让服务器给客户端建立信任关系,使得以后连接时不需要输入密码
具体步骤是
1.在客户端建立key, 如果没有创建过, 用ssh-keygen -t rsa创建一个
2.把生成的id文件(一般是~/.ssh/id_rsa)的内容追加到服务器的~/.ssh/authorized_keys文件中
就是这么简单。
evince 看中文pdf乱码的解决
有时候用evince看中文pdf会出乱码,大概是Adobe-GB1这个字体的问题, 装了xpdf-chinese*和cmap-adobe*之后,xpdf可以正常工作了, 但是evince仍不行, 控制台消息会报跟adobe-gb1有关的错误。
源里没有poppler相关的包, 我又搜了搜,终于发现了应该安装poppler-data,可以从http://poppler.freedesktop.org/下载到。那个由于是版权归Adobe,所以不能放源里。唉。
不过装了之后确实正常了,如果是ubuntu,注意用sudo make install prefix=/usr来安装。
源里没有poppler相关的包, 我又搜了搜,终于发现了应该安装poppler-data,可以从http://poppler.freedesktop.org/下载到。那个由于是版权归Adobe,所以不能放源里。唉。
不过装了之后确实正常了,如果是ubuntu,注意用sudo make install prefix=/usr来安装。
2007-10-22
ubuntu 7.10 rainlendar 崩溃的解决
日历软件我一直用rainlendar+GCALDaemon, 然而ubuntu升级至7.10后,rainlendar就出了问题, 虽然界面可以显示,但是点击菜单后就会崩溃。起初认为是和以前一样与scim冲突, 但是尝试修复未果。
今天搜到了http://www.rainlendar.net/cms/index.php?option=com_fireboard&Itemid=36&func=view&catid=4&id=4316
根据作者所说, 这个是和gtk和wxwidget有关。 之后我现在了2.2.b48版, 基本工作正常
链接在http://www.rainlendar.net/cms/index.php?option=com_fireboard&Itemid=36&func=view&id=4540&catid=7
今天搜到了http://www.rainlendar.net/cms/index.php?option=com_fireboard&Itemid=36&func=view&catid=4&id=4316
根据作者所说, 这个是和gtk和wxwidget有关。 之后我现在了2.2.b48版, 基本工作正常
链接在http://www.rainlendar.net/cms/index.php?option=com_fireboard&Itemid=36&func=view&id=4540&catid=7
2007-10-18
[转]AT&T汇编语言与GCC内嵌汇编简介
AT&T汇编语言与GCC内嵌汇编简介
版本 0.1
时间04/3/30
EMAIL chforest_chang@hotmail.com
1 AT&T 与INTEL的汇编语言语法的区别
1.1大小写
1.2操作数赋值方向
1.3前缀
1.4间接寻址语法
1.5后缀
1.6指令
2 GCC内嵌汇编
2.1简介
2.2内嵌汇编举例
2.3语法
2.3.1汇编语句模板
2.3.2输出部分
2.3.3输入部分
2.3.4限制字符
2.3.5破坏描述部分
2.4GCC如何编译内嵌汇编代码
3后记
本节先介绍
AT&T汇编语言语法与INTEL汇编语法的差别,然后介绍GCC内嵌汇编语法。阅读本节需要读者具有INTEL
汇编语言基础。
1 AT&T 与INTEL的汇编语言语法的区别
1.1
指令大小写
INTEL格式的指令使用大写字母,而AT&T
格式的使用小写字母。
例:
INTEL AT&T
MOV EAX,EBX movl %ebx,%eax
1.2
指令操作数赋值方向
在INTEL语法中,第一个表示目的操作数,第二个表示源操作数,赋值方向从右向左。
AT&T语法第一个为源操作数,第二个为目的操作数,方向从左到右,合乎自然。
例:
INTEL AT&T
MOV EAX,EBX movl %ebx,%eax
1.3
指令前缀
在INTEL语法中寄存器和立即数不需要前缀;
AT&T中寄存器需要加前缀“%”;立即数需要加前缀“$”。
例:
INTEL AT&T
MOV EAX,1 movl $1,%eax
符号常数直接引用,不需要加前缀,如:
movl value , %ebx
value为一常数;
在符号前加前缀 $, 表示引用符号地址,
如
movl $value, %ebx
是将value的地址放到ebx中。
总线锁定前缀“lock”:
总线锁定操作。“lock”前缀在Linux
核心代码中使用很多,特别是SMP
代码中。当总线锁定后其它CPU
不能存取锁定地址处的内存单元。
远程跳转指令和子过程调用指令的操作码使用前缀“l“,分别为ljmp,lcall,
与之相应的返回指令伪lret。
例:
INTEL AT&T
lcall $secion:$offset
JMP FAR SECTION:OFFSET ljmp $secion:$offset
RET FAR SATCK_ADJUST lret $stack_adjust
1.4 间接寻址语法
INTEL中基地址使用“[”、“]”,而在AT&T“(”、“)”;
另外处理复杂操作数的语法也不同,
INTEL为Segreg:[base+index*scale+disp]
,而在AT&T中为%segreg:disp(base,index,sale),其中segreg
,index,scale,disp都是可选的,在指定index而没有显式指定Scale
的情况下使用默认值1。Scale,disp不需要加前缀“&”。
INTEL AT&T
Instr foo,segreg:[base+index*scale+disp] instr %segreg:disp(base,index,scale),foo
1.5
指令后缀
AT&T
语法中大部分指令操作码的最后一个字母表示操作数大小,“b”表示byte
(一个字节);“w”表示word(2,个字节);“l”表示long(4,个字节)。
INTEL中处理内存操作数时也有类似的语法如:
BYTE PTR、WORD PTR、DWORD PTR。
例:
INTEL AT&T
mov al, bl movb %bl,%al
mov ax,bx movw %bx,%ax
mov eax, dword ptr [ebx] movl (%ebx), %eax
AT&T汇编指令中,操作数扩展指令有两个后缀,一个指定源操作数的字长,另一个指定目标操作数的字长。AT&T的符号扩展指令的为“movs”,零扩展指令为“movz
”(相应的Intel指令为“movsx”和“movzx”)。因此,“movsbl %al,%edx”表示对寄存器al
中的字节数据进行字节到长字的符号扩展,计算结果存放在寄存器edx
中。下面是一些允许的操作数扩展后缀:
l
bl: ,字节>->长字 l
bw: ,字节>->字 l
wl: ,字->长字
跳转指令标号后的后缀表示跳转方向,“f”表示向前(forward),
“b,”表示向后(back)。
例:
jmp 1f
jmp 1f
1.6 指令
INTEL汇编与AT&T汇编指令基本相同,差别仅在语法上。关于每条指令的语法可以参考I386Manual。
2 GCC内嵌汇编
2.1 简介
内核代码绝大部分使用C
语言编写,只有一小部分使用汇编语言编写,例如与特定体系结构相关的代码和对性能影响很大的代码。GCC提供了内嵌汇编的功能,可以在C代码中直接内嵌汇编语言语句,大大方便了程序设计。
简单的内嵌汇编很容易理解
例:
__asm__
__volatile__("hlt");
“__asm__”表示后面的代码为内嵌汇编,“asm”是“__asm__”的别名。
“__volatile__”表示编译器不要优化代码,后面的指令保留原样,
“volatile”是它的别名。括号里面是汇编指令。
2.2 内嵌汇编举例在内嵌汇编中,可以将C
语言表达式指定为汇编指令的操作数,而且不用去管如何将C
语言表达式的值读入哪个寄存器,以及如何将计算结果写回C
变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC
会自动插入代码完成必要的操作。
使用内嵌汇编,要先编写汇编指令模板,然后将C语言表达式与指令的操作数相关联,并告诉
GCC对这些操作有哪些限制条件。例如在下面的汇编语句:
__asm__ __violate__
("movl %1,%0" : "=r" (result) : "m" (input));
“movl %1,%0”是指令模板;“%0”和“%1”代表指令的操作数,称为占位符,内嵌汇编靠它们将C
语言表达式与指令操作数相对应。指令模板后面用小括号括起来的是C
语言表达式,本例中只有两个:“result”和“input”,他们按照出现的顺序分别与指令操作
数“%0”,“%1,”对应;注意对应顺序:第一个C表达式对应“%0”;第二个表达式对应“%1
”,依次类推,操作数至多有10个,分别用“%0”,“%1”….“%9,”表示。在每个操作数前
面有一个用引号括起来的字符串,字符串的内容是对该操作数的限制或者说要求。“result”前面
的限制字符串是“=r”,其中“=”表示“result”是输出操作数,“r
”表示需要将“result”与某个通用寄存器相关联,先将操作数的值读入寄存器,然后
在指令中使用相应寄存器,而不是“result”本身,当然指令执行完后需要将寄存器中的值
存入变量“result”,从表面上看好像是指令直接对“result”进行操作,实际上GCC
做了隐式处理,这样我们可以少写一些指令。“input”前面的“r”表示该表达式需要先放入
某个寄存器,然后在指令中使用该寄存器参加运算。
我们将上面的内嵌代码放到一个C源文件中,然后使用gcc –c–S得到该C
文件源代码相对应的汇编代码,然后查看一下汇编代码,看看GCC是如何处理的。
C源文件如下内容如下,注意该代码没有实际意义,仅仅作为例子。
extern int
input,result;
void test(void)
{
input
= 1;
__asm__ __volatile__ ("movl %1,%0" :
"=r" (result) : "r" (input));
return
;
}
对应的汇编代码如下;
行号 代码 解释
1
7
8 movl $1, input 对应C语言语句input = 1;
9 input, %eax
10 #APP GCC插入的注释,表示内嵌汇编开始
11 movl %eax,%eax 我们的内嵌汇编语句
12 #NO_APP GCC 插入的注释,表示内嵌汇编结束
13 movl %eax, result 将结果存入result变量
14
-
18
。。。。。。
从汇编代码可以看出,第9行和第13行是GCC,自动增加的代码,GCC
根据限定字符串决定如何处理C表达式,本例两个表达式都被指定为“r”型,所以先使用指令:
movl input, %eax
将input读入寄存器%eax;GCC,也指定一个寄存器与输出变量result
相关,本例也是%eax,等得到操作结果后再使用指令:
movl %eax, result
将寄存器的值写回C变量result中。从上面的汇编代码我们可以看出与result
和input,相关连的寄存器都是%eax,GCC使用%eax,替换内嵌汇编指令模板中的
%0,%1
movl %eax,%eax
显然这一句可以不要。但是没有优化,所以这一句没有被去掉。
由此可见,C表达式或者变量与寄存器的关系由GCC自动处理,我们只需使用限制字符串指导GCC
如何处理即可。限制字符必须与指令对操作数的要求相匹配,否则产生的汇编代码
将会有错,读者可以将上例中的两个“r”,都改为“m”(m,表示操作数放在内存,而不是寄
存器中),编译后得到的结果是:
movl input, result
很明显这是一条非法指令,因此限制字符串必须与指令对操作数的要求匹配。例如指令movl
允许寄存器到寄存器,立即数到寄存器等,但是不允许内存到内存的操作,因此两个操作数
不能同时使用“m”作为限定字符。
2.3 语法
内嵌汇编语法如下:
__asm__(
汇编语句模板:
输出部分:
输入部分:
破坏描述部分)
共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格
开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,
也需要用“:”格开,相应部分内容为空。例如:
__asm__ __volatile__(
"cli":
:
:"memory")
2.3.1 汇编语句模板
汇编语句模板由汇编语句序列组成,语句之间使用“;”、“\n”或“\n\t”分开。
指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1…,%9。
指令中使用占位符表示的操作数,总被视为long型(4,个字节),但对其施加的操作
根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节。
对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,
“b”代表低字节,“h”代表高字节,例如:%h1。
2.3.2 输出部分
输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和
C语言变量组成。每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数。
例:
__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字符串表示对该变量的限制条件,这样GCC就可以根据这些条件决定如何
分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。
2.3.3 输入部分
输入部分描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由
限定字符串和C语言表达式或者C语言变量组成。
例1:
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例二(bitops.h):
Static __inline__ void __set_bit(int nr,
volatile void * addr)
{
__asm__(
"btsl%1,%0" :
"=m"(ADDR) :
"Ir"(nr));
}
后例功能是将(*addr)的第nr位设为1。第一个占位符%0与C,语言变量ADDR
对应,第二个占位符%1与C,语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:
btsl nr, ADDR,该指令的两个操作数不能全是内存变量,因此将nr的限定字符串指定为“Ir”,
将nr,与立即数或者寄存器相关联,这样两个操作数中只有ADDR为内存变量。
2.3.4 限制字符
2.3.4.1 限制字符列表
限制字符有很多种,有些是与特定体系结构相关,此处仅列出常用的限定字符和i386
中可能用到的一些常用的限定符。它们的作用是指示编译器如何处理其后的C
语言变量与指令操作数之间的关系,例如是将变量放在寄存器中还是放在内存中等,
下表列出了常用的限定字母。
分类
限定符 描述 通用寄存器
“a”将输入变量放入eax
这里有一个问题:假设eax已经被使用,那怎么办?
其实很简单:因为GCC知道eax已经被使用,它在这段汇编代码的起始处插入一条
语句pushl %eax,将eax内容保存到堆栈,然后在这段代码结束处再增加一条
语句popl %eax,恢复eax的内容
“b”将输入变量放入ebx
“c”将输入变量放入ecx
“d”将输入变量放入edx
“s”将输入变量放入esi
“d”将输入变量放入edi
“q”将输入变量放入eax,ebx ,ecx ,edx中的一个
“r”将输入变量放入通用寄存器,也就是eax ,ebx,ecx,edx,esi,edi中的一个
“A”把eax和edx,合成一个64位的寄存器(uselong longs)
“m”内存变量
“o”操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址寻址
“V”操作数为内存变量,但寻址方式不是偏移量类型
“,” 操作数为内存变量,但寻址方式为自动增量
“p”操作数是一个合法的内存地址(指针)
寄存器或内存
“g” 将输入变量放入eax,ebx,ecx ,edx中的一个或者作为内存变量
“X”操作数可以是任何类型
立即数
“I” 0-31 之间的立即数(用于32位移位指令)
“J” 0-63 之间的立即数(用于64 位移位指令)
“N” 0-255 ,之间的立即数(用于out 指令)
“i” 立即数
“n” 立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n”而不是“i”
匹配
“0”,“1 ,”... “9 ”
表示用它限制的操作数与某个指定的操作数匹配,也即该操作数就是指定的那个操作数,
例如用“0 ”去描述“%1”操作数,那么“%1”引用的其实就是“%0”操作数,注意作为
限定符字母的0-9 ,与指令中的“%0”-“%9”的区别,前者描述操作数,后者代表操作数。
后面有详细描述 & 该输出操作数不能使用过和输入操作数相同的寄存器
后面有详细描述
操作数类型
“=” 操作数在指令中是只写的(输出操作数)
“+” 操作数在指令中是读写类型的(输入输出操作数)
浮点数
“f”
浮点寄存器
“t”第一个浮点寄存器
“u”第二个浮点寄存器
“G”标准的80387
浮点常数
% 该操作数可以和下一个操作数交换位置
例如addl的两个操作数可以交换顺序(当然两个操作数都不能是立即数)
# 部分注释,从该字符到其后的逗号之间所有字母被忽略
* 表示如果选用寄存器,则其后的字母被忽略
现在继续看上面的例子,
"=m" (ADDR)表示ADDR为内存变量(“m”),而且是输出变量(“=”);"Ir" (nr)表示nr,为
0-31之间的立即数(“I”)或者一个寄存器操作数(“r”)。
2.3.4.2
匹配限制符
I386
指令集中许多指令的操作数是读写型的(读写型操作数指先读取原来的值然后参加运算,最后
将结果写回操作数),例如addl %1,%0,它的作用是将操作数%0与操作数%1的和存入操作数%0,
因此操作数%0是读写型操作数。老版本的GCC对这种类型操作数的支持不是很好,它将操作数严格
分为输入和输出两种,分别放在输入部分和输出部分,而没有一个单独部分描述读写型操作数,
因此在GCC中读写型的操作数需要在输入和输出部分分别描述,靠匹配限制符将两者关联到一起
注意仅在输入和输出部分使用相同的C变量,但是不用匹配限制符,产生的代码很可能不对,后
面会分析原因。
匹配限制符是一位数字:“0”、“1”……“9,”,分别表示它限制的C表达式分别与
占位符%0,%1,……%9对应的C变量匹配。例如使用“0”作为%1,的限制字符,那么
%0和%1表示同一个C,变量。
看一下下面的代码就知道为什么要将读写型操作数,分别在输入和输出部分加以描述。
该例功能是求input+result的和,然后存入result:
extern int input,result;
void test_at_t()
{
result= 0;
input = 1;
__asm__
__volatile__ ("addl %1,%0":"=r"(result): "r"(input));
}
对应的汇编代码为:
movl $0,_result
movl $1,_input
movl _input,%edx /APP
addl %edx,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
input 为输入型变量,而且需要放在寄存器中,GCC给它分配的寄存器是%edx,在执行addl之前%edx,
的内容已经是input的值。可见对于使用“r”限制的输入型变量或者表达式,在使用之前GCC会插入
必要的代码将他们的值读到寄存器;“m”型变量则不需要这一步。读入input后执行addl,显然%eax
的值不对,需要先读入result的值才行。再往后看:movl %eax,%edx和movl %edx,_result
的作用是将结果存回result,分配给result的寄存器与分配给input的一样,都是%edx。
综上可以总结出如下几点:
1. 使用“r”限制的输入变量,GCC先分配一个寄存器,然后将值读入寄存器,最后
用该寄存器替换占位符;
2. 使用“r”限制的输出变量,GCC会分配一个寄存器,然后用该寄存器替换占位符,
但是在使用该寄存器之前并不将变量值先读入寄存器,GCC认为所有输出变量以前的
值都没有用处,不读入寄存器(可能是因为AT&T汇编源于CISC架构处理器的汇编语言
,在CISC处理器中大部分指令的输入输出明显分开,而不像RISC那样一个操作数既
做输入又做输出,例如add r0,r1,r2,r0,和r1是输入,r2是输出,输入和输出分开,
没有使用输入输出型操作数,这样我们就可以认为r2对应的操作数原来的值没有用处,
也就没有必要先将操作数的值读入r2,因为这是浪费处理器的CPU周期),最后GCC插入代码,
将寄存器的值写回变量;
3. 输入变量使用的寄存器在最后一处使用它的指令之后,就可以挪做其他用处,因为
已经不再使用。例如上例中的%edx。在执行完addl之后就作为与result对应的寄存器。
因为第二条,上面的内嵌汇编指令不能奏效,因此需要在执行addl之前把result的值读入
寄存器,也许再将result放入输入部分就可以了(因为第一条会保证将result
先读入寄存器)。修改后的指令如下(为了更容易说明问题将input限制符由“r,”改为“m”):
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %2,%0":"=r"(result):"r"(result),"m"(input));
}
看上去上面的代码可以正常工作,因为我们知道%0和%1都和result相关,应该使用同一个
寄存器,但是GCC并不去判断%0和%1,是否和同一个C表达式或变量相关联(这样易于产生与
内嵌汇编相应的汇编代码),因此%0和%1使用的寄存器可能不同。我们看一下汇编代码就知道了。
movl $0,_result
movl $1,_input
movl _result,%edx /APP
addl _input,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
现在在执行addl之前将result的值被读入了寄存器%edx,但是addl指令的操作数%0
却成了%eax,而不是%edx,与预料的不同,这是因为GCC给输出和输入部分的变量分配了不同
的寄存器,GCC没有去判断两者是否都与result相关,后面会讲GCC如何翻译内嵌汇编,看完之后
就不会惊奇啦。
使用匹配限制符后,GCC知道应将对应的操作数放在同一个位置(同一个寄存器或者同一个
内存变量)。使用匹配限制字符的代码如下:
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %2,%0":"=r"(result):"0"(result),"m"(input));
}
输入部分中的result用匹配限制符“0”限制,表示%1与%0,代表同一个变量,
输入部分说明该变量的输入功能,输出部分说明该变量的输出功能,两者结合表示result
是读写型。因为%0和%1,表示同一个C变量,所以放在相同的位置,无论是寄存器还是内存。
相应的汇编代码为:
movl $0,_result
movl $1,_input
movl _result,%edx
movl %edx,%eax /APP
addl _input,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
可以看到与result相关的寄存器是%edx,在执行指令addl之前先从%edx将result读入%eax,
执行之后需要将结果从%eax读入%edx,最后存入result中。这里我们可以看出GCC
处理内嵌汇编中输出操作数的一点点信息:addl并没有使用%edx,可见它不是简单的用result
对应的寄存器%edx去替换%0,而是先分配一个寄存器,执行运算,最后才将运算结果存入
对应的变量,因此GCC是先看该占位符对应的变量的限制符,发现是一个输出型寄存器变量,
就为它分配一个寄存器,此时没有去管对应的C变量,最后GCC,知道还要将寄存器的值写回变量,
与此同时,它发现该变量与%edx关联,因此先存入%edx,再存入变量。
至此读者应该明白了匹配限制符的意义和用法。在新版本的GCC中增加了一个限制字符“+”,
它表示操作数是读写型的,GCC知道应将变量值先读入寄存器,然后计算,最后写回变量,而
无需在输入部分再去描述该变量。
例;
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %1,%0":"+r"(result):"m"(input));
}
此处用“+”替换了“=”,而且去掉了输入部分关于result的描述,产生的汇编代码如下:
movl $0,_result
movl $1,_input
movl _result,%eax /APP
addl _input,%eax /NO_APP
movl %eax,_result
L2:
movl %ebp,%esp
处理的比使用匹配限制符的情况还要好,省去了好几条汇编代码。
2.3.4.3 “&”限制符
限制符“&”在内核中使用的比较多,它表示输入和输出操作数不能使用相同的寄存器,
这样可以避免很多错误。
举一个例子,下面代码的作用是将函数foo的返回值存入变量ret中:
__asm__ ( “call foo;movl %%edx,%1”, :”=a”(ret) : ”r”(bar) );
我们知道函数的int型返回值存放在%eax中,但是gcc编译的结果是输入和输出同时使用了
寄存器%eax,如下:
movl bar, %eax
#APP
call foo
movl %ebx,%eax
#NO_APP
movl %eax, ret
结果显然不对,原因是GCC并不知道%eax中的值是我们所要的。避免这种情况的方法是使用“&”
限定符,这样bar就不会再使用%eax寄存器,因为已被ret指定使用。
_asm__ ( “call foo;movl %%edx,%1”,:”=&a”(ret) : ”r”(bar) );
2.3.5 破坏描述部分
2.3.5.1 寄存器破坏描述符
通常编写程序只使用一种语言:高级语言或者汇编语言。高级语言编译的步骤大致如下:
l
预处理;
l
编译
l
汇编
l
链接
我们这里只关心第二步编译(将C代码转换成汇编代码):因为所有的代码都是用高级语言编写,
编译器可以识别各种语句的作用,在转换的过程中所有的寄存器都由编译器决定如何分配使用,
它有能力保证寄存器的使用不会冲突;也可以利用寄存器作为变量的缓冲区,因为寄存器的访问
速度比内存快很多倍。如果全部使用汇编语言则由程序员去控制寄存器的使用,只能靠程序员去
保证寄存器使用的正确性。但是如果两种语言混用情况就变复杂了,因为内嵌的汇编代码可以直接
使用寄存器,而编译器在转换的时候并不去检查内嵌的汇编代码使用了哪些寄存器(因为很难检测
汇编指令使用了哪些寄存器,例如有些指令隐式修改寄存器,有时内嵌的汇编代码会调用其他子过程,
而子过程也会修改寄存器),因此需要一种机制通知编译器我们使用了哪些寄存器(程序员自己知道
内嵌汇编代码中使用了哪些寄存器),否则对这些寄存器的使用就有可能导致错误,修改描述部分
可以起到这种作用。当然内嵌汇编的输入输出部分指明的寄存器或者指定为“r”,“g”型由编译器
去分配的寄存器就不需要在破坏描述部分去描述,因为编译器已经知道了。
破坏描述符由逗号格开的字符串组成,每个字符串描述一种情况,一般是寄存器名;除寄存器外
还有“memory”。例如:“%eax”,“%ebx”,“memory”等。
下面看个例子就很清楚为什么需要通知GCC内嵌汇编代码中隐式(称它为隐式是因为GCC并不知道)
使用的寄存器。
在内嵌的汇编指令中可能会直接引用某些寄存器,我们已经知道AT&T格式的汇编语言中,寄存器
名以“%”作为前缀,为了在生成的汇编程序中保留这个“%”号,在asm语句中对寄存器的
引用必须用“%%”作为寄存器名称的前缀。原因是“%”在asm,内嵌汇编语句中的作用与“\”在C
语言中的作用相同,因此“%%”转换后代表“%”。
例(没有使用修改描述符):
int main(void)
{
int input, output,temp;
input = 1;
__asm__ __volatile__ ("movl $0, %%eax;\n\t
movl %%eax, %1;\n\t
movl %2, %%eax;\n\t
movl %%eax, %0;\n\t"
:"=m"(output),"=m"(temp) /* output */
:"r"(input) /* input */
);
return 0;
}
这段代码使用%eax作为临时寄存器,功能相当于C代码:“temp = 0;output=input”,
对应的汇编代码如下:
movl $1,-4(%ebp)
movl -4(%ebp),%eax /APP
movl $0, %eax;
movl %eax, -12(%ebp);
movl %eax, %eax;
movl %eax, -8(%ebp); /NO_APP
显然GCC给input分配的寄存器也是%eax,发生了冲突,output的值始终为0,而不是input。
使用破坏描述后的代码:
int main(void)
{
int input, output,temp;
input = 1;
__asm__ __volatile__
( "movl $0, %%eax;\n\t
movl %%eax, %1;\n\t
movl %2, %%eax;\n\t
movl %%eax, %0;\n\t"
:"=m"(output),"=m"(temp) /* output */
:"r"(input) /* input */
:"eax"); /* 描述符 */
return 0;
}
对应的汇编代码:
movl $1,-4(%ebp)
movl -4(%ebp),%edx /APP
movl $0, %eax;
movl %eax, -12(%ebp);
movl %edx, %eax;
movl %eax, -8(%ebp); /NO_APP
通过破坏描述部分,GCC得知%eax已被使用,因此给input分配了%edx。在使用内嵌汇编时请记
住一点:尽量告诉GCC尽可能多的信息,以防出错。
如果你使用的指令会改变CPU的条件寄存器cc,需要在修改描述部分增加“cc”。
2.3.5.2 memory破坏描述符
“memory”比较特殊,可能是内嵌汇编中最难懂部分。为解释清楚它,先介绍一下编译器的
优化知识,再看C关键字volatile。最后去看该描述符。
2.3.5.2.1 编译器优化介绍
内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,
加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性
的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。
再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器
优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的
是重新排序读写指令。
对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux提供了一个宏解决编译器的执行顺序问题。
void Barrier(void)
这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU
寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。
2.3.5.2.2 C 语言关键字volatile
C 语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量
的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。
该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修
改,而程序通过该变量同步各个线程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal置为2,然后循环等待直到intSignal为1,时退出。显然intSignal
的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使
在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。记住,C
编译器是没有线程概念的!这时候就需要用到volatile。volatile的本意是指:这个值可能会在
当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile
关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作
的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
2.3.5.2.3 Memory
有了上面的知识就不难理解Memory
修改描述符了,Memory描述符告知GCC:
(1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,
它前面的指令都执行完毕。
(2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会
以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,
如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC本身却察觉不到,因为在输出部分没有描述,
此时就需要在修改描述部分增加“memory”,告诉GCC内存已经被修改,GCC得知这个信息后,
就会在这段指令之前,插入必要的指令将前面因为优化Cache到寄存器中的变量值先写回内存,
如果以后又要使用这些变量再重新读取。
例:
………..
Char test[100];
char a;
char c;
c = 0;
test[0] = 1;
……..
a = test [0];
……
__asm__(
"cld\n\t"
"rep\n\t"
"stosb"
: /* no output */
: "a" (c),"D" (test),"c" (100)
:
"cx","di","memory");
……….
// 我们知道test[0] 已经修改,所以重新读取
a=test[0];
……
这段代码中的汇编指令功能与
memset
相当,也就是相当于调用了memset(test,0,100);它使用stosb修改了test
数组的内容,但是没有在输入或输出部分去描述操作数,因为这两条指令都不需要
显式的指定操作数,因此需要增加“memory”通知GCC。现在假设:GCC在优化时将test[0]
放到了%eax寄存器,那么test[0] = 1对应于%eax=1,a = test [0]被换为a=%eax
,如果在那段汇编指令中不使用“memory”,Gcc,不知道现在test[0]
的值已经被改变了(如果整段代码都是我们自己使用汇编编写,我们自己当然知道
这些内存的修改情况,我们也可以人为的去优化,但是现在除了我们编写的那一小段外,
其他汇编代码都是GCC
生成的,它并没有那么智能,知道这段代码会修改test[0]),结果其后的a=test[0]
,转换为汇编后却是a=%eax,因为GCC不知道显式的改变了test数组,结果出错了。
如果增加了“memory”修饰符,GCC知道:
“这段代码修改了内存,但是也仅此而已,它并不知道到底修改了哪些变量”,
因此他将以前因优化而缓存到寄存器的变量值全部写回内存,从内嵌汇编开始,如果后面
的代码又要存取这些变量,则重新存取内存(不会将读写操作映射到以前缓存的那个寄存器)。
这样上面那段代码最后一句就不再是%eax=1,而是test[0] = 1。
这两条对实现临界区至关重要,第一条保证不会因为指令的重新排序将临界区内的代码调
到临界区之外(如果临界区内的指令被重排序放到临界区之外,What will happen?),
第二条保证在临界区访问的变量的值,肯定是最新的值,而不是缓存在
寄存器中的值,否则就会导致奇怪的错误。例如下面的代码:
int del_timer(struct timer_list * timer)
{
int
ret = 0;
if
(timer->next) {
unsigned
long flags;
struct
timer_list * next;
save_flags(flags);
cli();
// 临界区开始
if
((next = timer->next) != NULL) {
(next->prev = timer->prev)->next = next;
timer->next = timer->prev = NULL;
ret = 1;
} // 临界区结束
restore_flags(flags);
}
return
ret;
}
它先判断timer->next
的值,如果是空直接返回,无需进行下面的操作。如果不是空,则进入临界区进行操作,但是cli()
的实现(见下面)没有使用“memory”,timer->next的值可能会被缓存到寄存器中,
后面if ((next =timer->next) != NULL)会从寄存器中读取timer->next的值,如果
在if (timer->next)之后,进入临界区之前,timer->next的值可能被在外部改变,
这时肯定会出现异常情况,而且这种情况很难Debug。但是如果cli使用“memory”,
那么if ((next = timer->next) !=NULL)语句会重新从内存读取timer->next的值,而不会从寄存器
中取,这样就不会出现问题啦。
2.4 版内核中cli和sti的代码如下:
#define __cli()
__asm__
__volatile__("cli": : :"memory")
#define __sti()
__asm__
__volatile__("sti": : :"memory")
通过上面的例子,读者应该知道,为什么指令没有修改内存,但是却使用“memory
”修改描述符的原因了吧。应从指令的上下文去理解为什么要这样做。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,
不如使用“memory”方便。
2.4 GCC如何编译内嵌汇编代码
GCC 编译内嵌汇编代码的步骤如下:
1.输入变量与占位符
根据限定符和破坏描述部分,为输入和输出部分的变量分配合适的寄存器,如果限定符指定为立即数
(“i”),或内存变量(“m”),则不需要该步骤,如果限定符没有具体指定输入操作数的
类型(如“g”),GCC会视需要决定是否将该操作数输入到某个寄存器。这样每个占位符都与某个
寄存器、内存变量或立即数形成了一一对应的关系。对分配了寄存器的输入变量需要增加代码
将它的值读入寄存器。另外还要根据破坏描述符的部分增加额外代码。
2.指令模板部分
然后根据这种一一对应的关系,用这些寄存器、内存变量或立即数来取代汇编代码中的占位符。
3.变量输出
按照输出限定符的指定将寄存器的内容输出到某个内存变量中,如果输出操作数的限定符指定为内存变量(“m”),则该步骤被省略。
3 后记
该文档参照了Web上的许多与GCC内嵌汇编相关的文章编写而成,在此表示感谢,
如有问题请发Email至:chforest_chang@hotmail.com 一起讨论。
版本 0.1
时间04/3/30
EMAIL chforest_chang@hotmail.com
1 AT&T 与INTEL的汇编语言语法的区别
1.1大小写
1.2操作数赋值方向
1.3前缀
1.4间接寻址语法
1.5后缀
1.6指令
2 GCC内嵌汇编
2.1简介
2.2内嵌汇编举例
2.3语法
2.3.1汇编语句模板
2.3.2输出部分
2.3.3输入部分
2.3.4限制字符
2.3.5破坏描述部分
2.4GCC如何编译内嵌汇编代码
3后记
本节先介绍
AT&T汇编语言语法与INTEL汇编语法的差别,然后介绍GCC内嵌汇编语法。阅读本节需要读者具有INTEL
汇编语言基础。
1 AT&T 与INTEL的汇编语言语法的区别
1.1
指令大小写
INTEL格式的指令使用大写字母,而AT&T
格式的使用小写字母。
例:
INTEL AT&T
MOV EAX,EBX movl %ebx,%eax
1.2
指令操作数赋值方向
在INTEL语法中,第一个表示目的操作数,第二个表示源操作数,赋值方向从右向左。
AT&T语法第一个为源操作数,第二个为目的操作数,方向从左到右,合乎自然。
例:
INTEL AT&T
MOV EAX,EBX movl %ebx,%eax
1.3
指令前缀
在INTEL语法中寄存器和立即数不需要前缀;
AT&T中寄存器需要加前缀“%”;立即数需要加前缀“$”。
例:
INTEL AT&T
MOV EAX,1 movl $1,%eax
符号常数直接引用,不需要加前缀,如:
movl value , %ebx
value为一常数;
在符号前加前缀 $, 表示引用符号地址,
如
movl $value, %ebx
是将value的地址放到ebx中。
总线锁定前缀“lock”:
总线锁定操作。“lock”前缀在Linux
核心代码中使用很多,特别是SMP
代码中。当总线锁定后其它CPU
不能存取锁定地址处的内存单元。
远程跳转指令和子过程调用指令的操作码使用前缀“l“,分别为ljmp,lcall,
与之相应的返回指令伪lret。
例:
INTEL AT&T
lcall $secion:$offset
JMP FAR SECTION:OFFSET ljmp $secion:$offset
RET FAR SATCK_ADJUST lret $stack_adjust
1.4 间接寻址语法
INTEL中基地址使用“[”、“]”,而在AT&T“(”、“)”;
另外处理复杂操作数的语法也不同,
INTEL为Segreg:[base+index*scale+disp]
,而在AT&T中为%segreg:disp(base,index,sale),其中segreg
,index,scale,disp都是可选的,在指定index而没有显式指定Scale
的情况下使用默认值1。Scale,disp不需要加前缀“&”。
INTEL AT&T
Instr foo,segreg:[base+index*scale+disp] instr %segreg:disp(base,index,scale),foo
1.5
指令后缀
AT&T
语法中大部分指令操作码的最后一个字母表示操作数大小,“b”表示byte
(一个字节);“w”表示word(2,个字节);“l”表示long(4,个字节)。
INTEL中处理内存操作数时也有类似的语法如:
BYTE PTR、WORD PTR、DWORD PTR。
例:
INTEL AT&T
mov al, bl movb %bl,%al
mov ax,bx movw %bx,%ax
mov eax, dword ptr [ebx] movl (%ebx), %eax
AT&T汇编指令中,操作数扩展指令有两个后缀,一个指定源操作数的字长,另一个指定目标操作数的字长。AT&T的符号扩展指令的为“movs”,零扩展指令为“movz
”(相应的Intel指令为“movsx”和“movzx”)。因此,“movsbl %al,%edx”表示对寄存器al
中的字节数据进行字节到长字的符号扩展,计算结果存放在寄存器edx
中。下面是一些允许的操作数扩展后缀:
l
bl: ,字节>->长字 l
bw: ,字节>->字 l
wl: ,字->长字
跳转指令标号后的后缀表示跳转方向,“f”表示向前(forward),
“b,”表示向后(back)。
例:
jmp 1f
jmp 1f
1.6 指令
INTEL汇编与AT&T汇编指令基本相同,差别仅在语法上。关于每条指令的语法可以参考I386Manual。
2 GCC内嵌汇编
2.1 简介
内核代码绝大部分使用C
语言编写,只有一小部分使用汇编语言编写,例如与特定体系结构相关的代码和对性能影响很大的代码。GCC提供了内嵌汇编的功能,可以在C代码中直接内嵌汇编语言语句,大大方便了程序设计。
简单的内嵌汇编很容易理解
例:
__asm__
__volatile__("hlt");
“__asm__”表示后面的代码为内嵌汇编,“asm”是“__asm__”的别名。
“__volatile__”表示编译器不要优化代码,后面的指令保留原样,
“volatile”是它的别名。括号里面是汇编指令。
2.2 内嵌汇编举例在内嵌汇编中,可以将C
语言表达式指定为汇编指令的操作数,而且不用去管如何将C
语言表达式的值读入哪个寄存器,以及如何将计算结果写回C
变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC
会自动插入代码完成必要的操作。
使用内嵌汇编,要先编写汇编指令模板,然后将C语言表达式与指令的操作数相关联,并告诉
GCC对这些操作有哪些限制条件。例如在下面的汇编语句:
__asm__ __violate__
("movl %1,%0" : "=r" (result) : "m" (input));
“movl %1,%0”是指令模板;“%0”和“%1”代表指令的操作数,称为占位符,内嵌汇编靠它们将C
语言表达式与指令操作数相对应。指令模板后面用小括号括起来的是C
语言表达式,本例中只有两个:“result”和“input”,他们按照出现的顺序分别与指令操作
数“%0”,“%1,”对应;注意对应顺序:第一个C表达式对应“%0”;第二个表达式对应“%1
”,依次类推,操作数至多有10个,分别用“%0”,“%1”….“%9,”表示。在每个操作数前
面有一个用引号括起来的字符串,字符串的内容是对该操作数的限制或者说要求。“result”前面
的限制字符串是“=r”,其中“=”表示“result”是输出操作数,“r
”表示需要将“result”与某个通用寄存器相关联,先将操作数的值读入寄存器,然后
在指令中使用相应寄存器,而不是“result”本身,当然指令执行完后需要将寄存器中的值
存入变量“result”,从表面上看好像是指令直接对“result”进行操作,实际上GCC
做了隐式处理,这样我们可以少写一些指令。“input”前面的“r”表示该表达式需要先放入
某个寄存器,然后在指令中使用该寄存器参加运算。
我们将上面的内嵌代码放到一个C源文件中,然后使用gcc –c–S得到该C
文件源代码相对应的汇编代码,然后查看一下汇编代码,看看GCC是如何处理的。
C源文件如下内容如下,注意该代码没有实际意义,仅仅作为例子。
extern int
input,result;
void test(void)
{
input
= 1;
__asm__ __volatile__ ("movl %1,%0" :
"=r" (result) : "r" (input));
return
;
}
对应的汇编代码如下;
行号 代码 解释
1
7
8 movl $1, input 对应C语言语句input = 1;
9 input, %eax
10 #APP GCC插入的注释,表示内嵌汇编开始
11 movl %eax,%eax 我们的内嵌汇编语句
12 #NO_APP GCC 插入的注释,表示内嵌汇编结束
13 movl %eax, result 将结果存入result变量
14
-
18
。。。。。。
从汇编代码可以看出,第9行和第13行是GCC,自动增加的代码,GCC
根据限定字符串决定如何处理C表达式,本例两个表达式都被指定为“r”型,所以先使用指令:
movl input, %eax
将input读入寄存器%eax;GCC,也指定一个寄存器与输出变量result
相关,本例也是%eax,等得到操作结果后再使用指令:
movl %eax, result
将寄存器的值写回C变量result中。从上面的汇编代码我们可以看出与result
和input,相关连的寄存器都是%eax,GCC使用%eax,替换内嵌汇编指令模板中的
%0,%1
movl %eax,%eax
显然这一句可以不要。但是没有优化,所以这一句没有被去掉。
由此可见,C表达式或者变量与寄存器的关系由GCC自动处理,我们只需使用限制字符串指导GCC
如何处理即可。限制字符必须与指令对操作数的要求相匹配,否则产生的汇编代码
将会有错,读者可以将上例中的两个“r”,都改为“m”(m,表示操作数放在内存,而不是寄
存器中),编译后得到的结果是:
movl input, result
很明显这是一条非法指令,因此限制字符串必须与指令对操作数的要求匹配。例如指令movl
允许寄存器到寄存器,立即数到寄存器等,但是不允许内存到内存的操作,因此两个操作数
不能同时使用“m”作为限定字符。
2.3 语法
内嵌汇编语法如下:
__asm__(
汇编语句模板:
输出部分:
输入部分:
破坏描述部分)
共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格
开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,
也需要用“:”格开,相应部分内容为空。例如:
__asm__ __volatile__(
"cli":
:
:"memory")
2.3.1 汇编语句模板
汇编语句模板由汇编语句序列组成,语句之间使用“;”、“\n”或“\n\t”分开。
指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1…,%9。
指令中使用占位符表示的操作数,总被视为long型(4,个字节),但对其施加的操作
根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节。
对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,
“b”代表低字节,“h”代表高字节,例如:%h1。
2.3.2 输出部分
输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和
C语言变量组成。每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数。
例:
__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字符串表示对该变量的限制条件,这样GCC就可以根据这些条件决定如何
分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。
2.3.3 输入部分
输入部分描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由
限定字符串和C语言表达式或者C语言变量组成。
例1:
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例二(bitops.h):
Static __inline__ void __set_bit(int nr,
volatile void * addr)
{
__asm__(
"btsl%1,%0" :
"=m"(ADDR) :
"Ir"(nr));
}
后例功能是将(*addr)的第nr位设为1。第一个占位符%0与C,语言变量ADDR
对应,第二个占位符%1与C,语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:
btsl nr, ADDR,该指令的两个操作数不能全是内存变量,因此将nr的限定字符串指定为“Ir”,
将nr,与立即数或者寄存器相关联,这样两个操作数中只有ADDR为内存变量。
2.3.4 限制字符
2.3.4.1 限制字符列表
限制字符有很多种,有些是与特定体系结构相关,此处仅列出常用的限定字符和i386
中可能用到的一些常用的限定符。它们的作用是指示编译器如何处理其后的C
语言变量与指令操作数之间的关系,例如是将变量放在寄存器中还是放在内存中等,
下表列出了常用的限定字母。
分类
限定符 描述 通用寄存器
“a”将输入变量放入eax
这里有一个问题:假设eax已经被使用,那怎么办?
其实很简单:因为GCC知道eax已经被使用,它在这段汇编代码的起始处插入一条
语句pushl %eax,将eax内容保存到堆栈,然后在这段代码结束处再增加一条
语句popl %eax,恢复eax的内容
“b”将输入变量放入ebx
“c”将输入变量放入ecx
“d”将输入变量放入edx
“s”将输入变量放入esi
“d”将输入变量放入edi
“q”将输入变量放入eax,ebx ,ecx ,edx中的一个
“r”将输入变量放入通用寄存器,也就是eax ,ebx,ecx,edx,esi,edi中的一个
“A”把eax和edx,合成一个64位的寄存器(uselong longs)
“m”内存变量
“o”操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址寻址
“V”操作数为内存变量,但寻址方式不是偏移量类型
“,” 操作数为内存变量,但寻址方式为自动增量
“p”操作数是一个合法的内存地址(指针)
寄存器或内存
“g” 将输入变量放入eax,ebx,ecx ,edx中的一个或者作为内存变量
“X”操作数可以是任何类型
立即数
“I” 0-31 之间的立即数(用于32位移位指令)
“J” 0-63 之间的立即数(用于64 位移位指令)
“N” 0-255 ,之间的立即数(用于out 指令)
“i” 立即数
“n” 立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n”而不是“i”
匹配
“0”,“1 ,”... “9 ”
表示用它限制的操作数与某个指定的操作数匹配,也即该操作数就是指定的那个操作数,
例如用“0 ”去描述“%1”操作数,那么“%1”引用的其实就是“%0”操作数,注意作为
限定符字母的0-9 ,与指令中的“%0”-“%9”的区别,前者描述操作数,后者代表操作数。
后面有详细描述 & 该输出操作数不能使用过和输入操作数相同的寄存器
后面有详细描述
操作数类型
“=” 操作数在指令中是只写的(输出操作数)
“+” 操作数在指令中是读写类型的(输入输出操作数)
浮点数
“f”
浮点寄存器
“t”第一个浮点寄存器
“u”第二个浮点寄存器
“G”标准的80387
浮点常数
% 该操作数可以和下一个操作数交换位置
例如addl的两个操作数可以交换顺序(当然两个操作数都不能是立即数)
# 部分注释,从该字符到其后的逗号之间所有字母被忽略
* 表示如果选用寄存器,则其后的字母被忽略
现在继续看上面的例子,
"=m" (ADDR)表示ADDR为内存变量(“m”),而且是输出变量(“=”);"Ir" (nr)表示nr,为
0-31之间的立即数(“I”)或者一个寄存器操作数(“r”)。
2.3.4.2
匹配限制符
I386
指令集中许多指令的操作数是读写型的(读写型操作数指先读取原来的值然后参加运算,最后
将结果写回操作数),例如addl %1,%0,它的作用是将操作数%0与操作数%1的和存入操作数%0,
因此操作数%0是读写型操作数。老版本的GCC对这种类型操作数的支持不是很好,它将操作数严格
分为输入和输出两种,分别放在输入部分和输出部分,而没有一个单独部分描述读写型操作数,
因此在GCC中读写型的操作数需要在输入和输出部分分别描述,靠匹配限制符将两者关联到一起
注意仅在输入和输出部分使用相同的C变量,但是不用匹配限制符,产生的代码很可能不对,后
面会分析原因。
匹配限制符是一位数字:“0”、“1”……“9,”,分别表示它限制的C表达式分别与
占位符%0,%1,……%9对应的C变量匹配。例如使用“0”作为%1,的限制字符,那么
%0和%1表示同一个C,变量。
看一下下面的代码就知道为什么要将读写型操作数,分别在输入和输出部分加以描述。
该例功能是求input+result的和,然后存入result:
extern int input,result;
void test_at_t()
{
result= 0;
input = 1;
__asm__
__volatile__ ("addl %1,%0":"=r"(result): "r"(input));
}
对应的汇编代码为:
movl $0,_result
movl $1,_input
movl _input,%edx /APP
addl %edx,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
input 为输入型变量,而且需要放在寄存器中,GCC给它分配的寄存器是%edx,在执行addl之前%edx,
的内容已经是input的值。可见对于使用“r”限制的输入型变量或者表达式,在使用之前GCC会插入
必要的代码将他们的值读到寄存器;“m”型变量则不需要这一步。读入input后执行addl,显然%eax
的值不对,需要先读入result的值才行。再往后看:movl %eax,%edx和movl %edx,_result
的作用是将结果存回result,分配给result的寄存器与分配给input的一样,都是%edx。
综上可以总结出如下几点:
1. 使用“r”限制的输入变量,GCC先分配一个寄存器,然后将值读入寄存器,最后
用该寄存器替换占位符;
2. 使用“r”限制的输出变量,GCC会分配一个寄存器,然后用该寄存器替换占位符,
但是在使用该寄存器之前并不将变量值先读入寄存器,GCC认为所有输出变量以前的
值都没有用处,不读入寄存器(可能是因为AT&T汇编源于CISC架构处理器的汇编语言
,在CISC处理器中大部分指令的输入输出明显分开,而不像RISC那样一个操作数既
做输入又做输出,例如add r0,r1,r2,r0,和r1是输入,r2是输出,输入和输出分开,
没有使用输入输出型操作数,这样我们就可以认为r2对应的操作数原来的值没有用处,
也就没有必要先将操作数的值读入r2,因为这是浪费处理器的CPU周期),最后GCC插入代码,
将寄存器的值写回变量;
3. 输入变量使用的寄存器在最后一处使用它的指令之后,就可以挪做其他用处,因为
已经不再使用。例如上例中的%edx。在执行完addl之后就作为与result对应的寄存器。
因为第二条,上面的内嵌汇编指令不能奏效,因此需要在执行addl之前把result的值读入
寄存器,也许再将result放入输入部分就可以了(因为第一条会保证将result
先读入寄存器)。修改后的指令如下(为了更容易说明问题将input限制符由“r,”改为“m”):
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %2,%0":"=r"(result):"r"(result),"m"(input));
}
看上去上面的代码可以正常工作,因为我们知道%0和%1都和result相关,应该使用同一个
寄存器,但是GCC并不去判断%0和%1,是否和同一个C表达式或变量相关联(这样易于产生与
内嵌汇编相应的汇编代码),因此%0和%1使用的寄存器可能不同。我们看一下汇编代码就知道了。
movl $0,_result
movl $1,_input
movl _result,%edx /APP
addl _input,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
现在在执行addl之前将result的值被读入了寄存器%edx,但是addl指令的操作数%0
却成了%eax,而不是%edx,与预料的不同,这是因为GCC给输出和输入部分的变量分配了不同
的寄存器,GCC没有去判断两者是否都与result相关,后面会讲GCC如何翻译内嵌汇编,看完之后
就不会惊奇啦。
使用匹配限制符后,GCC知道应将对应的操作数放在同一个位置(同一个寄存器或者同一个
内存变量)。使用匹配限制字符的代码如下:
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %2,%0":"=r"(result):"0"(result),"m"(input));
}
输入部分中的result用匹配限制符“0”限制,表示%1与%0,代表同一个变量,
输入部分说明该变量的输入功能,输出部分说明该变量的输出功能,两者结合表示result
是读写型。因为%0和%1,表示同一个C变量,所以放在相同的位置,无论是寄存器还是内存。
相应的汇编代码为:
movl $0,_result
movl $1,_input
movl _result,%edx
movl %edx,%eax /APP
addl _input,%eax /NO_APP
movl %eax,%edx
movl %edx,_result
可以看到与result相关的寄存器是%edx,在执行指令addl之前先从%edx将result读入%eax,
执行之后需要将结果从%eax读入%edx,最后存入result中。这里我们可以看出GCC
处理内嵌汇编中输出操作数的一点点信息:addl并没有使用%edx,可见它不是简单的用result
对应的寄存器%edx去替换%0,而是先分配一个寄存器,执行运算,最后才将运算结果存入
对应的变量,因此GCC是先看该占位符对应的变量的限制符,发现是一个输出型寄存器变量,
就为它分配一个寄存器,此时没有去管对应的C变量,最后GCC,知道还要将寄存器的值写回变量,
与此同时,它发现该变量与%edx关联,因此先存入%edx,再存入变量。
至此读者应该明白了匹配限制符的意义和用法。在新版本的GCC中增加了一个限制字符“+”,
它表示操作数是读写型的,GCC知道应将变量值先读入寄存器,然后计算,最后写回变量,而
无需在输入部分再去描述该变量。
例;
extern int input,result;
void test_at_t()
{
result = 0;
input = 1;
__asm__
__volatile__ ("addl %1,%0":"+r"(result):"m"(input));
}
此处用“+”替换了“=”,而且去掉了输入部分关于result的描述,产生的汇编代码如下:
movl $0,_result
movl $1,_input
movl _result,%eax /APP
addl _input,%eax /NO_APP
movl %eax,_result
L2:
movl %ebp,%esp
处理的比使用匹配限制符的情况还要好,省去了好几条汇编代码。
2.3.4.3 “&”限制符
限制符“&”在内核中使用的比较多,它表示输入和输出操作数不能使用相同的寄存器,
这样可以避免很多错误。
举一个例子,下面代码的作用是将函数foo的返回值存入变量ret中:
__asm__ ( “call foo;movl %%edx,%1”, :”=a”(ret) : ”r”(bar) );
我们知道函数的int型返回值存放在%eax中,但是gcc编译的结果是输入和输出同时使用了
寄存器%eax,如下:
movl bar, %eax
#APP
call foo
movl %ebx,%eax
#NO_APP
movl %eax, ret
结果显然不对,原因是GCC并不知道%eax中的值是我们所要的。避免这种情况的方法是使用“&”
限定符,这样bar就不会再使用%eax寄存器,因为已被ret指定使用。
_asm__ ( “call foo;movl %%edx,%1”,:”=&a”(ret) : ”r”(bar) );
2.3.5 破坏描述部分
2.3.5.1 寄存器破坏描述符
通常编写程序只使用一种语言:高级语言或者汇编语言。高级语言编译的步骤大致如下:
l
预处理;
l
编译
l
汇编
l
链接
我们这里只关心第二步编译(将C代码转换成汇编代码):因为所有的代码都是用高级语言编写,
编译器可以识别各种语句的作用,在转换的过程中所有的寄存器都由编译器决定如何分配使用,
它有能力保证寄存器的使用不会冲突;也可以利用寄存器作为变量的缓冲区,因为寄存器的访问
速度比内存快很多倍。如果全部使用汇编语言则由程序员去控制寄存器的使用,只能靠程序员去
保证寄存器使用的正确性。但是如果两种语言混用情况就变复杂了,因为内嵌的汇编代码可以直接
使用寄存器,而编译器在转换的时候并不去检查内嵌的汇编代码使用了哪些寄存器(因为很难检测
汇编指令使用了哪些寄存器,例如有些指令隐式修改寄存器,有时内嵌的汇编代码会调用其他子过程,
而子过程也会修改寄存器),因此需要一种机制通知编译器我们使用了哪些寄存器(程序员自己知道
内嵌汇编代码中使用了哪些寄存器),否则对这些寄存器的使用就有可能导致错误,修改描述部分
可以起到这种作用。当然内嵌汇编的输入输出部分指明的寄存器或者指定为“r”,“g”型由编译器
去分配的寄存器就不需要在破坏描述部分去描述,因为编译器已经知道了。
破坏描述符由逗号格开的字符串组成,每个字符串描述一种情况,一般是寄存器名;除寄存器外
还有“memory”。例如:“%eax”,“%ebx”,“memory”等。
下面看个例子就很清楚为什么需要通知GCC内嵌汇编代码中隐式(称它为隐式是因为GCC并不知道)
使用的寄存器。
在内嵌的汇编指令中可能会直接引用某些寄存器,我们已经知道AT&T格式的汇编语言中,寄存器
名以“%”作为前缀,为了在生成的汇编程序中保留这个“%”号,在asm语句中对寄存器的
引用必须用“%%”作为寄存器名称的前缀。原因是“%”在asm,内嵌汇编语句中的作用与“\”在C
语言中的作用相同,因此“%%”转换后代表“%”。
例(没有使用修改描述符):
int main(void)
{
int input, output,temp;
input = 1;
__asm__ __volatile__ ("movl $0, %%eax;\n\t
movl %%eax, %1;\n\t
movl %2, %%eax;\n\t
movl %%eax, %0;\n\t"
:"=m"(output),"=m"(temp) /* output */
:"r"(input) /* input */
);
return 0;
}
这段代码使用%eax作为临时寄存器,功能相当于C代码:“temp = 0;output=input”,
对应的汇编代码如下:
movl $1,-4(%ebp)
movl -4(%ebp),%eax /APP
movl $0, %eax;
movl %eax, -12(%ebp);
movl %eax, %eax;
movl %eax, -8(%ebp); /NO_APP
显然GCC给input分配的寄存器也是%eax,发生了冲突,output的值始终为0,而不是input。
使用破坏描述后的代码:
int main(void)
{
int input, output,temp;
input = 1;
__asm__ __volatile__
( "movl $0, %%eax;\n\t
movl %%eax, %1;\n\t
movl %2, %%eax;\n\t
movl %%eax, %0;\n\t"
:"=m"(output),"=m"(temp) /* output */
:"r"(input) /* input */
:"eax"); /* 描述符 */
return 0;
}
对应的汇编代码:
movl $1,-4(%ebp)
movl -4(%ebp),%edx /APP
movl $0, %eax;
movl %eax, -12(%ebp);
movl %edx, %eax;
movl %eax, -8(%ebp); /NO_APP
通过破坏描述部分,GCC得知%eax已被使用,因此给input分配了%edx。在使用内嵌汇编时请记
住一点:尽量告诉GCC尽可能多的信息,以防出错。
如果你使用的指令会改变CPU的条件寄存器cc,需要在修改描述部分增加“cc”。
2.3.5.2 memory破坏描述符
“memory”比较特殊,可能是内嵌汇编中最难懂部分。为解释清楚它,先介绍一下编译器的
优化知识,再看C关键字volatile。最后去看该描述符。
2.3.5.2.1 编译器优化介绍
内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,
加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性
的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。
再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器
优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的
是重新排序读写指令。
对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux提供了一个宏解决编译器的执行顺序问题。
void Barrier(void)
这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU
寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。
2.3.5.2.2 C 语言关键字volatile
C 语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量
的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。
该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修
改,而程序通过该变量同步各个线程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal置为2,然后循环等待直到intSignal为1,时退出。显然intSignal
的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使
在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。记住,C
编译器是没有线程概念的!这时候就需要用到volatile。volatile的本意是指:这个值可能会在
当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile
关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作
的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
2.3.5.2.3 Memory
有了上面的知识就不难理解Memory
修改描述符了,Memory描述符告知GCC:
(1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,
它前面的指令都执行完毕。
(2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会
以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,
如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC本身却察觉不到,因为在输出部分没有描述,
此时就需要在修改描述部分增加“memory”,告诉GCC内存已经被修改,GCC得知这个信息后,
就会在这段指令之前,插入必要的指令将前面因为优化Cache到寄存器中的变量值先写回内存,
如果以后又要使用这些变量再重新读取。
例:
………..
Char test[100];
char a;
char c;
c = 0;
test[0] = 1;
……..
a = test [0];
……
__asm__(
"cld\n\t"
"rep\n\t"
"stosb"
: /* no output */
: "a" (c),"D" (test),"c" (100)
:
"cx","di","memory");
……….
// 我们知道test[0] 已经修改,所以重新读取
a=test[0];
……
这段代码中的汇编指令功能与
memset
相当,也就是相当于调用了memset(test,0,100);它使用stosb修改了test
数组的内容,但是没有在输入或输出部分去描述操作数,因为这两条指令都不需要
显式的指定操作数,因此需要增加“memory”通知GCC。现在假设:GCC在优化时将test[0]
放到了%eax寄存器,那么test[0] = 1对应于%eax=1,a = test [0]被换为a=%eax
,如果在那段汇编指令中不使用“memory”,Gcc,不知道现在test[0]
的值已经被改变了(如果整段代码都是我们自己使用汇编编写,我们自己当然知道
这些内存的修改情况,我们也可以人为的去优化,但是现在除了我们编写的那一小段外,
其他汇编代码都是GCC
生成的,它并没有那么智能,知道这段代码会修改test[0]),结果其后的a=test[0]
,转换为汇编后却是a=%eax,因为GCC不知道显式的改变了test数组,结果出错了。
如果增加了“memory”修饰符,GCC知道:
“这段代码修改了内存,但是也仅此而已,它并不知道到底修改了哪些变量”,
因此他将以前因优化而缓存到寄存器的变量值全部写回内存,从内嵌汇编开始,如果后面
的代码又要存取这些变量,则重新存取内存(不会将读写操作映射到以前缓存的那个寄存器)。
这样上面那段代码最后一句就不再是%eax=1,而是test[0] = 1。
这两条对实现临界区至关重要,第一条保证不会因为指令的重新排序将临界区内的代码调
到临界区之外(如果临界区内的指令被重排序放到临界区之外,What will happen?),
第二条保证在临界区访问的变量的值,肯定是最新的值,而不是缓存在
寄存器中的值,否则就会导致奇怪的错误。例如下面的代码:
int del_timer(struct timer_list * timer)
{
int
ret = 0;
if
(timer->next) {
unsigned
long flags;
struct
timer_list * next;
save_flags(flags);
cli();
// 临界区开始
if
((next = timer->next) != NULL) {
(next->prev = timer->prev)->next = next;
timer->next = timer->prev = NULL;
ret = 1;
} // 临界区结束
restore_flags(flags);
}
return
ret;
}
它先判断timer->next
的值,如果是空直接返回,无需进行下面的操作。如果不是空,则进入临界区进行操作,但是cli()
的实现(见下面)没有使用“memory”,timer->next的值可能会被缓存到寄存器中,
后面if ((next =timer->next) != NULL)会从寄存器中读取timer->next的值,如果
在if (timer->next)之后,进入临界区之前,timer->next的值可能被在外部改变,
这时肯定会出现异常情况,而且这种情况很难Debug。但是如果cli使用“memory”,
那么if ((next = timer->next) !=NULL)语句会重新从内存读取timer->next的值,而不会从寄存器
中取,这样就不会出现问题啦。
2.4 版内核中cli和sti的代码如下:
#define __cli()
__asm__
__volatile__("cli": : :"memory")
#define __sti()
__asm__
__volatile__("sti": : :"memory")
通过上面的例子,读者应该知道,为什么指令没有修改内存,但是却使用“memory
”修改描述符的原因了吧。应从指令的上下文去理解为什么要这样做。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,
不如使用“memory”方便。
2.4 GCC如何编译内嵌汇编代码
GCC 编译内嵌汇编代码的步骤如下:
1.输入变量与占位符
根据限定符和破坏描述部分,为输入和输出部分的变量分配合适的寄存器,如果限定符指定为立即数
(“i”),或内存变量(“m”),则不需要该步骤,如果限定符没有具体指定输入操作数的
类型(如“g”),GCC会视需要决定是否将该操作数输入到某个寄存器。这样每个占位符都与某个
寄存器、内存变量或立即数形成了一一对应的关系。对分配了寄存器的输入变量需要增加代码
将它的值读入寄存器。另外还要根据破坏描述符的部分增加额外代码。
2.指令模板部分
然后根据这种一一对应的关系,用这些寄存器、内存变量或立即数来取代汇编代码中的占位符。
3.变量输出
按照输出限定符的指定将寄存器的内容输出到某个内存变量中,如果输出操作数的限定符指定为内存变量(“m”),则该步骤被省略。
3 后记
该文档参照了Web上的许多与GCC内嵌汇编相关的文章编写而成,在此表示感谢,
如有问题请发Email至:chforest_chang@hotmail.com 一起讨论。
Labels:
asm
2007-10-16
ubuntu 升级至7.10后framebuffer console故障排除
症状:没有修改任何配置,升级后framebuffer console突然不工作了,纯控制台变成黑屏,手工加载fbcon, vesafb, nvidiafb(我的显卡是nvidia)也不行。
折腾了好半天,参考了一些网页,比如https://bugs.launchpad.net/ubuntu/+source/initramfs-tools/+bug/129910
了解了大致原因,一个是 fbcon不能自动加载,另外是framebuffer的驱动被加入了blacklist
得解决方案:
1.修改/etc/initramfs-tools/modules, 添加fbcon, vesafb(每个模块占一行)
2.修改/etc/modprobe.d/blacklist-framebuffer, 把含vesafb, nvidiafb的两行注释掉
3.确认grub里的启动参数中设置好了vga
问题解决。
注:第一步里似乎不用加上nvidiafb, 但是另外两个似乎要加上
折腾了好半天,参考了一些网页,比如https://bugs.launchpad.net/ubuntu/+source/initramfs-tools/+bug/129910
了解了大致原因,一个是 fbcon不能自动加载,另外是framebuffer的驱动被加入了blacklist
得解决方案:
1.修改/etc/initramfs-tools/modules, 添加fbcon, vesafb(每个模块占一行)
2.修改/etc/modprobe.d/blacklist-framebuffer, 把含vesafb, nvidiafb的两行注释掉
3.确认grub里的启动参数中设置好了vga
问题解决。
注:第一步里似乎不用加上nvidiafb, 但是另外两个似乎要加上
ubuntu 升级至7.10后网卡设备名自动改变的解决方法
症状是每次开机时会说invaid MAC address, 然后就会生成一个随机的MAC。
但是7.04时这个可以忽略。7.10里每次MAC一更改,网卡设备名就会更改,从eth0一直往上加。
查了一下,是udev这个东西搞的鬼。具体来说是/etc/udev/rules.d/75-persistent-net-generator.rules
我把一开始的GOTO="persistent_net_generator_do"该成了GOTO="persistent_net_generator_end",即直接跳过了生成新设备名的代码,问题得以解决。
但是7.04时这个可以忽略。7.10里每次MAC一更改,网卡设备名就会更改,从eth0一直往上加。
查了一下,是udev这个东西搞的鬼。具体来说是/etc/udev/rules.d/75-persistent-net-generator.rules
我把一开始的GOTO="persistent_net_generator_do"该成了GOTO="persistent_net_generator_end",即直接跳过了生成新设备名的代码,问题得以解决。
2007-10-15
ubuntu 升级至7.10后 vim-latexsuite 不工作
在https://bugs.launchpad.net/ubuntu/+source/vim-latexsuite/+bug/137205处找到了相关资料。
原因就是vim的runtimepath不包含/usr/share/vim/addons,但是vim-latexsuite默认是被装在那里的(不知道ubuntu7.04是如何配置的)。
解决办法,可以将/usr/share/vim/addons的内容拷至/usr/share/vim/vimfiles,也可以在.vimrc里加入set runtimepath+=/usr/share/vim/addons。
原因就是vim的runtimepath不包含/usr/share/vim/addons,但是vim-latexsuite默认是被装在那里的(不知道ubuntu7.04是如何配置的)。
解决办法,可以将/usr/share/vim/addons的内容拷至/usr/share/vim/vimfiles,也可以在.vimrc里加入set runtimepath+=/usr/share/vim/addons。
Labels:
linux
ubuntu 升级至7.10后无法登录
现象是输入密码的地方可以进入,但输入后X过一会儿就崩溃了,之后自动回到登录状态。
首先是要确定已装好显卡驱动,譬如我的nvidia显卡,更改内核后需要重新安装。
之后我把~/.gnome*和~/.config/autostart都改名后再进就正常了,然后试着该回来,又不行了。
后来折腾了几次,主要都是这几个文件夹,现在好了,发现新的gnome自启动好像不用~/.config/autostart了.
首先是要确定已装好显卡驱动,譬如我的nvidia显卡,更改内核后需要重新安装。
之后我把~/.gnome*和~/.config/autostart都改名后再进就正常了,然后试着该回来,又不行了。
后来折腾了几次,主要都是这几个文件夹,现在好了,发现新的gnome自启动好像不用~/.config/autostart了.
Labels:
linux
2007-10-12
Intel HD Audio 在 linux 下耳机插孔不工作的解决办法
症状是:使用hda-intel模块驱动声卡,扬声器工作正常,但插入耳机后耳机不出声,而扬声器仍发声。
参考了http://leufke.info/linux/asus/index.html, 可以尝试给snd-hda-intel模块加入position_fix=1的参数,一般来说是修改/etc/modprobe.d/options,加入options snd-hda-intel position_fix=1即可
参考了http://leufke.info/linux/asus/index.html, 可以尝试给snd-hda-intel模块加入position_fix=1的参数,一般来说是修改/etc/modprobe.d/options,加入options snd-hda-intel position_fix=1即可
游戏推荐: fish fillets
画面简单但关卡不简单的小游戏
关卡设计非常巧妙,到了后面有时我连看着攻略都过不去。
如果完全是自己研究出来的解法,过关后甚至有想哭的感觉:设计得太精妙了!
极力推荐!
现在版本叫fish fillets next generation.
关卡设计非常巧妙,到了后面有时我连看着攻略都过不去。
如果完全是自己研究出来的解法,过关后甚至有想哭的感觉:设计得太精妙了!
极力推荐!
现在版本叫fish fillets next generation.
2007-10-08
vaucanson-g: latex画自动机的宏包
最近作业要画自动机,找来找去发现这个vaucanson-g挺不错,只不过要注意根据它的文档所说,它只能生成 postscript指令,因此我这不能用pdflatex直接编译为pdf,而是要用pslatex编成dvi, 然后再依次转成ps,pdf才可以。
效果还不错,不过目前只用到英文,也没有插图,还没出现问题,不知道它对于中文和插图支持得如何。
效果还不错,不过目前只用到英文,也没有插图,还没出现问题,不知道它对于中文和插图支持得如何。
2007-10-05
wine Windows Media Player 10
参考:
http://feeds.feedburner.com/~r/winereview/~3/161431304/windows-media-player-9-10-on-linux-with.html
主要步骤是:
1.安装一些dll, 一般可以从windows目录拷来或下载,然后用regsvr32注册以及winecfg中native的设置。包括如下dll:
quartz.dll
devenum.dll
jscript.dll
MSCAT32.dll
2.安装Microsoft XML Parser
3.安装解码器,我用的K-Lite Mega Codec Pack
4.安装Windows Media Player 10
一开始几次都没弄好,主要是可能忘了注册某几个dll, 一般来说几次重装就没问题了。
根据引用的帖子所说,好像网络方面不能用,别的都没问题。
http://feeds.feedburner.com/~r/winereview/~3/161431304/windows-media-player-9-10-on-linux-with.html
主要步骤是:
1.安装一些dll, 一般可以从windows目录拷来或下载,然后用regsvr32注册以及winecfg中native的设置。包括如下dll:
quartz.dll
devenum.dll
jscript.dll
MSCAT32.dll
2.安装Microsoft XML Parser
3.安装解码器,我用的K-Lite Mega Codec Pack
4.安装Windows Media Player 10
一开始几次都没弄好,主要是可能忘了注册某几个dll, 一般来说几次重装就没问题了。
根据引用的帖子所说,好像网络方面不能用,别的都没问题。
wine qq
参考:http://linuxdesktop.cn/2007/04/13/step-by-step-install-qq2006-with-wine
我大概是按着这个帖子做的,但也不尽然。
主要应该还是弄来riched20.dll和riched32.dll, 可以从windows盘拷,也可以装一个。之后安装QQ,最后注意把TIMPlatform.exe改名基本就OK了。
原文说要把键盘加密那个sys也改名, 不过我这里没有找到。
还有就是一开始我用的是2007飘云版,但是虽然能正常登录及显示好友列表,而一进聊天窗口就卡住了。后来用的官方版本(也是2007)就好了。
最后就是我的wine没有打输入法补丁,因此用scim无法输入中文,不过问题不大。
我大概是按着这个帖子做的,但也不尽然。
主要应该还是弄来riched20.dll和riched32.dll, 可以从windows盘拷,也可以装一个。之后安装QQ,最后注意把TIMPlatform.exe改名基本就OK了。
原文说要把键盘加密那个sys也改名, 不过我这里没有找到。
还有就是一开始我用的是2007飘云版,但是虽然能正常登录及显示好友列表,而一进聊天窗口就卡住了。后来用的官方版本(也是2007)就好了。
最后就是我的wine没有打输入法补丁,因此用scim无法输入中文,不过问题不大。
关于在bochs的gdb-stub中加入对虚拟内存访问的支持
最近操统课需要用到bochs这个模拟器,我用的2.3.5. 但是实际出现了以个问题,就是gdb-stub这个功能不大正常,运行gdb后可以正常下断点,但是单步进可以,单步过却和单步进相同,这个问题不大比较恶心的是无法显示局部变量的值,总是显示0xffffffff
google了半天没看到一点有用的。。。。。唉,只好自己搞
参阅了gdb protocol后我修改了bochs的gdbstub.cc,让其显示出与gdb交互的内容,大致了解了内存这部分的步骤,主要是gdb给server(即bochs)发一个内存读取的请求,格式为m addr,length, 然后server返回十六进制的数据
在bochs的gdbstub.cc 558行为处理这个指令的,不难看懂,我修改了它,让它输出access_linear的返回值,然后再调试,查看局部变量,果然valid为0,进一步,到access_linear, 经过痛苦地多次调试,发现问题处在valid=BX_MEM(0)->dgb_fetch_mem...一行,最后进到或则个函数,在memory/misc_mem.cc 512行下看到了这部分,原来是bochs发现读取的地址越界,就用0xff填充,然后返回0, 我修改了0xff,然后用gdb调试时证实了这一点。
最后往上看,发现是addr比内存的长度还要大, 加入调试输出后发现gdb读的地址是0xf010
9fe0左右,而BX_MEM_THIS len仅有2000000,这显然会越界。
起初我考虑也许是那里关于内存大小的设置没有弄好,但没有找到具体位置,但后来发现其实那BX_MEM_THIS len 是在.bochsrc里有定义的,0x2000000=2^25=32k,而那个0xf0109fe0是内核做的虚拟内存,gdb只跟bochs打交道,而虚拟内存是内核弄的,虚拟机又管不着。因此好像没什么好办法。。。
之后我查了很多关于虚拟内存的资料,发现其实是cpu直接支持的,于是我看到了希望。仍是从bochs的源码开刀。
一开始我是把目光放在了gdt和ldt上,但是未果。最后又发现了它里面的CPU类有一个get_segment_base函数,几经尝试,找到了临时解决方法:
修改bochs的gdbstub.cc
将567行附近的
access_linear(addr,len,BX_READ,mem);
改成
access_linear((BX_CPU(0)->get_segment_base(BX_SEG_REG_CS))+addr,len,BX_READ,mem)
;
然后重新编译,记得是用gdb stub版本(./configure --enable-gdb-stub)
思路就是在gdb服务器端自动对虚拟内存作个映射。目前发现常用的关于断点,堆栈,数据,单步等相关命令都基本正常(不过好像有同学说w命令不好使,我没确认)。
不知道这个算不算bochs的一个bug, 跟它反映了一下,至今没有回应。
不过这还是第一次半系统地阅读了一个开源软件的代码,另外涉及到gdb协议,虚拟内存,cpu等知识,混在一起,最后还弄成功了,实在很过瘾。
google了半天没看到一点有用的。。。。。唉,只好自己搞
参阅了gdb protocol后我修改了bochs的gdbstub.cc,让其显示出与gdb交互的内容,大致了解了内存这部分的步骤,主要是gdb给server(即bochs)发一个内存读取的请求,格式为m addr,length, 然后server返回十六进制的数据
在bochs的gdbstub.cc 558行为处理这个指令的,不难看懂,我修改了它,让它输出access_linear的返回值,然后再调试,查看局部变量,果然valid为0,进一步,到access_linear, 经过痛苦地多次调试,发现问题处在valid=BX_MEM(0)->dgb_fetch_mem...一行,最后进到或则个函数,在memory/misc_mem.cc 512行下看到了这部分,原来是bochs发现读取的地址越界,就用0xff填充,然后返回0, 我修改了0xff,然后用gdb调试时证实了这一点。
最后往上看,发现是addr比内存的长度还要大, 加入调试输出后发现gdb读的地址是0xf010
9fe0左右,而BX_MEM_THIS len仅有2000000,这显然会越界。
起初我考虑也许是那里关于内存大小的设置没有弄好,但没有找到具体位置,但后来发现其实那BX_MEM_THIS len 是在.bochsrc里有定义的,0x2000000=2^25=32k,而那个0xf0109fe0是内核做的虚拟内存,gdb只跟bochs打交道,而虚拟内存是内核弄的,虚拟机又管不着。因此好像没什么好办法。。。
之后我查了很多关于虚拟内存的资料,发现其实是cpu直接支持的,于是我看到了希望。仍是从bochs的源码开刀。
一开始我是把目光放在了gdt和ldt上,但是未果。最后又发现了它里面的CPU类有一个get_segment_base函数,几经尝试,找到了临时解决方法:
修改bochs的gdbstub.cc
将567行附近的
access_linear(addr,len,BX_READ,mem);
改成
access_linear((BX_CPU(0)->get_segment_base(BX_SEG_REG_CS))+addr,len,BX_READ,mem)
;
然后重新编译,记得是用gdb stub版本(./configure --enable-gdb-stub)
思路就是在gdb服务器端自动对虚拟内存作个映射。目前发现常用的关于断点,堆栈,数据,单步等相关命令都基本正常(不过好像有同学说w命令不好使,我没确认)。
不知道这个算不算bochs的一个bug, 跟它反映了一下,至今没有回应。
不过这还是第一次半系统地阅读了一个开源软件的代码,另外涉及到gdb协议,虚拟内存,cpu等知识,混在一起,最后还弄成功了,实在很过瘾。
Labels:
code
2007-09-28
mldonkey+sancho试用
参考http://forum.ubuntu.org.cn/about42337.html
http://www.haijd.net/doc/read-29.html
http://sparkplugcn.wordpress.com/2007/08/15/%e4%bb%8eemule%e5%88%b0mldonkey/
很多地方说mldonkey比amule如何如何好,而我觉得 amule本身就挺好,下完了最近几个文件后想尝试一下mldonkey
首先我是想编译一个不带 gui的 mlnet, 按照它代码里的readme, 只需简单地./configure然后make就行了,但是我这里却说需要 ocaml (readme里似乎是说只有 gui版本才需要),一开始让它自动下载编译ocaml, 但很慢,于是放弃,改用二进制包。
之后是前端,推荐的是sancho, 我下了个gtk版本的。
再之后按着帖子里的大概弄弄,配置些关于网络的东西,然后再用mldonkey release里带的插件把firefox和mldonkey关联上,需要强调的几点是
1.导入文件似乎只能下载后再本地导入
2.以下命令可以用sachon的console输入,好像也可以用telnet连接mlnet输入
2.用servers命令导入服务器,推荐下载http://www.emule.org.cn/server.met
3.用ov_load命令导入overnet的node列表,推荐下载http://download.overnet.org/contact.dat
4.用kad_load命令导入kad的node列表,推荐下载http://www.emule-inside.net/nodes.dat,也可使用eMule的nodes.data
5.注意把kad和overnet的选项打开,我一开始就忘了。。
6.注意修改最大下载限速,默认是50,太小了
http://www.haijd.net/doc/read-29.html
http://sparkplugcn.wordpress.com/2007/08/15/%e4%bb%8eemule%e5%88%b0mldonkey/
很多地方说mldonkey比amule如何如何好,而我觉得 amule本身就挺好,下完了最近几个文件后想尝试一下mldonkey
首先我是想编译一个不带 gui的 mlnet, 按照它代码里的readme, 只需简单地./configure然后make就行了,但是我这里却说需要 ocaml (readme里似乎是说只有 gui版本才需要),一开始让它自动下载编译ocaml, 但很慢,于是放弃,改用二进制包。
之后是前端,推荐的是sancho, 我下了个gtk版本的。
再之后按着帖子里的大概弄弄,配置些关于网络的东西,然后再用mldonkey release里带的插件把firefox和mldonkey关联上,需要强调的几点是
1.导入文件似乎只能下载后再本地导入
2.以下命令可以用sachon的console输入,好像也可以用telnet连接mlnet输入
2.用servers命令导入服务器,推荐下载http://www.emule.org.cn/server.met
3.用ov_load命令导入overnet的node列表,推荐下载http://download.overnet.org/contact.dat
4.用kad_load命令导入kad的node列表,推荐下载http://www.emule-inside.net/nodes.dat,也可使用eMule的nodes.data
5.注意把kad和overnet的选项打开,我一开始就忘了。。
6.注意修改最大下载限速,默认是50,太小了
感觉上如果设好了确实挺快,但也没有传说中的那么快,当然可能跟我这里网络环境有关。但是它可以连接多个服务器确实是个创新的想法
2007-09-23
linux 旋转桌面
只需在 xorg.conf里给驱动程序加上
Option "RandRRotation" "on"
然后用xrandr命令即可
Option "RandRRotation" "on"
然后用xrandr命令即可
Labels:
linux
ubuntu下pdf打印机
是cups-pdf包,装了就会多一个打印机,用法跟windows下acrobat那个类似.
你还别说,效果真不错。
你还别说,效果真不错。
firefox 中 设置 ed2k与amule的关联
参考:http://forum.ubuntu.org.cn/weblog.php?w=646&previous=10
安装 amule-utils包
然后在firefox中利用about:config添加如下两项
新建一个Boolean,名字为network.protocol-handler.external.ed2k,值为true
新建一个String,名字为network.protocol-handler.app.ed2k,值为/usr/bin/ed2k
安装 amule-utils包
然后在firefox中利用about:config添加如下两项
新建一个Boolean,名字为network.protocol-handler.external.ed2k,值为true
新建一个String,名字为network.protocol-handler.app.ed2k,值为/usr/bin/ed2k
2007-09-21
gedit 查看 gbk 编码文件
gedit默认不能正常查看gbk编码的文件, 原因是它第一个尝试utf-8
解决办法是用gconf修改/apps/gedit-2/preferences/encodings/auto_detected,在最前面加入一个gbk即可。
解决办法是用gconf修改/apps/gedit-2/preferences/encodings/auto_detected,在最前面加入一个gbk即可。
[转] Python的转码
字符串内码的转换,是开发中经常遇到的问题。
在Java中,我们可以先对某个String调用getByte(),由结果生成新String的办法来转码,也可以用NIO包里面的Charset来实现。
在Python中,可以对String调用decode和encode方法来实现转码。
比如,若要将某个String对象s从gbk内码转换为UTF-8,可以如下操作
s.decode('gbk').encode('utf-8')
可是,在实际开发中,我发现,这种办法经常会出现异常:
UnicodeDecodeError: 'gbk' codec can't decode bytes in position 30664-30665: illegal multibyte sequence
这 是因为遇到了非法字符——尤其是在某些用C/C++编写的程序中,全角空格往往有多种不同的实现方式,比如\xa3\xa0,或者\xa4\x57,这些 字符,看起来都是全角空格,但它们并不是“合法”的全角空格(真正的全角空格是\xa1\xa1),因此在转码的过程中出现了异常。
这样的问题很让人头疼,因为只要字符串中出现了一个非法字符,整个字符串——有时候,就是整篇文章——就都无法转码。
幸运的是,tiny找到了完美的解决办法(我因此被批评看文档不仔细,汗啊……)
s.decode('gbk', 'ignore').encode('utf-8')
因为decode的函数原型是decode([encoding], [errors='strict']),可以用第二个参数控制错误处理的策略,默认的参数就是strict,代表遇到非法字符时抛出异常;
如果设置为ignore,则会忽略非法字符;
如果设置为replace,则会用?取代非法字符;
如果设置为xmlcharrefreplace,则使用XML的字符引用。
在Java中,我们可以先对某个String调用getByte(),由结果生成新String的办法来转码,也可以用NIO包里面的Charset来实现。
在Python中,可以对String调用decode和encode方法来实现转码。
比如,若要将某个String对象s从gbk内码转换为UTF-8,可以如下操作
s.decode('gbk').encode('utf-8')
可是,在实际开发中,我发现,这种办法经常会出现异常:
UnicodeDecodeError: 'gbk' codec can't decode bytes in position 30664-30665: illegal multibyte sequence
这 是因为遇到了非法字符——尤其是在某些用C/C++编写的程序中,全角空格往往有多种不同的实现方式,比如\xa3\xa0,或者\xa4\x57,这些 字符,看起来都是全角空格,但它们并不是“合法”的全角空格(真正的全角空格是\xa1\xa1),因此在转码的过程中出现了异常。
这样的问题很让人头疼,因为只要字符串中出现了一个非法字符,整个字符串——有时候,就是整篇文章——就都无法转码。
幸运的是,tiny找到了完美的解决办法(我因此被批评看文档不仔细,汗啊……)
s.decode('gbk', 'ignore').encode('utf-8')
因为decode的函数原型是decode([encoding], [errors='strict']),可以用第二个参数控制错误处理的策略,默认的参数就是strict,代表遇到非法字符时抛出异常;
如果设置为ignore,则会忽略非法字符;
如果设置为replace,则会用?取代非法字符;
如果设置为xmlcharrefreplace,则使用XML的字符引用。
Labels:
python
2007-09-20
rainlendar2.2无法运行的解决
症状:总是说cannot touch ~/.rainlendar2/rainlendar2.ini, 手工touch 也不行
原因:scim与之冲突
解决:安装scim-bridge代替scim, 具体是安装后修改/etc/X11/xinit/xinput.d/scim, 将GTK_IM_MODULE改为scim-bridge即可
原因:scim与之冲突
解决:安装scim-bridge代替scim, 具体是安装后修改/etc/X11/xinit/xinput.d/scim, 将GTK_IM_MODULE改为scim-bridge即可
2007-09-19
Amarok 不出声音
今天尝试Amarok,是以前非常喜欢的播放器。
但是就是不出声音, 还弹出一个No MP3 Support为标题的对话框。
确定插件安装全后,开始研究它的选项, 最后发现是在Settings--Configure Amarok--Engine--Configure xine Engine--Output plugin处为Autodetect,怀疑是此处问题。
改成alsa后问题解决。
但是就是不出声音, 还弹出一个No MP3 Support为标题的对话框。
确定插件安装全后,开始研究它的选项, 最后发现是在Settings--Configure Amarok--Engine--Configure xine Engine--Output plugin处为Autodetect,怀疑是此处问题。
改成alsa后问题解决。
2007-09-18
wine 程序菜单乱码的解决
我原来的locale 除了LC_CTYPE是zh_CN.UTF-8外,其余均为en_US.UTF-8, 但是这时wine的程序的菜单中文都不能正常显示,而程序内部有些中文则能够正常显示
解决方法是设置LC_ALL为zh_CN.UTF-8, 当然可能只改几个就行,但是没有进一步尝试。
解决方法是设置LC_ALL为zh_CN.UTF-8, 当然可能只改几个就行,但是没有进一步尝试。
2007-09-16
禁止自动运行的注册表项
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"NoDriveTypeAutoRun"=dword:000000B5
[HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"NoDriveTypeAutoRun"=dword:000000B5
"NoDriveTypeAutoRun"=dword:000000B5
[HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"NoDriveTypeAutoRun"=dword:000000B5
Windows XP 设置 NTFS 文件权限
一般来说直接点右键, 有个“安全”标签,就是了, 如果说不能改, 那么应该点“高级”, 然后去掉“从父项继承那些可以应用到子对象的权限项目, 包括那些在此明确定义的项目”
如果连“安全“标签”都没有,那么应该在资源管理器中选工具--文件夹选项--查看, 然后去掉“使用简单文件共享”。
如果连“安全“标签”都没有,那么应该在资源管理器中选工具--文件夹选项--查看, 然后去掉“使用简单文件共享”。
2007-09-13
再谈声卡识别但不出声的问题
上次在一个关于声卡识别的问题提到了一个关于声卡在设备管理器中能够识别, 但是声音选项中缺说找不到音频设备的问题。
我转的解法是用Plug and Play Software Device Enumerator替换一个已有设备ISAPNP Read Data Port。起初我认为是识别有误,
但后来感到有些不对劲, 觉得那本身是一个正常的系统设备。 虽然能正常工作, 但毕竟不是完美解决方案。 后来再次尝试了上次
也提到的重装Plug and Play Software Device Enumerator的方法:
1.假设windows装在c:\windows下, 将c:\windows\inf\machine.inf, c:\windows\system32\streamci.dll, c:\windows\system32
\drivers\swenum.sys拷到一个临时目录下
2.修改machine.inf,找到[ControlFlags], 把下面一行ExcludeFromSelect=*删去
3.如果现在设备管理器中有Plug and Play Software Device Enumerator, 那么用现在的临时目录给它更新驱动即可,否则依次进
入控制面板->添加硬件, 几次"下一步"后依次选"添加新的硬件设备","安装我手动从列表选择的硬件","显示所有设备","从磁盘
安装", 此时选那个目录, 然后厂商选"(标准系统设备)", 型号选"Plug and Play Software Device Enumerator", 然后安装便可
。
最后还查到一个解法, 是说在注册表中删除所有swenum的相关项, 然后就能自动识别Plug and Play Software Device Enumerator
了, 不过这个没试过, 不知是否真的可行。
我转的解法是用Plug and Play Software Device Enumerator替换一个已有设备ISAPNP Read Data Port。起初我认为是识别有误,
但后来感到有些不对劲, 觉得那本身是一个正常的系统设备。 虽然能正常工作, 但毕竟不是完美解决方案。 后来再次尝试了上次
也提到的重装Plug and Play Software Device Enumerator的方法:
1.假设windows装在c:\windows下, 将c:\windows\inf\machine.inf, c:\windows\system32\streamci.dll, c:\windows\system32
\drivers\swenum.sys拷到一个临时目录下
2.修改machine.inf,找到[ControlFlags], 把下面一行ExcludeFromSelect=*删去
3.如果现在设备管理器中有Plug and Play Software Device Enumerator, 那么用现在的临时目录给它更新驱动即可,否则依次进
入控制面板->添加硬件, 几次"下一步"后依次选"添加新的硬件设备","安装我手动从列表选择的硬件","显示所有设备","从磁盘
安装", 此时选那个目录, 然后厂商选"(标准系统设备)", 型号选"Plug and Play Software Device Enumerator", 然后安装便可
。
最后还查到一个解法, 是说在注册表中删除所有swenum的相关项, 然后就能自动识别Plug and Play Software Device Enumerator
了, 不过这个没试过, 不知是否真的可行。
2007-09-12
一个关于声卡识别的问题
环境为Windows XP SP2, 声卡realtek hd audio, 似乎是nvidia的主板
症状比较特别, 是偶然卸了某驱动后发现很多硬件不能用了, 打开设备管理器发现dvd-rom, 声卡等都有黄色叹号, 另外还有一个"Plug and Play Software Device Enumerator" 也是这样。
一开始想用"卸载-重新识别"的方法, 但是那个"Plug and Play Software Device Enumerator"卸掉便一去不复返了。 一开始我没在意, 全心折腾声卡, 但是总是不行, 后来在网上艰难搜索才发现问题就出现在那个"Plug and Play Software Device Enumerator"上。
网上有个从系统目录copy出inf, sys, dll个一个文件, 稍微修改并利用它们安装一个这个硬件的方法。 我没试成功(后来发现是没理解好说明)。 之后在
http://www.softwaretipsandtricks.com/forum/windows-xp/7021-no-sound-says-no-audio-device-but-drivers-there-enabled-working-properly-5.html找到了一种解法, 原文加翻译如下:
1. From Device Manager (be sure to select View/Show hidden devices), find the 'ISAPNP Read Data Port' device from the System Devices list. (Of note: once I successfully repaired this problem, this device no longer appears.)
在设备管理器中(注意选上查看->显示隐藏的设备。(译注:似乎不选也可))找到ISAPNP Read Data Port这个设备(当成功进行下面操作后, 这个设备将不再存在(译注:成功后会由Plug and Play Software Device Enumerator取而代之, 也就是说, 问题应该是对这个硬件的识别问题))
2. Right click the ISAPNP device, and select "Update Driver"
右击那个设备,选择"更新驱动程序"
3. Select "Install from a list or specific location"
选择"从列表或指定位置安装"
4. Select "Don't search. I will choose the driver to install."
选择"不要搜索, 我要自己选择要安装的驱动程序"
5. The step I left out on prior trials: UNSELECT "Show Compatible Hardware"
不要选中"显示兼容硬件"
6. Be sure that "(Standard System Devices)" is selected under Manufacturer.
厂商选择"(标准系统设备)"
7. Under Model, scroll down until you see "Plug and Play Software Device Enumerator". Select this item, and select "Next>".
设备选Plug and Play Software Device Enumerator, 单击下一步
8. You will receive some frightening error message that you should probably disregard.
你将会看到一个警告框, 忽略她。
9. Complete the installation of the driver.
完成安装。
经过实验, 这个方法确实可行, 至少是对我的机器配置。
症状比较特别, 是偶然卸了某驱动后发现很多硬件不能用了, 打开设备管理器发现dvd-rom, 声卡等都有黄色叹号, 另外还有一个"Plug and Play Software Device Enumerator" 也是这样。
一开始想用"卸载-重新识别"的方法, 但是那个"Plug and Play Software Device Enumerator"卸掉便一去不复返了。 一开始我没在意, 全心折腾声卡, 但是总是不行, 后来在网上艰难搜索才发现问题就出现在那个"Plug and Play Software Device Enumerator"上。
网上有个从系统目录copy出inf, sys, dll个一个文件, 稍微修改并利用它们安装一个这个硬件的方法。 我没试成功(后来发现是没理解好说明)。 之后在
http://www.softwaretipsandtricks.com/forum/windows-xp/7021-no-sound-says-no-audio-device-but-drivers-there-enabled-working-properly-5.html找到了一种解法, 原文加翻译如下:
1. From Device Manager (be sure to select View/Show hidden devices), find the 'ISAPNP Read Data Port' device from the System Devices list. (Of note: once I successfully repaired this problem, this device no longer appears.)
在设备管理器中(注意选上查看->显示隐藏的设备。(译注:似乎不选也可))找到ISAPNP Read Data Port这个设备(当成功进行下面操作后, 这个设备将不再存在(译注:成功后会由Plug and Play Software Device Enumerator取而代之, 也就是说, 问题应该是对这个硬件的识别问题))
2. Right click the ISAPNP device, and select "Update Driver"
右击那个设备,选择"更新驱动程序"
3. Select "Install from a list or specific location"
选择"从列表或指定位置安装"
4. Select "Don't search. I will choose the driver to install."
选择"不要搜索, 我要自己选择要安装的驱动程序"
5. The step I left out on prior trials: UNSELECT "Show Compatible Hardware"
不要选中"显示兼容硬件"
6. Be sure that "(Standard System Devices)" is selected under Manufacturer.
厂商选择"(标准系统设备)"
7. Under Model, scroll down until you see "Plug and Play Software Device Enumerator". Select this item, and select "Next>".
设备选Plug and Play Software Device Enumerator, 单击下一步
8. You will receive some frightening error message that you should probably disregard.
你将会看到一个警告框, 忽略她。
9. Complete the installation of the driver.
完成安装。
经过实验, 这个方法确实可行, 至少是对我的机器配置。
2007-09-09
跳过linux开机磁盘检查
linux挂载windows分区前总要扫描一下, 我觉得不大必要, 一直想跳过但不知怎么设置。
今天偶然看到了解法, 只需修改/etc/fstab, 把对应行的一列的值改为0即可
今天偶然看到了解法, 只需修改/etc/fstab, 把对应行的
Labels:
linux
初尝 64位 linux kernel
机器是athlon64的, 不跑跑64bit linux实在不过瘾, 于是下了个linux kernel 2.6.22.5自己编译看, 一开始下载是想编译32位的, 但是启动时有点小问题, 中间会卡住, 要不断按开机键才能继续, 挺诡异的, 但是64位的编译好后竟然没有这个问题了, 比较有趣。
在编译前进行配置时倒是出了些问题, 由于我要在32位系统下编译64位内核, 是交叉编译, 不是很熟练, 查了半天在发现应该在make是加入ARCH=x86_64的参数。(ubuntu里习惯称amd64, 但linux内核里叫x86_64)
另外configure的时候也有问题, 我直接make ARCH=x86_64总报错, make ARCH=x86_64 config也不行, 后来想到, 现在的.config是按i386配置的, 可能跟64位的有冲突, 于是用make ARCH=x86_64 oldconfig重新配置, 别说, 还真问了几个新问题, 之后再make ARCH=x86_64就可以编译了, 最后别忘了编译模块,再都安装就好了。
现在运行挺不错, 但是aptitude不能找到64位程序, 还有待研究。
在编译前进行配置时倒是出了些问题, 由于我要在32位系统下编译64位内核, 是交叉编译, 不是很熟练, 查了半天在发现应该在make是加入ARCH=x86_64的参数。(ubuntu里习惯称amd64, 但linux内核里叫x86_64)
另外configure的时候也有问题, 我直接make ARCH=x86_64总报错, make ARCH=x86_64 config也不行, 后来想到, 现在的.config是按i386配置的, 可能跟64位的有冲突, 于是用make ARCH=x86_64 oldconfig重新配置, 别说, 还真问了几个新问题, 之后再make ARCH=x86_64就可以编译了, 最后别忘了编译模块,再都安装就好了。
现在运行挺不错, 但是aptitude不能找到64位程序, 还有待研究。
Labels:
linux
2007-09-03
dojo学习笔记 (一)
最近几天开始学习dojo 0.9.0, 主要是看它的源码和测试样例, 以下是这几天的研究成果
一、 dojo 概述
我用的是dojo 0.9.0, 下载压缩包解压后有四个文件夹, 依次为:
dijit: 提供了很多ui控件, 被称为widget。
dojo: 提供了很多跨浏览器的实用函数,
dojox: 似乎是提供了一些插件性质的东西, 我只对dojox.flash有点了解
util:里面只有个doh, 功能不明
二、 dojo库的使用
首要的是在html里导入dojo.js文件, dojo加载支持很多参数, 其中我目前用到的有parseOnLoad和isDebug. 设定参数的方法有两种, 一种是在导入dojo.js前通过javascript建立一个对象djConfig,包含要设定的键值对,如djConfig={isDebug:true}; 另一种是在<script>标签里加入djConfig属性,值为JSON语法的键值对, 如<script djConfig="isDebug:true" src="dojo.js"></script>
一开始我对于后一种方法感到很神奇, 起初我以为javascript可以获得自己所在标签的信息, 后来看dojo.js, 查找djConfig, 发现它一上来就判断djConfig是否存在, 否则就建立新值,这时我认为djConfig标签可以作为一个对象在javascript中可以访问, 但亲自尝试后发现并不行。于是我甚至认为这是dojo的一个bug, 但在随后的研究中我发现它确实起作用,于是不得不再认真得看dojo.js(当然是dojo.js.uncompressed.js), 发现原来它会遍历所有script标签,并对导入dojo的标签的djConfig进行处理, 实际上就是加上一对括号然后eval一下。 此时我才恍然大悟。
再说说那两个参数, isDebug不必多说, parseOnLoad是指定dojo.js是否在html加载完毕后进行扫描,识别通过标记方法声明的对象(比如dijit的widget就都支持这种声明, 十分方便)如<div dojoType=dijit.form.Menu> </div>
三、dojo库的几个常用函数
1.dojo.connect(source_obj, event, func)或dojo.connect(source_obj, event, target_obj, target_method)
这个函数相当于把自定义的函数绑在source_obj.event上, 当此事件发生(即这个函数被调用)后,便会调用source_obj.func或target_obj.target_method, 而传进去的参数跟source_obj.event得到的参数相同。
这个函数十分方便, 也是我最早听说的几个dojo实用函数之一, 但关于其实现方法, 我是看了源码才明白。
2.dojo.provide(package_name)
这个是声明当前js提供的包名, 与dojo.require配合使用
3.dojo.require(package_name)
这个指定导入一个包, 这也是十分有用的, 一个是它非常智能, package_name一般为A.B.C的格式,我们暂且称A为bass_name, 它会根据A找到基准目录, 默认是按dojo.js所在路径加上../A,不过也可以用dojo.registerModulePath注册自定义的路径(dojo已内置了一些包的路径, 如dojo, doh)。有了基准路径后, 它会首先尝试B/C.js, 看是否成功, 否则再试B.js,如果有多极, 就逐层往上退。 如果一直找不到就报错。
另一个有用的地方是它可以实现动态导入和避免重复导入, 这一方面非常适合动态的Ajax, 又保证了性能。
4.dojo.declare(class_name, base_class, properties)
dojo提供的一个OOP机制, 而这个是声明一个新类, 举例说明:
dojo.declare("son", father, {
constructor:function(){
alert("hi");
}
});
这样就声明了一个名为son的类, 父类为father, 以后可以用 new son()来创建一个实例
另外base_class可以为null或数组, 后者时,里面必须全为function对象, 以后第一个元素为新类的父类, 其他类只是为了继承其方法。
关于继承, 首先constructor是构造函数, 不会覆盖父类的构造函数, 并且父类的构造函数会被自动调用, 而其他函数则会覆盖父类的。
此外, properties中也可以定义成员变量。
5.dojo.body(), dojo.doc
前者返回body的DOM node, 后者是当前document对象, 一般就是当前的document, 不过由于可以设置整体的上下文, 如dojo.setContext, 所以在dojo框架下应该尽量使用dojo.doc
6.dojo.byId(id_string)
应该比document.getElementById有所增强, 但无论如何, 精致的名字也是诱人之处。
当然还有很多其他函数, 比如dojo.io.bind。 只是我现在还没看到网络通信部分, 等下次再补上吧。
四、dijit库的使用
dijit库的函数我主要是用的dijit.byId, 注意他和dojo.byId的不同, 这里是根据id返回已经生成的dijit的widget对象, 并不是DOM node
dijit提供的控件主要有两种生成方法, 一种是用javascript直接生成, 如new dojo.form.Button(null, node), 另一种是在html中作标记, 如<button dojoType=dijit.form.Button label="button"></button>
关于其生成widget的机制, 我目前所知是它从原始DOM节点中获取信息, 如id, label, dojoType之类, 然后据此生成一个widget, 它本身应该是绑定了一个自己的DOM节点, 最后再将它替换原始DOM节点。
根据dijit.js,在创建一个widget过程中会调用如下几个函数
postMixInProperties: 刚刚从原始节点读入信息后调用, 如果扩展已有widget,可通过connect此函数来为widget对象添加新的属性(每个widget只从原始DOM节点(可用this.srcNodeRef得到其引用)中获取需要的节点, 其他的一概抛弃, 如果需要扩展, 要在销毁前得到它,而这里所列的几个函数中,仅有此函数执行时srcNodeRef尚未背销毁)
buildingRendering:刚刚为当前widget创建好DOM节点时调用
postCreate: 当前widget已创建好并放入UI后调用
startup: 所有widget都创建好后依次为每个widget调用此函数
尽管dijit提供了非常丰富的控件, 但也未必就能满足全部需要。 因此有时需要在现有控件基础上进行扩展。这时上面三个函数就显得非常有用。 不过需要注意的是, 用dojo.declare声明一个子类时, 不能直接重定义上面三个函数,否则父类的方法会被覆盖, 而且dojo似乎没有提供super之类的属性。 我的解决方法是用dojo.declare声明时仅添加新函数,而之后再用dojo.connect对已有函数进行扩展。 如果用此方法, 需要注意, 如果新类叫childClass, 那么应该connect childClass.prototype的方法, 我一开始忘了这一点, 结果怎么都不起作用。
五、其他
dojo与Firebug支持得非常好, 它能自动识别Firebug并与其交互, 再配合Firebug的强大功能, 开发javascript 轻松了很多
六、总结
通过几天的学习, 我对dojo库的基本思想和基本实现方法都有了些了解, 也十分欣赏和钦佩它的结构。它充分利用了javascript的灵活性, 创造了很多异想天开般的设计, 令我大开眼界
就先写这么多, 能些的也就是这么多了。 随着学习的深入, 我会再补充这个学习笔记的。
一、 dojo 概述
我用的是dojo 0.9.0, 下载压缩包解压后有四个文件夹, 依次为:
dijit: 提供了很多ui控件, 被称为widget。
dojo: 提供了很多跨浏览器的实用函数,
dojox: 似乎是提供了一些插件性质的东西, 我只对dojox.flash有点了解
util:里面只有个doh, 功能不明
二、 dojo库的使用
首要的是在html里导入dojo.js文件, dojo加载支持很多参数, 其中我目前用到的有parseOnLoad和isDebug. 设定参数的方法有两种, 一种是在导入dojo.js前通过javascript建立一个对象djConfig,包含要设定的键值对,如djConfig={isDebug:true}; 另一种是在<script>标签里加入djConfig属性,值为JSON语法的键值对, 如<script djConfig="isDebug:true" src="dojo.js"></script>
一开始我对于后一种方法感到很神奇, 起初我以为javascript可以获得自己所在标签的信息, 后来看dojo.js, 查找djConfig, 发现它一上来就判断djConfig是否存在, 否则就建立新值,这时我认为djConfig标签可以作为一个对象在javascript中可以访问, 但亲自尝试后发现并不行。于是我甚至认为这是dojo的一个bug, 但在随后的研究中我发现它确实起作用,于是不得不再认真得看dojo.js(当然是dojo.js.uncompressed.js), 发现原来它会遍历所有script标签,并对导入dojo的标签的djConfig进行处理, 实际上就是加上一对括号然后eval一下。 此时我才恍然大悟。
再说说那两个参数, isDebug不必多说, parseOnLoad是指定dojo.js是否在html加载完毕后进行扫描,识别通过标记方法声明的对象(比如dijit的widget就都支持这种声明, 十分方便)如<div dojoType=dijit.form.Menu> </div>
三、dojo库的几个常用函数
1.dojo.connect(source_obj, event, func)或dojo.connect(source_obj, event, target_obj, target_method)
这个函数相当于把自定义的函数绑在source_obj.event上, 当此事件发生(即这个函数被调用)后,便会调用source_obj.func或target_obj.target_method, 而传进去的参数跟source_obj.event得到的参数相同。
这个函数十分方便, 也是我最早听说的几个dojo实用函数之一, 但关于其实现方法, 我是看了源码才明白。
2.dojo.provide(package_name)
这个是声明当前js提供的包名, 与dojo.require配合使用
3.dojo.require(package_name)
这个指定导入一个包, 这也是十分有用的, 一个是它非常智能, package_name一般为A.B.C的格式,我们暂且称A为bass_name, 它会根据A找到基准目录, 默认是按dojo.js所在路径加上../A,不过也可以用dojo.registerModulePath注册自定义的路径(dojo已内置了一些包的路径, 如dojo, doh)。有了基准路径后, 它会首先尝试B/C.js, 看是否成功, 否则再试B.js,如果有多极, 就逐层往上退。 如果一直找不到就报错。
另一个有用的地方是它可以实现动态导入和避免重复导入, 这一方面非常适合动态的Ajax, 又保证了性能。
4.dojo.declare(class_name, base_class, properties)
dojo提供的一个OOP机制, 而这个是声明一个新类, 举例说明:
dojo.declare("son", father, {
constructor:function(){
alert("hi");
}
});
这样就声明了一个名为son的类, 父类为father, 以后可以用 new son()来创建一个实例
另外base_class可以为null或数组, 后者时,里面必须全为function对象, 以后第一个元素为新类的父类, 其他类只是为了继承其方法。
关于继承, 首先constructor是构造函数, 不会覆盖父类的构造函数, 并且父类的构造函数会被自动调用, 而其他函数则会覆盖父类的。
此外, properties中也可以定义成员变量。
5.dojo.body(), dojo.doc
前者返回body的DOM node, 后者是当前document对象, 一般就是当前的document, 不过由于可以设置整体的上下文, 如dojo.setContext, 所以在dojo框架下应该尽量使用dojo.doc
6.dojo.byId(id_string)
应该比document.getElementById有所增强, 但无论如何, 精致的名字也是诱人之处。
当然还有很多其他函数, 比如dojo.io.bind。 只是我现在还没看到网络通信部分, 等下次再补上吧。
四、dijit库的使用
dijit库的函数我主要是用的dijit.byId, 注意他和dojo.byId的不同, 这里是根据id返回已经生成的dijit的widget对象, 并不是DOM node
dijit提供的控件主要有两种生成方法, 一种是用javascript直接生成, 如new dojo.form.Button(null, node), 另一种是在html中作标记, 如<button dojoType=dijit.form.Button label="button"></button>
关于其生成widget的机制, 我目前所知是它从原始DOM节点中获取信息, 如id, label, dojoType之类, 然后据此生成一个widget, 它本身应该是绑定了一个自己的DOM节点, 最后再将它替换原始DOM节点。
根据dijit.js,在创建一个widget过程中会调用如下几个函数
postMixInProperties: 刚刚从原始节点读入信息后调用, 如果扩展已有widget,可通过connect此函数来为widget对象添加新的属性(每个widget只从原始DOM节点(可用this.srcNodeRef得到其引用)中获取需要的节点, 其他的一概抛弃, 如果需要扩展, 要在销毁前得到它,而这里所列的几个函数中,仅有此函数执行时srcNodeRef尚未背销毁)
buildingRendering:刚刚为当前widget创建好DOM节点时调用
postCreate: 当前widget已创建好并放入UI后调用
startup: 所有widget都创建好后依次为每个widget调用此函数
尽管dijit提供了非常丰富的控件, 但也未必就能满足全部需要。 因此有时需要在现有控件基础上进行扩展。这时上面三个函数就显得非常有用。 不过需要注意的是, 用dojo.declare声明一个子类时, 不能直接重定义上面三个函数,否则父类的方法会被覆盖, 而且dojo似乎没有提供super之类的属性。 我的解决方法是用dojo.declare声明时仅添加新函数,而之后再用dojo.connect对已有函数进行扩展。 如果用此方法, 需要注意, 如果新类叫childClass, 那么应该connect childClass.prototype的方法, 我一开始忘了这一点, 结果怎么都不起作用。
五、其他
dojo与Firebug支持得非常好, 它能自动识别Firebug并与其交互, 再配合Firebug的强大功能, 开发javascript 轻松了很多
六、总结
通过几天的学习, 我对dojo库的基本思想和基本实现方法都有了些了解, 也十分欣赏和钦佩它的结构。它充分利用了javascript的灵活性, 创造了很多异想天开般的设计, 令我大开眼界
就先写这么多, 能些的也就是这么多了。 随着学习的深入, 我会再补充这个学习笔记的。
2007-09-01
linux 下 驱动 Acer 笔记本 Realtek ALC268 声卡
我这个笔记本是Acer的, 声卡为nvidia mcp67集成, 解码器为realtek alc268
刚装上ubuntu 7.04后声卡能够识别, 有nvidia unknown device 和alc268字样, 但是不能发声。
起初认为是声卡没有识别好, 但是装了最新的alsa1.0.15rc1和ossv4都没效果, 了解了update-pciid才知道其实已经识别了声卡, 而问题出现在 realtek alc268上。
realtek官网上也有个驱动, 下了一看,里面原来也是alsa1.0.14, 只是驱动版本号比较特别。装上后仍不起作用。
在网上搜了很多文章, 其中这个比较好
Bug #116326 in linux-source-2.6.22 (Ubuntu): “No audio INTEL HD audio - Realtek ALC268 codec - Toshiba A205-S4577”
看来这个问题主要出在新型的笔记本上。 另外我还得知了alsa1.0.14对alc268支持的不是很好。上面那个链接里, 中间有几个人帖上了for realtek的alsa补丁, 看样子能起作用。 他们说1.0.14上支持的声卡列表里没有alc268。还有一点, 提出这个问题的人是toshiba的笔记本, 下面帖子中多次提到了model=toshiba这个参数, 于是我自然想到有没有for acer的。
于是我把目光放在了1.0.15rc1上, 看changlog里赫然有个alc268, 而其中专门多了一个acer笔记本的model。 我顿时眼前一亮。 有戏!
再看一下alsa的安装说明, 应该在snd_hda_intel模块加载时加上model=xxx 的参数。 那我这情况自然是在/etc/modprobe.d/options里加一句options snd_hda_intel model=acer
然后重启(可能仅重加载snd-hda-intel也可), 听到了熟悉的ubuntu启动声音。 啊哈哈哈!!!
刚装上ubuntu 7.04后声卡能够识别, 有nvidia unknown device 和alc268字样, 但是不能发声。
起初认为是声卡没有识别好, 但是装了最新的alsa1.0.15rc1和ossv4都没效果, 了解了update-pciid才知道其实已经识别了声卡, 而问题出现在 realtek alc268上。
realtek官网上也有个驱动, 下了一看,里面原来也是alsa1.0.14, 只是驱动版本号比较特别。装上后仍不起作用。
在网上搜了很多文章, 其中这个比较好
Bug #116326 in linux-source-2.6.22 (Ubuntu): “No audio INTEL HD audio - Realtek ALC268 codec - Toshiba A205-S4577”
看来这个问题主要出在新型的笔记本上。 另外我还得知了alsa1.0.14对alc268支持的不是很好。上面那个链接里, 中间有几个人帖上了for realtek的alsa补丁, 看样子能起作用。 他们说1.0.14上支持的声卡列表里没有alc268。还有一点, 提出这个问题的人是toshiba的笔记本, 下面帖子中多次提到了model=toshiba这个参数, 于是我自然想到有没有for acer的。
于是我把目光放在了1.0.15rc1上, 看changlog里赫然有个alc268, 而其中专门多了一个acer笔记本的model。 我顿时眼前一亮。 有戏!
再看一下alsa的安装说明, 应该在snd_hda_intel模块加载时加上model=xxx 的参数。 那我这情况自然是在/etc/modprobe.d/options里加一句options snd_hda_intel model=acer
然后重启(可能仅重加载snd-hda-intel也可), 听到了熟悉的ubuntu启动声音。 啊哈哈哈!!!
update-pciids
今天学会了这个命令, 主要是lspci时一大堆unknown, 而驱动里明明支持那些id, 但是就是不能显示正确的字符串, 后来知道了update-pciids这个命令, 可以下载到最新的pci id, 之后lspci少了几个unknown. 此时才恍然大悟, 原来lspci显示的字符串跟驱动没有关系, 而是自己有一个索引。
Labels:
linux
[转] Firefox 加速技巧
转载请注明出处:www.NiDaYe.orG,谢谢
作者:Roby Liang
很早很早以前曾经写过一篇“Firefox 终极加速”的文章,可以通过手动修改 user.js 文件来获取理论上的最佳浏览速度,后来发现这种修改方式对于普通用户来说真的有点勉为其难,而且国内的网络环境并不尽如人意,我们所做的 js 优化修改在臃堵的网路上显得那么渺小与无助。
不过聊胜于无,让我们暂且先把这些复杂的优化操作变的简单一点,然后再来亲自感觉一下这些已经在网络上流传了很久的提速技巧吧。下面的操作我们都在 about:config 里进行。在 Firefox 的地址栏中输入 about:config,让我们开始提速吧。
network.http.pipelining
在 Filter 中输入 network.http.pipelining,双击赋值为 true,默认为 false。如果没有找到这个键值,可以右键新建一个 Boolean,把她赋值为 true 就 OK 了。
还 是像我在从前解释过的那样,激活这个键值之后,Pipelining 同时发出成倍数的连接请求,从而达到提升连接速度的效果。网络上的大多数网站都是基于 HTTP 协议,而 HTTP 1.1 可以支持多线程的连接请求,通过这个操作可以减少 Firefox 载入网页的时间。不过并不是所有网页所在的服务器都支持这种操作,所以当你修改键值之后却看不到一点实际效果的时候,请不要对我破口大骂。
network.http.pipelining.maxrequests
在 Filter 中输入 network.http.pipelining.maxrequests,双击并赋值为 8,默认键值为 4。
network.http.proxy.pipelining
在 Filter 中输入 network.http.proxy.pipelining,双击并赋值为 true。
这两条优化的道理同上,这里就不再多解释了。
network.dns.disableIPv6
在 Filter 中输入 network.dns.disableIPv6,双击并赋值为 true。
IPv6 把 IP 地址由 32 位增加到 128 位,从而能够支持更大的地址空间,当用户在终端向一个 IPv6-capable DNS 服务器发送连接请求时,也许服务器端会错误的返回给用户一个 IPv4 地址。而 Firefox 可以对这一切明察秋毫,不过在 Firefox 纠错的同时也必然会导致信号的延迟,所以这里我们把她赋值为 true,禁用掉她。
content.interrupt.parsing
右键新建 Boolean 值,键名为 content.interrupt.parsing,赋值 true。
默 认这个键值并不存在。我们激活这个键值之后,当目标网页载入时,Firefox 会根据一定频率打断解析的过程,不断的向用户反馈她所收集到的网页信息,有点像流媒体的意思。这时的 Firefox 很聪明,不会一根筋的一直钻牛角。在下面的内容中我还会具体讲一下这个键值的魅力所在。
content.max.tokenizing.time
右键新建 Integer 值,键名为 content.max.tokenizing.time,赋值 2250000。
这 个键值的作用其实就是指定一个循环事件的处理周期,这里的单位是微秒,要是我没有算错的话。理论上当我们将这个值取的越小,网页就会从视觉上载入的越流 畅,因为 Firefox 会在很短的单位时间里反馈回解析到的网页信息。可是这样无疑延迟了网页整体载入的时间,所以在这里我们不妨将这个周期取的大一些,理论上可以加速网页的载 入。
content.notify.interval
右键新建 Integer 值,键名为 content.notify.interval,赋值 750000。
载 入一个网页其实也是一门很大的学问。让我们来放一个慢动作,我们姑且先把在终端第一次收到的网页信息很不专业的叫做预载入页面吧,这个页面有可能是不完整 的图片或者文字,或者别的媒体文件。从我们第一次向远端主机发出连接请求到我们在终端收到这个预载入页面花费的时间,就是这里我们要定义的键值。理论上当 我们将这个时间设置的很低时,肯定会更快的拿到所谓的预载入页面,可这是一种杀鸡取卵的做法,这样无形中反而增加了我们整体页面的载入时间。按照官方的说 法,低于 100,000 将会降低 Firefox 的性能,那好吧,那我们把她彪到 750000 吧。
content.notify.ontimer
右键新建 Boolean 值,键名为 content.notify.ontimer,赋值 true。
为了使我们上面设置的 750000 微秒生效,还需要把这个键值激活。只有这两个键值配合,才会起作用。
content.notify.backoffcount
右键新建 Integer 值,键名为 content.notify.backoffcount,赋值 5。
这 个键值控制 Firefox 的内置计数器在归零之前载入页面返回的次数。我们将目标网页分成好多个部分进行下载,每下载完一个部分,计数器归零一次。-1 就是没有限制,值为 0 时这项功能被禁用。这里我们将她设置成 5, 当返回的次数达到五次而这部分网页还没有完全下载完时,那么剩下的没有下载完的网页内容将不会再按照我们预告设置的周期,像之前的五次那样一点一点的搬运 回来,而是会一次性的下载完。也就是说在这个部分的网页下载过程中,Firefox 一共向我们反馈了 6 次信息,前 5 次的时间间隔是我们在上面的键值中设置的周期 2250000 微秒,而第 6 次也就是最后一次则没有时间限制,什么时候把剩下的下完了,什么时候反馈回来。
只有当我们在上面提到的 content.notify.ontimer 键值为 true 的时候,这里的设置才会生效。
content.switch.threshold
右键新建 Integer 值,键名为 content.switch.threshold ,赋值 750000,也就是四分之三秒。
在 前面我们提到了一个键值 content.interrupt.parsing,通过激活她实际上我们可以在载入页面的过程中跟 Firefox 产生互动,毕竟我们每一个人的心里都充满了爱。把 content.interrupt.parsing 激活后当页面载入时 Firefox 会有两种操作模式:高频和低频中断模式。使用高频模式时,网页回馈的频率也很高,我们坐在显示器前看到的网页载入过程也会更加的平滑。低频时网页回馈的频 率相对比较低,可是这时反而加快了网页载入的时间。当我们移动鼠标或者触击键盘时,高频模式被激活。在经过某一段时间我们没有碰鼠标和键盘,程序没有接到 鼠标和键盘发出的任何指令时,Firefox 就会自动进入低频模式工作,而这所谓的某一段时间,就是我们这里要指定的值。
nglayout.initialpaint.delay
右键新建 Integer 值,键名为 nglayout.initialpaint.delay,赋值 0。
这里实际上延迟了整个网页的显示速度,但是因为用户更喜欢在整个网页完全截入之前就开始阅读网页 (就像流媒体那样),所以在这里可以把值调为零,加速用户阅读网页的速度,有时候阅读速度和载入速度并不是成正比的。
今天先写这些吧。在网络状况稳定的情况下这些优化的确是会起到一些效果的,并不光是心理作用,大家在为自己的浏览器提速时,也可以稍微参考一下。
作者:Roby Liang
很早很早以前曾经写过一篇“Firefox 终极加速”的文章,可以通过手动修改 user.js 文件来获取理论上的最佳浏览速度,后来发现这种修改方式对于普通用户来说真的有点勉为其难,而且国内的网络环境并不尽如人意,我们所做的 js 优化修改在臃堵的网路上显得那么渺小与无助。
不过聊胜于无,让我们暂且先把这些复杂的优化操作变的简单一点,然后再来亲自感觉一下这些已经在网络上流传了很久的提速技巧吧。下面的操作我们都在 about:config 里进行。在 Firefox 的地址栏中输入 about:config,让我们开始提速吧。
在 Filter 中输入 network.http.pipelining,双击赋值为 true,默认为 false。如果没有找到这个键值,可以右键新建一个 Boolean,把她赋值为 true 就 OK 了。
还 是像我在从前解释过的那样,激活这个键值之后,Pipelining 同时发出成倍数的连接请求,从而达到提升连接速度的效果。网络上的大多数网站都是基于 HTTP 协议,而 HTTP 1.1 可以支持多线程的连接请求,通过这个操作可以减少 Firefox 载入网页的时间。不过并不是所有网页所在的服务器都支持这种操作,所以当你修改键值之后却看不到一点实际效果的时候,请不要对我破口大骂。
在 Filter 中输入 network.http.pipelining.maxrequests,双击并赋值为 8,默认键值为 4。
在 Filter 中输入 network.http.proxy.pipelining,双击并赋值为 true。
这两条优化的道理同上,这里就不再多解释了。
在 Filter 中输入 network.dns.disableIPv6,双击并赋值为 true。
IPv6 把 IP 地址由 32 位增加到 128 位,从而能够支持更大的地址空间,当用户在终端向一个 IPv6-capable DNS 服务器发送连接请求时,也许服务器端会错误的返回给用户一个 IPv4 地址。而 Firefox 可以对这一切明察秋毫,不过在 Firefox 纠错的同时也必然会导致信号的延迟,所以这里我们把她赋值为 true,禁用掉她。
右键新建 Boolean 值,键名为 content.interrupt.parsing,赋值 true。
默 认这个键值并不存在。我们激活这个键值之后,当目标网页载入时,Firefox 会根据一定频率打断解析的过程,不断的向用户反馈她所收集到的网页信息,有点像流媒体的意思。这时的 Firefox 很聪明,不会一根筋的一直钻牛角。在下面的内容中我还会具体讲一下这个键值的魅力所在。
右键新建 Integer 值,键名为 content.max.tokenizing.time,赋值 2250000。
这 个键值的作用其实就是指定一个循环事件的处理周期,这里的单位是微秒,要是我没有算错的话。理论上当我们将这个值取的越小,网页就会从视觉上载入的越流 畅,因为 Firefox 会在很短的单位时间里反馈回解析到的网页信息。可是这样无疑延迟了网页整体载入的时间,所以在这里我们不妨将这个周期取的大一些,理论上可以加速网页的载 入。
右键新建 Integer 值,键名为 content.notify.interval,赋值 750000。
载 入一个网页其实也是一门很大的学问。让我们来放一个慢动作,我们姑且先把在终端第一次收到的网页信息很不专业的叫做预载入页面吧,这个页面有可能是不完整 的图片或者文字,或者别的媒体文件。从我们第一次向远端主机发出连接请求到我们在终端收到这个预载入页面花费的时间,就是这里我们要定义的键值。理论上当 我们将这个时间设置的很低时,肯定会更快的拿到所谓的预载入页面,可这是一种杀鸡取卵的做法,这样无形中反而增加了我们整体页面的载入时间。按照官方的说 法,低于 100,000 将会降低 Firefox 的性能,那好吧,那我们把她彪到 750000 吧。
右键新建 Boolean 值,键名为 content.notify.ontimer,赋值 true。
为了使我们上面设置的 750000 微秒生效,还需要把这个键值激活。只有这两个键值配合,才会起作用。
右键新建 Integer 值,键名为 content.notify.backoffcount,赋值 5。
这 个键值控制 Firefox 的内置计数器在归零之前载入页面返回的次数。我们将目标网页分成好多个部分进行下载,每下载完一个部分,计数器归零一次。-1 就是没有限制,值为 0 时这项功能被禁用。这里我们将她设置成 5, 当返回的次数达到五次而这部分网页还没有完全下载完时,那么剩下的没有下载完的网页内容将不会再按照我们预告设置的周期,像之前的五次那样一点一点的搬运 回来,而是会一次性的下载完。也就是说在这个部分的网页下载过程中,Firefox 一共向我们反馈了 6 次信息,前 5 次的时间间隔是我们在上面的键值中设置的周期 2250000 微秒,而第 6 次也就是最后一次则没有时间限制,什么时候把剩下的下完了,什么时候反馈回来。
只有当我们在上面提到的 content.notify.ontimer 键值为 true 的时候,这里的设置才会生效。
右键新建 Integer 值,键名为 content.switch.threshold ,赋值 750000,也就是四分之三秒。
在 前面我们提到了一个键值 content.interrupt.parsing,通过激活她实际上我们可以在载入页面的过程中跟 Firefox 产生互动,毕竟我们每一个人的心里都充满了爱。把 content.interrupt.parsing 激活后当页面载入时 Firefox 会有两种操作模式:高频和低频中断模式。使用高频模式时,网页回馈的频率也很高,我们坐在显示器前看到的网页载入过程也会更加的平滑。低频时网页回馈的频 率相对比较低,可是这时反而加快了网页载入的时间。当我们移动鼠标或者触击键盘时,高频模式被激活。在经过某一段时间我们没有碰鼠标和键盘,程序没有接到 鼠标和键盘发出的任何指令时,Firefox 就会自动进入低频模式工作,而这所谓的某一段时间,就是我们这里要指定的值。
右键新建 Integer 值,键名为 nglayout.initialpaint.delay,赋值 0。
这里实际上延迟了整个网页的显示速度,但是因为用户更喜欢在整个网页完全截入之前就开始阅读网页 (就像流媒体那样),所以在这里可以把值调为零,加速用户阅读网页的速度,有时候阅读速度和载入速度并不是成正比的。
今天先写这些吧。在网络状况稳定的情况下这些优化的确是会起到一些效果的,并不光是心理作用,大家在为自己的浏览器提速时,也可以稍微参考一下。
2007-08-29
ubuntu nvidia 驱动安装
显卡是nvidia geforce 8400M G, ubuntu 论坛上说的方法是装nvidia-glx, 我看了看源里, 除了这个包还有个nvidia-glx-new, 但是看说明,似乎太老了, 不认geforce8。 事实确实如此。
于是上nvidia官网下了个驱动, 叫做NVIDIA-Linux-x86-100.14.11-pkg1.run,但是装了不能用, X的错误信息说nvidia kernel interface版本不匹配, 而这个应该是ubuntu的linux-restricted-modules包提供的, 问题可能在这里.
后来在nvidia论坛上证实了这一点, 在http://www.nvnews.net/vbulletin/showthread.php?s=5e6ef2b1d1fb97772f6b738b135f220b&t=72490说道
If you wish to install the NVIDIA Linux graphics driver on a Debian GNU/Linux or Ubuntu system that ships with Xorg 7.x, please ensure that your system meets the following requirements:
* development tools like make and gcc are installed
* the linux-headers package matching the installed Linux kernel is installed
* the pkg-config and xserver-xorg-dev packages are installed
* the nvidia-glx package has been uninstalled with the --purge option and the files /etc/init.d/nvidia-glx and /etc/init.d/nvidia-kernel do not exist
If you use Ubuntu, please also ensure that the linux-restricted-modules or linux-restricted-modules-common packages have been uninstalled. Alternatively, you can edit the /etc/default/linux-restricted-modules or /etc/default/linux-restricted-modules-common configuration file and disable the NVIDIA linux-restricted kernel modules (nvidia, nvidia_legacy) via:
DISABLED_MODULES="nv nvidia_new"
Additionally, delete the following file if it exists:
/lib/linux-restricted-modules/.nvidia_new_installed
Please note: unfortunately, it has become difficult to keep track of the pre-/post-installation steps required for [K]Ubuntu, and the above instructions may be incomplete. If in doubt, it is recommended that you use your distributor's NVIDIA Linux graphics driver packages, exclusively.
其实我更愿意用配置文件的方法禁掉nvidia-glx, 但是后来想了想, 还是把这个包删了。
这时再装一遍, 重启gdm, 哈哈, 出现了nvidia的logo, 成功了。 如果不想要logo, 在xorg.conf适当位置加入Option "NoLogo" "1"就行了
另外xserver-xorg-dev似乎不是必需的, 至少我这里没装这个也能成功安装驱动。
于是上nvidia官网下了个驱动, 叫做NVIDIA-Linux-x86-100.14.11-pkg1.run,但是装了不能用, X的错误信息说nvidia kernel interface版本不匹配, 而这个应该是ubuntu的linux-restricted-modules包提供的, 问题可能在这里.
后来在nvidia论坛上证实了这一点, 在http://www.nvnews.net/vbulletin/showthread.php?s=5e6ef2b1d1fb97772f6b738b135f220b&t=72490说道
If you wish to install the NVIDIA Linux graphics driver on a Debian GNU/Linux or Ubuntu system that ships with Xorg 7.x, please ensure that your system meets the following requirements:
* development tools like make and gcc are installed
* the linux-headers package matching the installed Linux kernel is installed
* the pkg-config and xserver-xorg-dev packages are installed
* the nvidia-glx package has been uninstalled with the --purge option and the files /etc/init.d/nvidia-glx and /etc/init.d/nvidia-kernel do not exist
If you use Ubuntu, please also ensure that the linux-restricted-modules or linux-restricted-modules-common packages have been uninstalled. Alternatively, you can edit the /etc/default/linux-restricted-modules or /etc/default/linux-restricted-modules-common configuration file and disable the NVIDIA linux-restricted kernel modules (nvidia, nvidia_legacy) via:
DISABLED_MODULES="nv nvidia_new"
Additionally, delete the following file if it exists:
/lib/linux-restricted-modules/.nvidia_new_installed
Please note: unfortunately, it has become difficult to keep track of the pre-/post-installation steps required for [K]Ubuntu, and the above instructions may be incomplete. If in doubt, it is recommended that you use your distributor's NVIDIA Linux graphics driver packages, exclusively.
其实我更愿意用配置文件的方法禁掉nvidia-glx, 但是后来想了想, 还是把这个包删了。
这时再装一遍, 重启gdm, 哈哈, 出现了nvidia的logo, 成功了。 如果不想要logo, 在xorg.conf适当位置加入Option "NoLogo" "1"就行了
另外xserver-xorg-dev似乎不是必需的, 至少我这里没装这个也能成功安装驱动。
2007-08-14
东方妖妖梦破解手记
最近无聊, 又开始玩东方系列了, 其实我一直挺喜欢的, 但是水平太差.
不过这次我发现我可以7人通妖妖梦easy了, 挺高兴. 但是lunatic...哦...难以想像.
我的原则是"玩游戏, 而不被游戏玩", 于是只好...虽说网上有一堆现成的修改器, 但是那样就没有乐趣了.
首先是考虑简单的方法---金山游侠, 失败.
然后就只能去分析程序了. 首先看了眼, 没有壳. 再看导入表, 异常地干净, 只有windows库和一些direct3d的库.
让我觉得它好像是直接用汇编写的.
之后就要找切入点了. 我第一反应就是配置文件, th07.cfg, 因为选项里能调整默认人数, 那必定存在这里
经过简单地研究, 发现它存在1c出, 单字节, 为实际人数-1
初次尝试是直接修改配置文件, 比如改成7f, 然后启动游戏, 但是发现人数回到默认值了, 且退出游戏后发现配置文件也改回来了. 嗯. 有防备啊...
那只好再看代码了, 给所有CreateFileA设断, 运行时只看那些打开th07.cfg的, 一共两个, 且一个是打开后写些数据就CloseHandle了, 重点再另一个. 再找有关的ReadFile, 只有一个, 且在那之前有个GetFileSize, 看来是一次读入的, 呵呵, 给ReadFile的buffer下内存断, 不一会儿断在了00436f3d, 是个串移动指令rep movs...此时注意目标地址, 56ba64, 这可是重要情报!
然后该上静态分析了, 搜这个地址(一看就是全局变量), 没有多少, 其中看到了读完文件后进行校验的, 比如00436fbd处, 判断它是否比5大, 若是就回到默认值, 真是一清二楚. 那没什么说的了, 给exe做个备份, 然后ultraedit之...
但是事情没有想的那么顺利, 程序自动退出了, 看来这里也有防备.
不过既然网上有内存修改器, 那说明它肯定内存方面没有设防, 于是用olly载入, 修改后再运行, 就成功了!
后来想再整理一下, 找到最关键的修改位置, 找到了0044ff3f处, 简单地说如果当前人数小于4就加1, 否则不变, 我挺奇怪这里是干什么的, 下了断运行了半天, 发现原来是设置人数是, 按下右方向键, 则人数加1, 但是如果已经加到5个人就不能再加了. 啊哈哈, 被我抓住尾巴了, 从这里返回的数据似乎没有再做什么校验, 把小于4的限制去了, 再看看配置, 哈, 想加多少就加多少.其实也可以把代码直接改成mov byte ptr [56ba64], ff
现在再去玩Lunatic, 啊哈哈, 这才叫玩游戏. "轻松"通关. (你要是这样都过不去, 神也救不了你了)
最后是想玩Extra, 但是数据是内置的, 比较麻烦, 一开始一筹莫展, 不知从何下手.
一番苦战后, 想到看看字符串资源, 发现了Extra Rank字样, 于004269bd, 向上看发现了各个级别的名称, 再看一下, 原来是switch的[61c260], 那这个就是级别没错了.
据此下断, 找所有对级别==4(即extra)的特殊判断, 运行游戏, 选extra, 断在了00451803, 这里特殊的地方只是改了个全局变量[62583c], 第二次断在了0042d09e, 运行至0042d0a4时发现原来eax此时指的就是配置文件区,这里它把一个变量设为2, 然后判断一个全局变量[625628]的最低位, 若为1则又把此变量改为8, 这里很可疑, 我断在此处, 清了zf, 让它改成8, 然后运行, 啊, 果然它就是人数, 于是恍然大悟, 赶快去0042d0a4把人数改成7f. Extra, 我来了...
总结一下, 这次的成功主要是由于源程序本身很干净, 而我的思路正确, 运气也不错, 希望下次还能这样.
另外补充几点:
1.人数如果改的比较小, 而你又死光了, 好像会出除零错, 这个我就没再研究了, 总之改大些呗
2.游戏进到图形模式后, olly断下了却看不到, 我的办法是把分辨率调到1600x1200, 然后游戏只占左上角一小片, 其它地方看olly就好了
3.有时卡得连分辨率也改不了, 这是我就让电脑待机再启动, 这样就行了.
4.后来玩东方永夜抄, 过程几乎一样, 有了上次经验, 这次轻松破解, 不过感觉还是直接改配置文件, 然后把配置文件校验改了方便些. 另外, 改extra时直接搜了mov byte ptr [eax+1c], 2就搜到了, 比较搞笑
5.注, 永夜抄人数在[17ce88c], 而级别在[160f538]
不过这次我发现我可以7人通妖妖梦easy了, 挺高兴. 但是lunatic...哦...难以想像.
我的原则是"玩游戏, 而不被游戏玩", 于是只好...虽说网上有一堆现成的修改器, 但是那样就没有乐趣了.
首先是考虑简单的方法---金山游侠, 失败.
然后就只能去分析程序了. 首先看了眼, 没有壳. 再看导入表, 异常地干净, 只有windows库和一些direct3d的库.
让我觉得它好像是直接用汇编写的.
之后就要找切入点了. 我第一反应就是配置文件, th07.cfg, 因为选项里能调整默认人数, 那必定存在这里
经过简单地研究, 发现它存在1c出, 单字节, 为实际人数-1
初次尝试是直接修改配置文件, 比如改成7f, 然后启动游戏, 但是发现人数回到默认值了, 且退出游戏后发现配置文件也改回来了. 嗯. 有防备啊...
那只好再看代码了, 给所有CreateFileA设断, 运行时只看那些打开th07.cfg的, 一共两个, 且一个是打开后写些数据就CloseHandle了, 重点再另一个. 再找有关的ReadFile, 只有一个, 且在那之前有个GetFileSize, 看来是一次读入的, 呵呵, 给ReadFile的buffer下内存断, 不一会儿断在了00436f3d, 是个串移动指令rep movs...此时注意目标地址, 56ba64, 这可是重要情报!
然后该上静态分析了, 搜这个地址(一看就是全局变量), 没有多少, 其中看到了读完文件后进行校验的, 比如00436fbd处, 判断它是否比5大, 若是就回到默认值, 真是一清二楚. 那没什么说的了, 给exe做个备份, 然后ultraedit之...
但是事情没有想的那么顺利, 程序自动退出了, 看来这里也有防备.
不过既然网上有内存修改器, 那说明它肯定内存方面没有设防, 于是用olly载入, 修改后再运行, 就成功了!
后来想再整理一下, 找到最关键的修改位置, 找到了0044ff3f处, 简单地说如果当前人数小于4就加1, 否则不变, 我挺奇怪这里是干什么的, 下了断运行了半天, 发现原来是设置人数是, 按下右方向键, 则人数加1, 但是如果已经加到5个人就不能再加了. 啊哈哈, 被我抓住尾巴了, 从这里返回的数据似乎没有再做什么校验, 把小于4的限制去了, 再看看配置, 哈, 想加多少就加多少.其实也可以把代码直接改成mov byte ptr [56ba64], ff
现在再去玩Lunatic, 啊哈哈, 这才叫玩游戏. "轻松"通关. (你要是这样都过不去, 神也救不了你了)
最后是想玩Extra, 但是数据是内置的, 比较麻烦, 一开始一筹莫展, 不知从何下手.
一番苦战后, 想到看看字符串资源, 发现了Extra Rank字样, 于004269bd, 向上看发现了各个级别的名称, 再看一下, 原来是switch的[61c260], 那这个就是级别没错了.
据此下断, 找所有对级别==4(即extra)的特殊判断, 运行游戏, 选extra, 断在了00451803, 这里特殊的地方只是改了个全局变量[62583c], 第二次断在了0042d09e, 运行至0042d0a4时发现原来eax此时指的就是配置文件区,这里它把一个变量设为2, 然后判断一个全局变量[625628]的最低位, 若为1则又把此变量改为8, 这里很可疑, 我断在此处, 清了zf, 让它改成8, 然后运行, 啊, 果然它就是人数, 于是恍然大悟, 赶快去0042d0a4把人数改成7f. Extra, 我来了...
总结一下, 这次的成功主要是由于源程序本身很干净, 而我的思路正确, 运气也不错, 希望下次还能这样.
另外补充几点:
1.人数如果改的比较小, 而你又死光了, 好像会出除零错, 这个我就没再研究了, 总之改大些呗
2.游戏进到图形模式后, olly断下了却看不到, 我的办法是把分辨率调到1600x1200, 然后游戏只占左上角一小片, 其它地方看olly就好了
3.有时卡得连分辨率也改不了, 这是我就让电脑待机再启动, 这样就行了.
4.后来玩东方永夜抄, 过程几乎一样, 有了上次经验, 这次轻松破解, 不过感觉还是直接改配置文件, 然后把配置文件校验改了方便些. 另外, 改extra时直接搜了mov byte ptr [eax+1c], 2就搜到了, 比较搞笑
5.注, 永夜抄人数在[17ce88c], 而级别在[160f538]
Labels:
crack
2007-08-12
再谈pdftohtml支持中文
以前的文章里提到过pdftohtml支持中文的问题, 是用ccmap里面的.cmap文件解决的.
所谓pdftohtml支持中文, 其实根fpdftohtml没什么关系, 而是pdf文件自己需要支持. 我认为它跟pdf支持复制粘贴是一回事.
今天突然发现我的pdftohtml又不支持中文了, 整蛊半天发现我不知什么时候我把那些.cmap文件删了-_-b
于是再去http://lsec.cc.ac.cn/cgi-bin/viewcvs.cgi/cct/ccmap/#dirlist逛逛, 发现原来的cmap的tar包已经删除了, 而多了一个makecmap.tex文件用于生成cmap文件.
我要转的是GBK编码的, 用命令 sudo latex \\def\\cmapEnc{GBK} \\input{makecmap.tex} 即可,
但是虽然弄出来了cmap文件, 编译出来的pdf还是不能复制粘贴, pdftohtml当然也不行.
于是上网搜了搜, 发现了CJKutf8, 说是能代替ccmap, 于是试了下, 真的可以, 只需要加入\usepackage{CJKutf8} 即可, 真不错.
不过,好像有超长行时会出些bug.
以后考虑用utf8了...
所谓pdftohtml支持中文, 其实根fpdftohtml没什么关系, 而是pdf文件自己需要支持. 我认为它跟pdf支持复制粘贴是一回事.
今天突然发现我的pdftohtml又不支持中文了, 整蛊半天发现我不知什么时候我把那些.cmap文件删了-_-b
于是再去http://lsec.cc.ac.cn/cgi-bin/viewcvs.cgi/cct/ccmap/#dirlist逛逛, 发现原来的cmap的tar包已经删除了, 而多了一个makecmap.tex文件用于生成cmap文件.
我要转的是GBK编码的, 用命令 sudo latex \\def\\cmapEnc{GBK} \\input{makecmap.tex} 即可,
但是虽然弄出来了cmap文件, 编译出来的pdf还是不能复制粘贴, pdftohtml当然也不行.
于是上网搜了搜, 发现了CJKutf8, 说是能代替ccmap, 于是试了下, 真的可以, 只需要加入\usepackage{CJKutf8} 即可, 真不错.
不过,好像有超长行时会出些bug.
以后考虑用utf8了...
2007-07-11
kernel 编译补遗
上回说到我的kernel能够启动了,不过还是有些问题。
一个是不断出modprobe: fatal: 说找不到.../modules.dep之类, 而且一出就好几屏,上网搜了一下,也有人遇到这问题,不过好像他们都是不能启动, 估计跟我情况不一样。后来经高人指点, 原来我的kernel不用initrd, 于是make-kpkg时不要--initrd就好了(一开始没出initrd我还以为出bug了)
另外是声卡的问题, 我查了下,并不是没认出声卡,而是多了dummy设备和virtual midi设备, 我没查到如何配置alsa默认声卡,于是呼把这两个选项去了。
最后是说dma没打开,不知道为什么当时漏掉了,不过它还真是体贴,提醒了一下,要不然我以后慢的都不知道怎么回事。
最后基本就都正常了,跟原来比,启动是多了一点点错误信息,但是似乎不影响使用。
一个是不断出modprobe: fatal: 说找不到.../modules.dep之类, 而且一出就好几屏,上网搜了一下,也有人遇到这问题,不过好像他们都是不能启动, 估计跟我情况不一样。后来经高人指点, 原来我的kernel不用initrd, 于是make-kpkg时不要--initrd就好了(一开始没出initrd我还以为出bug了)
另外是声卡的问题, 我查了下,并不是没认出声卡,而是多了dummy设备和virtual midi设备, 我没查到如何配置alsa默认声卡,于是呼把这两个选项去了。
最后是说dma没打开,不知道为什么当时漏掉了,不过它还真是体贴,提醒了一下,要不然我以后慢的都不知道怎么回事。
最后基本就都正常了,跟原来比,启动是多了一点点错误信息,但是似乎不影响使用。
2007-07-10
Linux kernel 2.6.20.3 编译成功!
几经波折,终于把Linux内核搞定了。
起因是原先ubuntu带的内核版本较低, 决定升级,可是发现安装后modules 有59M。 启动后lsmod
再一看,俺的神呀, 刷刷地, 竟然有两屏。 另外新内核把我的ide硬盘人成了sda, grub里还要改革选项(一开始不知道,怎么也不能启动), 于是决定自己编译一下。
不过过程并不顺利, 一个是我机器的配置, 是近10年前的笔记本, 编译一次内核要将近两个小时。 另外就是我没经验。
现在想想,可能一共得编译了十几次。 中间闹了很多笑话。比如一开始删的模块比较少, 编译完能启动, 但是modules仍有29M, 硬盘还是sda, 后来有狠心删了一次, 这回好了, modules小多了,硬盘也变回hda了, 可是initramfs mount根时说'no such device' , 有时则是 kernel panic。 我在这个问题上就折腾了好久。还有一次是配置文件搞错了,一下编译了很多模块, 中间吃掉硬盘1G多, 告诉我‘no space left’。。。
中间配置了好几次, 一般都是把ubuntu默认的config拷过来并在此基础上修改。 今天终于成功了, 最后kernel是2M左右, modules一共5M多。 感觉上还能删一些, 不过不用了。
经过几次配置,我对kernel倒是有了进一步了解, 至少对其大概架构有了个印象。若不是我机器如此之慢, 编译过程应该也是很有趣的。现在残留的问题是有关声卡的,应该很容易解决。不过到现在我仍不清楚为什么一开始新内核会认成sda, 也听说一些同学有类似情况。
另外,在编译过程中参考了http://lamp.linux.gov.cn/Linux/kernel_options.html,很有帮助, 对作者表示感谢!
起因是原先ubuntu带的内核版本较低, 决定升级,可是发现安装后modules 有59M。 启动后lsmod
再一看,俺的神呀, 刷刷地, 竟然有两屏。 另外新内核把我的ide硬盘人成了sda, grub里还要改革选项(一开始不知道,怎么也不能启动), 于是决定自己编译一下。
不过过程并不顺利, 一个是我机器的配置, 是近10年前的笔记本, 编译一次内核要将近两个小时。 另外就是我没经验。
现在想想,可能一共得编译了十几次。 中间闹了很多笑话。比如一开始删的模块比较少, 编译完能启动, 但是modules仍有29M, 硬盘还是sda, 后来有狠心删了一次, 这回好了, modules小多了,硬盘也变回hda了, 可是initramfs mount根时说'no such device' , 有时则是 kernel panic。 我在这个问题上就折腾了好久。还有一次是配置文件搞错了,一下编译了很多模块, 中间吃掉硬盘1G多, 告诉我‘no space left’。。。
中间配置了好几次, 一般都是把ubuntu默认的config拷过来并在此基础上修改。 今天终于成功了, 最后kernel是2M左右, modules一共5M多。 感觉上还能删一些, 不过不用了。
经过几次配置,我对kernel倒是有了进一步了解, 至少对其大概架构有了个印象。若不是我机器如此之慢, 编译过程应该也是很有趣的。现在残留的问题是有关声卡的,应该很容易解决。不过到现在我仍不清楚为什么一开始新内核会认成sda, 也听说一些同学有类似情况。
另外,在编译过程中参考了http://lamp.linux.gov.cn/Linux/kernel_options.html,很有帮助, 对作者表示感谢!
[转] nasm 中文手册
--------------------------------------------------------------------------------
第一章: 简介
-----------------------
1.1 什么是NASM
NASM是一个为可移植性与模块化而设计的一个80x86的汇编器。它支持相当多
的目标文件格式,包括Linux和NetBSD/FreeBSD,a.out,ELF,COFF,微软16
位的OBJ和Win32。它还可以输出纯二进制文件。它的语法设计得相当的简
洁易懂,和Intel语法相似但更简单。它支持Pentium,P6,MMX,3DNow!,
SSE and SSE2指令集,
1.1.1 为什么还需要一个汇编器?
NASM当初被设计出来的想法是comp.lang.asm.x86(或者可能是alt.lang.asm
,我忘了),从本质上讲,是因为没有一个好的免费的x86系例的汇编器可以使用,
所以,必须有人来写一个。
(*)a86不错,但不是免费的,而且你不可能得到32位代码编写的功能,除非你
付费,它只使用在dos上。
(*) gas是免费的,而且在dos下和unix下都可以使用,但是它是作为gcc的一
个后台而设计的,并不是很好,gcc一直就提供给它绝对正确的代码,所以它的
错误检测功能相当弱,还有就是对于任何一个想真正利用它写点东西的人来讲,
它的语法简直太可怕了,并且你无法在里面写正确的16位代码。
(*) as86是专门为Minix和Linux设计的,但看上去并没有很多文档可以参考。
(*) MASM不是很好,并且相当贵,还且只能运行在DOS下。
(*) TASM好一些,但却极入与MASM保持兼容,这就意味着无数的伪操作码和繁琐
的约定,并且它的语法本质上就是MASM的,伴随着的就是一些自相矛盾和奇怪的
东西。它也是相当贵的,并且只能运行在DOS下。
所以,只有NASM才能使您愉悦得编程。目前,它仍在原型设计阶段-我们不期望它
能够超越所有的这些汇编器。但请您发给我们bug报告,修正意见,和其他有用的
信息,还有其他任何你手头有的对我们有用的信息(感谢所有已经这样在做了的
人们),我们还会不断地改进它。
1.1.2 许可条件
请阅读作为NASM发布的一部分的文件Licence,只有在该许可条件下你才可以使
用NASM。
1.2 联系信息
当前版本的NASM(0.98.08)由一个开发小组在维护,你可以从nasm-devel邮件列表
中得到(看下面的链接),如果你想要报告bug,请先阅读10.2节
NASM有一个主页:http://www.web-sites.co.uk/nasm,更多的信息还可以在
`http://nasm.2y.net/上获取。
最初的作者你可以通过email:`jules@dsf.org.uk和`anakin@pobox.com和他们联
系,但后来的开发小组并不在其中。
最新的NASM发布被上传至官方网站`http://www.web-sites.co.uk/nasm和`ftp.kernel.org,
`ibiblio.org
公告被发布至`comp.lang.asm.x86, `alt.lang.asm 和`comp.os.linux.announce
如果你想了解NASM beta版的发布,和当前的开发状态,请通过在
`http://groups.yahoo.com/group/nasm-devel,
`http://www.pairlist.net/mailman/listinfo/nasm-devel and
`http://sourceforge.net/projects/nasm
注册来捐助nasm-devel邮件列表。
在网站Sourceforge上的列表是较好的一个列表,它也是最新nasm源代码与发布的
一个网站,另外的列表也是公开的,但有可能不会被继续长期支持。
1.3 安装
1.3.1 在dos和Windows下安装NASM
如果你拿到了NASM的DOS安装包,nasmXXX.zip(这里.XXX表示该安装包的NASM版
本号),把它解压到它自己的目录下(比如:‘c:\nasm)
该包中会包含有四个可执行文件:NASM可拟行文件nasm.exe和nasmw.exe,还有
NDISASM可执行文件ndisasm.exe和ndisasmw.exe。文件名以w结尾的是Win32
可执行格式。是运行在Windows 95或Windows NT的Intel处理器上的,另外的是
16位的DOS可执行文件。
NASM运行时需要的唯一文件就是它自己的可执行文件,所以可以拷贝nasm.exe
和nasmw.exe的其中一个到你自己的路径下,或者可以编写一个autoexec.bat把
nasm的路径加到你的PATH环境变量中去。(如果你只安装了Win32版本的,你可能
希望把文件名改成nasm.exe。)
就这样,NASM装好了。你不需要为了运行nasm而让nasm目录一直存在(除非你把它
加到了你的PATH中,所以如果你需要节省空间,你可删掉它,但是,你可能需要保留
文档或测试程序。
如果你下载了DOS版的源码包,nasmXXXs.zip,那nasm目录还会包含完整的NASM源
代码,你可以选择一个Makefiles来重新构造你的NASM版本。
注意源文件`insnsa.c, `insnsd.c, `insnsi.h和`insnsn.c是由standard.mac中
的指令自动生成的,尽管NASM0.98发布版中包含了这些产生的文件,你如果改动了
insns.dat,standard.mac或者文件,可能需要重新构造他们,在将来的源码发布中有
可能将不再包含这些文件,多平台兼容的Perl可以从www.cpan.org上得到。
1.3.2 在unix下安装NASM
如果你得到了Unix下的NASM源码包nasm-x.xx.tar.gz(这里x.xx表示该源码包中的
nasm的版本号),把它解压压到一个目录,比如/usr/local/src。包被解压后会创建
自己的子目录nasm-x.xx
NASM是一个自动配置的安装包:一旦你解压了它,cd到它的目录下,输入./configuer,
该脚本会找到最好的C编译器来构造NASM,并据此建立Makefiles。
一旦NASM被自动配置好后,你可以输入make来构造nasm和ndisasm二进制文件,
然后输入make install把它们安装到/usr/local/bin,并把man页安装到
/usr/local/man/man1下的nasm.1和ndisasm.1或者你可以给配置脚本一个
--prefix选项来指定安装目录,或者也可以自己来安装。
NASM还附带一套处理RDOFF目标文件格式的实用程序,它们在rdoff子目录下,
你可以用make rdf来构造它们,并使用make rdf_install来安装。如果你需
要的话。
如果NASM在自动配置的时候失败了,你还是可以使用文件Makefile.unx来编译它们,
把这个文件改名为Makefile,然后输入make。在rdoff子目录下同样有一个
Makefile.unx文件。
第二章 运行NASM
-----------------------
2.1 NASM命令行语法
要汇编一个文件,你可以以下面的格式执行一个命令:
nasm -f [-o
第一章: 简介
-----------------------
1.1 什么是NASM
NASM是一个为可移植性与模块化而设计的一个80x86的汇编器。它支持相当多
的目标文件格式,包括Linux和NetBSD/FreeBSD,a.out,ELF,COFF,微软16
位的OBJ和Win32。它还可以输出纯二进制文件。它的语法设计得相当的简
洁易懂,和Intel语法相似但更简单。它支持Pentium,P6,MMX,3DNow!,
SSE and SSE2指令集,
1.1.1 为什么还需要一个汇编器?
NASM当初被设计出来的想法是comp.lang.asm.x86(或者可能是alt.lang.asm
,我忘了),从本质上讲,是因为没有一个好的免费的x86系例的汇编器可以使用,
所以,必须有人来写一个。
(*)a86不错,但不是免费的,而且你不可能得到32位代码编写的功能,除非你
付费,它只使用在dos上。
(*) gas是免费的,而且在dos下和unix下都可以使用,但是它是作为gcc的一
个后台而设计的,并不是很好,gcc一直就提供给它绝对正确的代码,所以它的
错误检测功能相当弱,还有就是对于任何一个想真正利用它写点东西的人来讲,
它的语法简直太可怕了,并且你无法在里面写正确的16位代码。
(*) as86是专门为Minix和Linux设计的,但看上去并没有很多文档可以参考。
(*) MASM不是很好,并且相当贵,还且只能运行在DOS下。
(*) TASM好一些,但却极入与MASM保持兼容,这就意味着无数的伪操作码和繁琐
的约定,并且它的语法本质上就是MASM的,伴随着的就是一些自相矛盾和奇怪的
东西。它也是相当贵的,并且只能运行在DOS下。
所以,只有NASM才能使您愉悦得编程。目前,它仍在原型设计阶段-我们不期望它
能够超越所有的这些汇编器。但请您发给我们bug报告,修正意见,和其他有用的
信息,还有其他任何你手头有的对我们有用的信息(感谢所有已经这样在做了的
人们),我们还会不断地改进它。
1.1.2 许可条件
请阅读作为NASM发布的一部分的文件Licence,只有在该许可条件下你才可以使
用NASM。
1.2 联系信息
当前版本的NASM(0.98.08)由一个开发小组在维护,你可以从nasm-devel邮件列表
中得到(看下面的链接),如果你想要报告bug,请先阅读10.2节
NASM有一个主页:http://www.web-sites.co.uk/nasm,更多的信息还可以在
`http://nasm.2y.net/上获取。
最初的作者你可以通过email:`jules@dsf.org.uk和`anakin@pobox.com和他们联
系,但后来的开发小组并不在其中。
最新的NASM发布被上传至官方网站`http://www.web-sites.co.uk/nasm和`ftp.kernel.org,
`ibiblio.org
公告被发布至`comp.lang.asm.x86, `alt.lang.asm 和`comp.os.linux.announce
如果你想了解NASM beta版的发布,和当前的开发状态,请通过在
`http://groups.yahoo.com/group/nasm-devel,
`http://www.pairlist.net/mailman/listinfo/nasm-devel and
`http://sourceforge.net/projects/nasm
注册来捐助nasm-devel邮件列表。
在网站Sourceforge上的列表是较好的一个列表,它也是最新nasm源代码与发布的
一个网站,另外的列表也是公开的,但有可能不会被继续长期支持。
1.3 安装
1.3.1 在dos和Windows下安装NASM
如果你拿到了NASM的DOS安装包,nasmXXX.zip(这里.XXX表示该安装包的NASM版
本号),把它解压到它自己的目录下(比如:‘c:\nasm)
该包中会包含有四个可执行文件:NASM可拟行文件nasm.exe和nasmw.exe,还有
NDISASM可执行文件ndisasm.exe和ndisasmw.exe。文件名以w结尾的是Win32
可执行格式。是运行在Windows 95或Windows NT的Intel处理器上的,另外的是
16位的DOS可执行文件。
NASM运行时需要的唯一文件就是它自己的可执行文件,所以可以拷贝nasm.exe
和nasmw.exe的其中一个到你自己的路径下,或者可以编写一个autoexec.bat把
nasm的路径加到你的PATH环境变量中去。(如果你只安装了Win32版本的,你可能
希望把文件名改成nasm.exe。)
就这样,NASM装好了。你不需要为了运行nasm而让nasm目录一直存在(除非你把它
加到了你的PATH中,所以如果你需要节省空间,你可删掉它,但是,你可能需要保留
文档或测试程序。
如果你下载了DOS版的源码包,nasmXXXs.zip,那nasm目录还会包含完整的NASM源
代码,你可以选择一个Makefiles来重新构造你的NASM版本。
注意源文件`insnsa.c, `insnsd.c, `insnsi.h和`insnsn.c是由standard.mac中
的指令自动生成的,尽管NASM0.98发布版中包含了这些产生的文件,你如果改动了
insns.dat,standard.mac或者文件,可能需要重新构造他们,在将来的源码发布中有
可能将不再包含这些文件,多平台兼容的Perl可以从www.cpan.org上得到。
1.3.2 在unix下安装NASM
如果你得到了Unix下的NASM源码包nasm-x.xx.tar.gz(这里x.xx表示该源码包中的
nasm的版本号),把它解压压到一个目录,比如/usr/local/src。包被解压后会创建
自己的子目录nasm-x.xx
NASM是一个自动配置的安装包:一旦你解压了它,cd到它的目录下,输入./configuer,
该脚本会找到最好的C编译器来构造NASM,并据此建立Makefiles。
一旦NASM被自动配置好后,你可以输入make来构造nasm和ndisasm二进制文件,
然后输入make install把它们安装到/usr/local/bin,并把man页安装到
/usr/local/man/man1下的nasm.1和ndisasm.1或者你可以给配置脚本一个
--prefix选项来指定安装目录,或者也可以自己来安装。
NASM还附带一套处理RDOFF目标文件格式的实用程序,它们在rdoff子目录下,
你可以用make rdf来构造它们,并使用make rdf_install来安装。如果你需
要的话。
如果NASM在自动配置的时候失败了,你还是可以使用文件Makefile.unx来编译它们,
把这个文件改名为Makefile,然后输入make。在rdoff子目录下同样有一个
Makefile.unx文件。
第二章 运行NASM
-----------------------
2.1 NASM命令行语法
要汇编一个文件,你可以以下面的格式执行一个命令:
nasm -f
Labels:
asm
Subscribe to:
Posts (Atom)