我们只以其中一个接口write为线索,对套接字写(网络数据发送)的流程进行分析。系统调用
write会调用内核函数sys_write,sys_write调用vfs_write完成实际的写操作。
vfs_write会先调用file->f_op->write(file从套接字描述符获得)。如果file->f_op-> write不存在,则调用do_sync_write。该函数会调用sock_aio_write,
sock_aio_write又会调用 __sock_sendmsg,然后到myinet_sendmsg,最后才到sk->sk_prot->sendmsg,对于RAW协议来讲,即myraw_sendmsg。
sock_aio_write的函数原型如下:
static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf,
size_t size, loff_t pos)
ubuf是用户待发送数据,size是数据长度,pos是文件位置(永远为零)。在这个函数里,会把用户待发送数据封装成一个struct msghdr结构:
struct msghdr {
void * msg_name; /* Socket name */
int msg_namelen; /* Length of name */
struct iovec * msg_iov; /* Data blocks */
__kernel_size_t msg_iovlen; /* Number of blocks */
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
__kernel_size_t msg_controllen; /* Length of cmsg list */
unsigned msg_flags;
};
如果用户代码为: write(fd, "abcdef", 6 ),则在sock_aio_write中封装成的msghdr结构为:
struct msghdr thehdr{
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov.iov_base = "abcdef",
.msg_iov.iov_len = 6,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0
};
raw_sendmsg的函数原型为:
static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
所以,它拿到的是已经封装好的消息msg。该函数所做的第一件事情是检查len,其最大长度是16位(0xffff),然后,确认msg->msg_flags中没有MSG_OOB(RAW不支持带外数据的发送
)。
如果msg->msg_namelen不等于零,则name中存储的是域和目的地址的信息,如果等于零,则当前必须是已经建立了TCP连接的,否则数据不知道发往哪儿。
接下来,查看控制数据缓冲区长度是否为零,如果不是,则有控制信息msg->msg_control,调用ip_cmsg_send发送控制信息(实际上,主要是填充一个结构体struct
ipcm_cookie ipc,从代码来看,该结构应该用于构建ip头)。
inet->hdrincl表示需要自己来构建ip头,所以如果inet->hdrincl==1,并且,ipc->opt!=NULL则返回出错信息:无效参数。
接下来判断目的地址是否为组播地址(组播地址的最高四位为1110),是则作相应处理。
接下来,声明并初始化一个struct flowi结构,如果不是自己构建ip头,则调用raw_probe_proto_opt
接下来的内容,暂时未能很好理解,留待下文分析。
阅读(2147) | 评论(0) | 转发(0) |