Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

中学生综合素质评价标准

$
0
0
蚂蚁肝炎:你能做到几条?反正我觉得我上学时做到的勉强及格 作者:芳草地 来源:jxjy 点击:53299次 评论:0条

 

一、公民道德素养

1、遵纪守法 ①学法、守法、用法、在社会生活中用各种规范约束自己的言行。②按时到校,不早退、不旷课;遵守课堂纪律。③遵守通行规则,通过楼梯等过道时靠右行、不争抢。

2、明礼诚信①与他人平等、友好相处、懂礼貌,不欺侮同学。②待人诚恳守信、不说谎、不作弊。

3、自尊乐群 ①能够采取恰当措施维护自己的尊严和权益。②能够自我反省,有错即改。③积极参加力所能及的劳动,学会料理个人生活,自己的事自己做。④关心集体,乐于参加集体活动。⑤善于与他人交流,善于与他人合作共同完成任务。

4、遵守公德 ①爱护文物古迹,爱护公共设施,爱护庄稼。②不在公共场所大声喧哗,主动按顺序排队。③遇有侵害敢于斗争,善于斗争,学会自救自护。

5、爱护环境 ①爱护环境卫生。②爱惜粮食,节约水电。不攀比消费,不乱花钱。③积极参加环境教育和环保活动。

二、学习态度与能力

1、学习兴趣 ①学习主动,对知识有好奇心,努力克服学习中的困难。②在学习过程中注意力集中,上课专心听讲,勤于思考,勇于发表自己的见解,积极回答问题。③认真预习、复习,按时独立完成作业。

2、学习方法 ①制定并落实有效的学习计划。②使用多种有效的学习方法。③善于总结与反思。

3、学业表现 ①课堂表现优秀、板演、回答问题等质量高。②作业质量高。③阶段性考试成绩良好。

三、实践与创新

1、实践能力 ①绘图、实验操作等学习环节表现良好。②积极参加各类实践、创新活动,且成效显著。③自己设计并实施创造与发现活动。

2、创新表现 ①在作文、模型制作等方面表现出丰富的想象力。②善于收集、整理、运用信息。③能够综合运用所学知识进行研究性学习等探究活动。④见解独到,能够创造性解决问题。

四、运动与健康

1、身体健康 ①良好的身体素质。②精力充沛。

2、心理健康 ①活泼开朗,能够自我调控情绪。②保持乐观的生活态度。

3、生活健康①坚持锻炼身体与参加体育活动。②讲卫生、生活习惯良好,生活有秩序,作息有规律。③无不良嗜好,不吸烟、不喝酒、不赌博、远离毒品。

五、审美与表现

1、审美情趣 ①发现并欣赏自然、文学、艺术作品等的美。②在语言、衣着等方面具有正确的爱美观念,穿戴整洁得体、朴素大方;仪表庄重,和谐文雅。③情趣健康,不看***、暴力书刊音像制品。

2、审美表现

①积极参加音乐、美术等课程的学习。②积极参加各种艺术活动。


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

微信公众平台开发入门

$
0
0

 

在这篇微信公众平台开发教程中,我们假定你已经有了PHP语言程序、MySQL数据库、计算机网络通讯、及HTTP/XML/CSS/JS等基础。

我们将使用微信公众账号方倍工作室作为讲解的例子,二维码见底部。

本系列教程将引导你完成如下任务:

  1. 创建新浪云计算平台应用
  2. 启用微信公众平台开发模式
  3. 系统原理及消息数据格式
  4. 开发天气预报功能

 

第一章 申请服务器资源

创建新浪云计算应用

申请账号

我们使用SAE新浪云计算平台作为服务器资源,并且申请PHP环境+MySQL数据库作为程序运行环境。
申请地址: http://sae.sina.com.cn/ ,使用新浪微博账号可以直接登录SAE,登录后SAE将赠送500个免费云豆。

 

 

创建新应用 http://www.cnblogs.com/txw1958/p/wechat-tutorial.html  

登录后点击顶部【 我的首页

点击下侧的创建新应用,这时会弹出提示, 禁止放置违法违规内容,点击继续创建,弹出如下窗口。

 

选择一个未使用的appid,如果老是已经被使用不知道该什么好,就填写你的QQ号或者手机号吧。

填写二级域名AppID、应用名称、验证码,开发语言选择PHP,应用类型选择web应用。然后点击创建应用

 

应用创建成功。并自动跳转到应用列表中,可以看到已经有刚才创建的CCTV-7

 

 

创建版本 http://www.cnblogs.com/txw1958/p/wechat-tutorial.html 

选择CCTV-7右侧的 应用管理下面的 代码管理

 

跳转到代码管理

点击右侧的

版本号默认为1,点击创建,成功后如下图所示:

 

到这里,就成功创建了一个域名URL为  http://cctv7.sinaapp.com/ 的应用了。

 原文:http://www.cnblogs.com/txw1958/p/wechat-tutorial.html 

上传代码 http://www.cnblogs.com/txw1958/p/wechat-tutorial.html  

将以下代码复制下来,另存为index.php。必须使用专业的开发编辑软件操作,例如Notepad++,不要使用Windows自带的记事本等。

复制代码
<?php
/*方倍工作室 http://www.cnblogs.com/txw1958/
    CopyRight 2013 www.doucube.com  All Rights Reserved
*/

define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
if (isset($_GET['echostr'])) {
    $wechatObj->valid();
}else{
    $wechatObj->responseMsg();
}

class wechatCallbackapiTest
{
    public function valid()
    {
        $echoStr = $_GET["echostr"];
        if($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }

    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );

        if( $tmpStr == $signature ){
            return true;
        }else{
            return false;
        }
    }

    public function responseMsg()
    {
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

        if (!empty($postStr)){
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $fromUsername = $postObj->FromUserName;
            $toUsername = $postObj->ToUserName;
            $keyword = trim($postObj->Content);
            $time = time();
            $textTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[%s]]></MsgType><Content><![CDATA[%s]]></Content><FuncFlag>0</FuncFlag></xml>";
            if($keyword == "?" || $keyword == "?")
            {
                $msgType = "text";
                $contentStr = date("Y-m-d H:i:s",time());
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                echo $resultStr;
            }
        }else{
            echo "";
            exit;
        }
    }
}
?>
复制代码

然后将index.php文件压缩成ZIP格式,注意不能用RAR格式

这样会生成一个index.zip的文件。或者直接下载方倍已经压缩好的zip文件 点此下载

在代码管理界面中,选择操作按钮。

选择上传代码包。

点击上传文件,选择刚才压缩好的index.zip文件,点击上传,上传成功后如下所示, 如果上传有问题,请在Chrome浏览器下重试一下。

点击操作按钮下的代码编辑,

 

有时候需要输入自己的安全密码,如果不记得了就点击“找回密码”。

安全验证成功后继续之前的操作。

我们可以看到index.php已经上传成功,双击可以查看编辑里面的代码

新浪云应用的创建就成功了。

 

第二章 启用开发模式

微信公众平台开发模式

高级功能

微信公众平台地址: https://mp.weixin.qq.com 

登录微信公众平台后台,在左侧列表中最下方,找到“开发者中心”,点击进入

进入接口配置填写框。请填写接口配置信息,此信息需要你拥有自己的服务器资源。
填写的URL需要正确响应微信发送的Token验证,请阅读接入指南。

此处的URL为上篇中介绍的云应用的域名,而Token在index.php中定义为weixin。填写后点击提交。

返回到开发者中心界面,再点击启用按钮。

启用成功后,如下图所示。

恭喜,你成功启用开发模式。

 

自动回复

在上面的例子中,实现了一个发送“?”就能回复当前时间的功能。
效果如下:

至此,你的微信公众平台账号已经实现自动回复了。

 

 

第三章 系统开发原理及消息数据格式

 

开发模式成为开发者时的消息校验原理

在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。

此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。

参数 描述
signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp时间戳
nonce随机数
echostr随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

启用接口是由代码中的checkSignature()函数来实现校验的。如果对这一原理难以理解,可以暂时不用深究,继续看下面。

 

成为开发者后消息收发时的原理

上一章节中图,当用户发送一个“?”时,系统回复了一个时间

这一原理的消息流程图如下所示。

从上图可以看出,用户在发送一个?后,微信服务器将组装一个消息发送给我们自己的服务器,自己的服务器然后回复一个时间,并且将该时间也按一定的规则组装,回复给公众账号,公众账号再回复给用户,在这个收发过程中,发送方和接收方进行了调换(ToUserName和FromUserName值互换),收发都是以xml格式在后台进行传输的,

所以掌握各种消息类型的接收回复就是进行微信公众平台开发的基础!

下面对前面所述的各种消息类型讲解其XML数据包的格式。

 

各种收发消息的XML数据包分析

接收消息

1. 文本(包括表情)
接收文本及表情

文字后台格式:

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1359028446</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[测试文字]]></Content><MsgId>5836982729904121631</MsgId></xml>
复制代码

表情后台格式

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1359044526</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[/::)/::~/::B/::|/:8-)]]></Content><MsgId>5837051792978241864</MsgId></xml>
复制代码

XML格式讲解

ToUserName 消息接收方微信号,一般为公众平台账号微信号
FromUserName 消息发送方微信号
CreateTime 消息创建时间
MsgType 消息类型;文本消息为text
Content 消息内容
MsgId 消息ID号

可以看出,文本和表情的消息类型均为文本

2. 图片
接收图片

后台格式:

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1359028479</CreateTime><MsgType><![CDATA[image]]></MsgType><PicUrl><![CDATA[http://mmbiz.qpic.cn/mmbiz/L4qjYtOibummHn90t1mnaibYiaR8ljyicF3MW7XX3BLp1qZgUb7CtZ0DxqYFI4uAQH1FWs3hUicpibjF0pOqLEQyDMlg/0]]></PicUrl><MsgId>5836982871638042400</MsgId><MediaId><![CDATA[PGKsO3LAgbVTsFYO7FGu51KUYa07D0C_Nozz2fn1z6VYtHOsF59PTFl0vagGxkVH]]></MediaId></xml>
复制代码

XML格式讲解

ToUserName 消息接收方微信号,一般为公众平台账号微信号
FromUserName 消息发送方微信号
CreateTime 消息创建时间
MsgType 消息类型;图片消息为image
PicUrl 图片链接地址,可以用HTTP GET获取
MsgId 消息ID号

3. 语音
接收语音

后台格式:

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1359028479</CreateTime><MsgType><![CDATA[image]]></MsgType><PicUrl><![CDATA[http://mmbiz.qpic.cn/mmbiz/L4qjYtOibummHn90t1mnaibYiaR8ljyicF3MW7XX3BLp1qZgUb7CtZ0DxqYFI4uAQH1FWs3hUicpibjF0pOqLEQyDMlg/0]]></PicUrl><MsgId>5836982871638042400</MsgId><MediaId><![CDATA[PGKsO3LAgbVTsFYO7FGu51KUYa07D0C_Nozz2fn1z6VYtHOsF59PTFl0vagGxkVH]]></MediaId></xml>
复制代码

XML格式讲解

复制代码
ToUserName 消息接收方微信号,一般为公众平台账号微信号
FromUserName 消息发送方微信号
CreateTime 消息创建时间
MsgType 消息类型;语音消息为voice
MediaId 媒体ID
Format 语音格式,这里为amr
MsgId 消息ID号
复制代码
附:AMR接口简介
全称Adaptive Multi-Rate,主要用于移动设备的音频,压缩比比较大,但相对其他的压缩格式质量比较差,由于多用于人声,通话,效果还是很不错的。

4. 视频

接收视频

后台格式:

复制代码
xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1359028186</CreateTime><MsgType><![CDATA[video]]></MsgType><MediaId><![CDATA[DBVFRIj29LB2hxuYpc0R6VLyxwgyCHZPbRj_IIs6YaGhutyXUKtFSDcSCPeoqUYr]]></MediaId><ThumbMediaId><![CDATA[mxUJ5gcCeesJwx2T9qsk62YzIclCP_HnRdfTQcojlPeT2G9Q3d22UkSLyBFLZ01J]]></ThumbMediaId><MsgId>5836981613212624665</MsgId></xml>
复制代码

XML格式讲解

复制代码
ToUserName 消息接收方微信号,一般为公众平台账号微信号
FromUserName 消息发送方微信号
CreateTime 消息创建时间
MsgType 消息类型;视频消息为video
MediaId 媒体ID
ThumbMediaId 媒体缩略ID?
MsgId 消息ID号
复制代码

5. 位置

接收位置

后台格式:

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FLACFromUserName><CreateTime>1359036619</CreateTime><MsgType><![CDATA[location]]></MsgType><Location_X>22.539968</Location_X><Location_Y>113.954980</Location_Y><Scale>16</Scale><Label><![CDATA[中国广东省深圳市南山区华侨城深南大道9789号 邮政编码: 518057]]></Label><MsgId>5837017832671832047</MsgId></xml>
复制代码

XML格式讲解

复制代码
 ToUserName 消息接收方微信号,一般为公众平台账号微信号
 FromUserName 消息发送方微信号
 CreateTime 消息创建时间
 MsgType 消息类型,地理位置为location
 Location_X 地理位置纬度
 Location_Y 地理位置经度
 Scale 地图缩放大小
 Label 地理位置信息
 MsgId 消息ID号
复制代码

6. 链接

接收链接

后台格式:

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName> <FromUserName><![CDATA[oIDrpjl2LYdfTAM-oxDgB4XZcnc8]]></FromUserName> <CreateTime>1359709372</CreateTime> <MsgType><![CDATA[link]]></MsgType> <Title><![CDATA[微信公众平台开发者的江湖]]></Title> <Description><![CDATA[陈坤的微信公众号这段时间大火,大家..]]></Description> <Url><![CDATA[http://israel.duapp.com/web/photo.php]]></Url> <MsgId>5839907284805129867</MsgId> </xml> 
复制代码

XML格式讲解

复制代码
 ToUserName 消息接收方微信号,一般为公众平台账号微信号
 FromUserName 消息发送方微信号
 CreateTime 消息创建时间
 MsgType 消息类型,链接为link
 Title 图文消息标题
 Description 图文消息描述
 Url 点击图文消息跳转链接
 MsgId 消息ID号
复制代码

 

回复消息

只介绍三种格式的消息:文本、图文、音乐。其中图文消息包括单条图文消息和多条图文消息,展示方式有一点点不同。

1. 文本消息格式
回复文本

后台格式:

复制代码
<xml><ToUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></ToUserName><FromUserName><![CDATA[gh_680bdefc8c5d]]></FromUserName><CreateTime>1359036631</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[【深圳】天气实况 温度:27℃ 湿度:59% 风速:东北风3级
11月03日 周日 27℃~23℃ 小雨 东北风4-5级
11月04日 周一 26℃~21℃ 阵雨 微风
11月05日 周二 27℃~22℃ 阴 微风]]></Content><FuncFlag>0</FuncFlag></xml>
复制代码

XML格式讲解

 FromUserName 消息发送方
 ToUserName 消息接收方
 CreateTime 消息创建时间
 MsgType 消息类型,文本消息必须填写text
 Content 消息内容,大小限制在2048字节,字段为空为不合法请求
 FuncFlag 星标字段

 
2. 图文消息格式
2.1 单条图文消息
回复单条图文


后台格式:

复制代码
<xml><ToUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></ToUserName><FromUserName><![CDATA[gh_680bdefc8c5d]]></FromUserName><CreateTime>1359011899</CreateTime><MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>1</ArticleCount><Articles><item><Title><![CDATA[[苹果产品信息查询]]></Title><Description><![CDATA[序列号:USE IMEI NUMBER
IMEI号:358031058974471
设备名称:iPhone 5C
设备颜色:
设备容量:
激活状态:已激活
电话支持:未过期[2014-01-13]
硬件保修:未过期[2014-10-14]
生产工厂:中国]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/banner.jpg]]></PicUrl><Url><![CDATA[]]></Url></item></Articles><FuncFlag>0</FuncFlag></xml>
复制代码

2.2 多图文消息

回复多图文

后台数据格式

复制代码
<xml><ToUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></ToUserName><FromUserName><![CDATA[gh_680bdefc8c5d]]></FromUserName><CreateTime>1359011829</CreateTime><MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>5</ArticleCount><Articles><item><Title><![CDATA[【深圳】天气实况 温度:3℃ 湿度:43﹪ 风速:西南风2级]]></Title><Description><![CDATA[]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/banner.jpg]]></PicUrl><Url><![CDATA[]]></Url></item><item><Title><![CDATA[06月24日 周四 2℃~-7℃ 晴 北风3-4级转东南风小于3级]]></Title><Description><![CDATA[]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/d00.gif]]></PicUrl><Url><![CDATA[]]></Url></item><item><Title><![CDATA[06月25日 周五 -1℃~-8℃ 晴 东南风小于3级转东北风3-4级]]></Title><Description><![CDATA[]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/d00.gif]]></PicUrl><Url><![CDATA[]]></Url></item><item><Title><![CDATA[06月26日 周六 -1℃~-7℃ 多云 东北风3-4级转东南风小于3级]]></Title><Description><![CDATA[]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/d01.gif]]></PicUrl><Url><![CDATA[]]></Url></item><item><Title><![CDATA[06月27日 周日 0℃~-6℃ 多云 东南风小于3级转东北风3-4级]]></Title><Description><![CDATA[]]></Description><PicUrl><![CDATA[http://www.doucube.com/weixin/weather/icon/d01.gif]]></PicUrl><Url><![CDATA[]]></Url></item></Articles><FuncFlag>0</FuncFlag></xml>
复制代码

XML格式讲解

复制代码
FromUserName 消息发送方
 ToUserName 消息接收方
 CreateTime 消息创建时间
 MsgType 消息类型,图文消息必须填写news
 Content 消息内容,图文消息可填空
 ArticleCount 图文消息个数,限制为10条以内
 Articles 多条图文消息信息,默认第一个item为大图
  Title 图文消息标题
  Description 图文消息描述
  PicUrl 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80
  Url 点击图文消息跳转链接
FuncFlag 星标字段
复制代码

  3. 音乐消息

回复音乐消息

后台格式:

复制代码
<xml><ToUserName><![CDATA[ollB4jqgdO_cRnVXk_wRnSywgtQ8]]></ToUserName><FromUserName><![CDATA[gh_b629c48b653e]]></FromUserName><CreateTime>1372310544</CreateTime><MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[最炫民族风]]></Title><Description><![CDATA[凤凰传奇]]></Description><MusicUrl><![CDATA[http://zj189.cn/zj/download/music/zxmzf.mp3]]></MusicUrl><HQMusicUrl><![CDATA[http://zj189.cn/zj/download/music/zxmzf.mp3]]></HQMusicUrl></Music><FuncFlag>0</FuncFlag></xml>
复制代码

XML格式讲解

复制代码
ToUserName     接收方帐号(收到的OpenID)
FromUserName     开发者微信号
CreateTime     消息创建时间
MsgType          消息类型,此处为music
    Title       音乐标题
    Description 音乐描述
    MusicUrl     音乐链接
    HQMusicUrl     高质量音乐链接,WIFI环境优先使用该链接播放音乐
FuncFlag     位0x0001被标志时,星标刚收到的消息。
复制代码

 

事件消息类型

目前用户在关注和取消关注,以及点击菜单的时候会自动向公众平台发送事件推送消息:

1. 关注事件

复制代码
<xml><ToUserName><![CDATA[gh_b629c48b653e]]></ToUserName><FromUserName><![CDATA[ollB4jv7LA3tydjviJp5V9qTU_kA]]></FromUserName><CreateTime>1372307736</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event><EventKey><![CDATA[]]></EventKey></xml>
复制代码

2. 取消关注事件

复制代码
<xml><ToUserName><![CDATA[gh_b629c48b653e]]></ToUserName><FromUserName><![CDATA[ollB4jqgdO_cRnVXk_wRnSywgtQ8]]></FromUserName><CreateTime>1372309890</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[unsubscribe]]></Event><EventKey><![CDATA[]]></EventKey></xml>
复制代码

3. 菜单点击事件

复制代码
<xml><ToUserName><![CDATA[gh_680bdefc8c5d]]></ToUserName><FromUserName><![CDATA[oIDrpjqASyTPnxRmpS9O_ruZGsfk]]></FromUserName><CreateTime>1377886191</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[CLICK]]></Event><EventKey><![CDATA[天气深圳]]></EventKey></xml>
复制代码

XML格式讲解

ToUserName     接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime     消息创建时间
MsgType     消息类型,event
Event     事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

 

 

第四章 开发天气预报功能

 

这一章里,我们来快速开发天气预报功能、我们使用方倍工作室的相应接口来实现。下面代码实现了这样该功能。

复制代码
<?php
/*方倍工作室
    CopyRight 2014 All Rights Reserved
*/

define("TOKEN", "weixin");

$wechatObj = new wechatCallbackapiTest();
if (!isset($_GET['echostr'])) {
    $wechatObj->responseMsg();
}else{
    $wechatObj->valid();
}

class wechatCallbackapiTest
{
    public function valid()
    {
        $echoStr = $_GET["echostr"];
        if($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }

    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if($tmpStr == $signature){
            return true;
        }else{
            return false;
        }
    }

    public function responseMsg()
    {
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        if (!empty($postStr)){
            $this->logger("R ".$postStr);
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $RX_TYPE = trim($postObj->MsgType);

            switch ($RX_TYPE)
            {
                case "event":
                    $result = $this->receiveEvent($postObj);
                    break;
                case "text":
                    $result = $this->receiveText($postObj);
                    break;
            }
            $this->logger("T ".$result);
            echo $result;
        }else {
            echo "";
            exit;
        }
    }

    private function receiveEvent($object)
    {
        $content = "";
        switch ($object->Event)
        {
            case "subscribe":
                $content = "欢迎关注方倍工作室 ";
                break;
        }
        $result = $this->transmitText($object, $content);
        return $result;
    }

    private function receiveText($object)
    {
        $keyword = trim($object->Content);$url = "http://apix.sinaapp.com/weather/?appkey=".$object->ToUserName."&city=".urlencode($keyword); 
        $output = file_get_contents($url);
        $content = json_decode($output, true);

        $result = $this->transmitNews($object, $content);
        return $result;
    }

    private function transmitText($object, $content)
    {
        $textTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>";
        $result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
        return $result;
    }

    private function transmitNews($object, $newsArray)
    {
        if(!is_array($newsArray)){
            return;
        }
        $itemTpl = "    <item><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description><PicUrl><![CDATA[%s]]></PicUrl><Url><![CDATA[%s]]></Url></item>";
        $item_str = "";
        foreach ($newsArray as $item){
            $item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
        }
        $newsTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>%s</ArticleCount><Articles>
$item_str</Articles></xml>";

        $result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray));
        return $result;
    }

    private function logger($log_content)
    {
      
    }
}
?>
复制代码

在公众账号中使用的命令如下:

  1. 发送城市名称,如“深圳”,可以查询该城市的天气

在你的公众账号输入相应的命令,实现效果类似如下所示:

 

 

第五章 小结

 

总的来说,通过本教程,你得到了以下收获:

  • 1. 你通过本教程得到了一个免费的新浪云计算空间,云计算哦
  • 2. 你成功启用了开发模式,并且实现了时间的自动回复
  • 3. 你了解了微信公众平台开发的原理,并且熟悉了各种消息及发送是怎么一回事
  • 4. 你使用方倍工作室的接口,成功的开发了你的第一个微信公众平台功能——天气预报。

接下来该做什么呢?你可以学习开发一些基础的常用功能,推荐:方倍工作室 编写,机械工业出版社 出版的《微信公众平台开发最佳实践》,里面包含很多php开发技巧、数据库使用、及近30项微信公众平台实用功能或技术 。

 

点击图标购买:



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



四大常见陋习亲手葬送爱机 你中了几枪?

$
0
0

随着科技数码产品越来越多的参与到生活当中,每个人所拥有的设备也是越来越多。不可能所有人都对手中的设备一一掌握精通,这种情况下用户就很容易因为自己使用不当而造成设备的损坏最终造成精神和金钱的双重损失。以下这些典型错误习惯就很可能是你正在犯的,来看看你是否中枪吧。

手机、平板低电量不充电

手机和平板经过一段时间的发展和普及,已经成为了我们生活中最常见的数码装备之一。而随着大屏手机的流行,电池续航能力与大屏幕已经成为了一顿无法化解的矛盾。一天一充电已经成为了大部分用户必要的课题。然而许多朋友对于手机和平板充电手懒的程度令人难以想象(我自己就是其中一个),不用到低电量预警马上关机就绝对不会充电。

四大常见陋习亲手葬送爱机你中了几枪?

实际上与从前的镍氢电池和镍铬电池不同,如今的锂电池一般没有“电池记忆”的效果,用户实际上无需对其进行刻意的完全充放电,随用随充是最好的状态。

美国太空总署 NASA 专门研究延长电池寿命的电子工程教授 Tom Hartley 对锂电池寿命保养的全部心得可总结为精辟的两句话:”给电池充电充得越满,电池的损耗也会越大“,”电池放电放得越尽,电池的损耗就会越大“。那些依然手懒不愿意及时充电的小伙伴们,是提醒自己改掉坏毛病的时候了。

插着耳塞入睡

有不少数码爱好者,尤其是对音乐痴迷的爱好者,都有睡觉佩戴耳塞的习惯,喜欢在静谧的夜里探寻播放器和耳塞传达出来的感动。虽然这个修身养性的行为是值得鼓励的,但是实际上这个习惯相当的不好,具体体现在两个方面。

四大常见陋习亲手葬送爱机你中了几枪?

一方面插着耳塞睡觉,成倍增加了耳塞损坏的可能性。相信早上醒来被耳塞线缠绕脖子的经历一定是每个佩戴耳塞入睡的人都经历过的,相当一部分耳塞也就是这么损坏的。普通用户二、三十块钱的耳塞损坏了倒也没什么。万一真的是资深音频玩家,睡一觉就把 IE800、UE900、舒尔、铁三角这样的名贵耳塞拉扯坏了的话,那可能会是寻死的心都有了。

另外一方面,插耳塞入睡对对耳朵的损害也是非常严重的,长此以往有可能出现耳鸣、轻度听力下降、耳朵内疼痛等不良反映。部分人还会出现头晕,注意力不易集中,思维敏捷度和记忆力减退,容易变得烦躁不安、缺乏耐心的情况。当然这当中最大的问题还是听力受损,往往很多听力受损的情况是不可恢复的,希望大家都能够引起重视。

移动硬盘直接断电

很多拥有移动硬盘的朋友几乎无一例外的都会在着急的时候对移动硬盘进行强行的断电移除(有的时候真不赖咱,硬盘传输数据太多太慢了啊~)。虽然大家很可能是因为赶时间而图一时之快,但是长此以往很容易造成硬盘内部数据的损坏,甚至是硬盘数据的全部丢失。

我们知道硬盘写入数据的时候,是将数据首先移进缓存当中,然后再从缓存写入硬盘盘片上,中间突然断电必将导致传输的数据不完整。而我们熟悉的 Windows 系统中为了避免此类事件的发生,设计了一个“安全弹出 USB 设备“这个功能,这个功能实际上所起到的作用就是完全清理缓存,让硬盘退出时没有在进行数据交换,以此来保护硬盘上的数据。所以在移动硬盘没有完全允许大家移除设备的情况下,还是尽量不要陡然间断电移除吧。

笔记本平放在床上使用

笔记本电脑作为移动办公的终端,几乎是每个人上班族必备的数码装备。在平时的生活当中,笔记本几乎可以被你带到任何你想带到的地方,机场、家里、星巴克、旅途中都少不了它的身影,但是切记不要平放在床上使用。

实际上这种错误的使用方式更多来自于女性用户,尤其是一些还在上学大学生。宿舍中狭小的环境能够给笔记本以最大发挥空间的除了桌子上就是床上,然而你是否考虑过笔记本是否能够经受住床上的严峻考验呢。

四大常见陋习亲手葬送爱机你中了几枪?

平放在床上使用的直接问题体现在三个方面,一个是由于阻挡了笔记本的散热窗和风扇的进风口,所以这直接导致了笔记本散热不佳,笔记本核心和机表温度会在这个时候急剧上升,轻者影响机身运行性能,严重甚至导致内部主要零件损坏。

另外一方面,阻隔笔记本风扇的进风窗口会直接导致笔记本风扇由于受到吸风阻力,转速长时间被动处于低于正常转速的水平,长此以往就会导致风扇失灵,不少风扇出问题都是由于这个问题。

平放在床上使用,会让许多细毛灰尘进入到笔记本内部,在铜管散热片上形成一层厚厚的灰尘层,严重妨碍机身内外的通风导致机身变热。所以平放在床上使用笔记本除了能够让你过足懒人瘾之外,几乎对笔记本没有任何好处。

结语:

可以说以上的四个陋习是普通用户在使用电子产品时最常见的,具有一定的典型性代表性。如果你还在这么做的话,那么我要恭喜你,至少你心爱的数码设备还没有罢工。但是同时我也要劝诫你,是时候收手了。

本文链接

比较全面的gdb调试命令

$
0
0

转自 http://blog.csdn.net/dadalan/article/details/3758025

用GDB调试程序
GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于,其可以形成执行序
列,形成脚本。UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行
软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可
以做出一个非常强大的功能。
于是UNIX下的软件比Windows下的软件更能有机地结合,各自发挥各自的长处,组合成
更为强劲的功能。而Windows下的图形软件基本上是各自为营,互相不能调用,很不利于
各种软件的相互集成。在这里并不是要和Windows做个什么比较,所谓“寸有所长,尺有
所短”,图形化工具还是有不如命令行的地方。
用GDB调试程序
GDB概述
————
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那
种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你
会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长,
尺有所短”就是这个道理。
一般来说,GDB主要帮忙你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。
从上面看来,GDB和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细
节上,你会发现GDB这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有
时候,命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。
一个调试示例
——————
源程序:tst.c
1 #include
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<7; i++) {

8 sum+=i;
9 }
10 return sum;
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d /n", result );
24 printf("result[1-250] = %d /n", func(250) );
25 }
编译生成执行文件:(Linux下)
hchen/test> cc -g tst.c -o tst
使用GDB调试:
hchen/test> gdb tst <---------- 启动GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-suse-linux"...
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
1 #include
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i 7 {
8 sum+=i;
9 }
10 return sum;
(gdb) <-------------------- 直接回车表示,重复上一次命令
11 }

 

12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/hchen/test/tst
Breakpoint 1, main () at tst.c:17 <---------- 在断点处停住。
17 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.
result[1-100] = 5050 <----------程序输出。
Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)

 

(gdb) p sum
$2 = 1
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d /n", func(250) );
Value returned is $6 = 31375
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-250] = 31375 <----------程序输出。
Program exited with code 027. <--------程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
hchen/test>
好了,有了以上的感性认识,还是让我们来系统地认识一下gdb吧。
使用GDB
————
一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必
须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。
如:
> cc -g hello.c -o hello
> g++ -g hello.cpp -o hello
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。当你用
-g把调试信息加入之后,并成功编译目标代码以后,让我们来看看如何用gdb来调试他。
启动GDB的方法有以下几种:

 

1、gdb
program也就是你的执行文件,一般在当然目录下。
2、gdb core
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
3、gdb
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自
动attach上去,并调试他。program应该在PATH环境变量中搜索得到。
GDB启动时,可以加上一些GDB的启动开关,详细的开关可以用gdb -help查看。我在下
面只例举一些比较常用的参数:
-symbols
-s
从指定文件中读取符号表。
-se file
从指定文件中读取符号表信息,并把他用在可执行文件中。
-core
-c
调试时core dump的core文件。
-directory
-d
加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。
GDB的命令概貌
———————
启动gdb后,就你被带入gdb的调试环境中,就可以使用gdb的命令开始调试程序了,gdb
的命令可以使用help命令来查看,如下所示:
/home/hchen> gdb
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-suse-linux".
(gdb) help

 

List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
gdb的命令很多,gdb把之分成许多个种类。help命令只是例出gdb的命令种类,如果要看
种类中的命令,可以使用help 命令,如:help breakpoints,查看设置断点的所有命令。也
可以直接help 来查看命令的帮助。
gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令
的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐
命令的全称,如果有重复的,那么gdb会把其例出来。
示例一:在进入函数func时,设置一个断点。可以敲入break func,或是直接就是b func
(gdb) b func
Breakpoint 1 at 0x8048458: file hello.c, line 10.
示例二:敲入b按两次TAB键,你会看到所有b打头的命令:
(gdb) b
backtrace break bt
(gdb)
示例三:只记得函数的前缀,可以这样:
(gdb) b make_ <按TAB键>
(再按下一次TAB键,你会看到:)
make_a_section_from_file make_environ
make_abs_section make_function_type
make_blockvector make_pointer_type

 

make_cleanup make_reference_type
make_command make_symbol_completion_list
(gdb) b make_
GDB把所有make开头的函数全部例出来给你查看。
示例四:调试C++的程序时,有可以函数名一样。如:
(gdb) b 'bubble( M-?
bubble(double,double) bubble(int,int)
(gdb) b 'bubble(
你可以查看到C++中的所有的重载函数及参数。(注:M-?和“按两次TAB键”是一个意思)
要退出gdb时,只用发quit或命令简称q就行了。
GDB中运行UNIX的shell程序
————————————
在gdb环境中,你可以执行UNIX的shell的命令,使用gdb的shell命令来完成:
shell
调用UNIX的shell来执行,环境变量SHELL中定义的UNIX的shell将会被用来执行,如
果SHELL没有定义,那就使用UNIX的标准shell:/bin/sh。(在Windows中使用Command.com
或cmd.exe)
还有一个gdb命令是make:
make
可以在gdb中执行make命令来重新build自己的程序。这个命令等价于“shell make ”。
在GDB中运行程序
————————
当以gdb 方式启动gdb后,gdb会在PATH路径和当前目录中搜索的源文件。如要确认gdb
是否读到源文件,可使用l或list命令,看看gdb是否能列出源代码。
在gdb中,运行程序使用r或是run命令。程序的运行,你有可能需要设置下面四方面的事。
1、程序运行参数。
set args 可指定运行时参数。(如:set args 10 20 30 40 50)
show args 命令可以查看设置好的运行参数。
2、运行环境。
path
可设定程序的运行路径。
show paths 查看程序的运行路径。

 

set environment varname [=value] 设置环境变量。如:set env USER=hchen
show environment [varname] 查看环境变量。
3、工作目录。
cd
相当于shell的cd命令。
pwd 显示当前的所在目录。
4、程序的输入输出。
info terminal 显示你程序用到的终端的模式。
使用重定向控制程序输出。如:run > outfile
tty命令可以指写输入输出的终端设备。如:tty /dev/ttyb
调试已运行的程序
————————
两种方法:
1、在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用gdb PID格式挂接正在
运行的程序。
2、先用gdb 关联上源代码,并进行gdb,在gdb中用attach命令来挂接进程的PID。并用
detach来取消挂接的进程。
暂停 / 恢复程序运行
—————————
调试程序中,暂停程序运行是必须的,GDB可以方便地暂停程序的运行。你可以设置程序
的在哪行停住,在什么条件下停住,在收到什么信号时停往等等。以便于你查看运行时的变
量,以及运行时的流程。
当进程被gdb停住时,你可以使用info program 来查看程序的是否在运行,进程号,被暂停
的原因。
在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕
捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。如果要恢复程序运行,可
以使用c或是continue命令。
一、设置断点(BreakPoint)
我们用break命令来设置断点。正面有几点设置断点的方法:
break
在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函

 

数名。
break
在指定行号停住。
break +offset
break -offset
在当前行号的前面或后面的offset行停住。offiset为自然数。
break filename:linenum
在源文件filename的linenum行处停住。
break filename:function
在源文件filename的function函数的入口处停住。
break *address
在程序运行的内存地址处停住。
break
break命令没有参数时,表示在下一条指令处停住。
break ... if
...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设
置break if i=100,表示当i为100时停住程序。
查看断点时,可使用info命令,如下所示:(注:n表示断点号)
info breakpoints [n]
info break [n]
二、设置观察点(WatchPoint)
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马
上停住程序。我们有下面的几种方法来设置观察点:
watch
为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序。
rwatch
当表达式(变量)expr被读时,停住程序。
awatch
当表达式(变量)的值被读或被写时,停住程序。

 

info watchpoints
列出当前所设置了的所有观察点。
三、设置捕捉点(CatchPoint)
你可设置捕捉点来补捉程序运行时的一些事件。如:载入共享库(动态链接库)或是C++
的异常。设置捕捉点的格式为:
catch
当event发生时,停住程序。event可以是下面的内容:
1、throw 一个C++抛出的异常。(throw为关键字)
2、catch 一个C++捕捉到的异常。(catch为关键字)
3、exec 调用系统调用exec时。(exec为关键字,目前此功能只在HP-UX下有用)
4、fork 调用系统调用fork时。(fork为关键字,目前此功能只在HP-UX下有用)
5、vfork 调用系统调用vfork时。(vfork为关键字,目前此功能只在HP-UX下有用)
6、load 或 load 载入共享库(动态链接库)时。(load为关键字,目前此功能只在HP-UX
下有用)
7、unload 或 unload 卸载共享库(动态链接库)时。(unload为关键字,目前此功能只在
HP-UX下有用)
tcatch
只设置一次捕捉点,当程序停住以后,应点被自动删除。
四、维护停止点
上面说了如何设置程序的停止点,GDB中的停止点也就是上述的三类。在GDB中,如果你
觉得已定义好的停止点没有用了,你可以使用delete、clear、disable、enable这几个命令来
进行维护。
clear
清除所有的已定义的停止点。
clear
clear
清除所有设置在函数上的停止点。
clear
clear
清除所有设置在指定行上的停止点。
delete [breakpoints] [range...]
删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range
表示断点号的范围(如:3-7)。其简写命令为d。

 

比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要
时,enable即可,就好像回收站一样。
disable [breakpoints] [range...]
disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的
停止点。简写命令是dis.
enable [breakpoints] [range...]
enable所指定的停止点,breakpoints为停止点号。
enable [breakpoints] once range...
enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动disable。
enable [breakpoints] delete range...
enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动删除。
五、停止条件维护
前面在说到设置断点时,我们提到过可以设置一个条件,当条件成立时,程序自动停止,这
是一个非常强大的功能,这里,我想专门说说这个条件的相关维护命令。一般来说,为断点
设置一个条件,我们使用if关键词,后面跟其断点条件。并且,条件设置好后,我们可以
用condition命令来修改断点的条件。(只有break和watch命令支持if,catch目前暂不支持
if)
condition
修改断点号为bnum的停止条件为expression。
condition
清除断点号为bnum的停止条件。
还有一个比较特殊的维护命令ignore,你可以指定程序运行时,忽略停止条件几次。
ignore
表示忽略断点号为bnum的停止条件count次。
六、为停止点设定运行命令
我们可以使用GDB提供的command命令来设置停止点的运行命令。也就是说,当运行的
程序在被停止住时,我们可以让其自动运行一些别的命令,这很有利行自动化调试。对基于
GDB的自动化调试是一个强大的支持。
commands [bnum]

 

... command-list ...
end
为断点号bnum指写一个命令列表。当程序被该断点停住时,gdb会依次运行命令列表中的
命令。
例如:
break foo if x>0
commands
printf "x is %d/n",x
continue
end
断点设置在函数foo中,断点条件是x>0,如果程序被断住后,也就是,一旦x的值在foo
函数中大于0,GDB会自动打印出x的值,并继续运行程序。
如果你要清除断点上的命令序列,那么只要简单的执行一下commands命令,并直接在打个
end就行了。
七、断点菜单
在C++中,可能会重复出现同一个名字的函数若干次(函数重载),在这种情况下,break 不
能告诉GDB要停在哪个函数的入口。当然,你可以使用break 也就是把函数的参数类型告
诉GDB,以指定一个函数。否则的话,GDB会给你列出一个断点菜单供你选择你所需要的
断点。你只要输入你菜单列表中的编号就可以了。如:
(gdb) b String::after
[0] cancel
[1] all
[2] file:String.cc; line number:867
[3] file:String.cc; line number:860
[4] file:String.cc; line number:875
[5] file:String.cc; line number:853
[6] file:String.cc; line number:846
[7] file:String.cc; line number:735
> 2 4 6
Breakpoint 1 at 0xb26c: file String.cc, line 867.
Breakpoint 2 at 0xb344: file String.cc, line 875.
Breakpoint 3 at 0xafcc: file String.cc, line 846.
Multiple breakpoints were set.
Use the "delete" command to delete unwanted
breakpoints.
(gdb)

 

可见,GDB列出了所有after的重载函数,你可以选一下列表编号就行了。0表示放弃设置
断点,1表示所有函数都设置断点。
八、恢复程序运行和单步调试
当程序被停住了,你可以用continue命令恢复程序的运行直到程序结束,或下一个断点到来。
也可以使用step或next命令单步跟踪程序。
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次
数。continue,c,fg三个命令都是一样的意思。
step
单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有debug
信息。很像VC等工具中的step in。后面可以加count也可以不加,不加表示一条条地执行,
加表示执行后面的count条指令,然后再停住。
next
同样单步跟踪,如果有函数调用,他不会进入该函数。很像VC等工具中的step over。后面
可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后
再停住。
set step-mode
set step-mode on
打开step-mode模式,于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。
这个参数有很利于查看机器码。
set step-mod off
关闭step-mode模式。
finish
运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。
until 或 u
当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
stepi 或 si
nexti 或 ni
单步跟踪一条机器指令!一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步
执行机器指令。与之一样有相同功能的命令是“display/i $pc” ,当运行完这个命令后,单
步跟踪会在打出程序代码的同时打出机器指令(也就是汇编代码)

 

九、信号(Signals)
信号是一种软中断,是一种处理异步事件的方法。一般来说,操作系统都支持许多信号。尤
其是UNIX,比较重要应用程序一般都会处理信号。UNIX定义了许多信号,比如SIGINT
表示中断字符信号,也就是Ctrl+C的信号,SIGBUS表示硬件故障的信号;SIGCHLD表示
子进程状态改变信号;SIGKILL表示终止程序运行的信号,等等。信号量编程是UNIX下
非常重要的一种技术。
GDB有能力在你调试程序的时候处理任何一种信号,你可以告诉GDB需要处理哪一种信
号。你可以要求GDB收到你所指定的信号时,马上停住正在运行的程序,以供你进行调试。
你可以用GDB的handle命令来完成这一功能。
handle
在GDB中定义一个信号处理。信号可以以SIG开头或不以SIG开头,可以用定义一个要处
理信号的范围(如:SIGIO-SIGKILL,表示处理从SIGIO信号到SIGKILL的信号,其中包
括SIGIO,SIGIOT,SIGKILL三个信号),也可以使用关键字all来标明要处理所有的信号。
一旦被调试的程序接收到信号,运行程序马上会被GDB停住,以供调试。其可以是以下几
种关键字的一个或多个。
nostop
当被调试的程序收到信号时,GDB不会停住程序的运行,但会打出消息告诉你收到这种信
号。
stop
当被调试的程序收到信号时,GDB会停住你的程序。
print
当被调试的程序收到信号时,GDB会显示出一条信息。
noprint
当被调试的程序收到信号时,GDB不会告诉你收到信号的信息。
pass
noignore
当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程
序会处理。
nopass
ignore
当被调试的程序收到信号时,GDB不会让被调试程序来处理这个信号。
info signals
info handle
查看有哪些信号在被GDB检测中。
十、线程(Thread Stops)

 

如果你程序是多线程的话,你可以定义你的断点是否在所有的线程上,或是在某个特定的线
程。GDB很容易帮你完成这一工作。
break thread
break thread if ...
linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是
GDB分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你
不指定thread 则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体
情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。
查看栈信息
—————
当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一
个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以
用GDB命令来查看当前的栈中的信息。
下面是一些查看函数调用栈信息的GDB命令:
backtrace
bt
打印当前的函数调用栈的所有信息。如:
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
从上可以看出函数的调用栈信息:__libc_start_main --> main() --> func()
backtrace
bt
n是一个正整数,表示只打印栈顶上n层的栈信息。
backtrace <-n>
bt <-n>
-n表一个负整数,表示只打印栈底下n层的栈信息。
如果你要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈

 

就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
frame
f
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的
第二层。
up
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三
个命令:
select-frame 对应于 frame 命令。
up-silently 对应于 up 命令。
down-silently 对应于 down 命令。
查看当前栈层的信息,你可以用以下GDB命令:
frame 或 f
会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数
执行到的语句。
info frame
info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比
如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写
成的、函数参数地址及值、局部变量的地址等等。如:
(gdb) info f
Stack level 0, frame at 0xbffff5d4:
eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
called by frame at 0xbffff60c
source language c.
Arglist at 0xbffff5d4, args: n=250
Locals at 0xbffff5d4, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffff5d4, eip at 0xbffff5d8
info args

 

打印出当前函数的参数名及其值。
info locals
打印出当前函数中所有局部变量及其值。
info catch
打印出当前的函数中的异常处理信息。
查看源程序
—————
一、显示源代码
GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g的参数,把源程
序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后,GDB会报告程序
停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。还是来看一看查看源
代码的GDB命令吧。
list
显示程序第linenum行的周围的源程序。
list
显示函数名为function的函数的源程序。
list
显示当前行后面的源程序。
list -
显示当前行前面的源程序。
一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当
然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。
set listsize
设置一次显示源代码的行数。
show listsize
查看当前listsize的设置。
list命令还有下面的用法:
list ,

 

显示从first行到last行之间的源代码。
list ,
显示从当前行到last行之间的源代码。
list +
往后显示源代码。
一般来说在list后面可以跟以下这们的参数:
行号。
<+offset> 当前行号的正偏移量。
<-offset> 当前行号的负偏移量。
哪个文件的哪一行。
函数名。
哪个文件中的哪个函数。
<*address> 程序运行时的语句在内存中的地址。
二、搜索源代码
不仅如此,GDB还提供了源代码搜索的命令:
forward-search
search
向前面搜索。
reverse-search
全部搜索。
其中,就是正则表达式,也主一个字符串的匹配模式,关于正则表达式,我就不在这里讲了,
还请各位查看相关资料。
三、指定源文件的路径
某些时候,用-g编译过后的执行程序中只是包括了源文件的名字,没有路径名。GDB提供
了可以让你指定源文件的路径的命令,以便GDB进行搜索。
directory
dir
加一个源文件路径到当前路径的前面。如果你要指定多个路径,UNIX下你可以使用“:”,
Windows下你可以使用“;”。

 

directory
清除所有的自定义的源文件搜索路径信息。
show directories
显示定义了的源文件搜索路径。
四、源代码的内存
你可以使用info line命令来查看源代码在内存中的地址。info line后面可以跟“行号”,“函
数名”,“文件名:行号”,“文件名:函数名”,这个命令会打印出所指定的源码在运行时的内
存地址,如:
(gdb) info line tst.c:func
Line 5 of "tst.c" starts at address 0x8048456 and ends at 0x804845d .
还有一个命令(disassemble)你可以查看源程序的当前执行时的机器码,这个命令会把目前
内存中的指令dump出来。如下面的示例表示查看函数func的汇编代码。
(gdb) disassemble func
Dump of assembler code for function func:
0x8048450 : push %ebp
0x8048451 : mov %esp,%ebp
0x8048453 : sub $0x18,%esp
0x8048456 : movl $0x0,0xfffffffc(%ebp)
0x804845d : movl $0x1,0xfffffff8(%ebp)
0x8048464 : mov 0xfffffff8(%ebp),%eax
0x8048467 : cmp 0x8(%ebp),%eax
0x804846a : jle 0x8048470
0x804846c : jmp 0x8048480
0x804846e : mov %esi,%esi
0x8048470 : mov 0xfffffff8(%ebp),%eax
0x8048473 : add %eax,0xfffffffc(%ebp)
0x8048476 : incl 0xfffffff8(%ebp)
0x8048479 : jmp 0x8048464
0x804847b : nop
0x804847c : lea 0x0(%esi,1),%esi
0x8048480 : mov 0xfffffffc(%ebp),%edx
0x8048483 : mov %edx,%eax
0x8048485 : jmp 0x8048487
0x8048487 : mov %ebp,%esp
0x8048489 : pop %ebp
0x804848a : ret
End of assembler dump.

 

查看运行时数据
———————
在你调试程序时,当程序被停住时,你可以使用print命令(简写命令为p),或是同义命令
inspect来查看当前程序的运行数据。print命令的格式是:
print
print /
是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),是输出的格
式,比如,如果要把表达式按16进制的格式输出,那么就是/x。
一、表达式
print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据
来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函
数等内容。可惜的是GDB不能使用你在程序中所定义的宏。
表达式的语法应该是当前所调试的语言的语法,由于C/C++是一种大众型的语言,所以,本
文中的例子都是关于C/C++的。(而关于用GDB调试其它语言的章节,我将在后面介绍)
在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中。
@
是一个和数组有关的操作符,在后面会有更详细的说明。
::
指定一个在文件或是一个函数中的变量。
{}
表示一个指向内存地址的类型为type的一个对象。
二、程序变量
在GDB中,你可以随时查看以下三种变量的值:
1、全局变量(所有文件可见的)
2、静态全局变量(当前文件可见的)
3、局部变量(当前Scope可见的)
如果你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局
变量,也就是说,如果一个全局变量和一个函数中的局部变量同名时,如果当前停止点在函

 

数中,用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量
的值时,你可以使用“::”操作符:
file::variable
function::variable
可以通过这种形式指定你所想查看的变量,是哪个文件中的或是哪个函数中的。例如,查看
文件f2.c中的全局变量x的值:
gdb) p 'f2.c'::x
当然,“::”操作符会和C++中的发生冲突,GDB能自动识别“::” 是否C++的操作符,所
以你不必担心在调试C++程序时会出现异常。
另外,需要注意的是,如果你的程序编译时开启了优化选项,那么在用GDB调试被优化过
的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这个是很正常的,因为
优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB
调试这种程序时,运行时的指令和你所编写指令就有不一样,也就会出现你所想象不到的结
果。对付这种情况时,需要在编译程序时关闭编译优化。一般来说,几乎所有的编译器都支
持编译优化的开关,例如,GNU的C/C++编译器GCC,你可以使用“-gstabs”选项来解决
这个问题。关于编译器的参数,还请查看编译器的使用说明文档。
三、数组
有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大
小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右
边则你你想查看内存的长度。例如,你的程序中有这样的语句:
int *array = (int *) malloc (len * sizeof (int));
于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:
p *array@len
@的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其
保存在变量len中,其输出结果,大约是下面这个样子的:
(gdb) p *array@len
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。
四、输出格式

 

一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。
例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要
做到这样,你可以使用GDB的数据显示格式:
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
(gdb) p i
$21 = 101
(gdb) p/a i
$22 = 0x65
(gdb) p/c i
$23 = 101 'e'
(gdb) p/f i
$24 = 1.41531145e-43
(gdb) p/x i
$25 = 0x65
(gdb) p/t i
$26 = 1100101
五、查看内存
你可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
x/
n、f、u是可选的参数。
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是
指令地址,那么格式可以是i。

 

u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可
以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当
我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作
一个值取出来。
表示一个内存地址。
n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3
表示三个单位,u表示按十六进制显示。
六、自动显示
你可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显
示。相关的GDB命令是display。
display
display/
display/
expr是一个表达式,fmt表示显示的格式,addr表示内存地址,当你用display设定好了一
个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。
格式i和s同样被display支持,一个非常有用的命令是:
display/i $pc
$pc是GDB的环境变量,表示着指令的地址,/i则表示输出格式为机器指令码,也就是汇
编。于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的
功能。
下面是一些和display相关的GDB命令:
undisplay
delete display
删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以
用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
disable display
enable display
disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。

 

info display
查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多
少个自动显示设置,其中包括,设置的编号,表达式,是否enable。
七、设置显示选项
GDB中关于显示的选项比较多,这里我只例举大多数常用的选项。
set print address
set print address on
打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的,
如:
(gdb) f
#0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")
at input.c:530
530 if (lquote != def_lquote)
set print address off
关闭函数的参数地址显示,如:
(gdb) set print addr off
(gdb) f
#0 set_quotes (lq="<<", rq=">>") at input.c:530
530 if (lquote != def_lquote)
show print address
查看当前地址显示选项是否打开。
set print array
set print array on
打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗
号分隔。这个选项默认是关闭的。与之相关的两个命令如下,我就不再多说了。
set print array off
show print array
set print elements
这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一个来指定数据显示的
最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。
show print elements

 

查看print elements的选项信息。
set print null-stop
如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。
set print pretty on
如果打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮。如:
$1 = {
next = 0x0,
flags = {
sweet = 1,
sour = 1
},
meat = 0x54 "Pork"
}
set print pretty off
关闭printf pretty这个选项,GDB显示结构体时会如下显示:
$1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"}
show print pretty
查看GDB是如何显示结构体的。
set print sevenbit-strings
设置字符显示,是否按“/nnn”的格式显示,如果打开,则字符串或字符数据按/nnn显示,
如“/065”。
show print sevenbit-strings
查看字符显示开关是否打开。
set print union
设置显示结构体时,是否显式其内的联合体数据。例如有以下数据结构:
typedef enum {Tree, Bug} Species;
typedef enum {Big_tree, Acorn, Seedling} Tree_forms;
typedef enum {Caterpillar, Cocoon, Butterfly}
Bug_forms;
struct thing {
Species it;
union {

 

Tree_forms tree;
Bug_forms bug;
} form;
};
struct thing foo = {Tree, {Acorn}};
当打开这个开关时,执行 p foo 命令后,会如下显示:
$1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}
当关闭这个开关时,执行 p foo 命令后,会如下显示:
$1 = {it = Tree, form = {...}}
show print union
查看联合体数据的显示方式
set print object
在C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法
调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。这个选项默认是
off。
show print object
查看对象选项的设置。
set print static-members
这个选项表示,当显示一个C++对象中的内容是,是否显示其中的静态数据成员。默认是
on。
show print static-members
查看静态数据成员选项设置。
set print vtbl
当此选项打开时,GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。
show print vtbl
查看虚函数显示格式的选项。
八、历史记录
当你用GDB的print查看程序运行时的数据时,你每一个print都会被GDB记录下来。GDB
会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问
以前的表达式,如$1。这个功能所带来的好处是,如果你先前输入了一个比较长的表达式,
如果你还想查看这个表达式的值,你可以使用历史记录来访问,省去了重复输入。

 

九、GDB环境变量
你可以在GDB的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定
义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样,
也是以$起头。如:
set $foo = *object_ptr
使用环境变量时,GDB会在你第一次使用时创建这个变量,而在以后的使用中,则直接对
其賦值。环境变量没有类型,你可以给环境变量定义任一的类型。包括结构体和数组。
show convenience
该命令查看当前所设置的所有的环境变量。
这是一个比较强大的功能,环境变量和程序变量的交互使用,将使得程序调试更为灵活便捷。
例如:
set $i = 0
print bar[$i++]->contents
于是,当你就不必,print bar[0]->contents, print bar[1]->contents地输入命令了。输入这样的
命令后,只用敲回车,重复执行上一条语句,环境变量会自动累加,从而完成逐个输出的功
能。
十、查看寄存器
要查看寄存器的值,很简单,可以使用如下命令:
info registers
查看寄存器的情况。(除了浮点寄存器)
info all-registers
查看所有寄存器的情况。(包括浮点寄存器)
info registers
查看所指定的寄存器的情况。
寄存器中放置了程序运行时的数据,比如程序当前运行的指令地址(ip),程序的当前堆栈
地址(sp)等等。你同样可以使用print命令来访问寄存器的情况,只需要在寄存器名字前
加一个$符号就可以了。如:p $eip。

 

改变程序的执行
———————
一旦使用GDB挂上被调试程序,当程序运行起来后,你可以根据自己的调试思路来动态地
在GDB中更改当前被调试程序的运行线路或是其变量的值,这个强大的功能能够让你更好
的调试你的程序,比如,你可以在程序的一次运行中走遍程序的所有分支。
一、修改变量值
修改被调试程序运行时的变量值,在GDB中很容易实现,使用GDB的print命令即可完成。
如:
(gdb) print x=4
x=4这个表达式是C/C++的语法,意为把变量x的值修改为4,如果你当前调试的语言是
Pascal,那么你可以使用Pascal的语法:x:=4。
在某些时候,很有可能你的变量和GDB中的参数冲突,如:
(gdb) whatis width
type = double
(gdb) p width
$4 = 13
(gdb) set width=47
Invalid syntax in expression.
因为,set width是GDB的命令,所以,出现了“Invalid syntax in expression”的设置错误,
此时,你可以使用set var命令来告诉GDB,width不是你GDB的参数,而是程序的变量名,
如:
(gdb) set var width=47
另外,还可能有些情况,GDB并不报告这种错误,所以保险起见,在你改变程序变量取值
时,最好都使用set var格式的GDB命令。
二、跳转执行
一般来说,被调试程序会按照程序代码的运行顺序依次执行。GDB提供了乱序执行的功能,
也就是说,GDB可以修改程序的执行顺序,可以让程序执行随意跳跃。这个功能可以由GDB
的jump命令来完:
jump

 

指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,可以是+num这种偏
移量格式。表式着下一条运行语句从哪里开始。
jump
这里的
是代码行的内存地址。
注意,jump命令不会改变当前的程序栈中的内容,所以,当你从一个函数跳到另一个函数
时,当函数运行完返回时进行弹栈操作时必然会发生错误,可能结果还是非常奇怪的,甚至
于产生程序Core Dump。所以最好是同一个函数中进行跳转。
熟悉汇编的人都知道,程序运行时,有一个寄存器用于保存当前代码所在的内存地址。所以,
jump命令也就是改变了这个寄存器中的值。于是,你可以使用“set $pc”来更改跳转执行
的地址。如:
set $pc = 0x485
三、产生信号量
使用singal命令,可以产生一个信号量给被调试的程序。如:中断信号Ctrl+C。这非常方便
于程序的调试,可以在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量,
这种精确地在某处产生信号非常有利程序的调试。
语法是:signal ,UNIX的系统信号量通常从1到15。所以取值也在这个范围。
single命令和shell的kill命令不同,系统的kill命令发信号给被调试程序时,是由GDB截
获的,而single命令所发出一信号则是直接发给被调试程序的。
四、强制函数返回
如果你的调试断点在某个函数中,并还有语句没有执行完。你可以使用return命令强制函数
忽略还没有执行的语句并返回。
return
return
使用return命令取消当前函数的执行,并立即返回,如果指定了,那么该表达式的值会被认
作函数的返回值。
五、强制调用函数

 

call
表达式中可以一是函数,以此达到强制调用函数的目的。并显示函数的返回值,如果函数返
回值是void,那么就不显示。
另一个相似的命令也可以完成这一功能——print,print后面可以跟表达式,所以也可以用他
来调用函数,print和call的不同是,如果函数返回void,call则不显示,print则显示函数返
回值,并把该值存入历史数据中。
在不同语言中使用GDB
——————————
GDB支持下列语言:C, C++, Fortran, PASCAL, Java, Chill, assembly, 和 Modula-2。一般说
来,GDB会根据你所调试的程序来确定当然的调试语言,比如:发现文件名后缀为“.c”
的,GDB会认为是C程序。文件名后缀为“.C, .cc, .cp, .cpp, .cxx, .c++”的,GDB会认为是
C++程序。而后缀是“.f, .F”的,GDB会认为是Fortran程序,还有,后缀为如果是“.s, .S”
的会认为是汇编语言。
也就是说,GDB会根据你所调试的程序的语言,来设置自己的语言环境,并让GDB的命令
跟着语言环境的改变而改变。比如一些GDB命令需要用到表达式或变量时,这些表达式或
变量的语法,完全是根据当前的语言环境而改变的。例如C/C++中对指针的语法是*p,而在
Modula-2中则是p^。并且,如果你当前的程序是由几种不同语言一同编译成的,那到在调
试过程中,GDB也能根据不同的语言自动地切换语言环境。这种跟着语言环境而改变的功
能,真是体贴开发人员的一种设计。
下面是几个相关于GDB语言环境的命令:
show language
查看当前的语言环境。如果GDB不能识为你所调试的编程语言,那么,C语言被认为是默
认的环境。
info frame
查看当前函数的程序语言。
info source
查看当前文件的程序语言。
如果GDB没有检测出当前的程序语言,那么你也可以手动设置当前的程序语言。使用set
language命令即可做到。
当set language命令后什么也不跟的话,你可以查看GDB所支持的语言种类:

(gdb) set language
The currently understood settings are:
local or auto Automatic setting based on source file
c Use the C language
c++ Use the C++ language
asm Use the Asm language
chill Use the Chill language
fortran Use the Fortran language
java Use the Java language
modula-2 Use the Modula-2 language
pascal Use the Pascal language
scheme Use the Scheme language
于是你可以在set language后跟上被列出来的程序语言名,来设置当前的语言环境。
后记
——
GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于,其可以形成执行序
列,形成脚本。UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行
软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可
以做出一个非常强大的功能。
于是UNIX下的软件比Windows下的软件更能有机地结合,各自发挥各自的长处,组合成
更为强劲的功能。而Windows下的图形软件基本上是各自为营,互相不能调用,很不利于
各种软件的相互集成。在这里并不是要和Windows做个什么比较,所谓“寸有所长,尺有
所短”,图形化工具还是有不如命令行的地方。(看到这句话时,希望各位千万再也不要认为
我就是“鄙视图形界面”,和我抬杠了 )
我是根据版本为5.1.1的GDB所写的这篇文章,所以可能有些功能已被修改,或是又有更
为强劲的功能。而且,我写得非常仓促,写得比较简略,并且,其中我已经看到有许多错别
字了(我用五笔,所以错字让你看不懂),所以,我在这里对我文中的差错表示万分的歉意。
文中所罗列的GDB的功能时,我只是罗列了一些带用的GDB的命令和使用方法,其实,
我这里只讲述的功能大约只占GDB所有功能的60%吧,详细的文档,还是请查看GDB的
帮助和使用手册吧,或许,过段时间,如果我有空,我再写一篇GDB的高级使用



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



南方科技大学向学生颁发文凭

$
0
0

南方科技大学向两位本科毕业生 授予了学士学位。这是近半个世纪以来,首次有中国高校 自颁毕业证书和文凭。此前中国高校的所有毕业证书和文凭均由教育部统一授权颁发。其中一名毕业生已经被英国伦敦大学学院和布里斯托大学录取,将于今年 9 月份选择前往其中一所学校继续深造。

而另一名计划于 10 月前往牛津大学攻读博士学位。即将离任的南方科技大学首任校长朱清时在接受媒体采访时称,“我希望中国的高校以后都逐步走向自授文凭的路子,因为只有自授的文凭才跟学生的真实能力挂钩。而且,高校才能够竞争,没有行政保护。如果你办得不好,学生没有真本事,你的文凭社会不接受,学校就有危机,就要垮。如果你搞得好,文凭含金量高,社会就接受。这是世界上所有一流大学通常的做法。”

本文链接

EXE文件结构及读取方法

$
0
0

一、EXE文件概念

     EXE File英文全名executable file ,译作可执行文件,可移植可执行 (PE) 文件格式的文件,它可以加载到内存中,并由操作系统加载程序执行,是可在操作系统存储空间中浮动定位的可执行程序。如记事本程序notepad.exe ,可以用来编辑文档,如:测试.txt双击打开notepad.exe记事本程序来进行编辑处理。

二、EXE文件结构

      EXE文件分为两个部分: EXE文件头和程序本体。exe文件比较复杂,属于一种多段的结构,是DOS最成功和复杂的设计之一。每个exe文件包含一个文件头和一个可重定位程序的映像。文件头包含MS-DOS用于加载程序的信息,例如程序的大小和寄存器的初始值。文件头还指向一个重定位表,该表包含指向程序映像中可重定位段地址的指针链表。MS-DOS通过把该映像直接从文件复制到内存加载exe程序,然后调整定位表中说明的可重定位段地址。定位表是一个重定位指针数组,每个指向程序映像中的可重定位段地址。  预知详细原理与结构,请点击 EXE文件结构及原理

                                 

                                                           上表是EXE文件头

三、EXE文件打开方法

    由于EXE文件比较特殊,打开方式也有点特殊,需指定格式,件下面代码:

#include<iostream>
#include<string>
#include<fstream>
#include<ios>
using namespace std;

const int BUFFER_SIZE=1024;

void update(ifstream& in) 
{
	if (!in) 
	{
		return;
	}
	std::streamsize length;
	char buffer[BUFFER_SIZE];
	while (!in.eof())
	{
		in.read(buffer, BUFFER_SIZE);
		length = in.gcount();
		if (length > 0) 
		{
			printf("%s",buffer);
		}
	}
	in.close();
}


int main()
{
	update(ifstream("1.exe", ios_base::binary));
	return 0;
}

       其中,ios_base 是C++标准程序库中的一个类,定义于<ios>头文件中。ios_base类封装了C++标准中的流输入输出中不依赖于读写的数据的类型的基本信息,如格式化信息、异常状态、事件回调函数等。



作者:xj2419174554 发表于2014-7-11 20:25:10 原文链接
阅读:1 评论:0 查看评论

使用ActiveMQ+MQTT实现Android点对点消息通知-转载

$
0
0

 

原文地址: http://blog.csdn.net/kimmking/article/details/17449019

 

实现点对点消息通知的关键问题

 

ActiveMQ使用MQTT协议,加上android上的paho包,即可简单实现消息通知功能,但是mqtt协议只支持topic,而且不能用selector,使得点对点的消息投递变成问题。

 

有两个解决思路:

 

1、每个clientId,建一个topic...这个办法对解决消息点对点投递非常有效,但是有两个大问题:

 

  • 随着用户数增多,topic数量增多,对管理性要求增大,对内存的管理也有问题。
  • 消息广播操作也变得非常麻烦,只能一个个的发送了。

 

2、另一个思路,就是在消息广播的基础上,进行点对点控制,实现某些特征的消息投递到指定的订阅者。

 

这个的实现比较简单,而且没有上面方案的大问题。代码稍微改下即可:https://github.com/apache/activemq/pull/5/files

 

其实就只添加了一个新的类: ClientIdFilterDispatchPolicy

 

可以git clone所在版本源码,然后加上这个类,mvn package以后使用。

 

也可以用我打包好的jar: http://pan.baidu.com/s/1gjaf

 

 

 

使用说明

 

本修改实现mqtt协议使用单个topic,来做消息广播和点对点的投递。

1、将本文件夹下的activemq-broker-5.9.0.jar、activemq-spring-5.9.0.jar换掉apache-activemq-5.9.0\lib下的jar。
2、参考本文件夹下activemq.xml,在topic上配置
 <dispatchPolicy>
                    <clientIdFilterDispatchPolicy />
                  </dispatchPolicy>
3、对于此配置下的所有名称以.PTP结尾的队列,
   如果要投递消息的properties里包含PTP_CLIENTID,则系统只会将此消息发给clientId为此值的订阅者;如果当前没有此clientId的订阅者,消息不会被任何人接收到。
   如果投递消息的properties里不包含PTP_CLIENTID,则消息广播给所有的订阅者。 跟正常消息投递一致。

其中后缀.PTP和键值PTP_CLIENTID,是可以配置的:
  <dispatchPolicy>
                    <clientIdFilterDispatchPolicy ptpSuffix="" ptpClientId="clientId"/>
                  </dispatchPolicy>
如上配置,使得此policy下的所有topic都起作用,且消息的properties里获取clientId的key变成clientId。

消息发布者,如果要对所有人广播消息,直接发送消息即可。
            如果要对指定的消息订阅者发消息,请在消息里设置接收者的clientId:

               message.setStringProperty(PTP_CLIENTID, clientId);则此消息只有指定的订阅者可以拿到。

 

简单测试

 

两台android设备使用MQTT协议订阅到ActiveMQ的同一个topic,clientId分别为mqtt-1001和mqtt1002;

 

写代码发两条消息,设置消息属性中PTP_CLIENTID分别为mqtt-1001和mqtt1002;

 

两个设备分别接收到自己的消息通知,相互之间没有影响。还可以测试下如果消息没有PTP_CLIENTID,两个都能收到。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



[原]如何在Eclipse中使用依赖项目(Required projects)代替jar?

$
0
0

   在Eclipse中使用依赖项目(Required projects)替换jar的好处就是可以随时更改依赖项目中的代码,而在主项目中引用到最新的编译类。这种方式在Debug时用处非常大,避免了每次修改依赖包源代码后,又打包成jar,并不断更新主项目jar包的麻烦。在开源项目中,这样的项目引用方式非常常见。

   1.  删除主工程中对应的jar文件,如:framework.jar。

   2. 右键工程->Properties->Java Build Path->Projects->Add  , 然后选择所依赖的项目,如:framework ->OK(保存设置)  到这一步,你的主工程在编译的时候不会出错,因删除jar文件而报错的class们,也都能正常编译了。

   3.  如果你的工程是Web项目,需要部署调试,那还必须进行以下设置:右键工程->Properties->Deployment Assmebly->Add, 选择projects, 选择目标项目framework,保存。

这样在主工程发布的时候,同时也将依赖项目发布。否则,虽然能成功编译,但在发布到服务器运行时就会报错:ClassNotFound.


  接下来你的调试过程就会很轻松了 微笑



作者:bob007abc 发表于2014-7-11 11:04:31 原文链接
阅读:5 评论:0 查看评论

jmeter从文件获取输入参数数据

$
0
0

http://zhilingdong.blog.163.com/blog/static/20412613420139232442636/

 

首先,创建一个CSV文件中的登录名和密码。在文本编辑器中,你可以做到这一点,格式应为:

username,password
username2,password2
username3 , password3

注意中间以逗号分割, 保存后缀名为csv。

保存该文件到jmeter的安装目录里的bin目录。对于这个例子,我将文件名字保存为“ logins.csv“ 。

现在到JMeter并找到HTTP请求的一步,右键单击

添加配置元素配置设置> CSV数据。

你的树现在应该看起来像这样:

jmeter

 

在上面的树,你可以看到,我在“登录”这个 HTTP请求中使用了CSV数据。

现在点击"csv data set config"。它的屏幕看起来像这样:

jmeter

你需要在此屏幕上填写至少3个值:

文件名:如果您的csv文件在"jmeter安装目录/ bin"目录,这可以只是文件名。如果它是别的地方,使用的完整路径

该文件。
变量名:这相当于在电子表格中的“列名” 。
分隔符:逗号是默认的分隔符,但如果您的文件使用制表符,这是这样说的地方。
其他字段都是可选的,但可能对你有用。如果需要详细请阅读所有关于JMeter的CSV数据设置配置用户手册。

CSV步骤填写正确后,回去给你的HTTP请求和更改的登录名和密码的参数变量。

 

jmeter

这样看到${login} ${password} 这个值就会从csv中读取了。 

当你运行这个测试, JMeter会填在这两个变量的值与在CSV文件中。第一线程将使用第1行的用户名密码

${login} ${password}

,第二个线程使用第2行的用户名密码

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Apple 前无线工程师分享的4个 Wi-Fi 小技巧

$
0
0

Alf Watt 是 Apple 的前 Wi-Fi 工程师,离职后开发了 iStumbler无线网络探测器。在最近做客 MacObserver 网站的 Mac Geek Gab 节目时,该大牛分享了大量关于 Wi-Fi 的知识,下面这四个小技巧也是在做节目时分享出来的,看起来很有意思。

1. 如果你家里有多个路由器,请使用相同的 SSID

为了在家里有更好的无线网络覆盖,很多人家里都装了多个无线路由器并相互桥接起来,并把这些路由器放在不同的位置。为了方便辨认这些路由器,大家都喜欢将这些路由器设置不同的 SSID(也就是无线接入点名称),其实这是不科学的。

Alf Watt 表示在这种情况下,完全可以将这些路由器设置成一样的 SSID,因为 Apple 的设备在选择无线接入点时,是根据你自己的偏好顺序来选择的。比如你这些路由器的无线接入点名称都不一样,那么 iPhone 或者 Mac 始终会自动选择同步在你 iCloud 中的”首选网络”进行连接,即使这个接入点信号可能并不是最好的。但如果使用相同的 SSID, 就不会存在这个问题了。

2. 在一个小区域内的大量无线网络如何防止相互干扰?

比如你是一个网管,在一个小区域内部署多个无线网络接入点时,如何避免信号相互干扰呢?正确的做法是增加接入点的数量,同时调低接入点/路由器的发射功率。如果你是在家里,那么可以鼓励邻居这样做,这样他家的信号就不会干扰到你的路由器了。当然为了邻居,你也应该这样做。

20140710_wifipowerset

还有一个方案是切换到 5Ghz 频段,切换到5GHz 之后,不会和 2.4GHz 频段的路由器产生相互干扰;另一方面高频段的穿墙能力也很弱,即使附近的都用 5GHz 频段,由于穿墙能力很弱也不会产生什么干扰。

3. 不要在2.4GHz 频段上使用 40MHz 信道

有一些路由器厂家为了吸引眼球,整出了所谓“宽信道”的功能,也就是让用户能够在2.4GHz 频段上使用40MHz 信道。然而这个信道其实非常拥挤,蓝牙也是走的这个信道。如果你连接路由器的信号不是足够好(就在路由器面前使用差不多),强烈建议不要在2.4GHz 的频段上使用40MHz 信道。其实,Apple 的 Airport 已经在一年前就抛弃这个功能了。

20140710_no40MHz2point4

不过在 5GHz 频段上使用 40MHz 就是最好的选择。在802.11ac(只支持5GHz)路由器上,你还可以使用80MHz 甚至160MHz 的信道。

4. 路由器的天线应该怎么摆放?

对于内置天线的无线路由器,你其实不必研究怎么摆放信号最好。因为厂家都帮你想好了,你只需要将有脚垫的那一面放在地上/或者其他什么平台上即可,这样不仅有助于获得最好的信号,同时还有助于路由器内散热。

20140710_perpendicular_wifi_antenna_bk

而对于可以调节角度的天线(以双天线为例),最好的办法是让两根天线形成一个直角(如图),因为现在的设备内部,基本上都是朝着水平或者垂直方向放置天线的,而当发射天线和接收天线在同一个方向时,无线接收的效率最大。

via MacObserver

分享MYSQL中的各种高可用技术(源自姜承尧大牛) - 桦仔

$
0
0

分享MYSQL中的各种高可用技术(源自姜承尧大牛)

图片和资料来源于MYSQL大牛姜承尧老师( MYSQL技术内幕作者

姜承尧: 网易杭州研究院 技术经理 主导INNOSQL的开发

mysql高可用各个技术的比较

数据库的可靠指的是数据可靠 

数据库可用指的是数据库服务可用

可靠的是数据:例如工商银行,数据不能丢失

可用的是服务:服务器不能宕机

 


方案一:MYSQL主从复制(单活)

投票选举机制,较复杂

MySQL本身没有提供replication failover的解决方案,自动切换需要依赖 MHA脚本

可以有多台从库,从库可以做报表和备份

 


方案二:双主(单活),failover比单主简单

同样,自动切换需要 MMM脚本

缺点是某个主挂掉了,他下面的slave同样挂掉


方案三:双主配SAN存储(单活)

 

这个架构跟方案二是一样的,只不过两个master之间不需要同步数据,因为他们用的是共享磁盘

这个方案是 有钱人方案,无论哪个主挂掉都不会引起其他的slave挂掉,但是SAN存储死贵。。

像通信行业中国联通这些公司有用到

某个主挂掉了,下面的slave不会挂掉

注意:failover之后不会预热,数据没有预先加载到内存中,切换之后一段时间内存储会有一定的性能影响

 


方案四:DRBD  双主配 DRBD (单活)

结构跟方案三一样,唯一不同的是没有使用SAN网络存储 ,而是使用 local disk

由于是实时复制磁盘数据,性能会有影响

人们把DRBD称为 “屌丝的SAN”

POOR MAN'S SAN:穷人的SAN

 

 

 


方案五:NDB CLUSTER

 

国内用NDB集群的公司很少,貌似有些银行有用

NDB集群不需要依赖第三方组件,全部都使用官方组件,能保证数据的一致性

某个数据节点挂掉,其他数据节点依然可以提供服务

管理节点需要做冗余以防挂掉

缺点是:管理和配置都很复杂,而且某些SQL语句例如join语句需要避免

 


方案六:第三方的Tungsten软件

使用java编写,不是MYSQL内置的

同样是MYSQL数据库复制,不过他不是用MYSQL内置的组件来做的

不但支持MYSQL数据库复制也支持异构数据库的复制,而且对异构数据库复制支持较好,例如MYSQL复制到ORACLE


方案七:网易的INNOSQL

类似于SQLSERVER的 镜像高安全模式

High Safety 模式 (也就是同步模式)没有 witness服务器

数据库在Principle的事务, 需要马上得到mirror的确认,才能完成。这种情况下,Mirror和Principle的数据是同步的。

但是因为所有的事务需要mirror的确认,所以性能可能会有所影响。

区别:innosql的slave可以读,镜像的slave(从库)不可读

保证数据不会丢失,数据的高可靠性

 

mysql5.7开始支持这种模式

 

 


总结

每种方案都有不同的特点,配置和应用场景也各有不同

有些偏向于成本低的,有些偏向于成本高的,有些偏向于数据的可靠性,有些则偏向于数据库的可用性

反正各个方案都各有优缺点,DBA要结合自己公司的业务情况进行选择合适自己业务情况的高可用方案

 

更多参考资料:

读写分离:Amoeba

Ubuntu10下MySQL搭建Amoeba系列(文章索引)

集群技术: 数据库集群技术漫谈

 

如有不对的地方,欢迎大家拍砖o(∩_∩)o 


本文链接: 分享MYSQL中的各种高可用技术(源自姜承尧大牛),转载请注明。

JVM实用参数(五)新生代垃圾回收

$
0
0

本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation)。首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数。

单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作。新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法)

通过广泛研究面向对象实现的应用,发现一个共同特点:很多对象的生存时间都很短。同时研究发现,新生对象很少引用生存时间长的对象。结合这2个特点,很明显 GC 会频繁访问新生对象,例如在堆中一个单独的区域,称之为新生代。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”。

 

SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。

基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。

https://blog.codecentric.de/files/2011/08/young_gc.png
https://blog.codecentric.de/files/2011/08/young_gc.png
上图演示GC过程,黄色表示死对象,绿色表示剩余空间,红色表示幸存对象

总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,如果对象存活到适当的年龄,会被移动到老年代。当对象在老年代死亡时,就需要更高级别的GC,更重量级的GC算法(复制算法不适用于老年代,因为没有多余的空间用于复制)

现在应该能理解为什么新生代大小非常重要了(译者,有另外一种说法:新生代大小并不重要,影响GC的因素主要是幸存对象的数量),如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。我们需要找到一个合适大小,不幸的是,要想获得一个合适的大小,只能通过不断的测试调优。这就需要JVM参数了

-XX:NewSize and -XX:MaxNewSize
就像可以通过参数(-Xms and -Xmx) 指定堆大小一样,可以通过参数指定新生代大小。设置 XX:MaxNewSize 参数时,应该考虑到新生代只是整个堆的一部分,新生代设置的越大,老年代区域就会减少。一般不允许新生代比老年代还大,因为要考虑GC时最坏情况,所有对象都晋升到老年代。(译者:会发生OOM错误) -XX:MaxNewSize 最大可以设置为-Xmx/2 .

考虑性能,一般会通过参数 -XX:NewSize 设置新生代初始大小。如果知道新生代初始分配的对象大小(经过监控) ,这样设置会有帮助,可以节省新生代自动扩展的消耗。

-XX:NewRatio
可以设置新生代和老年代的相对大小。这种方式的优点是新生代大小会随着整个堆大小动态扩展。参数 -XX:NewRatio 设置老年代与新生代的比例。例如 -XX:NewRatio=3 指定老年代/新生代为3/1. 老年代占堆大小的 3/4 ,新生代占 1/4 .

如果针对新生代,同时定义绝对值和相对值,绝对值将起作用。下面例子:
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp

以上设置, JVM 会尝试为新生代分配四分之一的堆大小,但不会小于32MB或大于521MB

在设置新生代大小问题上,使用绝对值还是相对值,不存在通用准则 。如果了解应用的内存使用情况,设置固定大小的堆和新生代更有利,当然也可以设置相对值。如果对应用的内存使用一无所知,正确的做法是不要设置任何参数,如果应用运行良好。很好,我们不用做任何额外动作.如果遇到性能或OutOfMemoryErrors, 在调优之前,首先需要进行一系列有目的的监控测试,缩小问题的根源。

-XX:SurvivorRatio
参数 -XX:SurvivorRatio 与 -XX:NewRatio 类似,作用于新生代内部区域。-XX:SurvivorRatio 指定伊甸园区(Eden)与幸存区大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸园区(Eden)是 幸存区To 大小的10倍(也是幸存区From的10倍).所以,伊甸园区(Eden)占新生代大小的10/12, 幸存区From和幸存区To 每个占新生代的1/12 .注意,两个幸存区永远是一样大的..

设定幸存区大小有什么作用? 假设幸存区相对伊甸园区(Eden)太小, 相应新生对象的伊甸园区(Eden)永远很大空间, 我们当然希望,如果这些对象在GC时全部被回收,伊甸园区(Eden)被清空,一切正常.然而,如果有一部分对象在GC中幸存下来, 幸存区只有很少空间容纳这些对象.结果大部分幸存对象在一次GC后,就会被转移到老年代 ,这并不是我们希望的.考虑相反情况, 假设幸存区相对伊甸园区(Eden)太大,当然有足够的空间,容纳GC后的幸存对象. 但是过小的伊甸园区(Eden),意味着空间将越快耗尽,增加新生代GC次数,这是不可接受的。

总之,我们希望最小化短命对象晋升到老年代的数量,同时也希望最小化新生代GC 的次数和持续时间.我们需要找到针对当前应用的折中方案, 寻找适合方案的起点是 了解当前应用中对象的年龄分布情况。

-XX:+PrintTenuringDistribution
参数 -XX:+PrintTenuringDistribution 指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布。例如:
Desired survivor size 75497472 bytes, new threshold 15 (max 15)
- age 1: 19321624 bytes, 19321624 total
- age 2: 79376 bytes, 19401000 total
- age 3: 2904256 bytes, 22305256 total

第一行说明幸存区To大小为 75 MB. 也有关于老年代阀值(tenuring threshold)的信息, 老年代阀值,意思是对象从新生代移动到老年代之前,经过几次GC(即, 对象晋升前的最大年龄). 上例中,老年代阀值为15,最大也是15.

之后行表示,对于小于老年代阀值的每一个对象年龄,本年龄中对象所占字节 (如果当前年龄没有对象,这一行会忽略). 上例中,一次 GC 后幸存对象大约 19 MB, 两次GC 后幸存对象大约79 KB , 三次GC 后幸存对象大约 3 MB .每行结尾,显示直到本年龄全部对象大小.所以,最后一行的 total 表示幸存区To 总共被占用22 MB . 幸存区To 总大小为 75 MB ,当前老年代阀值为15,可以断定在本次GC中,没有对象会移动到老年代。现在假设下一次GC 输出为:

Desired survivor size 75497472 bytes, new threshold 2 (max 15)
- age 1: 68407384 bytes, 68407384 total
- age 2: 12494576 bytes, 80901960 total
- age 3: 79376 bytes, 80981336 total
- age 4: 2904256 bytes, 83885592 total

对比前一次老年代分布。明显的,年龄2和年龄3 的对象还保持在幸存区中,因为我们看到年龄3和4的对象大小与前一次年龄2和3的相同。同时发现幸存区中,有一部分对象已经被回收,因为本次年龄2的对象大小为 12MB ,而前一次年龄1的对象大小为 19 MB。最后可以看到最近的GC中,有68 MB 新对象,从伊甸园区移动到幸存区。

注意,本次GC 幸存区占用总大小 84 MB -大于75 MB. 结果,JVM 把老年代阀值从15降低到2,在下次GC时,一部分对象会强制离开幸存区,这些对象可能会被回收(如果他们刚好死亡)或移动到老年代。

-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
参数 -XX:+PrintTenuringDistribution 输出中的部分值可以通过其它参数控制。通过 -XX:InitialTenuringThreshold 和 -XX:MaxTenuringThreshold 可以设定老年代阀值的初始值和最大值。另外,可以通过参数 -XX:TargetSurvivorRatio 设定幸存区的目标使用率.例如 , -XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%。

有多种方式,设置新生代行为,没有通用准则。我们必须清楚以下2中情况:
1 如果从年龄分布中发现,有很多对象的年龄持续增长,在到达老年代阀值之前。这表示 -XX:MaxTenuringThreshold 设置过大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多对象年龄从未大于1.应该看下幸存区的目标使用率。如果幸存区使用率从未到达,这表示对象都被GC回收,这正是我们想要的。 如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率。

-XX:+NeverTenure and -XX:+AlwaysTenure
最后,我们介绍2个颇为少见的参数,对应2种极端的新生代GC情况.设置参数 -XX:+NeverTenure , 对象永远不会晋升到老年代.当我们确定不需要老年代时,可以这样设置。这样设置风险很大,并且会浪费至少一半的堆内存。相反设置参数 -XX:+AlwaysTenure, 表示没有幸存区,所有对象在第一次GC时,会晋升到老年代。
没有合理的场景使用这个参数。可以在测试环境中,看下这样设置会发生什么有趣的事.但是并不推荐使用这些参数.

结论
适当的配置新生代非常重要,有相当多的参数可以设置新生代。然而,单独调整新生代,而不考虑老年代是不可能优化成功的。当调整堆和GC设置时,我们总是应该同时考虑新生代和老年代。

在本系列的下面2部分,我们将讨论 HotSpot JVM 中老年代 GC 策略,我们会学习“吞吐量GC收集器” 和 “并发低延迟GC收集器”,也会了解收集器的基本准则,算法和调整参数.



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



JVM实用参数(四)内存调优

$
0
0

理想的情况下,一个Java程序使用JVM的默认设置也可以运行得很好,所以一般来说,没有必要设置任何JVM参数。然而,由于一些性能问题(很不幸的是,这些问题经常出现),一些相关的JVM参数知识会是我们工作中得好伙伴。在这篇文章中,我们将介绍一些关于JVM内存管理的参数。知道并理解这些参数,将对开发者和运维人员很有帮助。

所有已制定的HotSpot内存管理和垃圾回收算法都基于一个相同的堆内存划分:新生代(young generation)里存储着新分配的和较年轻的对象,老年代(old generation)里存储着长寿的对象。在此之外,永久代(permanent generation)存储着那些需要伴随整个JVM生命周期的对象,比如,已加载的对象的类定义或者String对象内部Cache。接下来,我们将假设堆内存是按照新生代、老年代和永久代这一经典策略划分的。然而,其他的一些堆内存划分策略也是可行的,一个突出的例子就是新的G1垃圾回收器,它模糊了新生代和老年代之间的区别。此外,目前的开发进程似乎表明在未来的HotSpot JVM版本中,将不会区分老年代和永久代。

-Xms and -Xmx (or: -XX:InitialHeapSize and -XX:MaxHeapSize)

-Xms和-Xmx可以说是最流行的JVM参数,它们可以允许我们指定JVM的初始和最大堆内存大小。一般来说,这两个参数的数值单位是Byte,但同时它们也支持使用速记符号,比如“k”或者“K”代表“kilo”,“m”或者“M”代表“mega”,“g”或者“G”代表“giga”。举个例子,下面的命令启动了一个初始化堆内存为128M,最大堆内存为2G,名叫“MyApp”的Java应用程序。

1java -Xms128m -Xmx2g MyApp

在实际使用过程中,初始化堆内存的大小通常被视为堆内存大小的下界。然而JVM可以在运行时动态的调整堆内存的大小,所以理论上来说我们有可能会看到堆内存的大小小于初始化堆内存的大小。但是即使在非常低的堆内存使用下,我也从来没有遇到过这种情况。这种行为将会方便开发者和系统管理员,因为我们可以通过将“-Xms”和“-Xmx”设置为相同大小来获得一个固定大小的堆内存。 -Xms和-Xmx实际上是-XX:InitialHeapSize和-XX:MaxHeapSize的缩写。我们也可以直接使用这两个参数,它们所起得效果是一样的:

1$ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp

需要注意的是,所有JVM关于初始\最大堆内存大小的输出都是使用它们的完整名称:“InitialHeapSize”和“InitialHeapSize”。所以当你查询一个正在运行的JVM的堆内存大小时,如使用-XX:+PrintCommandLineFlags参数或者通过JMX查询,你应该寻找“InitialHeapSize”和“InitialHeapSize”标志而不是“Xms”和“Xmx”。

-XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath

如果我们没法为-Xmx(最大堆内存)设置一个合适的大小,那么就有可能面临内存溢出(OutOfMemoryError)的风险,这可能是我们使用JVM时面临的最可怕的猛兽之一。就同 另外一篇关于这个主题的博文说的一样,导致内存溢出的根本原因需要仔细的定位。通常来说,分析堆内存快照(Heap Dump)是一个很好的定位手段,如果发生内存溢出时没有生成内存快照那就实在是太糟了,特别是对于那种JVM已经崩溃或者错误只出现在顺利运行了数小时甚至数天的生产系统上的情况。

幸运的是,我们可以通过设置-XX:+HeapDumpOnOutOfMemoryError 让JVM在发生内存溢出时自动的生成堆内存快照。有了这个参数,当我们不得不面对内存溢出异常的时候会节约大量的时间。默认情况下,堆内存快照会保存在JVM的启动目录下名为java_pid<pid>.hprof 的文件里(在这里<pid>就是JVM进程的进程号)。也可以通过设置-XX:HeapDumpPath=<path>来改变默认的堆内存快照生成路径,<path>可以是相对或者绝对路径。

虽然这一切听起来很不错,但有一点我们需要牢记。堆内存快照文件有可能很庞大,特别是当内存溢出错误发生的时候。因此,我们推荐将堆内存快照生成路径指定到一个拥有足够磁盘空间的地方。

-XX:OnOutOfMemoryError

当内存溢发生时,我们甚至可以可以执行一些指令,比如发个E-mail通知管理员或者执行一些清理工作。通过-XX:OnOutOfMemoryError 这个参数我们可以做到这一点,这个参数可以接受一串指令和它们的参数。在这里,我们将不会深入它的细节,但我们提供了它的一个例子。在下面的例子中,当内存溢出错误发生的时候,我们会将堆内存快照写到/tmp/heapdump.hprof 文件并且在JVM的运行目录执行脚本cleanup.sh

1$ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh"  MyApp

 -XX:PermSize and -XX:MaxPermSize

永久代在堆内存中是一块独立的区域,它包含了所有JVM加载的类的对象表示。为了成功运行应用程序,JVM会加载很多类(因为它们依赖于大量的第三方库,而这又依赖于更多的库并且需要从里面将类加载进来)这就需要增加永久代的大小。我们可以使用-XX:PermSize 和-XX:MaxPermSize 来达到这个目的。其中-XX:MaxPermSize 用于设置永久代大小的最大值,-XX:PermSize 用于设置永久代初始大小。下面是一个简单的例子:

1$ java -XX:PermSize=128m -XX:MaxPermSize=256m MyApp

请注意,这里设置的永久代大小并不会被包括在使用参数-XX:MaxHeapSize 设置的堆内存大小中。也就是说,通过-XX:MaxPermSize设置的永久代内存可能会需要由参数-XX:MaxHeapSize 设置的堆内存以外的更多的一些堆内存。

-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize

JVM一个有趣的,但往往被忽视的内存区域是“代码缓存”,它是用来存储已编译方法生成的本地代码。代码缓存确实很少引起性能问题,但是一旦发生其影响可能是毁灭性的。如果代码缓存被占满,JVM会打印出一条警告消息,并切换到interpreted-only 模式:JIT编译器被停用,字节码将不再会被编译成机器码。因此,应用程序将继续运行,但运行速度会降低一个数量级,直到有人注意到这个问题。就像其他内存区域一样,我们可以自定义代码缓存的大小。相关的参数是-XX:InitialCodeCacheSize 和-XX:ReservedCodeCacheSize,它们的参数和上面介绍的参数一样,都是字节值。

-XX:+UseCodeCacheFlushing

如果代码缓存不断增长,例如,因为热部署引起的内存泄漏,那么提高代码的缓存大小只会延缓其发生溢出。为了避免这种情况的发生,我们可以尝试一个有趣的新参数:当代码缓存被填满时让JVM放弃一些编译代码。通过使用-XX:+UseCodeCacheFlushing 这个参数,我们至少可以避免当代码缓存被填满的时候JVM切换到interpreted-only 模式。不过,我仍建议尽快解决代码缓存问题发生的根本原因,如找出内存泄漏并修复它。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



各种浏览器全屏模式的方法、属性和事件介绍

$
0
0

浏览器全屏模式的启动函数 requestFullscreen仍然需要附带各浏览器的js方言前缀,相信下面这段代码需要你花大量的搜索才能凑齐:

// 判断各种浏览器,找到正确的方法
function launchFullscreen(element) {
  if(element.requestFullscreen) {
    element.requestFullscreen();
  } else if(element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if(element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  } else if(element.msRequestFullscreen) {
    element.msRequestFullscreen();
  }
}

// 启动全屏!
launchFullScreen(document.documentElement); // 整个网页
launchFullScreen(document.getElementById("videoElement")); // 某个页面元素
 

 

对你想要全屏显示的页面元素调用全屏方法,浏览器窗口就会变成全屏,但会先请求用户允许全屏模式。要注意,用户很有可能会拒绝全屏模式。如果用户运行全屏模式,则浏览器的工具条等按钮菜单都会隐藏,你的页面会覆盖整个屏幕。

 

退出全屏模式

这个 exitFullscreen方法(也需要加浏览器前缀)会让浏览器退出全屏模式,变成正常模式。

// 判断浏览器种类
function exitFullscreen() {
  if(document.exitFullscreen) {
    document.exitFullscreen();
  } else if(document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if(document.webkitExitFullscreen) {
    document.webkitExitFullscreen();
  }
}

// 退出全屏模式!
exitFullscreen();
 

 

需要注意的是, exitFullscreen只能由document对象调用,而不是启动全屏时传入的对象。

 

全屏属性和事件

不幸的是,全屏属性和事件的相关方法也需要添加浏览器前缀,但我相信很快就不需要这样做了。

  • document.fullScreenElement: 全屏显示的网页元素。
  • document.fullScreenEnabled: 判断当前是否处于全屏状态。

fullscreenchange事件会在启动全屏或退出全屏时触发:

var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement;
var fullscreenEnabled = document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled;
 

 

你仍然可以使用上面判断浏览器种类的方法给这个事件加上前缀。

 

全屏样式CSS

各种浏览器都提供了一个非常有用的全屏模式时的css样式规则:

:-webkit-full-screen {
  /* properties */
}

:-moz-full-screen {
  /* properties */
}

:-ms-fullscreen {
  /* properties */
}

:full-screen { /*pre-spec */
  /* properties */
}

:fullscreen { /* spec */
  /* properties */
}

/* deeper elements */
:-webkit-full-screen video {
  width: 100%;
  height: 100%;
}

/* styling the backdrop*/
::backdrop {
  /* properties */
}
::-ms-backdrop {
  /* properties */
}
 

 

有些情况下,WebKit样式会出现一些问题,你最好把这些样式保持简洁。

 

这些全屏API都超级的简单,而且超级的有用。我第一次是在 MDN’s BananaBread demo中看到这个API的,那是一个枪击游戏,正好需要全屏化,它使用了事件监听来检测全屏状态。记住这些好用的API,需要的时候可以顺手拈来。

原文: http://www.webhek.com/fullscreen/

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Android Otto框架浅析

$
0
0

今天要介绍的是一个Android中使用得比较多的android 事件总线 EventBus模式的一个框架Otto。

Otto 官网: http://square.github.io/otto/


一、Android Studio中配置Otto (Eclipse中直接下载jar包导入)

跟之前介绍的其他的框架一样,它只需要简单地在build.gradle中配置下面一行红色字体即可

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:19.+'
    / /otto 所需要依赖的包
    compile 'com.squareup:otto:+'

}


二、Otto事件总线框架浅析

1、为什么要用Otto框架?

Otto框架的主要功能是帮助我们来降低多个类之间的耦合度的(解耦)。

譬如:一个类A和另一个类B之间,如果A要操作B中的某个方法。


传统的方法:A直接调用B对象的该方法(耦合在一起)

事件总线机制:A不需要调用B类的方法,而仅仅需要产生并发出 一个“事件通知”, 如果B订阅了该“事件”

那么它将会接受到这个事件,做出相应的操作。这样就被解耦了。


2. Otto框架使用(结合代码介绍)

Otto框架其实相对之前的几个Android开源框架来说,更容易理解。

它主要运用到一个类: Bus类(用于注册类,注销类,发布事件)

两个注解: @Subscribe (订阅)    @Produce (生产)    【都是针对“事件”的注解】




为了方便,我这边没有使用两个类来做这个Demo, 不过如果大家要试的话也可以,不过记住一点,无论是

发布者类  还是 订阅者类, 都需要用Bus进行类的注册,还有注销。否则将没法被Bus识别,这样就无法生效了。

下面的Demo, 仅为了让大家知道“事件”被产生了之后,post出来,所有订阅了该事件的类都会接到该事件,接受的先后顺序,不由我们控制!

public class MyActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        findViewById(R.id.button_change).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                BusProvider.getBusInstance().post(new DataChangedEvent("this is changed String"));//发布事件
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        BusProvider.getBusInstance().register(this);//注册
        
    }

    @Override
    protected void onPause() {
        super.onPause();
        BusProvider.getBusInstance().unregister(this);//注销
    }

    @Subscribe   //订阅事件DataChangedEvent
    public void sayGoodOnEvent(DataChangedEvent event){
        Log.e("event", "good");
    }

    @Subscribe  //订阅事件
    public void sayBadOnEvent(DataChangedEvent event){
        Log.e("event", "bad");
    }

    @Produce    //产生事件
    public DataChangedEvent produceDataChangedEvent(){
        return new DataChangedEvent("this is changed String");
    }

}

作者:u011133213 发表于2014-7-12 17:12:39 原文链接
阅读:100 评论:0 查看评论

中国公司的手持扫描仪被发现携带恶意程序

$
0
0
一家向全球物流和运输公司出售手持扫描仪硬件和软件的中国公司被发现其产品携带了恶意程序。恶意程序渗透到企业内网后会向位于中国的指令控制服务器发送数据,而其中一个指令控制服务器位于山东蓝翔职校。安全公司TrapX发布了分析报告(PDF),将这一攻击行动命名为Zombie Zero。它怀疑黑客攻击背后的支持者可能是国家。恶意程序嵌入在设备使用的Windows XP Embedded中,厂商支持网站上的Windows XP Embedded也包含病毒。当扫描仪连接到企业无线网络,恶意程序会使用SMB协议 (端口135/ 135)自动对企业内网发动攻击,如果被防火墙拦住,它随后会利用RADMIN协议(端口4899) 发动第二轮攻击。当成功渗透进内网后,恶意程序会寻找含有英文finance的主机名的特定企业服务器。在定位处理金融数据的服务器后,它会联系位于蓝翔和北京的指令控制服务器,扫描仪生产厂商与蓝翔职校位于同一区域。

【转】ETL中用到的一些基本技术概论

$
0
0

前话

在公司做了几年的数据,跟数据打了不少的交道。对软件的理解,也慢慢的偏向于对数据的流转上,虽然用户体验UI也占据着很大的地位。之前答应过坛子里的朋友,准备写关于写金蝶的系列文章。时间恍惚,匆匆几年就这样过去了。至今竟没有像样的文字。如今,笔者历经几年,从一个大男孩,变成了美丽女孩的丈夫。为我的程序人生翻开了新的篇章。曾今,朋友说到:有些人写代码是为了一辈子都去写代码,也有些人写代码是为了不去写代码。笔者也曾经在这样的思想中挣扎与徘徊。除了抱怨,也许平静下来钻研并分享技术才是我们的乐趣所在。

那么就从这篇摘抄的技术文档开始把。 

 

----------------------------------------------------- 我是分割线 ------------------------------------------------------------

 

1.ETL简介

随着企业信息化建设的发展,巨大的投资为企业建立了众多的信息系统,以帮助企业进行内外部业务的处理和管理工作。但是随着信息系统的增加,各自孤立工作的信息系统将会造成大量的冗余数据和业务人员的重复劳动。企业应用集成(EAI,Enterprise Application Integration)应运而生。EAI通过建立底层数据交换平台来联系横贯整个企业的异构系统、应用、数据源等,完成在企业内部的ERP、CRM、SCM、数据库、数据仓库,以及其它重要的内部系统之间无缝地共享和交换数据的需要。数据集成是企业应用集成的重要环节,企业实现数据集成,可以使更多的人更充分地使用已有数据资源,减少资料收集、数据采集等重复劳动和相应费用。

但是,在实施数据集成的过程中,由于不同用户提供的数据可能来自不同的途径,其数据内容、数据格式和数据质量千差万别,有时甚至会遇到数据格式不能转换或数据转换格式后丢失信息等棘手问题,严重阻碍了数据在各部门和各应用系统中的流动与共享。因此,如何对数据进行有效的集成管理已成为增强企业商业竞争力的必然选择。ETL是实现数据集成的主要技术。

ETL中三个字母分别代表的是Extract、Transform、Load,即抽取、转换、加载。(1)数据抽取:从源数据源系统抽取目的数据源系统需要的数据;(2)数据转换:将从源数据源获取的数据按照业务需求,转换成目的数据源要求的形式,并对错误、不一致的数据进行清洗和加工。(3)数据加载:将转换后的数据装载到目的数据源。

ETL原本是作为构建数据仓库的一个环节,负责将分布的、异构数据源中的数据如关系数据、平面数据文件等抽取到临时中间层后进行清洗、转换、集成,最后加载到数据仓库或数据集市中,成为联机分析处理、数据挖掘的基础。现在也越来越多地将ETL应用于一般信息系统中数据的迁移、交换和同步。一个简单的ETL体系结构如图1.1所示。

2.ETL中的关键技术

ETL过程中的主要环节就是数据抽取、数据转换和加工、数据装载。为了实现这些功能,各个ETL工具一般会进行一些功能上的扩充,例如工作流、调度引擎、规则引擎、脚本支持、统计信息等。

2.1 数据抽取

数据抽取是从数据源中抽取数据的过程。实际应用中,数据源较多采用的是关系数据库。从数据库中抽取数据一般有以下几种方式。

(1)全量抽取

全量抽取类似于数据迁移或数据复制,它将数据源中的表或视图的数据原封不动的从数据库中抽取出来,并转换成自己的ETL工具可以识别的格式。全量抽取比较简单。

(2)增量抽取

增量抽取只抽取自上次抽取以来数据库中要抽取的表中新增或修改的数据。在ETL使用过程中。增量抽取较全量抽取应用更广。如何捕获变化的数据是增量抽取的关键。对捕获方法一般有两点要求:准确性,能够将业务系统中的变化数据按一定的频率准确地捕获到;性能,不能对业务系统造成太大的压力,影响现有业务。目前增量数据抽取中常用的捕获变化数据的方法有:

a.触发器:在要抽取的表上建立需要的触发器,一般要建立插入、修改、删除三个触发器,每当源表中的数据发生变化,就被相应的触发器将变化的数据写入一个临时表,抽取线程从临时表中抽取数据,临时表中抽取过的数据被标记或删除。触发器方式的优点是数据抽取的性能较高,缺点是要求业务表建立触发器,对业务系统有一定的影响。

b.时间戳:它是一种基于快照比较的变化数据捕获方式,在源表上增加一个时间戳字段,系统中更新修改表数据的时候,同时修改时间戳字段的值。当进行数据抽取时,通过比较系统时间与时间戳字段的值来决定抽取哪些数据。有的数据库的时间戳支持自动更新,即表的其它字段的数据发生改变时,自动更新时间戳字段的值。有的数据库不支持时间戳的自动更新,这就要求业务系统在更新业务数据时,手工更新时间戳字段。同触发器方式一样,时间戳方式的性能也比较好,数据抽取相对清楚简单,但对业务系统也有很大的倾入性(加入额外的时间戳字段),特别是对不支持时间戳的自动更新的数据库,还要求业务系统进行额外的更新时间戳操作。另外,无法捕获对时间戳以前数据的delete和update操作,在数据准确性上受到了一定的限制。

c.全表比对:典型的全表比对的方式是采用MD5校验码。ETL工具事先为要抽取的表建立一个结构类似的MD5临时表,该临时表记录源表主键以及根据所有字段的数据计算出来的MD5校验码。每次进行数据抽取时,对源表和MD5临时表进行MD5校验码的比对,从而决定源表中的数据是新增、修改还是删除,同时更新MD5校验码。MD5方式的优点是对源系统的倾入性较小(仅需要建立一个MD5临时表),但缺点也是显而易见的,与触发器和时间戳方式中的主动通知不同,MD5方式是被动的进行全表数据的比对,性能较差。当表中没有主键或唯一列且含有重复记录时,MD5方式的准确性较差。

d.日志对比:通过分析数据库自身的日志来判断变化的数据。Oracle的改变数据捕获(CDC,Changed Data Capture)技术是这方面的代表。CDC 特性是在Oracle9i数据库中引入的。CDC能够帮助你识别从上次抽取之后发生变化的数据。利用CDC,在对源表进行insert、update或 delete等操作的同时就可以提取数据,并且变化的数据被保存在数据库的变化表中。这样就可以捕获发生变化的数据,然后利用数据库视图以一种可控的方式提供给目标系统。CDC体系结构基于发布者/订阅者模型。发布者捕捉变化数据并提供给订阅者。订阅者使用从发布者那里获得的变化数据。通常,CDC系统拥有一个发布者和多个订阅者。发布者首先需要识别捕获变化数据所需的源表。然后,它捕捉变化的数据并将其保存在特别创建的变化表中。它还使订阅者能够控制对变化数据的访问。订阅者需要清楚自己感兴趣的是哪些变化数据。一个订阅者可能不会对发布者发布的所有数据都感兴趣。订阅者需要创建一个订阅者视图来访问经发布者授权可以访问的变化数据。CDC分为同步模式和异步模式,同步模式实时的捕获变化数据并存储到变化表中,发布者与订阅都位于同一数据库中。异步模式则是基于Oracle的流复制技术。

ETL处理的数据源除了关系数据库外,还可能是文件,例如txt文件、excel文件、xml文件等。对文件数据的抽取一般是进行全量抽取,一次抽取前可保存文件的时间戳或计算文件的MD5校验码,下次抽取时进行比对,如果相同则可忽略本次抽取。

2.2 数据转换和加工

从数据源中抽取的数据不一定完全满足目的库的要求,例如数据格式的不一致、数据输入错误、数据不完整等等,因此有必要对抽取出的数据进行数据转换和加工。

数据的转换和加工可以在ETL引擎中进行,也可以在数据抽取过程中利用关系数据库的特性同时进行。

(1)ETL引擎中的数据转换和加工

ETL引擎中一般以组件化的方式实现数据转换。常用的数据转换组件有字段映射、数据过滤、数据清洗、数据替换、数据计算、数据验证、数据加解密、数据合并、数据拆分等。这些组件如同一条流水线上的一道道工序,它们是可插拔的,且可以任意组装,各组件之间通过数据总线共享数据。

有些ETL工具还提供了脚本支持,使得用户可以以一种编程的方式定制数据的转换和加工行为。

(2)在数据库中进行数据加工

关系数据库本身已经提供了强大的SQL、函数来支持数据的加工,如在SQL查询语句中添加where条件进行过滤,查询中重命名字段名与目的表进行映射,substr函数,case条件判断等等。下面是一个SQL查询的例子。

select ID as USERID, substr(TITLE, 1, 20) as TITLE, case when REMARK is null then ' ' else REMARK end as CONTENT from TB_REMARK where ID > 100;

相比在ETL引擎中进行数据转换和加工,直接在SQL语句中进行转换和加工更加简单清晰,性能更高。对于SQL语句无法处理的可以交由ETL引擎处理。

2.3 数据装载

将转换和加工后的数据装载到目的库中通常是ETL过程的最后步骤。装载数据的最佳方法取决于所执行操作的类型以及需要装入多少数据。当目的库是关系数据库时,一般来说有两种装载方式:

(1)直接SQL语句进行insert、update、delete操作。

(2)采用批量装载方法,如bcp、bulk、关系数据库特有的批量装载工具或api。

大多数情况下会使用第一种方法,因为它们进行了日志记录并且是可恢复的。但是,批量装载操作易于使用,并且在装入大量数据时效率较高。使用哪种数据装载方法取决于业务系统的需要。

3.主流ETL工具

ETL工具从厂商来看分为两种,一种是数据库厂商自带的ETL工具,如Oracle warehouse builder、Oracle Data Integrator。另外一种是第三方工具提供商,如Kettle。开源世界也有很多的ETL工具,功能各异,强弱不一。

(1)Oracle Data Integrator(ODI)

ODI前身是Sunopsis Active Integration Platform,在2006年底被Oracle收购,重新命名为Oracle Data Integrator,主要定位于在ETL和数据集成的场景里使用。ODI和Oracle原来的ETL工具OWB相比有一些显著的特点,比如和OWB一样是ELT架构,但是比OWB支持更多的异构的数据源,ODI提供了call web service的机制,并且ODI的接口也可以暴露为web service,从而可以和SOA环境进行交互。ODI能够检测事件,一个事件可以触发ODI的一个接口流程,从而完成近乎实时的数据集成。

ODI的主要功能特点有:

a.使用CDC作为变更数据捕获的捕获方式。

b.代理支持并行处理和负载均衡。

c.完善的权限控制、版本管理功能。

d.支持数据质量检查,清洗和回收脏数据。

e.支持与JMS消息中间件集成。

f.支持Web Service。

(2)SQL Server Integration Services(SSIS)

SSIS是SQL Server 2005的新成员,在SQL Server的早期版本中,其实就已经有了它的雏形,那时的名称叫做数据转换服务(DTS)。在SQL Server 2005的前两个版本SQL Server 7.0和SQL Server 2000中,DTS主要集中于提取和加载。通过使用DTS,可以从任何数据源中提取数据以及将数据加载到任何数据源中。在SQL Server 2005中,对DTS进行了重新设计和改进形成了SSIS。SSIS提供了数据相关的控制流、数据流、日志、变量、event、连接管理等基础设施。控制流也称为工作流或者任务流,它更像工作流,在工作流中每个组件都是一个任务。这些任务是按预定义的顺序执行的。在任务流中可能有分支。当前任务的执行结果决定沿哪条分支前进。数据流是新的概念。数据流也称为流水线,主要解决数据转换的问题。数据流由一组预定义的转换操作组成。数据流的起点通常是数据源(源表);数据流的终点通常是数据的目的地(目标表)。可以将数据流的执行认为是一个流水线的过程,在该过程中,每一行数据都是装配线中需要处理的零件,而每一个转换都是装配线中的处理单元。SSIS的体系结构如图3.1所示。

图3.1 SSIS体系结构图

4.ETL工具的选择

在数据集成中该如何选择ETL工具呢?一般来说需要考虑以下几个方面:

(1)对平台的支持程度。

(2)对数据源的支持程度。

(3)抽取和装载的性能是不是较高,且对业务系统的性能影响大不大,倾入性高不高。

(4)数据转换和加工的功能强不强。

(5)是否具有管理和调度功能。

(6)是否具有良好的集成性和开放性。

 

注:以上文字转自 http://tech.ccidnet.com/art/1105/20080407/1411567_1.html



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



BDRP分布式redis集群

$
0
0

背景简介

BDRP(baidu distributed redis platform)是包含 twemproxyredis,redis-sentinel等多个模块开发的分布式redis平台。bdrp已经在github上进行了开源, bdrp的github项目点这里

系统架构

1

目前redis集群架构主要有以下几个组件: twemproxy:redis的代理系统,可以选择多种数据分片算法 redis:集群的redis存储节点 sentinel:redis官方的集群高可用组件,可以监控redis主节点故障,并进行主备切换

对比开源版本twemproxy解决方案,我们为集群添加了每个redis数据分片的主从架构,并且添加了sentinel模块,基于这些架构的拓展,我们实现了集群的高可用,数据高可靠的解决方案,集群数据读写请求分离方案。另外,针对线上的复杂应用环境,我们还未twemproxy增加了权限管理,流量控制(开发中)两个功能。 下面我们就分别简单介绍下这四个新添加的功能。

功能介绍

1,高可用切换

由于twemproxy开源版本对于集群的高可用方案只提供了自动弹出数据分片功能,因此如果应用方如果需要高可用集群的话,需要打开自动弹出功能。但是,即便是对于一致性哈希分片算法,弹出一个分片也是会造成一个分片的数据丢失的。对于数据可靠性的要求比较高的服务来说,如果丢失整个分片的数据是无法接受的。 我们基于redis官方的sentinel模块,实现了redis集群的节点高可用,数据高可靠方案。如上面的架构图所示,redis集群的所有redis分片,都配置了对应的从库,通过redis主从传输进行元数据复制。同时,每个redis分片都会用sentinel进行监控,当分片的主redis节点故障时,sentinel会将该分片的从节点切换成主节点。 twemproxy和sentinel之前维护一个长连接,并订阅sentinel的主从切换事件频道。一旦sentinel上发生了redis的主从切换,twemproxy马上就能感知到,然后调整自己的分片拓扑图,将后续的请求发送到新的主库上。 twemproxy在每次和sentinel建立连接后,都会从sentinel拉取全量的数据分片地址信息,避免启动时的配置错误,或者运行中连接重建,丢失主从切换信息。

2,读写分离

3

对于数据量不是很大,但是请求量很大的服务来说,数据分片拆分并不是很好的解决方案,产品线通常会给数据分片配置多个只读从库,分担读请求。为了支持这种需求,我们给twemproxy增加了读写分离功能。 如上架构图,展示了一个server pool下面挂接两个数据分片,每个数据分片挂接两个只读从库的情景下的架构图。通过redis主从复制,给每个数据分片配置多个只读从库。Proxy从配置中加载到分片从库信息后,在每次转发请求时,判断请求的类型,然后根据请求是否修改redis元数据,来决定转发到主库或者从库。对于配置了多个从库的情况,我们会对这几个从库进行round-robin轮询 同样的,当一个从库发生故障,proxy会自动剔除这个从库,当全部从库都故障,proxy会将只读请求发送到主库上。剔除故障从库之后,proxy会定期对故障从库进行健康检查,如果从库恢复,proxy会自动将该从库加回读请求轮询列表当中。

3,权限验证白名单

对于线上服务来说,完善的权限管理是必不可少的。但是twemproxy并没有提供权限验证相关的功能。我们添加了基于IP白名单的权限验证功能。用户可以配置某些IP可以拥有的权限(读/写),在client连接上来之后,proxy会去判断client ip是否在白名单之内,如果在的话,会将对应权限赋予该client连接,否则将会强行断掉连接。当连接上的client发送命令过来后,会验证该client是否有该命令对应的权限(读/写),如果没有的话,该连接将会被强行关闭。

4,流量控制

twemproxy本身支持开启多个server_pool,后端挂接不同的redis数据分片。这样,单个proxy实例可以服务多个应用方。但是对于这种多个服务混用proxy的情况,twemproxy并没有对各个应用方做流量控制。这样造成的一个问题是,可能一个应用方的请求异常突增,占用了过多的proxy资源,从而影响了其他的应用方的正常服务请求。 针对这种情况,我们决定开发一个基于proxy的server pool的流控。简单来说就是给每个server pool一定的配额,当某个应用方请求异常突增将其配额耗尽后,我们将会拒绝这个应用方的请求,从而避免该异常应用方请求影响到其他应用方。 流控功能还在设计开发当中,因而本次开源版本中并不包括这个功能。

5,其他说明

github上面的开源方案使用的redis版本是2.6.16,这个版本是没有主从数据增量重传(partial synchronization)功能的,从库宕机需要进行全量重传,造成性能抖动。因此我们merge了2.8的增量重传代码以规避此问题。

应用范围

bdrp在百度检索系统,百度商业产品体系,LBS产品体系等在线业务广泛应用。

演讲中怎样用数据说话

$
0
0

“我不认为我是在放炮,我演讲都是用数据说话,每次演讲前我都要做大量的功课。”

上面这段文字是任志强老先生在某一次记着采访的时候谈到的一段话,对网友封的“任大炮”一名叫屈。里面讲到了他的演讲都是用数据说话,而很多的教授演讲和商务演示的老师也都会跟我们反复强调要学会用数据说话(用图表说话是一个性质,只不过是把数据图表化了而已,背后的本质还是数据),数据才有说服力等观念,我听了很多之后发现还是一头雾水,因为他们大多数讲的还是怎样去提炼数据,怎样去做图表,却没有告诉我们应该怎样去用数据说话。

直到最近在看完了乔老爷的一些演讲之后,才慢慢的对这个说法有了一些了解和体悟,开始体会到了数据的真正的魅力在于使用数据辅助讲故事,把数据带入到情境化的场景中让数据发挥力量,否则的话数据还仅仅只是数据而已,只能干巴巴的呆在那里,永远都讲不出动听的话。

我们迄今已经售出了 400 万部 iPhone,如果你用 400 万除以两百天,结果就是平均每天售出两万部 iPhone。

史蒂夫,乔布斯

上面这段话是乔布斯在 2008 年 macworld 大会上召开 iPhone200 天生日庆生会时候所讲的一段话,在开始乔布斯说道:“我非常高兴的向大家汇报,我们迄今为止已经售出了 400 万部 iPhone”,或许很多人觉得话讲到这里就已经可以了,但是乔布斯没有,他又讲出了后面的一句话,理论上来讲,这个已经很形象了,大家也都有了很清晰的认识了,每天两万部,很庞大的一个数据了。

可是乔布斯没有停下下来,他接着讲到:“在这段很短的时间里,iPhone 已经占据了 20% 的市场份额。”,20% 的市场份额,非常惊人的成绩了,但是乔布斯还是没有停止下来,接着乔布斯干了另外一个事情,把目前市场的手机的市场份额比打在了幻灯片上,其中第一名是黑莓,占比 39%,第二名是 iPhone,占比 19.5%,然后乔布斯把 iPhone 市场占有的份额和其他竞争对手的手机所占的市场份额做了一个对比,他得出的结论是 iPhone 的市场份额是其他三家的总和。

在这个案例中乔布斯没有使用太多的数据,仅仅是从 iPhone 的销量谈起,没有各种复杂的算法,没有复杂的数据结构,没有特别多的数据种类,只是把和 iPhone 相关的数据做了一个延伸,使得整个的演讲的效果达到了空前的好,分析一下主要有以下几点值得借鉴:

  • 数据使用非常具体,无论是 400 万还是 20%,都是很明确的数据
  • 数据的目的性非常清晰,所有的数据都是为了佐证 iPhone 的市场份额
  • 把数据的效果放大化,400 万部,咋 200 天内,或许很多人觉得没所谓,但是当分解到每天两万的时候,很多人就会产生很大的震撼力
  • 使用对比,20% 的市场份额,看字面意思感觉不是很高,到底是一个什么样的地位,也许很多人不清楚,但很多人知道诺基亚,黑莓是大公司,这些大公司的份额的总和也仅仅只是等于 iPhone 份额的时候,这样大家就会有一个比较明确的概念,容易引起共鸣。

“一个房产项目从开发到最终完成要好几年,你做一套衣服用多久?再说,胸罩那么大一点,要好几百块钱,按平米算,比房子贵多了。”

——任志强

上面的文字也是任老爷子的一段话,虽然看起来有那么点低俗,但是我本人非常喜欢,倒不是说因为有胸罩两个字我喜欢,而是这个比喻实在是精彩绝伦,把一个很抽象的房价给具象化了,如果有人熟悉这段文字的话,会知道任志强说这句话的情境是:依文企业集团董事长夏华和当当网董事长俞渝今日在“商界木兰”年会上向任志强抱怨房价太高,称“一套房把三辈子的收入都掏进去了”

对于两个女人讲房价的成本,施工等我相信基本上是对牛弹琴,起不到多大的作用,但是真正的落实到平米的数据上之后,对于两个女人,特别是夏华而言,我相信他们是很容易理解的,这个基本上是不存在认知偏差的了,沟通起来也就容易了。

同样的看乔老爷子,在推出 5GB 的 iPod 的时候,乔布斯说 5GB 能够提供 1000 首歌曲,iPod 只有 0.19 千克,非常小巧,以至于他能“直接装进你的口袋”,讲完老乔从裤兜里掏出了一个 iPod,现场的观众看完之后立刻就沸腾起来了,以至于媒体在宣传 iPod 的时候的词语都是“把 1000 首歌装进你的口袋”

我相信很多人跟我一样,很少能够对数字产生共鸣,除了发工资的那一刻和还信用卡的那一刻,除非是把数字放进一个人们都能够理解的情境之中,并且帮助人们以最容易理解的方式把数据展示出来,用一个大家已经熟悉的事物产生关联,这样大家才更容易产生共鸣。

对于 5GB 而言,第一这个数字对于很多人而言是没有概念的,特别是那个年代的劳苦大众,我相信现在很多小女生还都不清楚 5GB 到底是多大,但是 1000 首歌去就不一样,大家能够很容易想象到 1000 首歌的概念是什么样的;第二就是 5 本身是一个很小的数字,但是通过 1000 就把整个的数据放大了,给人的心理上的感觉会好上很多。再打一个简单的比方,很多人对电脑的性能是没有概念的,这个时候我们就延伸出来一个叫做开极速度的东西,通过开机速度来人为的创造一个概念,特别是 360 还出了一个你一打败全国多少人,这个就让很多人能够大概的知道自己的电脑怎么样勒,在此也不得不承认周教主是一个伟大的产品经理。

我记得 2003 年第一次开始有思考做网商大会的时候,2003 年整个淘宝的交易不到一个亿,今年淘宝网的交易会过一万亿,变化一万倍,网商从一个概念到今天变成落地,到今天变成中国主要的一个商帮力量,在改变着、影响着中国,大家知道一万亿是什么概念,所有的年轻人,这一万亿意味着中国排名第十七个省的 GDP,全中国超过万亿 GDP 的、人民币 GDP 的省只有十八个,去年陕西省的整个 GDP 就一万亿。

上面的一段文字是阿里巴巴教主马云先生在第九届网商大会上的演讲,这篇演讲被很多教演讲的老师奉为宝典,各种分析,我当年还专门为这篇文章和习主席的答记者的演讲设计过教学活动,看这段文字的时候,很多人都会觉得非常有震撼力,很容易受到感染,马教主在里面用了很多的数据,讲到最后的中国过万亿的 GDP 的省只有十八个的时候,很多人几乎是沸腾了,这个我们是能够想象到的,比干巴巴的一万亿可能要有影响力的多。

在每一个行业都会有自己的数据,但是几乎是所有的演讲者都没有把这些数据变得有意义,有趣味,感兴趣的大家可以看看李开复老师的 PPT,满屏幕的都是数据,但是鲜有精彩绝伦的关于数据方面的言论。对于任何行业的人而言,脱离了语境的数据都不会给人留下什么深刻的印象,只有把数据放到一个情境之中,结合人们熟悉的事物来讲解的时候,才能够让数据更有意思,更有说服力,才能够真正的实现用数据说话。

本文链接

Oracle sql语句执行顺序

$
0
0

sql语法的分析是从右到左

 

一、sql语句的执行步骤:

1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义。

2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限。

3)视图转换,将涉及视图的查询语句转换为相应的对基表查询语句。

4)表达式转换, 将复杂的 SQL 表达式转换为较简单的等效连接表达式。

5)选择优化器,不同的优化器一般产生不同的“执行计划”

6)选择连接方式, ORACLE 有三种连接方式,对多表连接 ORACLE 可选择适当的连接方式。

7)选择连接顺序, 对多表连接 ORACLE 选择哪一对表先连接,选择这两表中哪个表做为源数据表。

8)选择数据的搜索路径,根据以上条件选择合适的数据搜索路径,如是选用全表搜索还是利用索引或是其他的方式。

9)运行“执行计划”

 

 

二、oracle 共享原理:

     ORACLE将执行过的SQL语句存放在内存的共享池(shared buffer pool)中,可以被所有的数据库用户共享。

当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同,ORACLE就能很快获得已经被解析的语句以及最好的执行路径.。这个功能大大地提高了SQL的执行性能并节省了内存的使用。

 

三、oracle 语句提高查询效率的方法:

1:where column in(select * from ... where ...);

2:... where exists (select 'X' from ...where ...);

 

第二种格式要远比第一种格式的效率高。

 

在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。

使用EXIST,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,

这就节省了时间Oracle系统在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中。

 

避免使用having子句。HAVING 只会在检索出所有记录之后才对结果集进行过滤。

这个处理需要排序,总计等操作。如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。

 

四、SQL Select语句完整的执行顺序: 

1、from子句组装来自不同数据源的数据;

2、where子句基于指定的条件对记录行进行筛选;

3、group by子句将数据划分为多个分组;

4、使用聚集函数进行计算;

5、使用having子句筛选分组;

6、计算所有的表达式;

7、select 的字段;

8、使用order by对结果集进行排序。

SQL语言不同于其他编程语言的最明显特征是处理代码的顺序。在大多数据库语言中,代码按编码顺序被处理。但在SQL语句中,第一个被处理的子句式FROM,而不是第一出现的SELECT。SQL查询处理的步骤序号:

 

1  (8)SELECT  (9) DISTINCT (11) <TOP_specification> <select_list> 

2  (1)  FROM <left_table>  

3  (3) <join_type> JOIN <right_table> 

4  (2) ON <join_condition> 

5  (4) WHERE <where_condition> 

6  (5) GROUP BY <group_by_list> 

7  (6) WITH {CUBE | ROLLUP} 

8  (7) HAVING <having_condition> 

9 (10) ORDER BY <order_by_list>

  

以上每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只有最后一步生成的表才会会给调用者。如果没有在查询中指定某一个子句,将跳过相应的步骤。

逻辑查询处理阶段简介:

1、 FROM:对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1。

2、 ON:对VT1应用ON筛选器,只有那些使为真才被插入到TV2。

3、 OUTER (JOIN):如果指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表中未找到匹配的行将作为外部行添加到VT2,生成TV3。如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表位置。

4、 WHERE:对TV3应用WHERE筛选器,只有使为true的行才插入TV4。

5、 GROUP BY:按GROUP BY子句中的列列表对TV4中的行进行分组,生成TV5。

6、 CUTE|ROLLUP:把超组插入VT5,生成VT6。

7、 HAVING:对VT6应用HAVING筛选器,只有使为true的组插入到VT7。

8、 SELECT:处理SELECT列表,产生VT8。

9、 DISTINCT:将重复的行从VT8中删除,产品VT9。

10、ORDER BY:将VT9中的行按ORDER BY子句中的列列表顺序,生成一个游标(VC10)。

11、TOP:从VC10的开始处选择指定数量或比例的行,生成表TV11,并返回给调用者。

 

Oracle中SQL语句执行过程中,Oracle内部解析原理如下:

  1、当一用户第一次提交一个SQL表达式时,Oracle会将这SQL进行Hard parse,这过程有点像程序编译,检查语法、表名、字段名等相关信息(如下图),这过程会花比较长的时间,因为它要分析语句的语法与语义。然后获得最优化后的执行计划(sql plan),并在内存中分配一定的空间保存该语句与对应的执行计划等信息。

 

  2、当用户第二次请求或多次请求时,Oracle会自动找到先前的语句与执行计划,而不会进行Hard parse,而是直接进行Soft parse(把语句对应的执行计划调出,然后执行),从而减少数据库的分析时间。

 

  注意的是:Oracle中只能完全相同的语句,包大小写、空格、换行都要求一样时,才会重复使用以前的分析结果与执行计划。

 

  分析过程如下图:

 对于大量的、频繁访问的SQL语句,如果不采用Bind 变量的方式,哪Oracle会花费大量的Shared latch与CPU在做Hard parse处理,所以,要尽量提高语句的重用率,减少语句的分析时间,通过了解Oracle SQL语句的分析过程可以明白Oracle的内部处理逻辑,并在设计与实现上避免。

 

在用JDBC或其它持久化数据(如Hibernate,JDO等)操作时,尽量用占位符(?)

 

ORACLE sql 的处理过程大致如下:

 1.运用HASH算法,得到一个HASH值,这个值可以通过V$SQLAREA.HASH_VALUE 查看

 2.到shared pool 中的 library cache 中查找是否有相同的HASH值,如果存在,则无需硬解析,进行软解析

 3.如果shared pool不存在此HASH值,则进行语法检查,查看是否有语法错误

 4.如果没有语法错误,就进行语义检查,检查该SQL引用的对象是否存在,该用户是否具有访问该对象的权限

 5.如果没有语义错误,对该SQL进行解析,生成解析树,执行计划

 6.生成ORACLE能运行的二进制代码,运行该代码并且返回结果给用户

硬解析和软解析都在第5步进行

硬解析通常是昂贵的操作,大约占整个SQL执行的70%左右的时间,硬解析会生成执行树,执行计划,等等。

当再次执行同一条SQL语句的时候,由于发现library cache中有相同的HASH值,这个时候不会硬解析,而会软解析,

那么软解析究竟是干了什么呢?其实软解析就是跳过了生成解析树,生成执行计划这个耗时又耗CPU的操作,直接利用生成的执行计划运行

该SQL语句。

下面摘抄eygle深入解析ORACLE 中关于SQL执行过程的描述

  1.首先获得library cache latch,根据SQL的HASH_VALUE在library cache中查找是否存在此HASH_VALUE,如果找到这个HASH_VALUE,称之为软解析,Server获得改SQL执行计划转向第4步,如果找不到共享代码就进行硬解析。

  2.释放library pool cache,获得shared pool latch,查找并锁定自由空间(在bucket 中查找chunk)。如果找不到,报ORA-04031错误。

  3.释放shared pool latch,重新获得library cache latch,将SQL执行计划放入library cache中。

  4.释放library cache latch,保持null模式的library cache pin/lock。

  5.开始执行。

Library cache latch可以理解为硬/软解析的时候发生的,因为解析的时候会搜索library cache,所以会产生library cache latch

Library cache pin 是在执行的阶段发生的。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>