2009年6月13日星期六

所有已知的基本SIP应答

1xx = 通知性应答

100 正在尝试
180 正在拨打
181 正被转接
182 正在排队
183 通话进展

2xx = 成功应答

200 OK
202 被接受:用于转介

3xx = 转接应答

300 多项选择
301 被永久迁移
302 被暂时迁移
305 使用代理服务器
380 替代服务

4xx = 呼叫失败

400 呼叫不当
401 未经授权:只供注册机构使用,代理服务器应使用代理服务器授权407
402 要求付费(预订为将来使用)
403 被禁止的
404 未发现:未发现用户
405 不允许的方法
406 不可接受
407 需要代理服务器授权
408 呼叫超时:在预定时间内无法找到用户
410 已消失:用户曾经存在,但已从此处消失
413 呼叫实体过大
414 呼叫URI过长
415 不支持的媒体类型
416 不支持的URI方案
420 不当扩展:使用了不当SIP协议扩展,服务器无法理解该扩展
421 需要扩展
423 时间间隔过短
480 暂时不可使用
481 通话/事务不存在
482 检测到循环
483 跳数过多
484 地址不全
485 模糊不清
486 此处太忙
487 呼叫被终止
488 此处不可接受
491 呼叫待批
493 无法解读:无法解读 S/MIME文体部分

5xx = 服务器失败

500 服务器内部错误
501 无法实施:SIP呼叫方法在此处无法实施
502 不当网关
503 服务不可使用
504 服务器超时
505 不支持该版本:服务器不支持SIP协议的这个版本
513 消息过长

6xx = 全局失败

600 各处均忙
603 拒绝
604 无处存在
606 不可使用

--------------------------------------------------------------------------------

SIP协议应答码

应答代码
应答码是包含了,并且扩展了HTTP/1.1应答码。并不是所有的HTTP/1.1应答码都适当应用,只有在折里指出的是适当的。其他HTTP/1.1应答码不应当使用。并且,SIP也定义了新的应答码系列,6xx。

1 临时应答1xx
临时应答,也就是消息性质的应答,标志了对方服务器正在处理请求,并且还没有决定最后的应答。如果服务器处理请求需要花200ms以上才能产生终结应答的时候,它应当发送一个1xx应答。
注意1xx应答并不是可靠传输的。他们不会导致客户端传送一个ACK应答。临时性质的(1xx)应答可以包含消息体,包含会话描述。
1.1 100 Trying
这个应答表示下一个节点的服务器已经接收到了这个请求并且还没有执行这个请求的特定动作(比如,正在打开数据库的时候)。这个应答,就像其他临时应答一样,种植了UAC重新传送INVITE请求。100(Trying)应答和其他临时应答不同的是,在这里,它永远不会被有状态proxy转发到上行流中。
1.2 180 Ringing
UA收到INVITE请求并且试图提示给用户。这个应答应当出世化一个本地回铃。
1.3 818 Call is Being Forwarded(呼叫被转发)
服务器可以用这个应答代码来表示呼叫正在转发到另一个目的地集合。
1.4 182 Queued
当呼叫的对方暂时不能接收呼叫的时候,并且服务器决定将呼叫排队等候,而不是拒绝呼叫的时候,那么就应当发出这个应答。当被叫方一旦恢复接收呼叫,他会返回合适的终结应答。对于这个呼叫状态,可以有一个表示原因的短语,比如:”5 calls queued;expected waiting time is 15minutes”。服务器可以给出好几个182(Queued)应答告诉呼叫方排队的情况(比如排队靠前了等等)。
1.5 183 会话进度
183(Session Progress)应答用于提示建立对话的进度信息。Reason-Phrase(表达原因的句子)、头域或者消息体可以用于提示呼叫进度的更消息的信息。
2 成功信息2xx
这个应答表示请求是成功的。
2.1 200 OK
请求已经处理成功。这个信息取决于不同方法的请求的应答。
3 转发请求3XX
3xx系列的应答是用于提示用户的新位置信息的,或者为了满足呼叫而转发的额外服务地点。
3.1 300 Multiple Choices
请求的地址有多个选择,每个选择都有自己的地址,用户或者(UA)可以选择合适的通讯终端,并且转发这个请求到这个地址。
应答可以包含一个具有每一个地点的在Accept请求头域中允许的资源特性,这样用户或者UA可以选择一个最合适的地址来转发请求。没有未这个应答的消息体定义MIME类型。
这些地址选择也应当在Contact头域中列出(20.10节)。不同于HTTP,SIP应答可以包含多个Contact头域或者一个Contact头域中具有一个地址列表。UA可以使用Contact头域来自动转发或者要求用户确认转发。不过,本规范没有定义自动转发的标准。
如果被叫方可以在多个地址被找到,并且服务器不能或者不愿意转发请求的时候,可以使用这个应答来给呼叫方。
3.2 301 Moved Permently
当不能在Request-URI指定的地址找到用户的时候,请求的客户端应当使用Contact头域(20.10)所指出的新的地址重新尝试。请求者应当用这个新的值来更新本地的目录,地址本,和用户地址cache,并且在后续请求中,发送到这个/这些列出的地址。
3.3 302 Moved Temporarily
请求方应当把请求重新发到这个Contact头域所指出的新地址(20.10)。新请求的Request-URI应当用这个应答的Contact头域所指出的值。
在应答中的Expires(20.19节)或者Contact头域的expires参数定义了这个Contact URI的生存周期。UA或者proxy在这个生存周期内cache这个URI。如果没有严格的有效时见,那么这个地址仅仅本次有效,并且不能在以后的事务中保存。
如果cache的Contact头域的值失败了,那么被转发请求的Request-URI应当再次尝试一次。临时URI可以比超时时间更快的失效,并且可以有一个新的临时URI。
3.4 305 Use Proxy
请求的资源必须通过Contact头域中指出的proxy来访问。Contact头域指定了一个proxy的URI。接收到这个应答的对象应当通过这个proxy重新发送这个单个请求。305(UseProxy)必须是UAS产生的。
3.5 380 Alternative Service
呼叫不成工,但是可以尝试另外的服务。另外的服务在应答的消息体中定义。消息体的格式在这里没有定义,可能在以后的规范中定义。
4 请求失败4xx
4xx应答定义了特定服务器响应的请求失败的情况。客户端不应当在不更改请求的情况下重新尝试同一个请求。(例如,增加合适的认证信息)。不过,同一个请求交给不同服务器也许就会成功。
4.1 400 Bad Request
请求中的语法错误。Reason-Phrase应当标志这个详细的语法错误,比如”Missing Call-ID header field”。
4.2 401 Unauthorized
请求需要用户认证。这个应答是由UAS和注册服务器产生的,当407(Proxy Authentication Required)是proxy服务器产生的。
4.3 402 Payment Required
保留/以后使用
4.4 403 Forbidden
服务端支持这个请求,但是拒绝执行请求。增加验证信息是没有必要的,并且请求应当不被重试。
4.5 404 Not Found
服务器返回最终信息:用户在Request-URI指定的域上不存在。当Request-URI的domain和接收这个请求的domain不匹配的情况下,也会产生这个应答。
4.6 405 Method Not Allowed
服务器支持Request-Line中的方法,但是对于这个Request-URI中的地址来说,是不允许应用这个方法的。
应答必须包括一个Allow头域,这个头域包含了指定地址允许的方法列表。
4.7 Not Acceptable
请求中的资源只会导致产生一个在请求中的Accept头域外的,内容无法接收的错误。
4.8 407 Proxy Authentication Required
这个返回码和401(Unauthorized)很类四,但是标志了客户端应当首先在proxy上通过认证。SIP对认证的访问请参见26节和22.3节。
这个返回码用于应用程序访问通讯网关(比如,电话网关),而很少用于被叫方要求认证。
4.9 408 Request Timeout
在一段时间内,服务器不能产生一个终结应答,例如,如果它无法及时决定用户的位置。客户端可以在稍后不更改请求的内容然后重新尝试请求。
4.10 410 Gone
请求的资源在本服务器上已经不存在了,并且不知道应当把请求转发到哪里。这个问题将会使永久性的。如果服务器不知道,或者不容易检测,这个资源消失是临时性质的还是永久性质的,那么应当返回一个404(Not Found)。
4.11 413请求实体过大。
服务器拒绝处理请求,因为这个请求的实体超过了服务器希望或者能够处理的大小。这个服务器应当关闭连接避免客户端重发这个请求。
如果这个情况是暂时的,那么服务端应当包含一个Retry-After头域来表明这是一个暂时的故障,并且客户端可以过一段时间再次尝试。
4.12 414 Request-URI Too Long
服务器拒绝这个请求,因为Request-URI超过了服务器能够处理的长度。
4.13 415 Unsupported Media Type
服务器由于请求的消息体的格式本服务器不支持,所以拒绝处理这个请求。这个服务器必须根据内容的故障类型,返回一个Accept,Accpet-Encoding,或者Accept-Language头域列表。UAC根据8.1.3.5节定义的方法处理这个应答。
4.14 416 Unsupported URI Scheme
服务器由于不支持Request-URI中的URI方案而终止处理这个请求。客户端处理这个应答参照8.1.3.5。
4.15 Bad Extension
服务器不知道在请求中的Proxy-Require(20.29)或者Require(20.32)头域所指出的协议扩展。服务器必须在Unsupported头域中列出不支持的扩展。UAC处理这个应答请参见8.1.3.5
4.16 421Extension Required
UAS需要特定的扩展来处理这个请求,但是这个扩展并没有在请求的Supported头域中列出。具有这个应答码的应答必须包含一个Require头域列出所需要的扩展。
UAS不应当使用这个应答除非它真的不能给客户端提供有效的服务。相反,如果在Support头域中没有列出需要的扩展,服务器应当根据基准的SIP兼容的方法和客户端支持的扩展来进行处理。
4.17 423 Interval Too Brief
服务器因为在请求中设置的资源刷新时间(或者有效时间)过短而拒绝请求。这个应答可以用于注册服务器来拒绝那些Contact头域有效期过短的注册请求。这个应答的用法和相关的Min-Expires头域在10.2.8,10.3,20.23节中介绍和说明。
4.18 480 Temporarily Unavailable
请求成功到达被叫方的终端系统,但是被叫方当前不可用(例如,没有登陆,或者登陆了但是状态是不能通讯,或者有”请勿打扰”的标记)。应答应当在Retry-After中标志一个合适的重发时间。这个用户也有可能在其他地方是有效的(在本服务器中不知道)。Reason-Phrase(原因短句)应当提示更详细的原因,为什么被叫方暂时不可用。这个值应当是可以被UA设置的。状态码486(Busy Here)可以用来更精确的表示本请求失败的特定原因。
这个状态码也可以是转发服务或者proxy服务器返回的,因为他们发现Request-URI指定的用户存在,但是没有一个给这个用户的合适的当前转发的地址。
4.19 481 Call/Transaction Does Not Exist
这个状态表示了UAS接收到请求,但是没有和现存的对话或者事务匹配。
4.20 482 Loop Detected
服务器检测到了一个循环(16.3/4)
4.21 483 Too Many Hops
服务器接收到了一个请求包含的Max-Forwards(20.22)头域是0
4.22 484 Address InComplete
服务器接收到了一个请求,它的Request-URI是不完整的。在原因短语中应当有附加的信息说明。这个状态码可以和拨号交叠。在和拨号交叠中,客户端不知道拨号串的长度。它发送增加长度的字串,并且提示用户输入更多的字串,直到不在出现484(Address Incomplete)应答为止。
4.23 485 Ambiguous
Request-URI是不明确的。应答可以在Contact头域中包含一个可能的明确的地址列表。这个提示列表肯囊个在安全性和隐私性对用户或者组织造成破坏。必须能够由配置决定是否以404(NotFound)代替这个应答,又或者禁止对不明确的地址使用可能的选择列表。
给带有Request-URI的请求的一个应答例子:
sip: lee@example.com:
SIP/2.0 485 Ambiguous
Contact: Carol Lee
Contact: Ping Lee
Contact: Lee M.Foote
部分email和语音邮箱系统提供了这个功能。这个状态码和3xx状态码不同:对于300来说,它是假定同一个人或者服务有不同的地址选择。所以对3xx来说,自动选择系统或者连续查找就有效,但是对485(Ambiguous)应答来说,一定要用户的干预。
4.24 486 Busy Here
当成功联系到被叫方的终端系统,但是被叫方当前在这个终端系统上不能接听这个电话,那么应答应当回给呼叫方一个更合适的时间在Retry-After头域重试。这个用户也许在其他地方有效,比如电话邮箱系统等等。如果我们知道没有其他终端系统能够接听这个呼叫,那么应当返回一个状态码600(Busy Everywhere)。
4.25 487 Request Terminated
请求被BYE或者CANCEL所终止。这个应答永远不会给CANCEL请求本身回复。
4.26 488 Not Acceptable Here
这个应答和606(Not Acceptable)有相同的含义,但是只是应用于Request-URI所指出的特定资源不能接受,在其他地方请求可能可以接受。
包含了媒体兼容性描述的消息体可以出现在应答中,并且根据INVITE请求中的Accept头域进行规格化(如果没有Accept头域,那么就是application/sdp)。这个应答就像给OPTIONS请求的200(OK)应答的消息体一样。
4.27 491 Request Pending
在同一个对话中,UAS接收到的请求有一个依赖的请求正在处理。14.2描述了这种情况应当怎样解决。
4.28 493 Undecipherable
UAS接收到了一个请求,包含了一个加密的MIME,并且不知道或者没有提供合适的解密密钥。这个应答可以包含单个包体,这个包体包含了合适的公钥,这个公钥用于给这个UAS通讯中加密包体使用的。细节描述在23.2节。
5 Server Failure 5xx
5xx应答是当服务器本身故障的时候给出的失败应答。
5.1 500 Server Internal Error
服务器遇到了未知的情况,并且不能继续处理请求。客户端可以显示特定的错误情况,并且可以在几秒种以后重新尝试这个请求。
如果这个情况是临时的,服务器应当在Retry-After头域标志客户端过多少秒钟之后重新尝试这个请求。
5.2 501 Not Implemented
服务器没有实现相关的请求功能。当UAS不认识请求的方法的时候,并且对每一个用户都无法支持这个方法的时候,应当返回这个应答。(proxy不考虑请求的方法而转发请求)。
注意405(Method Not Allowed)是因为服务器实现了这个请求方法,但是这个请求方法在特定请求中不被支持。
5.3 502 Bad Gateway
如果服务器,作为gateway或者proxy存在,从下行服务器上接收到了一个非法的应答(这个应答对应的请求是本服务器为了完成请求而转发给下行服务器的)。
5.4 503 Service Unavailable
由于临时的过载或者服务器管理导致的服务器暂时不可用。这个服务器可以在应答中增加一个Retry-After来让客户端重试这个请求。如果没有Retry-After指出,客户端必须就像收到了一个500(Server Internal Error)应答一样处理。
客户端(proxy或者UAC)收到503(Service Unavailable)应当尝试转发这个请求到另外一个服务器处理。并且在Retry-After头域中指定的时间内,不应当转发其他请求到这个服务器。
作为503(Service Unavaliable)的替代,服务器可以拒绝连接或者把请求扔掉。
5.5 504 Server Time-out
服务器在一个外部服务器上没有收到一个及时的应答。这个外部服务器是本服务器用来访问处理这个请求所需要的。如果从上行服务器上收到的请求中的Expires头域超时,那么应当返回一个408(Request TimeOut)错误。
5.6 505 Version Not Supported
服务器不支持对应的SIP版本。服务器是无法处理具有客户端提供的相同主版本号的请求,就会导致这样的错误信息。
5.7 Message To Large
服务器无法处理请求,因为消息长度超过了处理的长度。
6 Global Failures 6xx
6xx应答意味这服务器给特定用户有一个最终的信息,并不只是在Request-URI的特定实例有最终信息。
6.1 600 Busy Everywhere
成功联系到被叫方的终端系统,但是被叫方处于忙的状态,并不打算接听电话。这个应答可以通过增加一个Retry-After头域更明确的告诉呼叫方多久以后可以继续呼叫。如果被叫方不希望提示拒绝的原因,被叫方应当使用603(Decline)。只有当终端系统知道没有其他终端节点(比如语音邮箱系统)能够访问到这个用户的时候才能使用这个应答。否则应当返回一个486(Busy Here)的应答。
6.2 603 Decline
当成功访问到被叫方的设备,但是用户明确的不想应答。这个应答可以通过增加一个Retry-After头域更明确的告诉呼叫方多久以后可以继续呼叫。只有当终端知道没有其他任何终端设备能够响应这个呼叫的势能才能给出这个应答。
6.3 604 Does Not Exists Anywhere
服务器验证了在请求中Request-URI的用户信息,哪里都不存在
6.4 606 Not Acceptable
当成功联系到一个UA,但是会话描述的一些部分比如请求的媒体,带宽,或者地址类型不被接收。
606(NotAcceptable)应答意味着用户希望通讯,但是不能充分支持会话描述。606(Not Acceptable)应答可以在Warning头域中包含一个原因列表,用于解释为何会话描述不能被支持。警告原因代码在20.43节中列出。
在应答中,可以出现一个包含媒体兼容性描述的消息体,这个消息体的格式根据INVITE请求中的Accept头域指出的格式进行规格化(如果没有Accept头域,那么就是application/sdp),就像给OPTIONS亲求的200(OK)应答中的消息一样。
我们希望这些媒体协商不要经常需要,并且当一个新用户被邀请加入已经存在的会话的时候,这个媒体协商可能不需要。这取决于邀请的初始化者是否需要对606(Not Acceptable)进行处理。
这个应答只有当客户端知道没有其他终端能够处理这个请求的时候才能发出。

2009年5月14日星期四

LVM2 扩展硬盘 -- vmare下

有用LVM2,现在空间不足,需再加一块硬盘。
在vmware下先添加一块新的硬盘选择自己需要的大小.
启动linux,用fdisk -l,可以看到新硬盘。
给新加的硬盘分区: fdisk /dev/sdb
>n
p default default
>t
>8e(linux LVM)分成一个分区,格式为linux LVM.
面开始把分区加到LVM内去:
1.建立物理卷 pvcreate /dev/sdb1
2.把新物理卷加入到卷组中去vgextend VolGroup00 /dev/sdb1
3.把新的空间加到逻辑卷中去lvextend -L+9G /dev/VolGroup00/LogVol00
4.加上去之后,目前用df -h还看不到新的空间,需要激活
5:resize2fs -p /dev/VolGroup00/LogVol00 全部搞掂,再用df -h,就可以看到新的空间了。

2009年4月20日星期一

SIP Intro --- REFER method 【转】

REFER method

講到這個method,就開始有點複雜了!

因為要有以前講過的觀念,然後加上REFER與Subscrib的概念。整個例子的流程會很大、Msg的量也會增加很多! 但是,又因為有了之前的基礎,因此可以省略很多個Msg的說明,所以我就可以把整個REFER這個例子的流程簡化了很多!

首先,REFER是用在CALL Transfer上面。以下就來說明一個例子吧,假設有A,B,C三個人,A打給B,然後HOLD B,再打給C,然後把B轉接給C。最後B與C結束通訊。

A INVITE B
A HOLD B
A INVITE C
A HOLD C
A REFER B TO C
B INVITE C
C BYE A
A BYE B
...
C BYE B (or B BYE C)

而CALL Transfer又不只一種形式,其中有兩種較為常見,一種是Attended transfer,另一種為Blind transfer。前者為轉接者(發REFER的UA),在接通兩方電話後,再做Transfer,上面的例子就是Attended transfer。而後者為先接通一方電話,再發送REFER要求對方去CALL第三方。

在此先不探討CALL Transfer的方式,我將focus在REFER這個method的使用,以及其相關概念。

下面的CALL flow是一個在Dialog外的REFER,因此其To不會有tag。而在Dialog外或內也何差異?主要差異在於在Dialog內的REFER不會有Fork的產生,但在Dialog外的REFER則允許有Fork的產生。Fork有分支的意思,在此先簡單瞭解一下,當一個Event package允許使用Fork時,就會產生數個SUBSCRIBE requests,因而就能產生multiple subscriptions,就如同分支一樣,所以才稱之為Fork。(SUBSCRIBE概念會在另一篇說明)

先來看這個session外REFER的例子。再看後面的例子的時候,可以注意一下幾個Header fields的值~

Call-ID:
CSeq:
Event:
Subscription-State:

以下是在A與B建立好通訊之後的情況

Agent A             Agent B
| |
| F1 REFER |
|----------------------->|
| F2 202 Accepted |
|<-----------------------|
| F3 NOTIFY |
|<-----------------------|
| F4 200 OK |
|----------------------->|
| |
| |
| |------->
| | (whatever)
| |<------
| |
| F5 NOTIFY |
|<-----------------------|
| F6 200 OK |
|----------------------->|
| |
| |


Message One (F1)
REFER sip:b@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agenta.atlanta.example.com;branch=z9hG4bK2293940223
To:
From: ;tag=193402342
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 93809823 REFER
Max-Forwards: 70
Refer-To: (whatever URI)
Contact: sip:a@atlanta.example.com
Content-Length: 0


Message Two (F2)
SIP/2.0 202 Accepted
Via: SIP/2.0/UDP agenta.atlanta.example.com;branch=z9hG4bK2293940223
To: ;tag=4992881234
From: ;tag=193402342
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 93809823 REFER
Contact: sip:b@atlanta.example.com
Content-Length: 0


Message Three (F3)
NOTIFY sip:a@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9922ef992-25
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993402 NOTIFY
Max-Forwards: 70
Event: refer
Subscription-State: active;expires=(depends on Refer-To URI)
Contact: sip:b@atlanta.example.com
Content-Type: message/sipfrag;version=2.0
Content-Length: 20
SIP/2.0 100 Trying


Message Four (F4)
SIP/2.0 200 OK
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9922ef992-25
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993402 NOTIFY
Contact: sip:a@atlanta.example.com
Content-Length: 0


Message Five (F5)
NOTIFY sip:a@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9323394234
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993403 NOTIFY
Max-Forwards: 70
Event: refer
Subscription-State: terminated;reason=noresource
Contact: sip:b@atlanta.example.com
Content-Type: message/sipfrag;version=2.0
Content-Length: 16
SIP/2.0 200 OK


Message Six (F6)
SIP/2.0 200 OK
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9323394234
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993403 NOTIFY
Contact: sip:a@atlanta.example.com
Content-Length: 0



以下為接續上面的例子,情況為在A在現有的Dialog裡,發送第二次REFER。(第一次REFER是在Dialog外)

Agent A               Agent B
| |
| F7 REFER |
|----------------------->|
| F8 202 Accepted |
|<-----------------------|
| F9 NOTIFY |
|<-----------------------|
| F10 200 OK |
|----------------------->|
| |------->
| | (something different)
| |<------
| |
| F11 NOTIFY |
|<-----------------------|
| F12 200 OK |
|----------------------->|
| |
| |


Message Seven (F7)
REFER sip:b@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agenta.atlanta.example.com;branch=z9hG4bK9390399231
To: ;tag=4992881234
From: ;tag=193402342
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 93809824 REFER
Max-Forwards: 70
Refer-To: (some different URI)
Contact: sip:a@atlanta.example.com
Content-Length: 0


Message Eight (F8)
SIP/2.0 202 Accepted
Via: SIP/2.0/UDP agenta.atlanta.example.com;branch=z9hG4bK9390399231
To: ;tag=4992881234
From: ;tag=193402342
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 93809824 REFER
Contact: sip:b@atlanta.example.com
Content-Length: 0


Message Nine (F9)
NOTIFY sip:a@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9320394238995
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993404 NOTIFY
Max-Forwards: 70
Event: refer;id=93809824
Subscription-State: active;expires=(depends on Refer-To URI)
Contact: sip:b@atlanta.example.com
Content-Type: message/sipfrag;version=2.0
Content-Length: 20
SIP/2.0 100 Trying


Message Ten (F10)
SIP/2.0 200 OK
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK9320394238995
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993404 NOTIFY
Contact: sip:a@atlanta.example.com
Content-Length: 0


Message Eleven (F11)
NOTIFY sip:a@atlanta.example.com SIP/2.0
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK2994a93eb-fe
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993405 NOTIFY
Max-Forwards: 70
Event: refer;id=93809824
Subscription-State: terminated;reason=noresource
Contact: sip:b@atlanta.example.com
Content-Type: message/sipfrag;version=2.0
Content-Length: 16
SIP/2.0 200 OK


Message Twelve (F12)
SIP/2.0 200 OK
Via: SIP/2.0/UDP agentb.atlanta.example.com;branch=z9hG4bK2994a93eb-fe
To: ;tag=193402342
From: ;tag=4992881234
Call-ID: 898234234@agenta.atlanta.example.com
CSeq: 1993405 NOTIFY
Contact: sip:a@atlanta.example.com
Content-Length: 0

2009年1月5日星期一

GDB调试基础

GDB是一个极为强大的调试工具,绝对比那些IDE下辖的调试器强大得多。在GDB的学习过程中一个核心的问题就是Call Stack的理解,这是GDB安身立命之根本。进一步的,就要涉及到core dump的使用了。既然是只谈论GDB的基础,那么本文中就不涉及到core dump的问题,有兴趣的可以自己google一下。

准备工作
gdb需要的文件是可执行文件。要想使用gdb去调试某个可执行文件,必须在用gcc或g++生成该文件的时候使用-g选项加入调试信息。

载入可执行文件
产生了可调试的可执行文件后,必须将其载入到gdb中才可进行调试。载入文件有两种办法:

使用命令 gdb execfilename 载入;

在进入gdb后,使用

file execfilename 或是

target exec execfilename

断点管理
使用break(简写为b即可)命令设置程序断点。

break:可以在程序运行时利用frame命令选定某一个stack frame,然后执行命令break。这样使得一旦程序执行到该选中stack frame中时,就停下。

break location:其中的location可以指定为当前源文件的第几行,或是针对当前位置的偏移量,也可以指定为函数名,甚至可以指定内存中的位置。

break location if condition:这是一个条件断点设置语句。

除了break之外,还有几个断点设置的关键字:

tbreak:在程序的一次运行中只停止一次。

rbreak regex:利用正则表达式设置一系列符合regex的断点。

可以使用info命令来查看断点信息,使用disable和enable来禁用启用断点,使用clear和delete来删除指定断点。

观察点管理
使用watch命令设置程序观察点。

watch expr:其中的expr可以是参数名,也可以是表达式。当expr的值被改写的时候程序就会暂停执行。

除了watch之外,还有几个观察点设置的关键字:

rwatch:当参数或是表达式的值被程序读取的时候暂停。

awatch:当参数或是表达式的值被读取或改写的时候都会暂停。

可以使用info命令来查看断点信息,使用disable和enable来禁用启用观察点,使用delete来删除指定观察点。

设置程序运行参数
set args:可指定运行时参数。(如:set args 10 20 30 40 50);

show args:命令可以查看设置好的运行参数。

变量管理
变量值显示:print var

变量值修改:set var=value

按指定地址输出内存值:x /NFU ADDR

其中,U指定一个单元的大小,可以是b(单字节)、h(双字节)、w(四字节)、g(八字节);N表示输出的单元个数;F表示输出的数据形式,可以是x(16进制整数格式 )、d(有符号十进制整数格式)、u(无符号十进制整数格式)、f(浮点数格式 )。

调试过程控制
step:单步执行,进入函数内部;

next:单步执行,不进入函数内部;

continue:恢复正常连续执行。

信息查询
info:这个应该是调试时用得最多的信息显示命令。它的主要作用是显示正在被调试的程序的一些指定信息,如breakpoints、watchpoints,frame中的args等等。

print:一般用来打印变量的当前值。

show:显示当前gdb本身的一些参数信息。

list:一般用于源文件的显示。

Call Stack
stack frame:当某个函数被调用的时候,它的调用入口,参数,局部变量都将会被保存在栈中的一块内存中,这块内存就叫stack frame。

call stack:所有的stack frames所占据的空间就叫call stack。最近被调用的函数的stack frame总是位于call stack的最里面,依次向外扩散:最里面的stack frame编号为0.

有了以上两个概念之后,我们就可以讲述gdb调试实现的机制了。gdb工作的基本单元就是stack frame,简称frame。比如用print var来打印变量名时,gdb是会在当前选中的frame(默认是当前工作的frame,除非手动选择别的frame)中寻找并打印该变量。这就是理解gdb工作的关键!

以下是一些操作frame并显示frame信息的命令:

frame framlocation:指定gdb工作的frame。其中,framlocation可以是frame的编号,也可以是frame的内存地址。

bt:显示当前程序Call stack中的函数调用顺序,或者说是frame的顺序。命令where和info stack和bt是一样的。

info frame:显示当前frame的详细信息。

info args:显示当前frame所对应的函数的参数信息。

info locals:显示当前frame所对应的函数的局部变量信息。

执行Shell命令
直接在gdb提示符后输入 shell commandname 。而如果需要在gdb中直接make源文件的话,直接在gdb提示符后输入 make make_args 即可;也可输入shell make make_args。

gdbserver调试

in target borad
#./gdbserver 192.168.3.111:2345 voip_task VoIPCfgFile_Proxy_tulip_improved_appl.cfg

in host pc
#./gdb voip_task
(gdb) target remote 192.168.3.111:2345

2009年1月4日星期日

Linux系统下MTD/CFI驱动介绍 【转】

某些Intel的FLASH芯片(如StrataFlash系列)支持多分区,也就是各个分区可以同时进行操作。应该说这是不错的特性,但是也会带来些问题。记得当初移植Linux-2.4.21,挂JFFS2文件系统的时候,经常会报一些"Magic bitmask not found"之类的错误,跟进去发现FLASH读出来的都是些0x80之类的数据,查看资料发现该款FLASH有分区的特性,而Linux的FLASH驱动只用一个状态变量表示整个FLASH的状态,这就会造成某个分区的实际状态和系统记录的不符,从而导致读FLASH的时候该点实际上不处在读状态。当时的解决办法是,每次读的时候,不管记录的状态是什么,先进入读状态再说,当然这会带来性能的下降,具体损失多少个时钟周期就不算了。

  话说进入Linux-2.6.x的时代(具体是2.6.13),除了Lock/Unlock(Linux在擦/写的时候不先Unlock,解决办法就是初始化的时候先全部Unlock)这个老问题外,竟然多分区的错误没有出现,惊讶之下决定好好研究下Linux的MTD/FLASH驱动。

  说驱动之前,先明确几个编程要点:

  1:读写,要按照总线位宽读写,注意不是FLASH芯片位宽(例如背靠背)。

  2:寻址,程序要访问的地址和FLASH芯片地址引脚得到的值是不一样的,例如16位的FLASH芯片,对于CPU,0x00和0x01表示2个不同的字节,但是到了FLASH引脚得到的都是0,也就是都指向FLASH的第一个WORD。可以认为地址总线的bit0悬空,或者认为转换总线, bit0上实际输出的是bit1。这个解释了要点1。

  3:芯片手册提到偏移量都是基于WORD的,而WORD的位宽取决于芯片的位宽,因此在下命令的时候,实际偏移=手册偏移*buswidth/8。

  4:芯片手册提到的变量长度(典型如CFI信息)例如2,指的是,变量是个16bit数,但是读的时候,要读2个WORD,然后把每个WORD的低8位拼成1个16bit数。读WORD再拼凑确实挺麻烦,尤其是读取大结构的时候,不过参照cfi_util.c的cfi_read_pri函数的做法就简单了。

  5:背靠背,也就是比方说2块16位的芯片一起接在32位的总线上。带来的就是寻址的问题,很显然,首先要按32位读写;其次就是下命令的地址,实际偏移=手册偏移*interleave*device_type/8,device_type=buswidth/interleave,而buswidth这个时候是32(总线位宽)。另外就是背靠背的时候,命令和返回的状态码是“双份的”,例如2块16位背靠背,读命令是0x00ff00ff。

  如果不是想写像Linux那么灵活的代码(考虑各种接法/位宽/CFI获取信息等),那事情就简单很多,只要考虑要点1以及擦除块的大小就好了,当然如果是背靠背接法,擦除块的实际大小要乘个interleave。

  下面就进入Linux代码,不过关于CHIP/MAP/MTD之间绕来绕去的关系现在还糊涂着呢,因此下面只是简单的跟一下脉络和各个编程要点。

  1:构造map_info结构,指定基址/位宽/大小等信息以及"cfi_probe"限定,然后调用do_map_probe()。

  2:do_map_probe()根据名字"cfi_probe"找到芯片驱动"cfi_probe.c"直接调用cfi_probe()。

  3:cfi_probe()直接调用mtd_do_chip_probe(),传入cfi_probe_chip()函数指针。

  4:mtd_do_chip_probe()分2步,先调用genprobe_ident_chips()探测芯片信息,后调用check_cmd_set()获取和初始化芯片命令集(多分区初始化就在里面)。

  5:genprobe_ident_chips()函数如果不考虑多芯片串连的情况,那只需看前面的genprobe_new_chip()调用,完成后cfi.chipshift=cfi.cfiq->DevSize,2^chipshift=FLASH大小。

  6:genprobe_new_chip()枚举各种不同的芯片位宽和背靠背数量,结合配置设定依次调用步骤3的cfi_probe_chip(),注意cfi->device_type=bankwidth/nr_chips,bankwidth是总线位宽,device_type是芯片位宽。这里我们只需要注意有限复杂情况即可,所谓有限复杂指的是编译时确定的复杂连接。这样,cfi_probe_chip()只有第1次调用才成功,如果考虑32位宽的FLASH插在16bit总线上的情况,那第2次调用成功。

  7:cfi_probe_chip(),由于步骤6的原因,函数就在cfi_chip_setup()直接返回,后面的代码就不用考虑了。

  8:cfi_chip_setup()读取CFI信息,可以留意下Linux是怎么实现要点4的。

  9:回到步骤4的check_cmd_set()阶段,进入cfi_cmdset_0001()函数,先调用read_pri_intelext()读取Intel的扩展信息,然后调用cfi_intelext_setup()初始化自身结构。

  10:read_pri_intelext()函数,可以留意下怎么读取变长结构的技巧,也就是"need_more"的用法。这里说明下一些变量的含义,例如对于StrataFlash 128Mb Bottom类型的的FLASH芯片,块结构是4*32KB+127*128KB=16MB,一共16个分区,每个分区1MB。nb_parts=2。

  第1部分

  NumIdentPartitions=1 // 有1个重复的分区

  NumBlockTypes=2 // 分区内有2种不同的Block类型

  第1类型

  NumIdentBlocks=3 // 有4个Block(3+1)

  BlockSize=0x80 // 32KB(0x80*256)

  第2类型

  NumIdentBlocks=6 // 有7个Block(6+1)

  BlockSize=0x200 // 128KB(0x200*256)

  第2部分

  NumIdentPartitions=15// 有15个重复的分区
  NumBlockTypes=1 // 分区内有1种Block类型

  第1类型

  NumIdentBlocks=7 // 有8个Block(7+1)

  BlockSize=0x200 // 128KB(0x200*256)

  11:cfi_intelext_setup()函数首先根据CFI建立mtd_erase_region_info信息,然后调用cfi_intelext_partition_fixup()来支持分区。

  12:cfi_intelext_partition_fixup()用来建立虚拟Chip,每个分区对应1个Chip,不过并没有完全根据CFI扩展信息来建立,而是假定每个分区的大小都一致。cfi->chipshift调整为partshift,各个虚拟chip->start调整为各分区的基址。将来访问FLASH的入口函数cfi_varsize_frob()就根据ofs得到chipnum(chipnum=ofs>>cfi->chipshift),这也是为什么要假定分区一致的原因。

2009年1月1日星期四

调试技术: Linux core dump file

1. 前言:
有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的.
但这不像编译错误一样会提示到文件->行, 而是没有任何信息, 使得我们的调试变得困难起来.

2. gdb:
有一种办法是, 我们用gdb的step, 一步一步寻找.
这放在短小的代码中是可行的, 但要让你step一个上万行的代码, 我想你会从此厌恶程序员这个名字, 而把他叫做调试员.
我们还有更好的办法, 这就是core file.

3. ulimit:
如果想让系统在信号中断造成的错误时产生core文件, 我们需要在shell中按如下设置:
#设置core大小为无限
ulimit -c unlimited
#设置文件大小为无限
ulimit unlimited

这些需要有root权限, 在ubuntu下每次重新打开中断都需要重新输入上面的第一条命令, 来设置core大小为无限.

4. 用gdb查看core文件:
下面我们可以在发生运行时信号引起的错误时发生core dump了.
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.
gdb [exec file] [core file]
如:
gdb ./test test.core
在进入gdb后, 用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件->行.