当前位置:首页 » 操作系统 » PHP幸运28源码

PHP幸运28源码

发布时间: 2025-04-28 07:34:18

Ⅰ 英雄无敌3

英雄无敌3特长代码2006-10-02 03:3800 箭术
01 弓箭手
02 狮鹫
03 航海术
04 理财术
05 剑士
06 驽车
07 骑兵
08 急救术
09 圣灵佑佐
0a 虚弱无力
0b 霹雳寒冰
0c 僧侣
0d 鹰眼术
0e 祈祷
0f +350金钱
10 防御术
11 矮人
12 +350金钱
13 枯木卫士
14 抵抗力
15 精灵
16 独角兽
17 后勤学
18 屠戮成性
19 疗伤
1a 智力
1b 急救术
1c 鹰眼术
1d 幸运之神
1e 霹雳寒冰
1f 飞马
20 石像怪
21 神怪
22 人偶
23 防御术
24 驽车
25 蛇女
26 +1水银
27 神怪
28 蛊惑人心
29 神秘术
2a 鹰眼术
2b 幸运之神
2c 法师
2d 连锁闪电
2e 加速攻击
2f +350金钱
30 地狱猎犬
31 火精灵
32 恶鬼
33 小怪物
34 +350金钱
35 歌革
36 驽车
37 邪神
38 智力
39 地狱烈焰
3a 神秘术
3b 虚弱无力
3c +1硫磺
3d 嗜血奇术
3e 魔力
3f 连珠火球
40 行尸
41 吸血鬼
42 尸巫
43 幽灵
44 暗黑骑士
45 招魂术
46 +350金钱
47 骷髅兵
48 死亡波纹
49 流星火雨
4a 魔力
4b 鹰眼术
4c 聚灵奇术
4d 护体石肤
4e 招魂术
4f +350金钱
50 鹰身女妖
51 驽车
52 牛头怪
53 邪眼
54 +350金钱
55 后勤学
56 蝎狮
57 洞穴人
58 转世重生
59 神秘术
5a 魔力
5b 转世重生
5c 鹰眼术
5d 流星火雨
5e +1水晶
5f 护体石肤
60 独眼巨人
61 驽车
62 半兽人
63 大雕
64 大耳怪
65 食人魔
66 进攻术
67 恶狼骑士
68 魔力
69 食人魔
6a 后勤学
6b 攻击加速
6c 百发百中
6d 进攻术
6e 鹰眼术
6f +1宝石
70 蜥蜴
71 狼人
72 蜥蜴人
73 防御术
74 野牛
75 毒蝇
76 驽车
77 飞龙
78 虚弱无力
79 神秘术
7a 航海术
7b 急救术7c 护体石肤7d 魔力7e 智力7f 鹰眼术80 精神元素81 土元素82 火元素83 水元素84 精神元素
85 土元素86 火元素87 水元素88 烈火魔墙89 攻击加速8a 魔法神箭
8b 护体石肤8c 嗜血奇术8d 毁灭之光8e +350金钱8f +350金钱91 火系魔法92 剑士
93 魔幻法师94 幻影射手95 比蒙96 暗黑骑士97 龙98 剑士99 龙9a 食人魔9b 恶魔
http://www.gamerhome.net/bbs/viewthread.php?tid=103905
http://www.wakeofgods.com/ERM%20Research.htm
http://post..com/f?kz=198004578

Ⅱ 开发一个小程序大概得用多少钱

小程序的开发价格主要和开发方式和需求有关:

  1. 企业自主开发
    一支专业的小程序开发团队,往往需要有5名工作人员,职位为:产品经理、前端开发、后端开发、UI设计、测试维护。一名资深的前端、后端开发人员的月薪大约是1万元左右。再加上产品经理、UI设计、测试人员的薪资,企业需要付出的费用大约为8万元/月。再加上招聘费用、管理费用等,企业开发一个小程序大约需要付出10万元左右。

  2. 三方平台模板
    若企业通过第三方平台模板来开发自己的小程序,通常需要付出的费用为几千元。但需要注意的是,这个费用是按年收取的。也就是说,想要长期使用,就需要不断续费。

  3. 委托专业的开发公司开发
    专业的开发公司收费,一般都是根据企业的需求来定的。如果企业的需求比较复杂,付出的费用自然就要高一点。相反,若需求比较简单,付出的费用就要低一点。通常情况下,企业若选择将开发工作交给专业的开发公司来完成,需要付出的费用约为1-10万元左右。

Ⅲ 求一点PHP对TXT文件和文章的操作教程

PHP教程.安装PHP
安装PHP
本章将按下列步骤编译安装以下软件:
* Apache v.1.3.4 -- 世界上最流行的Web服务器。
* gcc v.2.8.1 -- 来源于GNU的C和C++编译器。
* Mysql v.3.22.16b-gamma -- 世界范围内许多人使用的数据库
* PHP v.3.0.11 -- 本书所主要介绍的软件。从http://www.php.net(或此站点的镜象站点)直接下载可以得到较新的版本。本章的其它步骤仍然适用。
* expact -- 用于读取和处理XML文档的函数库。
* phplib -- 用于进行会话管理的PHP函数库。
* libiodbc -- 主要用于存取在非UNIX计算机中数据库的ODBC函数库。
* MyODBC -- PHP、iODBC和MySQL之间的函数库。
即便如此,本章也并不是介绍编译应用程序所需要的那些指令,而只是简单的进行描述。总之,本书的重点是介绍PHP语言,而不是C语言。每一个应用程序的编译对我来说都十分清楚,希望每个读者也能没有困难的进行编译。假如你确实碰到了一些问题,可以在某个IRC频道中有礼貌的询问,或者可以尝试使用新闻组(可以从http://www.dejanews.com访问)得到帮助来解决问题。然而,最好的帮助还是来自http://www.php.net 上的PHP邮件列表。
在开始编译过程之前,让我们先讨论一下如何从错误中恢复。在得到有关错误的帮助以后,可以采用以下的命令重新初始化源码目录:
* rm config.cache--几乎所有的linux应用程序都使用configure命令来检查自己的系统,以寻找有关如何剪裁编译过程的信息。检查的结果将存贮在一个名为config.cache的文件中。如果移走此文件,将迫使配置程序从头开始重新检查系统。
* make distclean--所有的用C语言编写的Linux应用程序都是用make程序编译的。make程序会寻找每一个源文件,以确认是需要编译还是已经编译过的。make distclean命令可以“重置”所有的源程序,以便能重新编译它们。
* make clean--有的应用程序不支持make distclean命令,而用make clean命令来代替。make clean命令“重置”所有的源程序,以便能重新编译它们。
即使要安装的一些应用程序不支持以上的每个命令,但是试一下这些命令也不会有什么害处。

注意:Red Hat v5.2使用glibc库。如果用户从互联网中下载或更新在本章中安装的程序时,必须知道glibc的信息。使用者可以使用rpm -q glibc命令来查出已经安装的glibc库的版本号。在作者的系统中,使用rpm -q glibc命令将显示glibc-2.0.7.29。

2.1 基本概念
也许读者以前从没有编译过Linux应用程序,在这里介绍一些基本概念,以便在出现问题时有可能诊断出问题所在。
tar
tar,即tape archiver,它可以把几个文件组合成一个文件,并可以选择是否进行压缩。这个命令过去通常用来进行备份,以便使数据存储在磁带中。当tar文件被压缩时,它们有一个.gz的后缀;当tar文件没有压缩时,它们有一个.tar的后缀。
gcc
gcc 是GNU的C编译器。它的工作是把人可以看懂的源代码文件编译成机器可以读懂的目标文件。C源文件通常有.c的后缀名,目标文件通常有.o的后缀名。如果编译工作不能正常进行,就是碰到了一个编译期的错误,或者说:语法错误。在大多数情况下,不彻底的编译通常是编译器找不到一个或几个包含文件而产生的。包含文件都有一个.h的扩展名,通常用来定义不同的系统信息,以及将多个不同的.c文件所共有的信息收集在一起。
make
make是一个常用的工具程序,是用来帮助进行编译的。它的工作是用来只编译那些还没有编译过的源文件。编译.c文件将生成一个.o文件,如果.c文件比.o文件更新,也就是说到上次编译之前,源文件被编辑过,那么make将会重新编译.c文件。make指令一般是寻找一个Makefile文件,在这个文件中包含有一个或多个能执行的目标,例如,make clean会告诉make执行清除目标。
ld
ld是GNU的链接程序。它的工作是把所有的目标文件和库链接起来,创建一个单一的可执行文件。幸运的是,基本上不用手工运行这个程序,因为Makefile将会考虑到所有的编译细节。
ldconfig
ldconfig会为在多个库目录(在/etc/ld.so.conf中指定)中寻找共享库。共享库常被多个的应用程序使用,它们的文件名中的某个地方有.so,例如,libqt.so.1.42是一个共享库。在编译完毕之后,有可能需要在/etc/ld.so.conf文件中增加一个目录,并且运行ldconfig -v命令。
./configure
configure将会在计算机中寻找一些关键信息,例如,安装的是哪一种C编译器,包含文件在哪里等等。然后,configure将会按照所用的计算机配置重新修改Makefile文件。应该使用./configure来在当前目录下运行程序,以避免偶然运行$PATH环境变量中的目录下的其他程序。
符号连接
符号连接允许使用者可以为一个已经现有的文件指定另一个不同的名字。例如,使用者有可能想把一个名为libqt.so.1.42的文件称为libqt.so.1。符号连接基本上可以使用户把一个文件复制到另一个不同的目录下面,但事实上并没有真正复制此文件。第二个文件实例只是简单地指向第一个文件实例。使用符号连接至少可以有两点好处,第一点是符号连接可以节省硬盘空间,少到只有16字节;第二点有一点微妙,让我们先看一下一个名为libqt.so.1且指向libqt.so.1.42的符号连接。如果需要升级到libqt.so.1.88,该怎么办呢?用户只要简单地改变符号连接,使libqt.so.1指向libqt.so.1.88,而非libqt.so.1.42即可。这意味着涉及到libqt.so.1的程序将自动地使用这个库的新版本。按照惯例,符号连接常用于完整的发布版本号。换句话说,libqt.so.1.88和libqt.so.2.32应该有两个不同的动态连接(分别为libqt.so.1和libqt.so.2)。
2.2 编译前的准备工作
以下步骤将为系统做好编译前的准备工作。
1. mkdir /usr/local/src -- 我选用/usr/local树作为所安装应用程序的根目录。其他人可能选用/usr/opt、/opt或/var目录。为了更方便的按照本章中的指令操作,请选用/usr/local目录作为根目录。当编译完成以后,用户可以把这些文件复制到不同的目录中。

注意:为了能完成本章中的其它步骤,使用者必须具备root权限。

2. cd /usr/local/src--转到源文件目录下。
3. 下载以下文件(可以从http://www.mtolive.com/phpbook站点下载),或者从本书随机附带的光盘中,复制这些文件到/usr/local/src目录下:
. apache_1.3.4.tar.gz
. gcc-2.8.1.tar.gz
. mysql-3.22.16b-gamma.tar.gz
. php-3.0.11.tar.gz
. expat.tar.gz
. phplib.tar.gz
. libiodb-2.50.3
. myodbc-2.50.24-src
4. 使用tar命令解压缩以上文件。tar的x选项为解开选项,v选项将在屏幕上显示文档中解出来的文件名,z选项为解压缩选项,f选项用于在命令行中指定.tar文件名。
tar xvzf apache_1.3.4.tar.gz
tar xvzf gcc-2.8.1.tar.gz
tar xvzf mysql-3.22.16b-gamma.tar.gz
tar xvzf php-3.0.11.tar.gz
tar xvzf expat.tar.gz
tar xvzf phplib.tar.gz
5. gcc -v
确定系统中现有gcc的版本。如果你的系统不是v2.7.2.3,在下面的操作步骤中采用你的gcc版本号替换v.2.7.2.3。
6. cp `which gcc` /usr/bin/gcc-2.7.2.3
如果需要,把现有的gcc备份,以便以后使用。Linux的另一个优点是可以很容易在目录下存储一个程序的多个版本。
7. httpd -v
确定系统中的Apache的版本号。如果你的系统中的Apache不是v1.3.4,在下面的操作步骤中使用你的版本号替换v1.3.4。
8. mv `which httpd` /usr/sbin/httpd-1.3.4
如果需要,把现有的Apache备份,以便以后使用。

2.3 编译C编译器gcc
使用者应该安装、编译程序的第一个程序就是C编译器。随Red Hat v5.2附带的C编译器是gcc v2.7.2.3,由于它的版本比较低,以至于不能正确的编译PHP。然而gcc v.2.7.3可以用来编译版本较高的gcc v2.8.1。
编译新版本的gcc需要执行:
1. cd /usr/local/src/gcc-2.8.1
转到gcc的顶级目录下。
2. ./configure -prefix=/usr/local/gcc
运行配置程序,强制安装程序在后面的安装过程中使用/usr/local/gcc作为gcc的安装目录。
3. make bootstrap LANGUAGES="c c++" BOOT_CFLAGS="-g -02"
编译新的C和C++编译器。
4. make install LANGUAGES="c c++" BOOT_CFLAGS="-g -02"
安装新的C和C++编译器。
5. mv /usr/local/gcc/bin/gcc /usr/local/gcc/bin/gcc-2.8.1
把新的gcc编译器重新命名,使它的名字中含有版本号。
6. ln -s \
/usr/local/gcc/bin/gcc-2.8.1 \
/usr/bin/gcc-
给新编译过的gcc可执行文件创建一个符号连接/usr/bin/gcc。
7. gcc -v
显示gcc的版本号。如果编译和安装都正常的话,将显示v.2.8.1。

2.3 编译MySQL
现在来编译MySQL。在编译工作完成之后,就可以使用MySQL的应用程序来测试安装。
1. cd /usr/src/mysql-3.22.16a-gamma
转到MySQL的顶级目录下。
2. ./configure --prefix=/usr/local/mysql
运行配置程序,强制安装程序在后面的安装过程中使用/usr/local/mysql作为MySQL的安装目录。
3. make
编译MySQL。
4. make install
安装MySQL。
5. echo "/usr/local/mysql/lib/mysql" >> /etc/ld.so.conf
将MySQL库所在的目录添加进配置文件中。这样,当Linux启动或者执行ldconfig命令时,将会在该配置文件中的目录里搜索库文件。
6. ldconfig -v | grep libmysqlclient
ldconfig命令能读取/etc/ld.so.conf文件中列出的目录,并对在那些目录里找到的库文件进行缓冲。grep命令在ldconfig命令的大量输出结果中查找MySQL库文件,并限定文本以类似下面方式进行显示:
libmysqlclient.so.6 => libmysqlclient.so.6.0.0.
7. echo "/usr/local/mysql/bin/safe_mysqld > /dev/null &" >> /etc/rc.d/rc.local
将MySQL启动指令添加到/etc/rc.d/rc.local文件中,这样每次启动Linux后就会自动运行MySQL。
8. ./scripts/mysql_install_db
初始化数据库。
9. /usr/local/mysql/bin/safe_mysqld > /dev/null &
启动MySQL服务器作为后台程序,为了测试安装,MySQL服务器必须启动。
10. ln -s \
/usr/local/mysql/bin/mysql \
/usr/bin/mysql
我比较喜欢在/usr/bin目录下为安装目录建立符号链接。这一方法减少了PATH环境变量中的目录数量,还可以将不希望其他人运行的MySQL工具程序隐藏起来,(例如,mysqladmin命令)。另一方法是将命令PATH="$PATH:/usr/local/mysql/bin"放在/etc/profile文件中。以上两种方法都很可以采用。
11. ln -s\
/usr/local/mysql/bin/mysqlshow \
/usr/bin/mysqlshow
该指令是让一般用户都可以运行mysqlshow命令。

2.4 测试MySQL
在继续Apache和PHP编译之前,首先来测试MySQL的安装是否成功。正式发布的MySQL带有许多有用的工具,这里我们只使用mysql和mysqlshow命令来进行测试。(如果对数据库不熟悉,请不用担心,诸如用户、表、记录等数据库的概念将在第六章“数据库和SQL”中加以介绍。)
mysqladmin的作用是建立和删除数据库、检查SQL的状态,以及其他一些用途。首先,通过检查版本号以确认是否已经正确安装了MySQL:

>PATH="usr/local/mysql/bin:$PATH"
>mysqladmin version
Ver 7.8 Distrib 3.22.16a-gamma, for pc-linux-gun on i686 TCX Datakonsult AB, by Monty
Server version 3.22.16a-gamma
Protocol version 10
Connection Localhost via UNIX socket
UNIX socket /tmp/mysql.sock
Uptime: 2 hours 30 min 39 sec

Threads: 1 Questions: 7 Slow queries: 0
Opens: 6 Flush tables: 1 Open tables: 2

可以用以下命令观看mysqladmin的所有功能.

mysqladmin --help | less

也许mysqlshow是更让人激动的工具,它能够列出数据库、表和字段名,如下所示:

清单2.1 mysqlshow--显示数据库,表和字段名清单

Page 18(第13行)-19(倒数第10行) , 清单 2.1

最后要介绍的MySQL工具是mysql,这个程序将深入到MySQL的中心,并且可以使你可以立刻在Linux命令行提示下执行SQL语句。在shell模式下运行mysql。

Page 19(倒数第5行)-20(倒数第5行)

小心:设置密码要使用password()函数。如果需要进一步了解,请查阅MySQL文档。

小心:在系统进入正常工作模式的时候,请选用一个比“password”更好的root密码。

2.5 编译iODBC和MyODBC
iODBC是一个实现开放性数据库互连(Open Database connectivity)协议的函数库。它主要用于连接运行于Microsoft Windows的数据库引擎。
1. cd /usr/local/src/libiodbc-2.50.3
转到iODBC目录。
2. ./configure \
--prefix=/usr/local/iodbc \
--with-iodbc-inidir=/usr/local/etc
运行配置程序,并强制设置iODBC为安装到目录/usr/local/iodbc下。此外,应确认odbc的初始化文件为/etc/odbc.ini。
3. make
4. make install
把库文件复制到目录/usr/local/iodbc/lib下,并且把包含文件复制到目录/usr/local/iodbc/include下。
5. cd /usr/local/src/myodbc-2.50.24
转到MyODBC目录下。
6. ./configure \
--prefix=/usr/local/myodbc \
--with-mysql-sources=/usr/local/mysql-3.22.16a-gamma \
--with-odbc-ini=/etc/odbc.ini \
--with-iodbc=/usr/local/iodbc
运行MyODBC配置程序。
7. make
8. make install
把库复制到目录/usr/local/myodbc/lib下。
2.6 编译PHP
编译PHP比编译以前的应用程序更复杂,这是因为编译PHP事实上是expat、Apache和PHP的组合。编译的结果将生成一个带有PHP的Apache版本。为了要编译PHP,可以采用以下步骤:
1. cd /usr/local/src/expat
转到expat目录。
2. make
编译expat源文件。
3. 把以下命令加到Makefile文件中,必须保证在输入ar和ralib之前使用的是Tab键。
libexpat.a: $(OBJS)
ar -rc $@ $(OBJS)
ranlib $@
4. make libexpat.a
把expat目标文件组合成库文件。
5. mv libexpat.a /usr/local/lib
PHP配置文件在/usr/local/lib目录下寻找libexpat.a文件。把libexpat.a文件移到PHP配置文件已知的目录下,可以为以后的操作减少麻烦。
6. cd /usr/local/src/php-3.0.11
转到PHP的顶级目录下。
7. mkdir /usr/local/include/xml
确认/usr/local/include/xml目录是存在的。
8. ln -s \
/usr/local/src/expat/xmltok/xmltok.h \
/usr/local/include/xml/xmltok.h
当能创建符号连接时,为什么还要复制呢?
9. ln -s \
/usr/local/src/expat/xmlparse.h \
/usr/local/include/xml/xmlparse.h
这是PHP为了能够正确地被编译所需要的另一个包含文件。
10. cd /usr/local/src/apache_1.3.4
转到Apache的顶级目录下。
11. ./configure -prefix=/usr/local/apache
运行配置程序,强制设置Apache安装目录为/usr/local/apache。
12. cd /usr/local/src/php-3.0.11
转到PHP的顶级目录下。
13. ./configure \
--with-apache=../apache_1.3.4 \
--with-iodbc=/usr/local/iodbc \
--with-mysql=/usr/local/mysql \
--with-xml
运行配置程序,并且告诉配置程序支持Apache、MySQL和XML。
14. make
编译PHP源文件。
15. make install
安装已编译的文件。PHP库文件会被放在Apache的模块目录下,这样可以在编译Apache时能找到它们。
16. cd /usr/local/src/apache_1.3.4
转到Apache的顶级目录下。
17. ./configure \
--prefix=/usr/local/apache \
--active-mole=src/moles/php3/libphp3.a
再一次配置Apache,这次告诉Apache要加载PHP模块。
18. make
编译Apache源文件。
19. make install
安装已编译的文件。
20. mv \
/usr/local/apache/bin/httpd \
/usr/local/apache/bin/httpd-1.3.4
重新命名新创建的httpd可执行文件,这样就能够安装多种版本。
21. ln -s \
/usr/local/apache/bin/httpd-1.3.4 \
/usr/sbin/httpd
建立一个指向新的可执行文件的符号链接。
22. httpd -v
证实可以访问新的可执行文件。该命令的执行结果将显示版本1.3.4,而且建立日期也应该正确无误。
23. 编辑/usr/local/apache/conf/http.conf文件,搜索AddType并确认下列行未加注释。
AddType application/x-httpd-php3 .phtml
AddType application/x-httpd-php3 .php3
AddType application/x-httpd-php3-source .phps
24. 接着编辑/usr/local/apache/conf/http.conf文件,搜索DirectoryIndex并将index.php3放在行尾。
25. 创建一个名为/usr/local/local/php3.ini的文件,该文件应包含如下的命令行:
include_path=.:/usr/local/apache/php/
auto_prepend_file=/usr/local/apache/php/prepend.php3
track_vars = on
magic_quotes_gpc = on
sendmail_path /usr/sbin/sendmail -t
26. ln -s \
/usr/local/src/php-3.0.11/doc/manual.html \
/usr/local/src/php-3.0.11/doc/index.html
建立一个符号链接,这样绝大多数Web浏览器就会正确地自动显示PHP文件起始页。
27. ln -s \
/usr/local/src/php-3.0.11/doc \
/usr/local/apache/htdocs/phpdocs
建立一个符号链接,这样就可以通过http://localhost/phpdocs/, 来访问PHP文档。
28. 创建一个叫做/usr/local/apache/htdocs/robots.txt的文件,这样就可避免搜索引擎为PHPLIB,phpMyAdmin和PHP文件建立索引,新创建的文件应包括如下设置行:
#robots.txt for (hostname)
User-agent *
Disallow: /phpdocs/
Disallow: /php/
Disallow:/phpMyAdmin/

2.7 安装PHPLIB
在阅读第15章“处理并发的访问”之前,应先安装PHPLIB,可以按照下列步骤进行安装:
1. 以root用户或其他任何可以在/usr/local/apache目录下写文件的用户注册。
2. cd /usr/local/apache/
在开始下载文件之前,进入到Web服务器的顶级目录下。
3. 从如下Web网址下载最新版本的文件,注意gz后缀的文件名,以免它的文件名phplib.tar.gz被改动。
http://phplib.shonline.de/
4. tar xv2f phplib.tar.gz
解压缩PHPLIB模块。
5. 编辑/usr/local/lib/php3.ini文件,应包括下列设置内容:

include_path=.:/usr/local/apache/phplib-6.1/php
auto_prepend_file = /usr/local/apache/phplib-
6.1/php/prepend.php3
track_vars = on
magic_quotes_gpc = on
sendmail_path /usr/sbin/sendmail -t

6. 建立一个名为poe_sessions的mysql数据库。我一般使用phpMyAdmin,如果愿意,也可以使用SQL命令创建数据库。
7. cd /usr/local/apache/phplib-6.1/stuff
进入建表目录下。
8. mysql php_book --user=root --password <
create_database.mysql
创建PHPLIB所需要的数据库表。
9. 使用以下值在mysql数据库表中增加一条新记录:

host: %
password: <-- no password.
select_priv: Yes
insert_priv: Yes
update_priv: Yes
delete_priv: Yes
for users named "kris","user01",and "user02".

注意:也可以使用如下SQL语句:

INSERT INTO
user
(
Host
,User
,Password
,Select_priv
,Insert_priv
,Update_priv
,Delete_priv
)
VALUES (
'%'
, 'kris'
, ''
, 'Y', 'Y', 'Y', 'Y'
)

10. 使用以下值在mysql数据库的db表中增加一条新记录:

host: %
db: poe_sessions
select_priv: Yes
insert_priv: Yes
update_priv: Yes
delete_priv: Yes
for users named "kris","user01",and "user03".

注意:也可以使用如下SQL程序。

INSERT INTO
db
(
Host
,Db
,User
,Select_priv
,Insert_priv
,Update_priv
,Delete_priv
)
VALUES (
'%'
, 'poe_sessions '
, 'kris'
, 'Y', 'Y', 'Y', 'Y'
)

11. /usr/local/mysql/bin/mysqladmin -u root -p reload
重新装载MySQL权限表。
12. 在php_book数据库中创建两个PHPLIB已认证的用户(user01和user02),可以使用如下SQL语句:

INSERT INTO
auto_user
(
uid
,username
,password
,perms
) VALUES (
''
,'user01'
,'test'
,'admin'
)

INSERT INTO
auth_user
(
uid
,username
,password
,perms
) VALUES (
''
,'user02'
,'test'
,'admin'
)

13. mv\ /use/local/apache/phplib-6.1/pages \
/use/local/apach/htdocs
将演示子目录置于Web服务器的根目录下,这样就可以用浏览器访问它。
14. 编辑文本文件/usr/local/apache/htdocs/robots.txt,使它包括如下内容:

Disallow: /phplib/

15. 使用Web浏览器访问http://localhost/phplib/。将会看到如图2.1所示页面。

Page 29 图2.1

图2.1 重新装载两次以后出现的PHPLIB演示页面

2.8 测试Linux环境下的ODBC
对所装软件进行基本测试是很重要的,特别是需要手工编辑配置文件时。如下的测试可确保你的MySQL驱动程序运行正常,并且编译的iODBC库文件也是正确无误的。
1. 使用清单2.1所列的内容,建立一个名为/usr/local/etc/odbc.ini的文件。

清单 2.2 /usr/local/etc/odbc.ini- 系统ODBC 配置文件
;
;odbc.ini
;

[ODBC Data Sources]
mysql = mysql

[php_book]
driver = /usr/local/myodbc/lib/libmyodbc.so
host = localhost
database = mysql
user = root

2. cd /usr/local/src/libiodbc-2.50.3/samples
3. ./odbctest
运行ODBC测试程序。
4. DSN=mysql;PWD=password
告诉测试程序要访问的数据源。
5. select host,user from user
在提示符下执行一个SQL语句,如果有回应,说明iODBC和MySQL已经可以使用了。
现在,在计算机中已经安装好了ODBC,由于它的安装步骤不太清晰而且文档又很少,所以本章中讲述了其安装和测试指令,然而鉴于ODBC主要是一项Microsoft技术,且很少在Linux中用到,所以这里是本书中最后一次有关介绍。
2.9 总结:
本章讲述了如何获取PHP,及在计算机中进行安装所需要的步骤,编译了内建PHP的特定版本Apache 服务器。同时,还安装了将在第15章“中场四:处理并发访问”中用所到的PHPLIB。

Ⅳ CPU和CPUID是什么关系

在 Linux 2.4 内核中,用户态 Ring3 代码请求内核态 Ring0 代码完成某些功能是通过系统调用完成的,而系统调用的是通过软中断指令(int 0x80)实现的。在 x86 保护模式中,处理 INT 中断指令时,CPU 首先从中断描述表 IDT 取出对应的门描述符,判断门描述符的种类,然后检查门描述符的级别 DPL 和 INT 指令调用者的级别 CPL,当 CPL<=DPL 也就是说 INT 调用者级别高于描述符指定级别时,才能成功调用,最后再根据描述符的内容,进行压栈、跳转、权限级别提升。内核代码执行完毕之后,调用 IRET 指令返回,IRET 指令恢复用户栈,并跳转会低级别的代码。

其实,在发生系统调用,由 Ring3 进入 Ring0 的这个过程浪费了不少的 CPU 周期,例如,系统调用必然需要由 Ring3 进入 Ring0(由内核调用 INT 指令的方式除外,这多半属于 Hacker 的内核模块所为),权限提升之前和之后的级别是固定的,CPL 肯定是 3,而 INT 80 的 DPL 肯定也是 3,这样 CPU 检查门描述符的 DPL 和调用者的 CPL 就是完全没必要。正是由于如此,Intel x86 CPU 从 PII 300(Family 6,Model 3,Stepping 3)之后,开始支持新的系统调用指令 sysenter/sysexit。sysenter 指令用于由 Ring3 进入 Ring0,SYSEXIT 指令用于由 Ring0 返回 Ring3。由于没有特权级别检查的处理,也没有压栈的操作,所以执行速度比 INT n/IRET 快了不少。

不同系统调用方式的性能比较:

下面是一些来自互联网的有关 sysenter/sysexit 指令和 INT n/IRET 指令在 Intel Pentium CPU 上的性能对比:

表1:系统调用性能测试测试硬件:Intel® Pentium® III CPU, 450 MHzProcessor Family: 6 Model: 7 Stepping: 2

用户模式花费的时间 核心模式花费的时间
基于 sysenter/sysexit 指令的系统调用 9.833 microseconds 6.833 microseconds
基于中断 INT n 指令的系统调用 17.500 microseconds 7.000 microseconds

数据来源:[1]

数据来源:[2]

表2:各种 CPU 上 INT 0x80 和 SYSENTER 执行速度的比较

CPU Int0x80 sysenter
Athlon XP 1600+ 277 169
800MHz mode 1 athlon 279 170
2.8GHz p4 northwood ht 1152 442

上述数据为对 100000 次 getppid() 系统调用所花费的 CPU 时钟周期取的平均值
数据来源[3]

自这种技术推出之后,人们一直在考虑在 Linux 中加入对这种指令的支持,在 Kernel.org 的邮件列表中,主题为 "Intel P6 vs P7 system call performance" 的大量邮件讨论了采用这种指令的必要性,邮件中列举的理由主要是 Intel 在 Pentium 4 的设计上存在问题,造成 Pentium 4 使用中断方式执行的系统调用比 Pentium 3 以及 AMD Athlon 所耗费的 CPU 时钟周期多上 5~10 倍。因此,在 Pentium 4 平台上,通过 sysenter/sysexit 指令来执行系统调用已经是刻不容缓的需求。

sysenter/sysexit 系统调用的机制:

在 Intel 的软件开发者手册第二、三卷(Vol.2B,Vol.3)中,4.8.7 节是关于 sysenter/sysexit 指令的详细描述。手册中说明,sysenter 指令可用于特权级 3 的用户代码调用特权级 0 的系统内核代码,而 SYSEXIT 指令则用于特权级 0 的系统代码返回用户空间中。sysenter 指令可以在 3,2,1 这三个特权级别调用(Linux 中只用到了特权级 3),而 SYSEXIT 指令只能从特权级 0 调用。

执行 sysenter 指令的系统必须满足两个条件:1.目标 Ring 0 代码段必须是平坦模式(Flat Mode)的 4GB 的可读可执行的非一致代码段。2.目标 RING0 堆栈段必须是平坦模式(Flat Mode)的 4GB 的可读可写向上扩展的栈段。

在 Intel 的手册中,还提到了 sysenter/sysexit 和 int n/iret 指令的一个区别,那就是 sysenter/sysexit 指令并不成对,sysenter 指令并不会把 SYSEXIT 所需的返回地址压栈,sysexit 返回的地址并不一定是 sysenter 指令的下一个指令地址。调用 sysenter/sysexit 指令地址的跳转是通过设置一组特殊寄存器实现的。这些寄存器包括:

SYSENTER_CS_MSR - 用于指定要执行的 Ring 0 代码的代码段选择符,由它还能得出目标 Ring 0 所用堆栈段的段选择符;

SYSENTER_EIP_MSR - 用于指定要执行的 Ring 0 代码的起始地址;

SYSENTER_ESP_MSR-用于指定要执行的Ring 0代码所使用的栈指针

这些寄存器可以通过 wrmsr 指令来设置,执行 wrmsr 指令时,通过寄存器 edx、eax 指定设置的值,edx 指定值的高 32 位,eax 指定值的低 32 位,在设置上述寄存器时,edx 都是 0,通过寄存器 ecx 指定填充的 MSR 寄存器,sysenter_CS_MSR、sysenter_ESP_MSR、sysenter_EIP_MSR 寄存器分别对应 0x174、0x175、0x176,需要注意的是,wrmsr 指令只能在 Ring 0 执行。

这里还要介绍一个特性,就是 Ring0、Ring3 的代码段描述符和堆栈段描述符在全局描述符表 GDT 中是顺序排列的,这样只需知道 SYSENTER_CS_MSR 中指定的 Ring0 的代码段描述符,就可以推算出 Ring0 的堆栈段描述符以及 Ring3 的代码段描述符和堆栈段描述符。

在 Ring3 的代码调用了 sysenter 指令之后,CPU 会做出如下的操作:

1. 将 SYSENTER_CS_MSR 的值装载到 cs 寄存器

2. 将 SYSENTER_EIP_MSR 的值装载到 eip 寄存器

3. 将 SYSENTER_CS_MSR 的值加 8(Ring0 的堆栈段描述符)装载到 ss 寄存器。

4. 将 SYSENTER_ESP_MSR 的值装载到 esp 寄存器

5. 将特权级切换到 Ring0

6. 如果 EFLAGS 寄存器的 VM 标志被置位,则清除该标志

7. 开始执行指定的 Ring0 代码

在 Ring0 代码执行完毕,调用 SYSEXIT 指令退回 Ring3 时,CPU 会做出如下操作:

1. 将 SYSENTER_CS_MSR 的值加 16(Ring3 的代码段描述符)装载到 cs 寄存器

2. 将寄存器 edx 的值装载到 eip 寄存器

3. 将 SYSENTER_CS_MSR 的值加 24(Ring3 的堆栈段描述符)装载到 ss 寄存器

4. 将寄存器 ecx 的值装载到 esp 寄存器

5. 将特权级切换到 Ring3

6. 继续执行 Ring3 的代码

由此可知,在调用 SYSENTER 进入 Ring0 之前,一定需要通过 wrmsr 指令设置好 Ring0 代码的相关信息,在调用 SYSEXIT 之前,还要保证寄存器edx、ecx 的正确性。

如何得知 CPU 是否支持 sysenter/sysexit 指令

根据 Intel 的 CPU 手册,我们可以通过 CPUID 指令来查看 CPU 是否支持 sysenter/sysexit 指令,做法是将 EAX 寄存器赋值 1,调用 CPUID 指令,寄存器 edx 中第 11 位(这一位名称为 SEP)就表示是否支持。在调用 CPUID 指令之后,还需要查看 CPU 的 Family、Model、Stepping 属性来确认,因为据称 Pentium Pro 处理器会报告 SEP 但是却不支持 sysenter/sysexit 指令。只有 Family 大于等于 6,Model 大于等于 3,Stepping 大于等于 3 的时候,才能确认 CPU 支持 sysenter/sysexit 指令。

Linux 对 sysenter/sysexit 系统调用方式的支持

在 2.4 内核中,直到最近的发布的 2.4.26-rc2 版本,没有加入对 sysenter/sysexit 指令的支持。而对 sysenter/sysexit 指令的支持最早是2002 年,由 Linus Torvalds 编写并首次加入 2.5 版内核中的,经过多方测试和多次 patch,最终正式加入到了 2.6 版本的内核中。

http://kerneltrap.org/node/view/531/1996

http://lwn.net/Articles/18414/

具体谈到系统调用的完成,不能孤立的看内核的代码,我们知道,系统调用多被封装成库函数提供给应用程序调用,应用程序调用库函数后,由 glibc 库负责进入内核调用系统调用函数。在 2.4 内核加上老版的 glibc 的情况下,库函数所做的就是通过 int 指令来完成系统调用,而内核提供的系统调用接口很简单,只要在 IDT 中提供 INT 0x80 的入口,库就可以完成中断调用。

在 2.6 内核中,内核代码同时包含了对 int 0x80 中断方式和 sysenter 指令方式调用的支持,因此内核会给用户空间提供一段入口代码,内核启动时根据 CPU 类型,决定这段代码采取哪种系统调用方式。对于 glibc 来说,无需考虑系统调用方式,直接调用这段入口代码,即可完成系统调用。这样做还可以尽量减少对 glibc 的改动,在 glibc 的源码中,只需将 "int $0x80" 指令替换成 "call 入口地址" 即可。

下面,以 2.6.0 的内核代码配合支持 SYSENTER 调用方式的 glibc2.3.3 为例,分析一下系统调用的具体实现。

内核在启动时做的准备

前面说到的这段入口代码,根据调用方式分为两个文件,支持 sysenter 指令的代码包含在文件 arch/i386/kernel/vsyscall-sysenter.S 中,支持int中断的代码包含在arch/i386/kernel/vsyscall-int80.S中,入口名都是 __kernel_vsyscall,这两个文件编译出的二进制代码由arch/i386/kernel/vsyscall.S所包含,并导出起始地址和结束地址。

2.6 内核在启动的时候,调用了新增的函数sysenter_setup(参见arch/i386/kernel/sysenter.c),在这个函数中,内核将虚拟内存空间的顶端一个固定地址页面(从0xffffe000开始到0xffffeffff的4k大小)映射到一个空闲的物理内存页面。然后通过之前执行CPUID的指令得到的数据,检测CPU是否支持sysenter/sysexit指令。如果CPU不支持,那么将采用INT调用方式的入口代码拷贝到这个页面中,然后返回。相反,如果CPU支持SYSETER/SYSEXIT指令,则将采用SYSENTER调用方式的入口代码拷贝到这个页面中。使用宏 on_each_cpu在每个CPU上执行enable_sep_cpu这个函数。

在enable_sep_cpu函数中,内核将当前CPU的TSS结构中的ss1设置为当前内核使用的代码段,esp1设置为该TSS结构中保留的一个256字节大小的堆栈。在X86中,TSS结构中ss1和esp1本来是用于保存Ring 1进程的堆栈段和堆栈指针的。由于内核在启动时,并不能预知调用sysenter指令进入Ring 0后esp的确切值,而应用程序又无权调用wrmsr指令动态设置,所以此时就借用esp1指向一个固定的缓冲区来填充这个MSR寄存器,由于Ring 1根本没被启用,所以并不会对系统造成任何影响。在下面的文章中会介绍进入Ring 0之后,内核如何修复ESP来指向正确的Ring 0堆栈。关于TSS结构更细节的应用可参考代码include/asm-i386/processor.h)。

然后,内核通过wrmsr(msr,val1,val2)宏调用wrmsr指令对当前CPU设置MSR寄存器,可以看出调用宏的第三个参数即edx都被设置为0。其中SYSENTER_CS_MSR的值被设置为当前内核用的所在代码段;SYSENTER_ESP_MSR被设置为esp1,即指向当前CPU的 TSS结构中的堆栈;SYSENTER_EIP_MSR则被设置为内核中处理sysenter指令的接口函数sysenter_entry(参见 arch/i386/kernel/entry.S)。这样,sysenter指令的准备工作就完成了。

通过内核在启动时进行这样的设置,在每个进程的进程空间中,都能访问到内核所映射的这个代码页面,当然这个页面对于应用程序来说是只读的。我们通过新版的ldd工具查看任意一个可执行程序,可以看到下面的结果:

[root@test]# file dynamic
dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped
[root@test]# ldd dynamic
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/tls/libc.so.6 (0x4002c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

这个所谓的"linux-gate.so.1"的内容就是内核映射的代码,系统中其实并不存在这样一个链接库文件,它的名字是由ldd自己起的,而在老版本的ldd中,虽然能够检测到这段代码,但是由于没有命名而且在系统中找不到对应链接库文件,所以会有一些显示上的问题。有关这个问题的背景,可以参考下面这个网址: http://sources.redhat.com/ml/libc-alpha/2003-09/msg00263.html。

由用户态经库函数进入内核态

为了配合内核使用新的系统调用方式,glibc中要做一定的修改。新的glibc-2.3.2(及其以后版本中)中已经包含了这个改动,在glibc源代码的sysdeps/unix/sysv/linux/i386/sysdep.h文件中,处理系统调用的宏INTERNAL_SYSCALL在不同的编译选项下有不同的结果。在打开支持sysenter/sysexit指令的选项I386_USE_SYSENTER下,系统调用会有两种方式,在静态链接(编译时加上-static选项)情况下,采用"call *_dl_sysinfo"指令;在动态链接情况下,采用"call *%gs:0x10"指令。这两种情况由glibc库采用哪种方法链接,实际上最终都相当于调用某个固定地址的代码。下面我们通过一个小小的程序,配合 gdb来验证。

首先是一个静态编译的程序,代码很简单:

main()
{
getuid();
}

将代码加上static选项用gcc静态编译,然后用gdb装载并反编译main函数。

[root@test opt]# gcc test.c -o ./static -static
[root@test opt]# gdb ./static
(gdb) disassemble main
0x08048204 <main+0>: push %ebp
0x08048205 <main+1>: mov %esp,%ebp
0x08048207 <main+3>: sub $0x8,%esp
0x0804820a <main+6>: and $0xfffffff0,%esp
0x0804820d <main+9>: mov $0x0,%eax
0x08048212 <main+14>: sub %eax,%esp
0x08048214 <main+16>: call 0x804cb20 <__getuid>
0x08048219 <main+21>: leave
0x0804821a <main+22>: ret

可以看出,main函数中调用了__getuid函数,接着反编译__getuid函数。

(gdb) disassemble 0x804cb20
0x0804cb20 <__getuid+0>: push %ebp
0x0804cb21 <__getuid+1>: mov 0x80aa028,%eax
0x0804cb26 <__getuid+6>: mov %esp,%ebp
0x0804cb28 <__getuid+8>: test %eax,%eax
0x0804cb2a <__getuid+10>: jle 0x804cb40 <__getuid+32>
0x0804cb2c <__getuid+12>: mov $0x18,%eax
0x0804cb31 <__getuid+17>: call *0x80aa054
0x0804cb37 <__getuid+23>: pop %ebp
0x0804cb38 <__getuid+24>: ret

上面只是__getuid函数的一部分。可以看到__getuid将eax寄存器赋值为getuid系统调用的功能号0x18然后调用了另一个函数,这个函数的入口在哪里呢?接着查看位于地址0x80aa054的值。

(gdb) X 0x80aa054
0x80aa054 <_dl_sysinfo>: 0x0804d7f6

看起来不像是指向内核映射页面内的代码,但是,可以确认,__dl_sysinfo指针的指向的地址就是0x80aa054。下面,我们试着启动这个程序,然后停在程序第一条语句,再查看这个地方的值。

(gdb) b main
Breakpoint 1 at 0x804820a
(gdb) r
Starting program: /opt/static
Breakpoint 1, 0x0804820a in main ()
(gdb) X 0x80aa054
0x80aa054 <_dl_sysinfo>: 0xffffe400

可以看到,_dl_sysinfo指针指向的数值已经发生了变化,指向了0xffffe400,如果我们继续运行程序,__getuid函数将会调用地址0xffffe400处的代码。

接下来,我们将上面的代码编译成动态链接的方式,即默认方式,用gdb装载并反编译main函数

[root@test opt]# gcc test.c -o ./dynamic
[root@test opt]# gdb ./dynamic
(gdb) disassemble main
0x08048204 <main+0>: push %ebp
0x08048205 <main+1>: mov %esp,%ebp
0x08048207 <main+3>: sub $0x8,%esp
0x0804820a <main+6>: and $0xfffffff0,%esp
0x0804820d <main+9>: mov $0x0,%eax
0x08048212 <main+14>: sub %eax,%esp
0x08048214 <main+16>: call 0x8048288
0x08048219 <main+21>: leave
0x0804821a <main+22>: ret

由于libc库是在程序初始化时才被装载,所以我们先启动程序,并停在main第一条语句,然后反汇编getuid库函数



(gdb) b main
Breakpoint 1 at 0x804820a
(gdb) r
Starting program: /opt/dynamic
Breakpoint 1, 0x0804820a in main ()
(gdb) disassemble getuid
Dump of assembler code for function getuid:
0x40219e50 <__getuid+0>: push %ebp
0x40219e51 <__getuid+1>: mov %esp,%ebp
0x40219e53 <__getuid+3>: push %ebx
0x40219e54 <__getuid+4>: call 0x40219e59 <__getuid+9>
0x40219e59 <__getuid+9>: pop %ebx
0x40219e5a <__getuid+10>: add $0x84b0f,%ebx
0x40219e60 <__getuid+16>: mov 0xffffd87c(%ebx),%eax
0x40219e66 <__getuid+22>: test %eax,%eax
0x40219e68 <__getuid+24>: jle 0x40219e80 <__getuid+48>
0x40219e6a <__getuid+26>: mov $0x18,%eax
0x40219e6f <__getuid+31>: call *%gs:0x10
0x40219e76 <__getuid+38>: pop %ebx
0x40219e77 <__getuid+39>: pop %ebp
0x40219e78 <__getuid+40>: ret

可以看出,库函数getuid将eax寄存器设置为getuid系统调用的调用号0x18,然后调用%gs:0x10所指向的函数。在gdb中,无法查看非DS段的数据内容,所以无法查看%gs:0x10所保存的实际数值,不过我们可以通过编程的办法,内嵌汇编将%gs:0x10的值赋予某个局部变量来得到这个数值,而这个数值也是0xffffe400,具体代码这里就不再赘述。

由此可见,无论是静态还是动态方式,最终我们都来到了0xffffe400这里的一段代码,这里就是内核为我们映射的系统调用入口代码。在gdb中,我们可以直接反汇编来查看这里的代码

(gdb) disassemble 0xffffe400 0xffffe414
Dump of assembler code from 0xffffe400 to 0xffffe414:0xffffe400: push %ecx
0xffffe401: push %edx
0xffffe402: push %ebp
0xffffe403: mov %esp,%ebp
0xffffe405: sysenter
0xffffe407: nop
0xffffe408: nop
0xffffe409: nop
0xffffe40a: nop
0xffffe40b: nop
0xffffe40c: nop
0xffffe40d: nop
0xffffe40e: jmp 0xffffe403
0xffffe410: pop %ebp
0xffffe411: pop %edx
0xffffe412: pop %ecx
0xffffe413: ret
End of assembler mp.

这段代码正是arch/i386/kernel/vsyscall- sysenter.S文件中的代码。其中,在sysenter之前的是入口代码,在0xffffe410开始的是内核返回处理代码(后面提到的 SYSENTER_RETURN即指向这里)。在入口代码中,首先是保存当前的ecx,edx(由于sysexit指令需要使用这两个寄存器)以及 ebp。然后调用sysenter指令,跳转到内核Ring 0代码,也就是sysenter_entry入口处。

内核中的处理和返回

sysenter_entry整个的实现可以参见arch/i386/kernel/entry.S。内核处理SYSENTER的代码和处理INT的代码不太一样。通过sysenter指令进入Ring 0之后,由于当前的ESP并非指向正确的内核栈,而是当前CPU的TSS结构中的一个缓冲区(参见上文),所以首先要解决的是修复ESP,幸运的是,TSS结构中ESP0成员本身就保存有Ring 0状态的ESP值,所以在这里将TSS结构中ESP0的值赋予ESP寄存器。将ESP恢复成指向正确的堆栈之后,由于SYSENTER不是通过调用门进入Ring 0,所以在堆栈中的上下文和使用INT指令的不一样,INT指令进入Ring 0后栈中会保存如下的值。

低地址

返回用户态的EIP
用户态的CS
用户态的EFLAGS
用户态的ESP
用户态的SS(和DS相同)
高地址
因此,为了简化和重用代码,内核会用pushl指令往栈中放入上述各值,值得注意的是,内核在栈中放入的相对应用户态EIP的值,是一个代码标签 SYSENTER_RETURN,在vsyscall-sysenter.S可以看到,它就在sysenter指令的后面(在它们之间,有一段NOP,是内核返回出错时的处理代码)。接下来,处理系统调用的代码就和中断方式的处理代码一模一样了,内核保存所有的寄存器,然后系统调用表找到对应系统调用的入口,完成调用。最后,内核从栈中取出前面存入的用户态的EIP和ESP,存入edx和ecx寄存器,调用SYSEXIT指令返回用户态。返回用户态之后,从栈中取出ESP,edx,ecx,最终返回glibc库。

其它操作系统以及其它硬件平台的支持

值得一提的是,从 Windows XP 开始,Windows 的系统调用方式也从软中断 int 0x2e 转换到采用 sysenter 方式,由于完全不再支持 int 方式,因此 Windows XP 的对 CPU 的最低配置要求是 PentiumII 300MHz。在其它的操作系统例如 *BSD 系列,目前并没有提供对 sysenter 指令的支持。

在 CPU 方面,AMD 的 CPU 支持一套与之对应的指令 SYSCALL/SYSRET。在纯 32 位的 AMD CPU 上,还没有支持 sysenter 指令,而在 AMD 推出的 AMD64 系列 CPU 上,处于某些模式的情况下,CPU 能够支持 sysenter/sysexit 指令。在 Linux 内核针对 AMD64 架构的代码中,采用的还是 SYSCALL/SYSRET 指令。至于这两种指令最终谁将成为标准,目前还无法得出结论。

未来

我们将 Intel 的 sysenter/sysexit 指令,AMD 的 SYSCALL/SYSRET 指令统称为"快速系统调用指令"。"快速系统调用指令"比起中断指令来说,其消耗时间必然会少一些,但是随着 CPU 设计的发展,将来应该不会再出现类似 Intel Pentium4 这样悬殊的差距。而"快速系统调用指令"比起中断方式的系统调用方式,还存在一定局限,例如无法在一个系统调用处理过程中再通过"快速系统调用指令"调用别的系统调用。因此,并不一定每个系统调用都需要通过"快速系统调用指令"来实现。比如,对于复杂的系统调用例如 fork,两种系统调用方式的时间差和系统调用本身运行消耗的时间来比,可以忽略不计,此处采取"快速系统调用指令"方式没有什么必要。而真正应该使用" 快速系统调用指令"方式的,是那些本身运行时间很短,对时间精确性要求高的系统调用,例如 getuid、gettimeofday 等等。因此,采取灵活的手段,针对不同的系统调用采取不同的方式,才能得到最优化的性能和实现最完美的功能。

参考资料

[1] VxWorks Optimized for Intel Architecture, Hdei Nunoe, Wind River, Member of Technical Staff Leo Samson, Wind River, Technical Marketing Engineer David Hillyard, Intel Corporation, Mgr., Platform Architect

[2] Kernel Entry / Kernel Exit , Marcus Voelp & University Karlsruhe

[3] Dave Jones' blog, http://diary.codemonkey.org.uk/index.php?month=12&year=2002

[4] Linux 内核源码 v2.6.0 http://www.kernel.org/ [Linus Torvalds,2004]

[5] GNU C Library glibc 2.3.3 源码 http://www.gnu.org/software/libc/libc.html

Linux Kernel Mailing List 中对系统调用方式的讨论: [5] Linux Kernel Mailing List, "Intel P6 vs P7 system call performance" http://www.ussg.iu.e/hypermail/linux/kernel/0212.1/index.html#1286 http://www.ussg.iu.e/hypermail/linux/kernel/0212.3/index.html#54

Linux 内核首次引入对 sysenter/sysexit 指令的支持: [6] Linux Kernel Mailing List, "Add "sysenter" support on x86, and a "vsyscall" page." http://lwn.net/Articles/18414/

热点内容
电脑日志错误服务器超时未响应 发布:2025-04-28 16:41:54 浏览:883
带点文件夹删除 发布:2025-04-28 16:24:16 浏览:196
Java数据库教学 发布:2025-04-28 16:20:32 浏览:430
电脑编程难吗 发布:2025-04-28 16:19:10 浏览:908
手机存储电影删除怎样恢复 发布:2025-04-28 16:19:02 浏览:397
php下载网页 发布:2025-04-28 16:19:01 浏览:191
安卓手机怎么设置只打电话 发布:2025-04-28 16:18:59 浏览:33
正态分布随机数C语言 发布:2025-04-28 16:07:11 浏览:889
安卓没有原生设置怎么安装 发布:2025-04-28 16:00:31 浏览:672
青岛少儿编程哪家好 发布:2025-04-28 15:49:53 浏览:848