當前位置:首頁 » 操作系統 » 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:24:16 瀏覽:192
Java資料庫教學 發布:2025-04-28 16:20:32 瀏覽:427
電腦編程難嗎 發布:2025-04-28 16:19:10 瀏覽:907
手機存儲電影刪除怎樣恢復 發布:2025-04-28 16:19:02 瀏覽:394
php下載網頁 發布:2025-04-28 16:19:01 瀏覽:188
安卓手機怎麼設置只打電話 發布:2025-04-28 16:18:59 瀏覽:30
正態分布隨機數C語言 發布:2025-04-28 16:07:11 瀏覽:888
安卓沒有原生設置怎麼安裝 發布:2025-04-28 16:00:31 瀏覽:671
青島少兒編程哪家好 發布:2025-04-28 15:49:53 瀏覽:847
密碼箱密碼正確打不開怎麼辦 發布:2025-04-28 15:49:00 瀏覽:19