FileOutputStream os=Activity.this.openFileOutput(“txtme.txt”,MODE_PRIVATE);—>在/data/data/包名/files/目錄下會創建txtme.txt文件(如果該文件不存在的話),MODE_PRIVATE的文件是應用程序私有的,MODE_WORLD_READABLE則所有應用程序都可以訪問的,MODE_WORLD_WRITEABLE所有應用程序都可以寫,mode_APPEND則是如果要創建的文件存在則新寫入的數據不會覆蓋以前的數據。
package wl.android;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.henii.service.FileService;
import android.content.Context;
import android.test.AndroidTestCase;
import android.util.Log;
public class DataSaveTest extends AndroidTestCase {
private static final String TAG = "DataSaveTest";
public void testSave() throws Exception{
//openFileOutput方法的第一個參數用于指定文件名稱,不能包含路徑分割符“/”,
//參數二為對文件的訪問權限,如果希望有多個權限,則寫成如下形式:
//Context.MODE_PRIVATE + Context.MODE_APPEND (注意是用“+”號)
//如果文件不存在,Android會自動創建它。
//創建的文件保存在/data/data/Activity所在的包/files目錄,
//如/data/data/com.henii.android/files/myText.txt,
//--------------------------------------------------------
//通過Window-Show View-Other,在對話窗口中展開Android文件夾,
//選擇下面的File Explorer視圖,就可以看到創建的文件了
//-------------------------------------------------------
//this.getContext方法獲取相應的當前應用Activity的上下文環境
FileOutputStream outStream = this.getContext().openFileOutput("myText.txt", Context.MODE_PRIVATE + Context.MODE_APPEND);
FileService.save(outStream, "Henii");
}
public void testRead()throws Exception{
//如果您要讀取其他應用的文件的話,你要完整寫出這個文件的路徑,如:
//如/data/data/com.henii.android/files/myText.txt,
//而且不能使用openFileInput方法(因為這個方法的參數是不能帶分隔符“/”的)
//所以您只能使用最一般的讀取文件的方法進行讀取
FileInputStream inStream = this.getContext().openFileInput("myText.txt");
String henii = FileService.read(inStream);
Log.i(TAG, henii);
}
}
posted @
2012-02-08 14:13 小果子 閱讀(6982) |
評論 (0) |
編輯 收藏
摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->/*以下為圖片處理類*/ <?php/* 圖片處理函數功能:縮放、剪切、相框、水印、銳化、旋轉、翻轉、透明度、反色 處理并保存歷史記錄的思路:當有圖片有改動時自動生成一...
閱讀全文
posted @
2012-01-29 14:59 小果子 閱讀(240) |
評論 (0) |
編輯 收藏
1 引言
Mysql的觸發器和存儲過程一樣,都是嵌入到mysql的一段程序。觸發器是mysql5新增的功能,目前線上鳳巢系統、北斗系統以及哥倫布系統使用的數據庫均是mysql5.0.45版本,很多程序比如fc-star管理端,sfrd(das),dorado都會用到觸發器程序,實現對于數據庫增、刪、改引起事件的關聯操作。本文介紹了觸發器的類型和基本使用方法,講述了觸發器使用中容易產生的誤區,從mysql源碼中得到觸發器執行順序的結論,本文最后是實戰遭遇的觸發器經典案例。沒有特殊說明時,本文的實驗均基于mysql5.0.45版本。
2 Mysql觸發器的類型
2.1 Mysql觸發器的基本使用
創建觸發器。創建觸發器語法如下:
CREATE TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_stmt
其中trigger_name標識觸發器名稱,用戶自行指定;
trigger_time標識觸發時機,用before和after替換;
trigger_event標識觸發事件,用insert,update和delete替換;
tbl_name標識建立觸發器的表名,即在哪張表上建立觸發器;
trigger_stmt是觸發器程序體;觸發器程序可以使用begin和end作為開始和結束,中間包含多條語句;
下面給出sfrd一個觸發器實例:
CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER trig_useracct_update
AFTER UPDATE
ON SF_User.useracct FOR EACH ROW
BEGIN
IF OLD.ulevelid = 10101 OR OLD.ulevelid = 10104 THEN
IF NEW.ulevelid = 10101 OR NEW.ulevelid = 10104 THEN
if NEW.ustatid != OLD.ustatid OR NEW.exbudget != OLD.exbudget THEN
INSERT into FC_Output.fcevent set type = 2, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
end if;
ELSE
INSERT into FC_Output.fcevent set type = 1, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
END IF;
END IF;
END;
上述觸發器實例使用了OLD關鍵字和NEW關鍵字。OLD和NEW可以引用觸發器所在表的某一列,在上述實例中,OLD.ulevelid表示表 SF_User.useracct修改之前ulevelid列的值,NEW.ulevelid表示表SF_User.useracct修改之后 ulevelid列的值。另外,如果是insert型觸發器,NEW.ulevelid也表示表SF_User.useracct新增行的 ulevelid列值;如果是delete型觸發器OLD.ulevelid也表示表SF_User.useracct刪除行的ulevelid列原值。
另外,OLD列是只讀的,NEW列則可以在觸發器程序中再次賦值。
上述實例也使用了IF,THEN ,ELSE,END IF等關鍵字。在觸發器程序體中,在beigin和end之間,可以使用順序,判斷,循環等語句,實現一般程序需要的邏輯功能。
查看觸發器。查看觸發器語法如下,如果知道觸發器所在數據庫,以及觸發器名稱等具體信息:
SHOW TRIGGERS from SF_User like "usermaps%"; //查看SF_User庫上名稱和usermaps%匹配的觸發器
如果不了解觸發器的具體的信息,或者需要查看數據庫上所有觸發器,如下:
SHOW TRIGGERS; //查看所有觸發器
用上述方式查看觸發器可以看到數據庫的所有觸發器,不過如果一個庫上的觸發器太多,由于會刷屏,可能沒有辦法查看所有觸發器程序。這時,可以采用如下方式:
Mysql中有一個information_schema.TRIGGERS表,存儲所有庫中的所有觸發器,desc information_schema. TRIGGERS,可以看到表結構:
+----------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| TRIGGER_CATALOG | varchar(512) | YES | | NULL | |
| TRIGGER_SCHEMA | varchar(64) | NO | | | |
| TRIGGER_NAME | varchar(64) | NO | | | |
| EVENT_MANIPULATION | varchar(6) | NO | | | |
| EVENT_OBJECT_CATALOG | varchar(512) | YES | | NULL | |
| EVENT_OBJECT_SCHEMA | varchar(64) | NO | | | |
| EVENT_OBJECT_TABLE | varchar(64) | NO | | | |
| ACTION_ORDER | bigint(4) | NO | | 0 | |
| ACTION_CONDITION | longtext | YES | | NULL | |
| ACTION_STATEMENT | longtext | NO | | | |
| ACTION_ORIENTATION | varchar(9) | NO | | | |
| ACTION_TIMING | varchar(6) | NO | | | |
| ACTION_REFERENCE_OLD_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_NEW_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_OLD_ROW | varchar(3) | NO | | | |
| ACTION_REFERENCE_NEW_ROW | varchar(3) | NO | | | |
| CREATED | datetime | YES | | NULL | |
| SQL_MODE | longtext | NO | | | |
| DEFINER | longtext | NO | | | |
+----------------------------+--------------+------+-----+---------+-------+
這樣,用戶就可以按照自己的需要,查看觸發器,比如使用如下語句查看上述觸發器:
select * from information_schema. TRIGGERS where TRIGGER_NAME= 'trig_useracct_update'\G;
刪除觸發器。刪除觸發器語法如下:
DROP TRIGGER [schema_name.]trigger_name
2.2 Msyql觸發器的trigger_time和trigger_event
現在,重新注意到trigger_time和trigger_event,上文說過, trigger_time可以用before和after替換,表示觸發器程序的執行在sql執行的前還是后;trigger_event可以用 insert,update,delete替換,表示觸發器程序在什么類型的sql下會被觸發。
在一個表上最多建立6個觸發器,即1)before insert型,2)before update型,3)before delete型,4)after insert型,5)after update型,6)after delete型。
觸發器的一個限制是不能同時在一個表上建立2個相同類型的觸發器。這個限制的一個來源是觸發器程序體的“begin和end之間允許運行多個語句”(摘自mysql使用手冊)。
另外還有一點需要注意,msyql除了對insert,update,delete基本操作進行定義外,還定義了load data和replace語句,而load data和replace語句也能引起上述6中類型的觸發器的觸發。
Load data語句用于將一個文件裝入到一個數據表中,相當與一系列insert操作。replace語句一般來說和insert語句很像,只是在表中有 primary key和unique索引時,如果插入的數據和原來primary key和unique索引一致時,會先刪除原來的數據,然后增加一條新數據;也就是說,一條replace sql有時候等價于一條insert sql,有時候等價于一條delete sql加上一條insert sql。即是:
? Insert型觸發器:可能通過insert語句,load data語句,replace語句觸發;
? Update型觸發器:可能通過update語句觸發;
? Delete型觸發器:可能通過delete語句,replace語句觸發;
3 Mysql觸發器的執行順序
先拋出觸發器相關的幾個問題
3.1 如果before類型的觸發器程序執行失敗,sql會執行成功嗎?
實驗如下:
1)在FC_Word.planinfo中建立before觸發器:
DELIMITER |
create trigger trigger_before_planinfo_update
before update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)查看:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1; 觸發觸發器程序;
4)由于不存在FC_Output.abc,before觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次查看:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果before觸發器執行失敗,sql也會執行失敗。
3.2 如果sql執行失敗,會執行after類型的觸發器程序嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
INSERT INTO FC_Output.fcevent set level = 2, type = 2, tabid = 5, userid = NEW.userid, planid = NEW.planid, planstat2 = NEW.planstat2, showprob = NEW.showprob, showrate = NEW.showrate, showfactor = NEW.showfactor, planmode = NEW.planmode;
END
|
2)查看觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
沒有planid=1的記錄
3)執行sql:
mysql> update planinfo set showprob1=200 where planid=1;
4)由于不存在showprob1列,提示錯誤:
ERROR 1054 (42S22): Unknown column 'showprob1' in 'field list'
5)再次查看觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
觸發表中沒有planid=1的記錄,sql在執行失敗時,after型觸發器不會執行。
3.3 如果after類型的觸發器程序執行失敗,sql會回滾嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)查看:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1;觸發觸發器程序;
4)由于不存在FC_Output.abc,after觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次查看:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果after觸發器執行失敗,sql會回滾。
這里需要說明一下,上述實驗所使用的mysql引擎是innodb,innodb引擎也是目前線上鳳巢系統、北斗系統以及哥倫布系統所使用的引擎,在 innodb上所建立的表是事務性表,也就是事務安全的。“對于事務性表,如果觸發程序失敗(以及由此導致的整個語句的失敗),該語句所執行的所有更改將回滾。對于非事務性表,不能執行這類回滾”(摘自mysql使用手冊)。因而,即使語句失敗,失敗之前所作的任何更改依然有效,也就是說,對于 innodb引擎上的數據表,如果觸發器中的sql或引發觸發器的sql執行失效,則事務回滾,所有操作會失效。
3.4 mysql觸發器程序執行的順序
當一個表既有before類型的觸發器,又有after類型的觸發器時;當一條sql語句涉及多個表的update時,sql、觸發器的執行順序經過mysql源碼包裝過,有時比較復雜。
可以先看一段mysql的源代碼,當SQL中update多表的時候,Mysql的執行過程如下(省去了無關代碼):
/* 遍歷要更新的所有表 */
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
org_updated = updated
/* 如果有 BEFORE 觸發器,則執行;如果執行失敗,跳到err2位置 */
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,TRG_ACTION_BEFORE, TRUE))
goto err2;
/*執行更新,如果更新失敗,跳到err位置*/
if(local_error=table->file->update_row(table->record[1], table->record[0])))
goto err;
updated++; // 更新計數器
/* 如果有 AFTER 觸發器,則執行;如果執行失敗,跳到err2位置*/
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
goto err2;
err:
{
/*標志錯誤信息,寫日志等*/
}
err2:
{
/*恢復執行過的操作*/
check_opt_it.rewind();
/*如果執行了更新,且表是有事務的,做標志*/
if (updated != org_updated)
{
if (table->file->has_transactions())
transactional_tables= 1;
}
}
}
從上面代碼可以找到本章開始時拋出問題的答案。
1) 如果before型觸發器執行失敗,直接goto跳到err2位置,不會執行后續sql語句;
2) 如果sql執行失敗,直接goto跳到err位置,不會執行或許的after型觸發器;
3) 如過after觸發器執行失敗,goto到err2位置,恢復執行過的操作,且在事務型的表上做標記。
另外,在使用復雜的sql時,由于有些復雜的sql是mysql自己定義的,所以存在不確定性,使用簡單的sql比較可控。
4 Mysql觸發器在數據庫同步中的表現
4.1 觸發器運行失敗時,數據庫同步會失敗嗎?
有同步關系如下dbA?dbB。初始時同步正常。
1)在dbB上建立觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)在dbA上執行sql,執行成功;
mysql> update planinfo set showprob=200 where planid= 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
3)由于dbB上沒有FC_Output.abc表,觸發器會執行失敗,這時,檢查一下同步狀態:
Slave_IO_Running: Yes
Slave_SQL_Running: NO
Last_Errno: 1146
Last_Error: Error 'Table 'FC_Output.abc' doesn't exist' on query. Default database: 'FC_Word'. Query: 'update planinfo set showprob=200 where planid= 1'
可以看到IO線程運行正常,sql線程運行失敗,并提示觸發器運行失敗的錯誤信息。
回憶一下3.1和3.3所述部分,無論是before部分的觸發器還是after類型的觸發器,對于innodb引擎,當觸發器執行失敗時,相應sql也會執行失敗,所以數據庫同步也會失敗。
4.2 創建、刪除觸發器寫bin-log
創建和刪除觸發器的語句也會寫入bin-log里,所以也會如一般的insert,update,delete語句一樣同步到下游數據庫中,即上游創建觸發器,下游也會創建。
這里再引出兩個小問題:有同步關系dbA?dbB,
1) 在dbA上創建一個觸發器,如果dbB上已經有同表同類型的觸發器,同步狀態如何?
2) 在dbB上刪除一個觸發器,如果dbB上沒有對應觸發器,同步狀態如何?
這兩個問題可以類比同步中的insert語句和delete語句,答案就是
1) 同步失敗,因為不允許重復創建同表同類型的觸發器;
2) 同步正常,因為drop一個不存在的觸發器,不影響運行結果;
5 Mysql觸發器經典案例
5.1 案例1 一條sql涉及多個表的update時,觸發得到update之前的舊值
【現象】表test_info上建有觸發器如下:
CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER trig_test_info_update
AFTER UPDATE
ON FC_Word.test_info FOR EACH ROW
BEGIN
DECLARE tlevel INTEGER DEFAULT 0;
DECLARE ttype INTEGER DEFAULT 0;
SET tlevel = 4;
SET ttype = 33;
INSERT INTO TEST_Output.fcevent (te, le, uid, pid, uid, wid, bi, mbid, wl) SELECT ttype, tlevel, NEW.uid, NEW.pid, NEW.uid, NEW.wid, NEW.bi, NEW.mbid, wl FROM TEST_Word.wext2 where wid = NEW.wid;
/*。。。其余部分邏輯省略*/
END IF;
END;
這個觸發器程序有點長,可以單看飄黃的兩句,即更新操作滿足第一個條件執行飄黃語句時,觸發器的行為。觸發器是建立在test_info表上的,飄黃語句中可以看到,也需要查詢wext2表。
執行如下sql1:
Update test_info a, wext2 b set a.th=(a.th+1), a.w4=(a.w4&8), b.wl=NULL where a.wid=b.wid and a.wid=142394379;
可以看到sql中既修改了test_info2表,同時修改了wext2表,程序原意是觸發得到wext2表wl字段修改后的新值(即NULL);不過實驗得到,執行上述sql后,觸發器程序查詢到的wurl是sql修改之前的舊值。
再執行下面類似sql2:
Update wext2 a, test_info2 b set b.th=(b.th+1), b.w4=(b.w4&8), a.wl=NULL where a.wid=b.wid and a.wid=142394379;
實驗的到,執行上述sql后,觸發器程序查詢到的wurl是sql修改之后的新值。
【原因】原因當然與sql中的別名a,b無關,而是和wext2表和test_info表的書寫順序有關。如本文3.4部分所述,一條sql涉及多個表的 update操作時,數據表字段、觸發器執行順序是mysql源碼包裝過的。在執行上述sql1時,先執行test_info的更新,然后是after觸發器,最后是wext2的更新,也就是說,在執行after觸發器時,wext2還沒有進行更新,所以觸發得到的是舊值。而執行sql2時,先執行 wext2更新,然后是test_info更新,最后是after觸發器,也就是說,在執行after觸發器時,wext2已經更新完畢,所以出去得到的是新值。
引起上述現象是順序關系的,無論該表是否支持事務。在使用復雜的sql時,由于有些復雜的sql是mysql自己定義的,所以存在不確定性,存在風險,使用簡單的sql比較可控。
5.2 案例2 mysql5.0.19版本修改表結構后觸發器失效
【現象】userpref表上建有after類型觸發器,修改userpref表的外鍵關聯后,在userpref表中的新增記錄沒有觸發下來,即觸發器失效。
【原因】mysql5.0.19修改表結構是,觸發器消失。這是mysql5.0.19的一個bug,在創建觸發器時,會把觸發器的內容保存在 information_schema.TRIGGERS表中,同時在var目錄下創建觸發器的數據庫目錄下創建一個觸發器名稱為前綴,以TRN為后綴的文件,當修改觸發器的表時,information_schema.TRIGGERS表的內容會刪除,導致觸發器消失。
在mysql5.0.45版本中,這個bug已經被修復。Mysql5.0.45版本的觸發器,無論是修改表的索引、外鍵,還是改變表字段,觸發器都不會失效。
5.3 案例3 刪除數據表后觸發器失效
【現象】聯調環境中存在dbA?dbB,主庫dbA上沒有觸發器,在從庫dbB上的FC_Word.wnegative表,FC_Word.wbuget 表上建有觸發器;觸發器開始運行正常,期間沒有對從庫的任何直接操作,有一日發現對wnegative表上的修改無法觸發。查看從庫狀態,同步正常;用 select TRIGGER_NAME from information_schema.TRIGGERS發現wnegative表上的觸發器消失了;在var/FC_Word目錄下也沒有 wnegative的.TRN文件,wnegative表上的觸發器不見了。
【分析】查找dbB的查詢日志,發現有一條:
100223 18:27:45 135939 Query DROP TABLE IF EXISTS `wnegative`
135939 Query CREATE TABLE `wnegative` (
KEY `Index_wnegative_planid` (`planid`),
KEY `Index_wnegative_unitid` (`unitid`)
135939 Query /*!40000 ALTER TABLE `wnegative` DISABLE KEYS */
100223 18:27:46 135939 Query INSERT INTO `wnegative` VALUES (614,1,289026,2911155,1848481);
可以看到,在100223 18:27:45時,刪除了表wnegative,緊接著有創建表wnegative;查找觸發表發現,在100223 18:27:45時間后對wnegative的修改就沒有觸發了,而在這個之前對wnegative的修改是觸發正常的。故,懷疑對wnegative表的刪除使wnegative表上的觸發器也被刪除。對wnegative表的刪除是在主庫dbA上操作后,被同步到dbB上。
【原因】在刪除wnegative表時,mysql同時刪除了wegative表上的觸發器。
可以通過下面實驗證明上述猜測:
1) 首先在wnegative建立after insert型觸發器;
2) 增加一條wnegative中記錄;
3) 查看結果發現觸發器正確觸發;
4) 刪除wnegative表;
5) 使用select TRIGGER_NAME from information_schema.TRIGGERS查看所有觸發器,wnegative表上觸發器已經不存在了;同時到var/FC_Word目錄下,對應觸發器的.TRN文件也不存在了;
6) 重新創建wnegative表,并增加一條wnegative中記錄;沒有了wnegative表上觸發器,自然也不能觸發任何結果。
6 結束語
Mysql中的觸發器功能已經在鳳巢系統的各個模塊中有廣泛應用,究其細節,還有很多值得注意的地方;本文建立在實驗和案例的基礎上,數據庫基于線上系統使用的mysql5.0.45版本,分析了觸發器相關的一些特殊情況下msyql的處理方式。
(全文完)
posted @
2012-01-16 13:04 小果子 閱讀(7200) |
評論 (1) |
編輯 收藏
1 背景:
x86平臺有完善的用戶態檢測內存工具比如valgrind等,可以監控程序運行中詳細的內存信息,從而精確定位內存問題。然而隨著新平臺的快速誕生(比如Tilera的TilePro64 CPU),這些工具不能被及時地移植,導致新平臺缺乏相應的手段來定位內存錯誤,如內存越界,泄漏等,而只能使用粗粒度的方法top,free 等指令觀察進程的動態內存總額。其缺點是粒度太粗,而且內存的總數變化有很多原因引起,在復雜的系統里,很難精確定位內存問題的根源,甚至會漏報錯報,這嚴重影響了新平臺(如Tilera)開發與測試的效率。針對這個問題,我們提出了一個通用的新平臺針對c/c++內存錯誤檢測框架。
該框架可適用于任何平臺。其通過重寫標準庫的內存分配和釋放函數(如malloc, new, new[], free,delete,delete[]等), 以及維護一個全局的內存分配map數據結構實現。重寫后的內存分配比如my_malloc首先調用系統malloc功能,然后記錄每一次malloc執行過程中的內存操作信息(包括文件名、行號以及內存尺寸,函數調用棧),以指針值為 key值,存進維護的全局map表。而重寫的my_free則是根據傳入的指針值在 map 中查找相應的數據項并將之刪除,而后調用系統的free 將指針所指向的內存塊釋放。這樣當程序退出的時候,map 中的剩余的數據項就是我們期望檢測的內存泄漏信息。我們可以輸出泄漏內存塊的詳細日志,比如文件名,行號等等。這將大大提高類似Tilera平臺的內存問題追查效率,提高開發和測試的速度和質量。
2 基本原理:
1) 通過重寫非共享內存分配釋放函數malloc, new, new[],free,delete,delete[]截獲 它們在執行過程中的內存操作信息。重載形式如下:
※ void* malloc( size_t nSize, char* pszFileName, int nLineNum )
※ void* new( size_t nSize, char* pszFileName, int nLineNum )
※ void* operator new[]( size_t nSize, char* pszFileName, int nLineNum )
※ void free( void *ptr )
※ void delete( void *ptr )
※ void operator delete[]( void *ptr )
2) 通過重寫共享內存分配釋放函數mspace_malloc,mspace_free,重寫形式如下:
※ void* my_mspace_malloc( mspace msp,unsigned int Size, char* pszFileName, int nLineNum )
※ void my_mspace_free(mspace msp, void *ptr )
3) 我們對malloc, new, new[],mspace_malloc進行了重載,參數包括size_t nSize、文件名和行號,這里的文件名和行號就是這次malloc, new, new[],mspace_malloc操作符被調用時所在的文件名和行號,這個信息將在發現內存泄漏時輸出,以幫助定位泄漏具體位置。對于 free,delete,delete[],mspace_free參數為指針地址。
3 實例:
以My_malloc, My_free為例,重寫函數結構如下:

1. 在重寫的my_malloc函數版本中,我們將調用malloc 的原始版本并將相應的 size_t 參數傳入,然后,我們將malloc的原始版本返回的指針值以及該次分配所在的文件名和行號信息記錄下來,這里采用STL 的 map數據結構存儲,以指針值為 key 值,文件名,行號等信息作為一個結構體是value值。
My_free重寫函數結構如下:

2. 當my_free 被調用時,我們根據傳入的指針值在 map 中找到相應的數據項并將之刪除,而后調用 free 將指針所指向的內存塊釋放。這樣當程序退出的時候,map 中的剩余的數據項就是我們企圖檢測的內存泄漏信息。
4 實現關鍵點:
1) 如何取得內存分配代碼所在的文件名和行號?
利用 C 的預編譯宏 __FILE__ 和 __LINE__,這兩個宏將在編譯時在指定位置展開為該文件的文件名和該行的行號
2) 利用宏定義開關決定走普通分支還是內存檢測分支?
#if defined( MEM_DEBUG )
#define malloc DEBUG_MALLOC
#define free DEBUG_FREE
#endif
3) 何時創建用于存儲內存數據的 map 數據結構,如何管理,何時打印內存泄漏信息?
設計一個類來封裝這個 map 以及對它的插入刪除操作,然后構造這個類的一個全局對象(appMemory),在全局對象(appMemory)的構造函數中創建并初始化這個數據結 構,而在其析構函數中對數據結構中剩余數據進行分析和輸出。new 中將調用這個全局對象的 insert 接口將指針、文件名、行號、內存塊大小等信息以指針值為 key 記錄到 map 中,在delete 中調用 erase 接口將對應指針值的 map 中的數據項刪除,同時對 map 的訪問需要進行互斥同步,因為同一時間可能會有多個進程進行堆上的內存操作。
4) 如何為非共享內存申請map?如何為共享內存申請map?
非共享內存的map,對于多進程則有多個map,可按(3)的方法處理。而對于共享內存,可能A進程申請到B進程才釋放,但是每個進程一個map,我們去掃描這些每個map時就會出現誤報的現象,因此需要采取將map放入共享內存方法:我們申請一塊共享內存區域為map,這個map對各進程是共享的。當程序中各進程調用共享內存時,將各進程分配的指針及文件名行號等詳細信息存進這共享的map。程序結束時,掃描該共享的map,就能得到未釋放的信息。將 map放入共享內存使用c++標準庫時需要我們自己實現一個基于共享內存的allocator,替換map默認的allocator,在這個allocator中實現map的內存分配方案。也可以使用boost庫(1.35以上版本),它增加了一個叫做boost::interprocess的庫,具體可參考http://blog.cong.co/stl-alloc.html
5) 如何使用該工具?
內存泄露檢測框架提供接口libmemck.h,內容如下

在被測試程序里包含如下代碼即可使用

6) 何時如何捕獲信號,生成leak文件?
定義一個全局的類得對象,在該類得構造函數里通過signal函數捕獲SIGINT、SIGABRT、SIGFPE、SIGTERM信號,當捕獲到這些信號其中之一時,開始掃描map并將map剩余信息寫入leak文件展示。
7) 對臨界資源的控制?
共享內存的各進程共享的map,各進程間進行讀寫操作需要加鎖,我們這里采用的是信號燈實現。
非共享內存各個進程對應的map,在各進程進行插入刪除操作時也需要加鎖實現
8) 程序中malloc作為函數指針?
由于將原型malloc(size)重寫為my_malloc(size,__FILE__,__LINE__),這樣由于函數類型不一致,導致程序調用該工具時編譯無法通過,真對這種情況的解決辦法為:重寫malloc(size)為my_malloc_p(size)這樣除了文件名和行號無法得知外,泄露的多少內存可以報出。
轉自http://hi.baidu.com/baiduqa/blog/item/e4c991c5ef46e5c7d10060fb.html
posted @
2012-01-16 13:03 小果子 閱讀(589) |
評論 (0) |
編輯 收藏
下面是Jquery中AJAX參數詳細列表:
參數名
類型
描述
url
String
(默認: 當前頁地址) 發送請求的地址。
type
String
(默認: "GET") 請求方式 ("POST" 或 "GET"), 默認為 "GET"。注意:其它 HTTP 請求方法,如 PUT 和 DELETE 也可以使用,但僅部分瀏覽器支持。
timeout
Number
設置請求超時時間(毫秒)。此設置將覆蓋全局設置。
async
Boolean
(默認: true) 默認設置下,所有請求均為異步請求。如果需要發送同步請求,請將此選項設置為 false。注意,同步請求將鎖住瀏覽器,用戶其它操作必須等待請求完成才可以執行。
beforeSend
Function
發送請求前可修改 XMLHttpRequest 對象的函數,如添加自定義 HTTP 頭。XMLHttpRequest 對象是唯一的參數。
function (XMLHttpRequest) { this; // the options for this ajax request }
cache
Boolean
(默認: true) jQuery 1.2 新功能,設置為 false 將不會從瀏覽器緩存中加載請求信息。
complete
Function
請求完成后回調函數 (請求成功或失敗時均調用)。參數: XMLHttpRequest 對象,成功信息字符串。
function (XMLHttpRequest, textStatus) { this; // the options for this ajax request }
contentType
String
(默認: "application/x-www-form-urlencoded") 發送信息至服務器時內容編碼類型。默認值適合大多數應用場合。
data
Object,
String
發送到服務器的數據。將自動轉換為請求字符串格式。GET 請求中將附加在 URL 后。查看 processData 選項說明以禁止此自動轉換。必須為 Key/Value 格式。如果為數組,jQuery 將自動為不同值對應同一個名稱。如 {foo:["bar1", "bar2"]} 轉換為 '&foo=bar1&foo=bar2'。
dataType
String
預期服務器返回的數據類型。如果不指定,jQuery 將自動根據 HTTP 包 MIME 信息返回 responseXML 或 responseText,并作為回調函數參數傳遞,可用值:
"xml": 返回 XML 文檔,可用 jQuery 處理。
"html": 返回純文本 HTML 信息;包含 script 元素。
"script": 返回純文本 JavaScript 代碼。不會自動緩存結果。
"json": 返回 JSON 數據 。
"jsonp": JSONP 格式。使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 將自動替換 ? 為正確的函數名,以執行回調函數。
error
Function
(默認: 自動判斷 (xml 或 html)) 請求失敗時將調用此方法。這個方法有三個參數:XMLHttpRequest 對象,錯誤信息,(可能)捕獲的錯誤對象。
function (XMLHttpRequest, textStatus, errorThrown) { // 通常情況下textStatus和errorThown只有其中一個有值 this; // the options for this ajax request }
global
Boolean
(默認: true) 是否觸發全局 AJAX 事件。設置為 false 將不會觸發全局 AJAX 事件,如 ajaxStart 或 ajaxStop 。可用于控制不同的Ajax事件
ifModified
Boolean
(默認: false) 僅在服務器數據改變時獲取新數據。使用 HTTP 包 Last-Modified 頭信息判斷。
processData
Boolean
(默認: true) 默認情況下,發送的數據將被轉換為對象(技術上講并非字符串) 以配合默認內容類型 "application/x-www-form-urlencoded"。如果要發送 DOM 樹信息或其它不希望轉換的信息,請設置為 false。
success
Function
請求成功后回調函數。這個方法有兩個參數:服務器返回數據,返回狀態
function (data, textStatus) { // data could be xmlDoc, jsonObj, html, text, etc... this; // the options for this ajax request }
代碼:$(document).ready(function() {
jQuery("#clearCac").click(function() {
jQuery.ajax({
url: "/Handle/Do.aspx",
type: "post",
data: { id: '0' },
dataType: "json",
success: function(msg) {
alert(msg);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status);
alert(XMLHttpRequest.readyState);
alert(textStatus);
},
complete: function(XMLHttpRequest, textStatus) {
this; // 調用本次AJAX請求時傳遞的options參數
}
});
});
});
一、error:function (XMLHttpRequest, textStatus, errorThrown)
{
}
(默認: 自動判斷 (xml 或 html)) 請求失敗時調用時間。參數有以下三個:XMLHttpRequest 對象、錯誤信息、(可選)捕獲的錯誤對象。如果發生了錯誤,錯誤信息(第二個參數)除了得到null之外,還可能是"timeout", "error", "notmodified" 和 "parsererror"。
textStatus:
"timeout", "error", "notmodified" 和 "parsererror"。
二、error事件返回的第一個參數XMLHttpRequest有一些有用的信息:
XMLHttpRequest.readyState:
狀態碼
0 - (未初始化)還沒有調用send()方法
1 - (載入)已調用send()方法,正在發送請求
2 - (載入完成)send()方法執行完成,已經接收到全部響應內容
3 - (交互)正在解析響應內容
4 - (完成)響應內容解析完成,可以在客戶端調用了
三、data:"{}", data為空也一定要傳"{}";不然返回的是xml格式的。并提示parsererror.
四、parsererror的異常和Header 類型也有關系。及編碼header('Content-type: text/html; charset=utf8');
五、XMLHttpRequest.status:
1xx-信息提示
這些狀態代碼表示臨時的響應。客戶端在收到常規響應之前,應準備接收一個或多個1xx響應。
100-繼續。
101-切換協議。
2xx-成功
這類狀態代碼表明服務器成功地接受了客戶端請求。
200-確定。客戶端請求已成功。
201-已創建。
202-已接受。
203-非權威性信息。
204-無內容。
205-重置內容。
206-部分內容。
3xx-重定向
客戶端瀏覽器必須采取更多操作來實現請求。例如,瀏覽器可能不得不請求服務器上的不同的頁面,或通過代理服務器重復該請求。
301-對象已永久移走,即永久重定向。
302-對象已臨時移動。
304-未修改。
307-臨時重定向。
4xx-客戶端錯誤
發生錯誤,客戶端似乎有問題。例如,客戶端請求不存在的頁面,客戶端未提供有效的身份驗證信息。400-錯誤的請求。
401-訪問被拒絕。IIS定義了許多不同的401錯誤,它們指明更為具體的錯誤原因。這些具體的錯誤代碼在瀏覽器中顯示,但不在IIS日志中顯示:
401.1-登錄失敗。
401.2-服務器配置導致登錄失敗。
401.3-由于ACL對資源的限制而未獲得授權。
401.4-篩選器授權失敗。
401.5-ISAPI/CGI應用程序授權失敗。
401.7–訪問被Web服務器上的URL授權策略拒絕。這個錯誤代碼為IIS6.0所專用。
403-禁止訪問:IIS定義了許多不同的403錯誤,它們指明更為具體的錯誤原因:
403.1-執行訪問被禁止。
403.2-讀訪問被禁止。
403.3-寫訪問被禁止。
403.4-要求SSL。
403.5-要求SSL128。
403.6-IP地址被拒絕。
403.7-要求客戶端證書。
403.8-站點訪問被拒絕。
403.9-用戶數過多。
403.10-配置無效。
403.11-密碼更改。
403.12-拒絕訪問映射表。
403.13-客戶端證書被吊銷。
403.14-拒絕目錄列表。
403.15-超出客戶端訪問許可。
403.16-客戶端證書不受信任或無效。
403.17-客戶端證書已過期或尚未生效。
403.18-在當前的應用程序池中不能執行所請求的URL。這個錯誤代碼為IIS6.0所專用。
403.19-不能為這個應用程序池中的客戶端執行CGI。這個錯誤代碼為IIS6.0所專用。
403.20-Passport登錄失敗。這個錯誤代碼為IIS6.0所專用。
404-未找到。
404.0-(無)–沒有找到文件或目錄。
404.1-無法在所請求的端口上訪問Web站點。
404.2-Web服務擴展鎖定策略阻止本請求。
404.3-MIME映射策略阻止本請求。
405-用來訪問本頁面的HTTP謂詞不被允許(方法不被允許)
406-客戶端瀏覽器不接受所請求頁面的MIME類型。
407-要求進行代理身份驗證。
412-前提條件失敗。
413–請求實體太大。
414-請求URI太長。
415–不支持的媒體類型。
416–所請求的范圍無法滿足。
417–執行失敗。
423–鎖定的錯誤。
5xx-服務器錯誤
服務器由于遇到錯誤而不能完成該請求。
500-內部服務器錯誤。
500.12-應用程序正忙于在Web服務器上重新啟動。
500.13-Web服務器太忙。
500.15-不允許直接請求Global.asa。
500.16–UNC授權憑據不正確。這個錯誤代碼為IIS6.0所專用。
500.18–URL授權存儲不能打開。這個錯誤代碼為IIS6.0所專用。
500.100-內部ASP錯誤。
501-頁眉值指定了未實現的配置。
502-Web服務器用作網關或代理服務器時收到了無效響應。
502.1-CGI應用程序超時。
502.2-CGI應用程序出錯。application.
503-服務不可用。這個錯誤代碼為IIS6.0所專用。
504-網關超時。
505-HTTP版本不受支持。
FTP
1xx-肯定的初步答復
這些狀態代碼指示一項操作已經成功開始,但客戶端希望在繼續操作新命令前得到另一個答復。
110重新啟動標記答復。
120服務已就緒,在nnn分鐘后開始。
125數據連接已打開,正在開始傳輸。
150文件狀態正常,準備打開數據連接。
2xx-肯定的完成答復
一項操作已經成功完成。客戶端可以執行新命令。200命令確定。
202未執行命令,站點上的命令過多。
211系統狀態,或系統幫助答復。
212目錄狀態。
213文件狀態。
214幫助消息。
215NAME系統類型,其中,NAME是AssignedNumbers文檔中所列的正式系統名稱。
220服務就緒,可以執行新用戶的請求。
221服務關閉控制連接。如果適當,請注銷。
225數據連接打開,沒有進行中的傳輸。
226關閉數據連接。請求的文件操作已成功(例如,傳輸文件或放棄文件)。
227進入被動模式(h1,h2,h3,h4,p1,p2)。
230用戶已登錄,繼續進行。
250請求的文件操作正確,已完成。
257已創建“PATHNAME”。
3xx-肯定的中間答復
該命令已成功,但服務器需要更多來自客戶端的信息以完成對請求的處理。331用戶名正確,需要密碼。
332需要登錄帳戶。
350請求的文件操作正在等待進一步的信息。
4xx-瞬態否定的完成答復
該命令不成功,但錯誤是暫時的。如果客戶端重試命令,可能會執行成功。421服務不可用,正在關閉控制連接。如果服務確定它必須關閉,將向任何命令發送這一應答。
425無法打開數據連接。
426Connectionclosed;transferaborted.
450未執行請求的文件操作。文件不可用(例如,文件繁忙)。
451請求的操作異常終止:正在處理本地錯誤。
452未執行請求的操作。系統存儲空間不夠。
5xx-永久性否定的完成答復
該命令不成功,錯誤是永久性的。如果客戶端重試命令,將再次出現同樣的錯誤。500語法錯誤,命令無法識別。這可能包括諸如命令行太長之類的錯誤。
501在參數中有語法錯誤。
502未執行命令。
503錯誤的命令序列。
504未執行該參數的命令。
530未登錄。
532存儲文件需要帳戶。
550未執行請求的操作。文件不可用(例如,未找到文件,沒有訪問權限)。
551請求的操作異常終止:未知的頁面類型。
552請求的文件操作異常終止:超出存儲分配(對于當前目錄或數據集)。
553未執行請求的操作。不允許的文件名。
本篇文章來源于 Linux公社網站(www.linuxidc.com) 原文鏈接:http://www.linuxidc.com/Linux/2011-11/47495.htm
posted @
2012-01-15 13:02 小果子 閱讀(2849) |
評論 (0) |
編輯 收藏