Linux网络编程中 SCTP 套接字的常用接口详解
转载地址: oracle开发帮助文档:docs.oracle.com/cd/E19253-0…
SCTP 套接字接口
当 socket()
调用为 IPPROTO_SCTP
创建套接字时,它会调用特定于 SCTP 的套接字创建例程。针对 SCTP 套接字执行的套接字调用会自动调用相应的 SCTP 套接字例程。在一对一套接字中,每个套接字都对应一个 SCTP 关联。可以通过调用以下函数来创建一对一套接字:
socket(AF_INET[6], SOCK_STREAM, IPPROTO_STCP); \
在一对多风格套接字中,每个套接字都处理多个 SCTP 关联。每个关联都具有一个名为 sctp_assoc_t
的关联标识符。可以通过调用以下函数来创建一对多套接字:
socket(AF_INET[6], SOCK_SEQPACKET, IPPROTO_STCP);
sctp_bindx()
int sctp_bindx(int sock
, void *addrs
, int addrcnt
, int flags
); \
sctp_bindx()
函数管理 SCTP 套接字上的地址。如果 sock
参数为 IPv4 套接字,则传送给 sctp_bindx()
函数的地址必须为 IPv4 地址。如果 sock
参数为 IPv6 套接字,则传送给 sctp_bindx()
函数的地址可以为 IPv4 或 IPv6 地址。当传送给 sctp_bindx()
函数的地址为 INADDR_ANY
或 IN6ADDR_ANY
时,此套接字将绑定到所有可用地址。可以使用 bind(3SOCKET) 绑定 SCTP 端点。
*addrs
参数的值是指向包含一个或多个套接字地址的数组的指针。每个地址都包含在其相应的结构中。如果地址为 IPv4 地址,则它们可以包含在 sockaddr_in
结构或 sockaddr_in6
结构中。如果地址为 IPv6 地址,则它们可以包含在 sockaddr_in6
结构中。可以通过地址类型系列区分地址长度。调用方使用 addrcnt
参数指定数组中的地址数。
如果成功,则 sctp_bindx()
函数将返回 0。如果失败,则 sctp_bindx()
函数将返回 -1,并将 errno
的值设置为相应的错误代码。
如果没有为每个套接字地址提供同一端口,则 sctp_bindx()
函数将失败,并将 errno
的值设置为 EINVAL
。
通过对以下零个或多个当前定义的标志执行按位 OR 运算,即可形成 flags
参数:
SCTP_BINDX_ADD_ADDR
SCTP_BINDX_REM_ADDR
SCTP_BINDX_ADD_ADDR
指示 SCTP 将给定地址添加到关联中。SCTP_BINDX_REM_ADDR
指示 SCTP 从关联中删除给定地址。这两个标志相互排斥。如果同时提供这两个标志,则 sctp_bindx()
将失败,并将 errno
的值设置为 EINVAL
。
调用方无法删除关联中的所有地址。sctp_bindx()
函数拒绝此类尝试的表现为:函数失败并将 errno
的值设置为 EINVAL
。应用程序可以在调用 bind()
函数之后使用 sctp_bindx(SCTP_BINDX_ADD_ADDR)
,将其他地址与端点关联。应用程序可以使用 sctp_bindx(SCTP_BINDX_REM_ADDR)
删除与侦听套接字关联的地址。使用 sctp_bindx(SCTP_BINDX_REM_ADDR)
删除地址之后,接受新关联将不会重新关联已删除的地址。如果端点支持动态地址,则可以使用 SCTP_BINDX_REM_ADDR
或 SCTP_BINDX_ADD_ADDR
向对等方发送消息来更改对等方的地址列表。在已连接的关联中添加和删除地址为可选功能。不支持此功能的实现将返回 EOPNOTSUPP
。
如果地址族不是 AF_INET
或 AF_INET6
,则 sctp_bindx()
函数将失败并返回 EAFNOSUPPORT
。如果 sock
参数中传递给 sctp_bindx()
的文件描述符无效,则 sctp_bindx()
函数将失败并返回 EBADF
。
sctp_opt_info()
int sctp_opt_info(int sock
, sctp_assoc_id_t id
, int opt
, void *arg
, socklen_t *len
); \
sctp_opt_info()
函数将返回与 sock
参数中所述的套接字关联的 SCTP 级别选项。如果此套接字为一对多风格 SCTP 套接字,则 id
参数的值是某个特定关联。对于一对一风格 SCTP 套接字,将忽略 id
参数。opt
参数的值指定要获取的 SCTP 套接字选项。arg
参数的值是为调用程序而分配的特定于选项的结构缓冲区。*len
参数的值为选项长度。
opt
参数可以采用以下值:
-
-
SCTP_RTOINFO
-
返回用于初始化和绑定重新传输超时 (retransmission timeout, RTO) 可调参数的协议参数。这些协议参数使用以下结构:
struct sctp_rtoinfo { sctp_assoc_t srto_assoc_id; uint32_t srto_initial; uint32_t srto_max; uint32_t srto_min; };
-
srto_assoc_id
- 调用程序提供此值,它指定所关注的关联。
srto_initial
- 此值为初始 RTO 值。
srto_max
- 此值为最大 RTO 值。
srto_min
- 此值为最小 RTO 值。
-
-
SCTP_ASSOCINFO
-
返回特定于关联的参数。这些参数使用以下结构:
struct sctp_assocparams { sctp_assoc_t sasoc_assoc_id; uint16_t sasoc_asocmaxrxt; uint16_t sasoc_number_peer_destinations; uint32_t sasoc_peer_rwnd; uint32_t sasoc_local_rwnd; uint32_t sasoc_cookie_life; };
-
sasoc_assoc_id
- 调用程序提供此值,它指定所关注的关联。
sasoc_assocmaxrxt
- 此值指定关联的最大重新传输计数。
sasoc_number_peer_destinations
- 此值指定对等方具有的地址数。
sasoc_peer_rwnd
- 此值指定对等方接收窗口的当前值。
sasoc_local_rwnd
- 此值指定对等方传输到的上一个已报告的接收窗口。
sasoc_cookie_life
- 此值指定关联 cookie 的生命周期。可在发出 cookie 时使用此值。
所有使用时间值的参数均以毫秒为单位。
-
-
SCTP_DEFAULT_SEND_PARAM
-
返回 sendto(3SOCKET) 函数调用在此关联中使用的缺省参数集。这些参数使用以下结构:
struct sctp_sndrcvinfo { uint16_t sinfo_stream; uint16_t sinfo_ssn; uint16_t sinfo_flags; uint32_t sinfo_ppid; uint32_t sinfo_context; uint32_t sinfo_timetolive; uint32_t sinfo_tsn; uint32_t sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; };
-
sinfo_stream
- 此值指定
sendmsg()
调用的缺省流。 sinfo_ssn
- 此值始终为 0。
-
-
sinfo_flags
-
此值包含
sendmsg()
调用的缺省标志。此标志可以采用以下值:MSG_UNORDERED
MSG_ADDR_OVER
MSG_ABORT
MSG_EOF
MSG_PR_SCTP
-
sinfo_ppid
-
此值为
sendmsg()
调用的缺省有效负荷协议标识符。 -
sinfo_context
-
此值为
sendmsg()
调用的缺省上下文。 -
sinfo_timetolive
-
此值指定时间段(以毫秒为单位)。在此时间段过后,如果消息传输尚未开始,则消息将过期。值为 0 指示消息尚未过期。如果设置了
MSG_PR_SCTP
标志,当消息传输未在sinfo_timetolive
所指定的时间段内成功完成时,消息将过期。 -
sinfo_tsn
-
此值始终为 0。
-
sinfo_cumtsn
-
此值始终为 0。
-
sinfo_assoc_id
-
此值由调用程序填充。它指定所关注的关联。
-
SCTP_PEER_ADDR_PARAMS
-
返回所指定对等地址的参数。这些参数使用以下结构:
struct sctp_paddrparams { sctp_assoc_t spp_assoc_id; struct sockaddr_storage spp_address; uint32_t spp_hbinterval; uint16_t spp_pathmaxrxt; };
-
spp_assoc_id
- 调用程序提供此值,它指定所关注的关联。
spp_address
- 此值指定所关注的对等地址。
spp_hbinterval
- 此值指定心跳间隔(以毫秒为单位)。
spp_pathmaxrxt
- 此值指定在认为地址不可访问之前针对此地址尝试的最大重新传输数。
-
-
SCTP_STATUS
-
返回有关关联的当前状态信息。这些参数使用以下结构:
struct sctp_status { sctp_assoc_t sstat_assoc_id; int32_t sstat_state; uint32_t sstat_rwnd; uint16_t sstat_unackdata; uint16_t sstat_penddata; uint16_t sstat_instrms; uint16_t sstat_outstrms; uint32_t sstat_fragmentation_point; struct sctp_paddrinfo sstat_primary; };
-
-
sstat_assoc_id
-
调用程序提供此值,它指定所关注的关联。
-
sstat_state
-
此值为关联的当前状态。关联可以采用以下状态:
-
SCTP_IDLE
- SCTP 端点没有任何与其关联的关联。一旦
socket()
函数调用打开一个端点或端点关闭,则端点便会处于此状态。 SCTP_BOUND
- SCTP 端点在调用
bind()
之后绑定到一个或多个本地地址。 SCTP_LISTEN
- 此端点在等待来自任何远程 SCTP 端点的关联请求。
SCTP_COOKIE_WAIT
- 此 SCTP 端点已发送 INIT 块并在等待 INIT-ACK 块。
SCTP_COOKIE_ECHOED
- 此 SCTP 端点已将从其对等方的 INIT-ACK 块接收的 cookie 回显到对等方。
SCTP_ESTABLISHED
- 此 SCTP 端点可以与其对等方交换数据。
SCTP_SHUTDOWN_PENDING
- 此 SCTP 端点已从其上层接收了 SHUTDOWN 元语。此端点不再从其上层接受数据。
SCTP_SHUTDOWN_SEND
- 处于 SCTP_SHUTDOWN_PENDING 状态的 SCTP 端点已向其对等方发送了 SHUTDOWN 块。仅在确认所有从此端点到其对等方的未完成数据之后,才发送 SHUTDOWN 块。当此端点的对等方发送 SHUTDOWN ACK 块时,此端点会发送 SHUTDOWN COMPLETE 块并认为关联已关闭。
SCTP_SHUTDOWN_RECEIVED
- SCTP 端点已从其对等方接收了 SHUTDOWN 块。此端点不再从其用户接受新数据。
SCTP_SHUTDOWN_ACK_SEND
- 处于 SCTP_SHUTDOWN_RECEIVED 状态的 SCTP 端点已向其对等方发送了 SHUTDOWN ACK 块。此端点仅在其对等方确认来自此端点的所有未完成数据之后发送 SHUTDOWN ACK 块。当此端点的对等方发送 SHUTDOWN COMPLETE 块时,将关闭关联。
-
-
sstat_rwnd
-
此值为关联对等方的当前接收窗口。
-
sstat_unackdata
-
此值为未确认的 DATA 块数。
-
sstat_penddata
-
此值为等待接收的 DATA 块数。
-
sstat_instrms
-
此值为传入的流数。
-
sstat_outstrms
-
此值为外发的流数。
-
sstat_fragmentation_point
-
如果消息、SCTP 数据包头和 IP 数据包头的组合大小超出
sstat_fragmentation_point
的值,则消息会分段。此值等于包目标地址的路径最大传输单元 (Path Maximum Transmission Unit, P-MTU)。 -
sstat_primary
-
此值包含有关主要对等地址的信息。此信息使用以下结构:
struct sctp_paddrinfo { sctp_assoc_t spinfo_assoc_id; struct sockaddr_storage spinfo_address; int32_t spinfo_state; uint32_t spinfo_cwnd; uint32_t spinfo_srtt; uint32_t spinfo_rto; uint32_t spinfo_mtu; };
-
spinfo_assoc_id
- 调用程序提供此值,它指定所关注的关联。
spinfo_address
- 此值为主要对等地址。
spinfo_state
- 此值可以采用
SCTP_ACTIVE
或SCTP_INACTIVE
两个值中的任意一个。 spinfo_cwnd
- 此值为对等地址的拥塞窗口。
spinfo_srtt
- 此值为对等地址的当前平滑往返时间计算结果,以毫秒为单位。
spinfo_rto
- 此值为对等地址的当前重新传输超时值,以毫秒为单位。
spinfo_mtu
- 此值为对等地址的 P-MTU。
-
-
-
-
如果成功,则 sctp_opt_info()
函数将返回 0。如果失败,则 sctp_opt_info()
函数将返回 -1,并将 errno
的值设置为相应的错误代码。如果 sock
参数中传递给 sctp_opt_info()
的文件描述符无效,则 sctp_opt_info()
函数将失败并返回 EBADF
。如果 sock
参数中传递给 sctp_opt_info()
函数的文件描述符没有描述套接字,则 sctp_opt_info()
函数将失败并返回 ENOTSOCK
。如果关联 ID 对于一对多风格 SCTP 套接字而言无效,则 sctp_opt_info()
函数将失败,并将 errno
的值设置为 EINVAL
。如果输入缓冲区长度对于指定的选项而言过短,则 sctp_opt_info()
函数将失败,并将 errno
的值设置为 EINVAL
。如果对等地址的地址族不是 AF_INET
或 AF_INET6
,则 sctp_opt_info()
函数将失败,并将 errno
的值设置为 EAFNOSUPPORT
。
sctp_recvmsg()
ssize_t sctp_recvmsg(int s
, void *msg
, size_t len
, struct sockaddr *from
, socklen_t *fromlen
, struct sctp_sndrcvinfo *sinfo
, int *msg_flags
); \
使用 sctp_recvmsg()
函数,可以从 s
参数所指定的 SCTP 端点接收消息。调用程序可以指定以下属性:
-
msg
- 此参数为消息缓冲区的地址。
len
- 此参数为消息缓冲区的长度。
from
- 此参数为指向包含发送主机地址的地址的指针。
fromlen
- 此参数为与
from
参数中的地址关联的缓冲区的大小。 sinfo
- 此参数仅在调用程序启用
sctp_data_io_events
时处于活动状态。要启用sctp_data_io_events
,请使用套接字选项SCTP_EVENTS
调用setsockopt()
函数。如果启用了sctp_data_io_events
,则应用程序将接收每个传入消息的sctp_sndrcvinfo
结构的内容。此参数为指向sctp_sndrcvinfo
结构的指针。此结构将在接收消息时进行填充。 msg_flags
- 此参数包含所有存在的消息标志。
sctp_recvmsg()
函数将返回其接收的字节数。sctp_recvmsg()
函数将在出现错误时返回 -1。
如果在 s
参数中传递的文件描述符无效,则 sctp_recvmsg()
函数将失败,并将 errno
的值设置为 EBADF
。如果在 s
参数中传递的文件描述符没有描述套接字,则 sctp_recvmsg()
函数将失败,并将 errno
的值设置为 ENOTSOCK
。如果 msg_flags
参数包括值 MSG_OOB
,则 sctp_recvmsg()
函数将失败,并将 errno
的值设置为 EOPNOTSUPP
。如果没有建立关联,则 sctp_recvmsg()
函数将失败,并将 errno
的值设置为 ENOTCONN
。
sctp_sendmsg()
ssize_t sctp_sendmsg(int s
, const void *msg
, size_t len
, const struct sockaddr *to
, socklen_t tolen
, uint32_t ppid
, uint32_t flags
, uint16_t stream_no
, uint32_t timetolive
, uint32_t context
); \
sctp_sendmsg()
函数在发送来自 SCTP 端点的消息时启用高级 SCTP 功能。
-
-
s
-
此值指定发送消息的 SCTP 端点。
-
msg
-
此值包含
sctp_sendmsg()
函数所发送的消息。 -
len
-
此值为消息的长度,以字节为单位。
-
to
-
此值为消息的目标地址。
-
tolen
-
此值为目标地址的长度。
-
ppid
-
此值为应用程序指定的有效负荷协议标识符。
-
stream_no
-
此值为此消息的目标流。
-
timetolive
-
此值为消息未能成功发送到对等方的情况下消息过期之前可以等待的时间段,以毫秒为单位。
-
context
-
如果在发送消息时出现错误,则返回此值。
-
flags
-
此值在将逻辑运算 OR 以按位形式应用于以下零个或多个标志位时形成:
-
MSG_UNORDERED
- 设置此标志之后,
sctp_sendmsg()
函数将无序传送消息。 MSG_ADDR_OVER
- 设置此标志之后,
sctp_sendmsg()
函数将使用to
参数中的地址,而不使用关联的主要目标地址。此标志仅用于一对多风格 SCTP 套接字。 MSG_ABORT
- 设置此标志之后,指定的关联将异常中止,同时向其对等方发送 ABORT 信号。此标志仅用于一对多风格 SCTP 套接字。
MSG_EOF
- 设置此标志之后,指定的关联将进入正常关机状态。此标志仅用于一对多风格 SCTP 套接字。
MSG_PR_SCTP
- 设置此标志之后,如果消息传输未在
timetolive
参数所指定的时间段内成功完成,则消息将过期。
-
-
sctp_sendmsg()
函数将返回其发送的字节数。sctp_sendmsg()
函数将在出现错误时返回 -1。
如果在 s
参数中传递的文件描述符无效,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EBADF
。如果在 s
参数中传递的文件描述符没有描述套接字,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 ENOTSOCK
。如果 flags
参数包括值 MSG_OOB
,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EOPNOTSUPP
。如果一对一风格套接字的 flags
参数包括 MSG_ABORT
或 MSG_EOF
值,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EOPNOTSUPP
。如果没有建立关联,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 ENOTCONN
。如果套接字关闭,禁止进一步写入,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EPIPE
。如果套接字为非阻止套接字并且传输队列已满,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EAGAIN
。
如果控制消息长度不正确,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EINVAL
。如果指定的目标地址不属于关联,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EINVAL
。如果 stream_no
的值不在关联所支持的外发流数之内,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EINVAL
。如果所指定的目标地址的地址族不是 AF_INET
或 AF_INET6
,则 sctp_sendmsg()
函数将失败,并将 errno
的值设置为 EINVAL
。
sctp_send()
ssize_t sctp_send(int s
, const void *msg
, size_t len
, const struct sctp_sndrcvinfo *sinfo
, int flags
); \
sctp_send()
函数可供一对一及一对多风格套接字使用。sctp_send()
函数在发送来自 SCTP 端点的消息时启用高级 SCTP 功能。
-
s
- 此值指定
socket()
函数所创建的套接字。 msg
- 此值包含
sctp_send()
函数所发送的消息。 len
- 此值为消息的长度,以字节为单位。
sinfo
- 此值包含用于发送消息的参数。对于一对多风格套接字,此值可以包含消息所发送到的关联 ID。
flags
- 此值与
sendmsg()
函数中的标志参数相同。
sctp_send()
函数将返回其发送的字节数。sctp_send()
函数将在出现错误时返回 -1。
如果在 s
参数中传递的文件描述符无效,则 sctp_send()
函数将失败,并将 errno
的值设置为 EBADF
。如果在 s
参数中传递的文件描述符没有描述套接字,则 sctp_send()
函数将失败,并将 errno
的值设置为 ENOTSOCK
。如果 sinfo
参数的 sinfo_flags
字段包括值 MSG_OOB
,则 sctp_send()
函数将失败,并将 errno
的值设置为 EOPNOTSUPP
。如果一对一风格套接字中 sinfo
参数的 sinfo_flags
字段包括 MSG_ABORT
或 MSG_EOF
值,则 sctp_send()
函数将失败,并将 errno
值的设置为 EOPNOTSUPP
。如果没有建立关联,则 sctp_send()
函数将失败,并将 errno
的值设置为 ENOTCONN
。如果套接字关闭,禁止进一步写入,则 sctp_send()
函数将失败,并将 errno
的值设置为 EPIPE
。如果套接字为非阻止套接字并且传输队列已满,则 sctp_send()
函数将失败,并将 errno
的值设置为 EAGAIN
。
如果控制消息长度不正确,则 sctp_send()
函数将失败,并将 errno
的值设置为 EINVAL
。如果指定的目标地址不属于关联,则 sctp_send()
函数将失败,并将 errno
的值设置为 EINVAL
。如果 stream_no
的值不在关联所支持的外发流数之内,则 sctp_send()
函数将失败,并将 errno
的值设置为 EINVAL
。如果所指定的目标地址的地址族不是 AF_INET
或 AF_INET6
,则 sctp_send()
函数将失败,并将 errno
的值设置为 EINVAL
。
分叉关联
应用程序可以将一对多风格套接字上已建立的关联分叉为独立的套接字和文件描述符。对于具有多个偶发消息发送者或接收者的应用程序,如果这些发送者或接收者需要存在于原始一对多风格套接字之下,则独立的套接字和文件描述符非常有用。应用程序会将传输大量数据通信流量的关联分叉为独立的套接字描述符。应用程序使用 sctp_peeloff()
调用将关联分叉为独立的套接字。新套接字为一对一风格套接字。sctp_peeloff()
函数的语法如下:
int sctp_peeloff(int sock
, sctp_assoc_t id
);
-
sock
- 从
socket()
系统调用返回的原始一对多风格套接字描述符 id
- 要分叉为独立的文件描述符的关联的标识符
如果在 sock
参数中传递的套接字描述符不是一对多风格 SCTP 套接字,则 sctp_peeloff()
函数将失败并返回 EOPTNOTSUPP
。如果 id
的值为 0 或者 id
的值大于在 sock
参数中传递的套接字描述符的最大关联数,则 sctp_peeloff()
函数将失败并返回 EINVAL
。如果 sctp_peeloff()
函数无法创建新的用户文件描述符或文件结构,则此函数将失败并返回 EMFILE
。
sctp_getpaddrs()
sctp_getpaddrs()
函数将返回关联中的所有对等地址。
int sctp_getpaddrs(int sock
, sctp_assoc_t id
, void **addrs
); \
当 sctp_getpaddrs()
函数成功返回时,**addrs
参数的值将指向每个地址相应类型的动态分配的压缩 sockaddr
结构数组。调用线程使用 sctp_freepaddrs()
函数释放内存。**addrs
参数的值不能为 NULL
。如果 sock
中给定的套接字描述符用于 IPv4 套接字,则 sctp_getpaddrs()
函数将返回 IPv4 地址。如果 sock
中给定的套接字描述符用于 IPv6 套接字,则 sctp_getpaddrs()
函数将同时返回 IPv4 和 IPv6 地址。对于一对多风格套接字,id
参数指定要查询的关联。对于一对一风格套接字,sctp_getpaddrs()
函数将忽略 id
参数。当 sctp_getpaddrs()
函数成功返回时,它将返回关联中的对等地址数。如果此套接字上没有关联,则 sctp_getpaddrs()
函数将返回 0,并且不定义 **addrs
参数的值。如果出现错误,则 sctp_getpaddrs()
函数将返回 -1,并且不定义 **addrs
参数的值。
如果 sock
参数中传递给 sctp_getpaddrs()
函数的文件描述符无效,则 sctp_getpaddrs()
函数将失败并返回 EBADF
。如果 sock
参数中传递给 sctp_getpaddrs()
函数的文件描述符没有描述套接字,则 sctp_getpaddrs()
函数将失败并返回 ENOTSOCK
。如果 sock
参数中传递给 sctp_getpaddrs()
函数的文件描述符描述了未连接的套接字,则 sctp_getpaddrs()
函数将失败并返回 ENOTCONN
。
sctp_freepaddrs()
sctp_freepaddrs()
函数将释放所有由之前的 sctp_getpaddrs()
调用所分配的资源。sctp_freepaddrs()
函数的语法如下:
void sctp_freepaddrs(void *addrs
); \
*addrs
参数为包含 sctp_getpaddrs()
函数所返回的对等地址的数组。
sctp_getladdrs()
sctp_getladdrs()
函数将返回套接字上的所有本地绑定的地址。sctp_getladdrs()
函数的语法如下:
int sctp_getladdrs(int sock
, sctp_assoc_t id
, void **addrs
); \
当 sctp_getladdrs()
函数成功返回时,addrs
的值将指向动态分配的压缩 sockaddr
结构数组。sockaddr
结构为每个本地地址的相应类型。调用应用程序使用 sctp_freeladdrs()
函数释放内存。addrs
参数的值不能为 NULL。
如果 sd
参数引用的套接字为 IPv4 套接字,则 sctp_getladdrs()
函数将返回 IPv4 地址。如果 sd
参数引用的套接字为 IPv6 套接字,则 sctp_getladdrs()
函数将同时返回相应的 IPv4 或 IPv6 地址。
针对一对多风格套接字调用 sctp_getladdrs()
函数时,id
参数的值指定要查询的关联。sctp_getladdrs()
函数在一对一套接字上运行时将忽略 id
参数。
当 id
参数的值为 0 时,无论为何种特定关联,sctp_getladdrs()
函数都将返回本地绑定的地址。当 sctp_getladdrs()
函数成功返回时,它将报告绑定到套接字的本地地址数。如果未绑定套接字,则 sctp_getladdrs()
函数将返回 0,并且不定义 *addrs
的值。如果出现错误,则 sctp_getladdrs()
函数将返回 -1,并且不定义 *addrs
的值。
sctp_freeladdrs()
sctp_freeladdrs()
函数将释放所有由之前的 sctp_getladdrs()
调用所分配的资源。sctp_freeladdrs()
函数的语法如下:
void sctp_freeladdrs(void *addrs
); \
*addrs
参数为包含 sctp_getladdrs()
函数所返回的对等地址的数组。
\
创作打卡挑战赛
赢取流量/现金/****周边激励大奖
推荐阅读
-
Linux网络编程中 SCTP 套接字的常用接口详解
-
go语言Socket编程-Socket编程 什么是Socket Socket,英文含义是插座、插孔,一般称之为套接字,用于描述IP地址和端口。可以实现不同程序间的数据通信。 Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket,该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。 套接字的内核实现较为复杂,不宜在学习初期深入学习,了解到如下结构足矣。 套接字通讯原理示意 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。 常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。 网络应用程序设计模式 C/S模式 传统的网络应用设计模式,客户机(client)/服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。 B/S模式 浏览器(Browser)/服务器(Server)模式。只需在一端部署服务器,而另外一端使用每台PC都默认配置的浏览器即可完成数据的传输。 优缺点 对于C/S模式来说,其优点明显。客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率。且,一般来说客户端和服务器程序由一个开发团队创作,所以他们之间所采用的协议相对灵活。可以在标准协议的基础上根据需求裁剪及定制。例如,腾讯所采用的通信协议,即为ftp协议的修改剪裁版。 因此,传统的网络应用程序及较大型的网络应用程序都首选C/S模式进行开发。如,知名的网络游戏魔兽世界。3D画面,数据量庞大,使用C/S模式可以提前在本地进行大量数据的缓存处理,从而提高观感。 C/S模式的缺点也较突出。由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提升,开发周期较长。另外,从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁。这也是很多用户不愿使用C/S模式应用程序的重要原因。 B/S模式相比C/S模式而言,由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小。只需开发服务器端即可。另外由于其采用浏览器显示数据,因此移植性非常好,不受平台限制。如早期的偷菜游戏,在各个平台上都可以完美运行。 B/S模式的缺点也较明显。由于使用第三方浏览器,因此网络应用支持受限。另外,没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制。应用的观感大打折扣。第三,必须与浏览器一样,采用标准http协议进行通信,协议选择不灵活。 因此在开发过程中,模式的选择由上述各自的特点决定。根据实际需求选择应用程序设计模式。 简单的C/S模型通信 Server端:Listen函数 func Listen(network, address string) (Listener, error) network:选用的协议:TCP、UDP, 如:“tcp”或 “udp” address:IP地址+端口号, 如:“127.0.0.1:8000”或 “:8000” Listener 接口: type Listener interface { Accept (Conn, error) Close error Addr Addr } Conn 接口: type Conn interface { Read(b byte) (n int, err error) Write(b byte) (n int, err error) Close error LocalAddr Addr RemoteAddr Addr SetDeadline(t time.Time) error SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error } 参看 [<u>https://studygolang.com/pkgdoc</u>](https://studygolang.com/pkgdoc) 中文帮助文档中的demo: 示例代码:TCP服务器.go package main import ( "net" "fmt" ) func main { // 创建监听 listener, err:= net.Listen("tcp", ":8000") if err != nil { fmt.Println("listen err:", err) return } defer listener.Close // 主协程结束时,关闭listener fmt.Println("服务器等待客户端建立连接...") // 等待客户端连接请求 conn, err := listener.Accept if err != nil { fmt.Println("accept err:", err) return } defer conn.Close // 使用结束,断开与客户端链接 fmt.Println("客户端与服务器连接建立成功...") // 接收客户端数据 buf := make(byte, 1024) // 创建1024大小的缓冲区,用于read n, err := conn.Read(buf) if err != nil { fmt.Println("read err:", err) return } fmt.Println("服务器读到:", string(buf[:n])) // 读多少,打印多少。 }
-
包婷婷 (201550484)作业一 统计软件简介与数据操作-SPSS(Statistical Product and Service Solutions),"统计产品与服务解决方案"软件。最初软件全称为"(SolutionsStatistical Package for the Social Sciences),但是随着SPSS产品服务领域的扩大和服务深度的增加,SPSS公司已于2000年正式将英文全称更改为"统计产品与服务解决方案",标志着SPSS的战略方向正在做出重大调整。为IBM公司推出的一系列用于统计学分析运算、数据挖掘、预测分析和决策支持任务的软件产品及相关服务的总称SPSS,有Windows和Mac OS X等版本。 1984年SPSS总部首先推出了世界上第一个统计分析软件微机版本SPSS/PC+,开创了SPSS微机系列产品的开发方向,极大地扩充了它的应用范围,并使其能很快地应用于自然科学、技术科学、社会科学的各个领域。世界上许多有影响的报刊杂志纷纷就SPSS的自动统计绘图、数据的深入分析、使用方便、功能齐全等方面给予了高度的评价。 R统计软件介绍 R是一套完整的数据处理、计算和制图软件系统。其功能包括:数据存储和处理系统;数组运算工具(其向量、矩阵运算方面功能尤其强大);完整连贯的统计分析工具;优秀的统计制图功能;简便而强大的编程语言:可操纵数据的输入和输出,可实现分支、循环,用户可自定义功能。 与其说R是一种统计软件,还不如说R是一种数学计算的环境,因为R并不是仅仅提供若干统计程序、使用者只需指定数据库和若干参数便可进行一个统计分析。R的思想是:它可以提供一些集成的统计工具,但更大量的是它提供各种数学计算、统计计算的函数,从而使使用者能灵活机动的进行数据分析,甚至创造出符合需要的新的统计计算方法。 该语言的语法表面上类似 C,但在语义上是函数设计语言(functional programming language)的变种并且和Lisp 以及 APL有很强的兼容性。特别的是,它允许在"语言上计算"(computing on the language)。这使得它可以把表达式作为函数的输入参数,而这种做法对统计模拟和绘图非常有用。 R是一个免费的*软件,它有UNIX、LINUX、MacOS和WINDOWS版本,都是可以免费下载和使用的。在R主页那儿可以下载到R的安装程序、各种外挂程序和文档。在R的安装程序中只包含了8个基础模块,其他外在模块可以通过CRAN获得。 二、R语言 R是用于统计分析、绘图的语言和操作环境。R是属于GNU系统的一个*、免费、源代码开放的软件,它是一个用于统计计算和统计制图的优秀工具。 R作为一种统计分析软件,是集统计分析与图形显示于一体的。它可以运行于UNIX,Windows和Macintosh的操作系统上,而且嵌入了一个非常方便实用的帮助系统,相比于其他统计分析软件,R还有以下特点: 1.R是*软件。这意味着它是完全免费,开放源代码的。可以在它的网站及其镜像中下载任何有关的安装程序、源代码、程序包及其源代码、文档资料。标准的安装文件身自身就带有许多模块和内嵌统计函数,安装好后可以直接实现许多常用的统计功能。[2] 2.R是一种可编程的语言。作为一个开放的统计编程环境,语法通俗易懂,很容易学会和掌握语言的语法。而且学会之后,我们可以编制自己的函数来扩展现有的语言。这也就是为什么它的更新速度比一般统计软件,如,SPSS,SAS等快得多。大多数最新的统计方法和技术都可以在R中直接得到。[2] 3. 所有R的函数和数据集是保存在程序包里面的。只有当一个包被载入时,它的内容才可以被访问。一些常用、基本的程序包已经被收入了标准安装文件中,随着新的统计分析方法的出现,标准安装文件中所包含的程序包也随着版本的更新而不断变化。在另外版安装文件中,已经包含的程序包有:base一R的基础模块、mle一极大似然估计模块、ts一时间序列分析模块、mva一多元统计分析模块、survival一生存分析模块等等.[2] 4.R具有很强的互动性。除了图形输出是在另外的窗口处,它的输入输出窗口都是在同一个窗口进行的,输入语法中如果出现错误会马上在窗口口中得到提示,对以前输入过的命令有记忆功能,可以随时再现、编辑修改以满足用户的需要。输出的图形可以直接保存为JPG,BMP,PNG等图片格式,还可以直接保存为PDF文件。另外,和其他编程语言和数据库之间有很好的接口。[2] 5.如果加入R的帮助邮件列表一,每天都可能会收到几十份关于R的邮件资讯。可以和全球一流的统计计算方面的专家讨论各种问题,可以说是全世界最大、最前沿的统计学家思维的聚集地.[2] R是基于S语言的一个GNU项目,所以也可以当作S语言的一种实现,通常用S语言编写的代码都可以不作修改的在R环境下运行。 R的语法是来自Scheme。R的使用与S-PLUS有很多类似之处,这两种语言有一定的兼容性。S-PLUS的使用手册,只要稍加修改就可作为R的使用手册。所以有人说:R,是S-PLUS的一个“克隆”。 但是请不要忘了:R是免费的(R is free)。R语言源代码托管在github,具体地址可以看参考资料。[3] 。 R语言的下载可以通过CRAN的镜像来查找。 R语言有域名为.cn的下载地址,有六个,其中两个由Datagurn,由 中国科学技术大学提供的。R语言Windows版,其中由两个下载地点是Datagurn和 USTC提供的。 三、stata Stata 是一套提供其使用者数据分析、数据管理以及绘制专业图表的完整及整合性统计软件。它提供许许多多功能,包含线性混合模型、均衡重复反复及多项式普罗比模式。用Stata绘制的统计图形相当精美。 新版本的STATA采用最具亲和力的窗口接口,使用者自行建立程序时,软件能提供具有直接命令式的语法。Stata提供完整的使用手册,包含统计样本建立、解释、模型与语法、文献等超过一万余页的出版品。 除此之外,Stata软件可以透过网络实时更新每天的最新功能,更可以得知世界各地的使用者对于STATA公司提出的问题与解决之道。使用者也可以透过Stata. Journal获得许许多多的相关讯息以及书籍介绍等。另外一个获取庞大资源的管道就是Statalist,它是一个独立的listserver,每月交替提供使用者超过1000个讯息以及50个程序。 四、PYTHON
-
epoll简介及触发模式(accept、read、send)-epoll的简单介绍 epoll在LT和ET模式下的读写方式 一、epoll的接口非常简单,一共就三个函数:1. int epoll_create(int size);创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close关闭,否则可能导致fd被耗尽。2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll的事件注册函数,它不同与select是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create的返回值,第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */};events可以是以下几个宏的集合:EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLIN事件:EPOLLIN事件则只有当对端有数据写入时才会触发,所以触发一次后需要不断读取所有数据直到读完EAGAIN为止。否则剩下的数据只有在下次对端有写入时才能一起取出来了。现在明白为什么说epoll必须要求异步socket了吧?如果同步socket,而且要求读完所有数据,那么最终就会在堵死在阻塞里。 EPOLLOUT:表示对应的文件描述符可以写; EPOLLOUT事件:EPOLLOUT事件只有在连接时触发一次,表示可写,其他时候想要触发,那要先准备好下面条件:1.某次write,写满了发送缓冲区,返回错误码为EAGAIN。2.对端读取了一些数据,又重新可写了,此时会触发EPOLLOUT。简单地说:EPOLLOUT事件只有在不可写到可写的转变时刻,才会触发一次,所以叫边缘触发,这叫法没错的!其实,如果真的想强制触发一次,也是有办法的,直接调用epoll_ctl重新设置一下event就可以了,event跟原来的设置一模一样都行(但必须包含EPOLLOUT),关键是重新设置,就会马上触发一次EPOLLOUT事件。1. 缓冲区由满变空.2.同时注册EPOLLIN | EPOLLOUT事件,也会触发一次EPOLLOUT事件这个两个也会触发EPOLLOUT事件 EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR:表示对应的文件描述符发生错误;EPOLLHUP:表示对应的文件描述符被挂断;EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的产生,类似于select调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。-------------------------------------------------------------------------------------------- 从man手册中,得到ET和LT的具体描述如下EPOLL事件有两种模型:Edge Triggered (ET)Level Triggered (LT)假如有这样一个例子:1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符2. 这个时候从管道的另一端被写入了2KB的数据3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作4. 然后我们读取了1KB的数据5. 调用epoll_wait(2)......Edge Triggered 工作模式:如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中,会有一个事件产生在RFD句柄上,因为在第2步执行了一个写操作,然后,事件将会在第3步被销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据,因此我们在第5步调用 epoll_wait(2)完成后,是否挂起是不确定的。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。 i 基于非阻塞文件句柄 ii 只有当read(2)或者write(2)返回EAGAIN时才需要挂起,等待。但这并不是说每次read时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。Level Triggered 工作模式相反的,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标志,在 epoll_wait(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。因此当EPOLLONESHOT设定后,使用带有 EPOLL_CTL_MOD标志的epoll_ctl(2)处理文件句柄就成为调用者必须作的事情。然后详细解释ET, LT:LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。在许多测试中我们会看到如果没有大量的idle -connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当我们遇到大量的idle- connection(例如WAN环境中存在大量的慢速连接),就会发现epoll的效率大大高于select/poll。(未测试)另外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,读数据的时候需要考虑的是当recv返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取: 这里只是说明思路(参考《UNIX网络编程》) while(rs) {buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);if(buflen < 0){// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读// 在这里就当作是该次事件已处理处.if(errno == EAGAIN)break; else return; }else if(buflen == 0) { // 这里表示对端的socket已正常关闭. } if(buflen == sizeof(buf) rs = 1; // 需要再次读取 else rs = 0; } 还有,假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,需要封装socket_send的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_send内部,当写缓冲已满(send返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send内部,但暂没有更好的办法. ssize_t socket_send(int sockfd, const char* buffer, size_t buflen) { ssize_t tmp; size_t total = buflen; const char *p = buffer; while(1) { tmp = send(sockfd, p, total, 0); if(tmp < 0) { // 当send收到信号时,可以继续写,但这里返回-1. if(errno == EINTR) return -1; // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满, // 在这里做延时后再重试. if(errno == EAGAIN) { usleep(1000); continue; } return -1; } if((size_t)tmp == total) return buflen; total -= tmp; p += tmp; } return tmp; } 二、epoll在LT和ET模式下的读写方式 在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK) 从字面上看, 意思是: * EAGAIN: 再试一次 * EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block * perror输出: Resource temporarily unavailable 总结: 这个错误表示资源暂时不够, 可能read时, 读缓冲区没有数据, 或者, write时,写缓冲区满了 。 遇到这种情况, 如果是阻塞socket, read/write就要阻塞掉。 而如果是非阻塞socket, read/write立即返回-1, 同 时errno设置为EAGAIN. 所以, 对于阻塞socket, read/write返回-1代表网络出错了. 但对于非阻塞socket, read/write返回-1不一定网络真的出错了. 可能是Resource temporarily unavailable. 这时你应该再试, 直到Resource available. 综上, 对于non-blocking的socket, 正确的读写操作为: 读: 忽略掉errno = EAGAIN的错误, 下次继续读 写: 忽略掉errno = EAGAIN的错误, 下次继续写 对于select和epoll的LT模式, 这种读写方式是没有问题的. 但对于epoll的ET模式, 这种方式还有漏洞. epoll的两种模式 LT 和 ET
-
windows下进程间通信的(13种方法)-摘 要 本文讨论了进程间通信与应用程序间通信的含义及相应的实现技术,并对这些技术的原理、特性等进行了深入的分析和比较。 ---- 关键词 信号 管道 消息队列 共享存储段 信号灯 远程过程调用 Socket套接字 MQSeries 1 引言 ---- 进程间通信的主要目的是实现同一计算机系统内部的相互协作的进程之间的数据共享与信息交换,由于这些进程处于同一软件和硬件环境下,利用操作系统提供的的编程接口,用户可以方便地在程序中实现这种通信;应用程序间通信的主要目的是实现不同计算机系统中的相互协作的应用程序之间的数据共享与信息交换,由于应用程序分别运行在不同计算机系统中,它们之间要通过网络之间的协议才能实现数据共享与信息交换。进程间通信和应用程序间通信及相应的实现技术有许多相同之处,也各有自己的特色。即使是同一类型的通信也有多种的实现方法,以适应不同情况的需要。 ---- 为了充分认识和掌握这两种通信及相应的实现技术,本文将就以下几个方面对这两种通信进行深入的讨论:问题的由来、解决问题的策略和方法、每种方法的工作原理和实现、每种实现方法的特点和适用的范围等。 2 进程间的通信及其实现技术 ---- 用户提交给计算机的任务最终都是通过一个个的进程来完成的。在一组并发进程中的任何两个进程之间,如果都不存在公共变量,则称该组进程为不相交的。在不相交的进程组中,每个进程都独立于其它进程,它的运行环境与顺序程序一样,而且它的运行环境也不为别的进程所改变。运行的结果是确定的,不会发生与时间相关的错误。 ---- 但是,在实际中,并发进程的各个进程之间并不是完全互相独立的,它们之间往往存在着相互制约的关系。进程之间的相互制约关系表现为两种方式: ---- (1) 间接相互制约:共享CPU ---- (2) 直接相互制约:竞争和协作 ---- 竞争——进程对共享资源的竞争。为保证进程互斥地访问共享资源,各进程必须互斥地进入各自的临界段。 ---- 协作——进程之间交换数据。为完成一个共同任务而同时运行的一组进程称为同组进程,它们之间必须交换数据,以达到协作完成任务的目的,交换数据可以通知对方可以做某事或者委托对方做某事。 ---- 共享CPU问题由操作系统的进程调度来实现,进程间的竞争和协作由进程间的通信来完成。进程间的通信一般由操作系统提供编程接口,由程序员在程序中实现。UNIX在这个方面可以说最具特色,它提供了一整套进程间的数据共享与信息交换的处理方法——进程通信机制(IPC)。因此,我们就以UNIX为例来分析进程间通信的各种实现技术。 ---- 在UNIX中,文件(File)、信号(Signal)、无名管道(Unnamed Pipes)、有名管道(FIFOs)是传统IPC功能;新的IPC功能包括消息队列(Message queues)、共享存储段(Shared memory segment)和信号灯(Semapores)。 ---- (1) 信号 ---- 信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断控制。例如在发生浮点错、非法内存访问、执行无效指令、某些按键(如ctrl-c、del等)等都会产生一个信号,操作系统就会调用有关的系统调用或用户定义的处理过程来处理。 ---- 信号处理的系统调用是signal,调用形式是: ---- signal(signalno,action) ---- 其中,signalno是规定信号编号的值,action指明当特定的信号发生时所执行的动作。 ---- (2) 无名管道和有名管道 ---- 无名管道实际上是内存中的一个临时存储区,它由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出方式管理,并严格按顺序操作,例如不能对管道进行搜索,管道中的信息只能读一次。 ---- 无名管道只能用于两个相互协作的进程之间的通信,并且访问无名管道的进程必须有共同的祖先。 ---- 系统提供了许多标准管道库函数,如: pipe——打开一个可以读写的管道; close——关闭相应的管道; read——从管道中读取字符; write——向管道中写入字符; ---- 有名管道的操作和无名管道类似,不同的地方在于使用有名管道的进程不需要具有共同的祖先,其它进程,只要知道该管道的名字,就可以访问它。管道非常适合进程之间快速交换信息。 ---- (3) 消息队列(MQ) ---- 消息队列是内存中独立于生成它的进程的一段存储区,一旦创建消息队列,任何进程,只要具有正确的的访问权限,都可以访问消息队列,消息队列非常适合于在进程间交换短信息。 ---- 消息队列的每条消息由类型编号来分类,这样接收进程可以选择读取特定的消息类型——这一点与管道不同。消息队列在创建后将一直存在,直到使用msgctl系统调用或iqcrm -q命令删除它为止。 ---- 系统提供了许多有关创建、使用和管理消息队列的系统调用,如: ---- int msgget(key,flag)——创建一个具有flag权限的MQ及其相应的结构,并返回一个唯一的正整数msqid(MQ的标识符); ---- int msgsnd(msqid,msgp,msgsz,msgtyp,flag)——向队列中发送信息; ---- int msgrcv(msqid,cmd,buf)——从队列中接收信息; ---- int msgctl(msqid,cmd,buf)——对MQ的控制操作; ---- (4) 共享存储段(SM) ---- 共享存储段是主存的一部分,它由一个或多个独立的进程共享。各进程的数据段与共享存储段相关联,对每个进程来说,共享存储段有不同的虚拟地址。系统提供的有关SM的系统调用有: ---- int shmget(key,size,flag)——创建大小为size的SM段,其相应的数据结构名为key,并返回共享内存区的标识符shmid; ---- char shmat(shmid,address,flag)——将当前进程数据段的地址赋给shmget所返回的名为shmid的SM段; ---- int shmdr(address)——从进程地址空间删除SM段; ---- int shmctl (shmid,cmd,buf)——对SM的控制操作; ---- SM的大小只受主存限制,SM段的访问及进程间的信息交换可以通过同步读写来完成。同步通常由信号灯来实现。SM非常适合进程之间大量数据的共享。 ---- (5) 信号灯 ---- 在UNIX中,信号灯是一组进程共享的数据结构,当几个进程竞争同一资源时(文件、共享内存或消息队列等),它们的操作便由信号灯来同步,以防止互相干扰。 ---- 信号灯保证了某一时刻只有一个进程访问某一临界资源,所有请求该资源的其它进程都将被挂起,一旦该资源得到释放,系统才允许其它进程访问该资源。信号灯通常配对使用,以便实现资源的加锁和解锁。 ---- 进程间通信的实现技术的特点是:操作系统提供实现机制和编程接口,由用户在程序中实现,保证进程间可以进行快速的信息交换和大量数据的共享。但是,上述方式主要适合在同一台计算机系统内部的进程之间的通信。 3 应用程序间的通信及其实现技术 ---- 同进程之间的相互制约一样,不同的应用程序之间也存在竞争和协作的关系。UNIX操作系统也提供一些可用于应用程序之间实现数据共享与信息交换的编程接口,程序员可以通过自己编程来实现。如远程过程调用和基于TCP/IP协议的套接字(Socket)编程。但是,相对普通程序员来说,它们涉及的技术比较深,编程也比较复杂,实现起来困难较大。 ---- 于是,一种新的技术应运而生——通过将有关通信的细节完全掩盖在某个独立软件内部,即底层的通讯工作和相应的维护管理工作由该软件内部来实现,用户只需要将通信任务提交给该软件去完成,而不必理会它的具体工作过程——这就是所谓的中间件技术。 ---- 我们在这里分别讨论这三种常用的应用程序间通信的实现技术——远程过程调用、会话编程技术和MQSeries消息队列技术。其中远程过程调用和会话编程属于比较低级的方式,程序员参与的程度较深,而MQSeries消息队列则属于比较高级的方式,即中间件方式,程序员参与的程度较浅。 ---- 4.1 远程过程调用(RPC)
-
Linux设备驱动开发详解——学习笔记-设备驱动来联系。在没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口。而在有操作系统的情况下,驱动的架构则由相应的操作系统来定义。驱动存在的意义就是给上层应用提供便利。 驱动针对的对象是存储器和外设。Linux将存储器和外设分为 3 个基础大类:字符设备、块设备、网络设备。 字符设备和块设备都被 Linux 映射到文件系统的文件和目录中,通过文件系统的接口(open、read、write、close等)来访问。其中,块设备可以通过类似 dd 命令对应的原始块设备来访问,也可以通过建立文件系统,以文件路径来访问。 学习 Linux 设备驱动,要求非常好的硬件基础、非常好的软件基础、一定的 Linux 内核基础和非常好的多任务并发控制和同步的基础。学习 Linux 设备驱动要将学习的函数、数据结构等放到整体架构中去理解,才能理清驱动中各组成部分之间的关系。 驱动设计的硬件基础 驱动工程师需要掌握 处理器、存储器、接口和总线、可编程门电路、原理图、硬件时序、芯片手册、仪器使用 等方面的内容。 处理器