Socket層はシリーズ—send()類の伝送関数の実現を実現します.
主な内容:socket送信関数のシステム呼び出し、Socket層実現.
カーネルバージョン:3.15.2
私のブログ:http://blog.csdn.net/zhangskd
送信フローチャート
以下はsend()、sendto()、sendmsg()とsendmmsg()の送信フローチャートであり、これらの4つの関数はシステムコールレベル以外のものである.
少し違っていますが、ソケット層とTCP層の実装は同じです.
アプリケーション層
アプリケーション層は、以下のソケット関数を使用してデータを送信することができる.
ssize_t write(int fd、const void*buf、size count);
ssize_t send(int s,const void*buf,size len,int flags)
ssize_t sendto(int s、const void*buf、sizaut len、int flags、const struct sockaddr*to、socklen tolen);
ssize_t sendmsg(int s、const struct msghdr*msg、int flags)
int sendmmsg(int s,struct mmsghdr*msgvec, unsigned int vlen,unsigned int flags);
これらの送信関数にはどんな違いがありますか?
flagsが0の場合、send()とwrite()の機能は同じです.
send(s,buf,len,flags)とsendto(s,buf,len,flags,NULL,0)の機能は同じです.
write()とsend()はソケットが接続状態の時に使用できますが、sendto()、sendmsg()とsendmmsg()はいつでも使えます.
ユーザ層のデータは最後にメッセージヘッダで記述される.
送信のデフォルトはブロック送信であり、非ブロック送信に設定することもできます.
ブロックマークではない:O_NONBBLOCK、MSG_DONTWAIT
When the message does not fit into the send buffer of the socket,send()normally blocks,unless the
socket has been placed in non-blocking I/O mode.
Enbles non-blocking operation;if the operation would block,EAGAIN is returned(this can also be enabled)
using the OuNON-BLOCK with the FuSETEL fcntl(2).
システムの呼び出し
送信関数はglibcによって提供され、ステートメントはinclude/sys/sockett.hにあり、sysdeps/mach/hurd/connect.cに位置し、
主にユーザ空間からsys_という名前に入るために使われます.socketcallのシステム呼び出しとパラメータ伝達.sys.socketcalは実は全部です.
socket関数はカーネル空間の共通入口に入る.
じゃ、次にinet_を呼びます.sendmsg()処理します
ソケット層の送信関数inet_sendmsg()は次の処理を行い、ここではもはや説明しない.
ソケット層
SOCK_STREAMセットインターフェースのsocket層操作関数セットの例はinet_である.ストリームopsでは、送信関数はinet_です.sendmsg()
カーネルバージョン:3.15.2
私のブログ:http://blog.csdn.net/zhangskd
送信フローチャート
以下はsend()、sendto()、sendmsg()とsendmmsg()の送信フローチャートであり、これらの4つの関数はシステムコールレベル以外のものである.
少し違っていますが、ソケット層とTCP層の実装は同じです.
アプリケーション層
アプリケーション層は、以下のソケット関数を使用してデータを送信することができる.
ssize_t write(int fd、const void*buf、size count);
ssize_t send(int s,const void*buf,size len,int flags)
ssize_t sendto(int s、const void*buf、sizaut len、int flags、const struct sockaddr*to、socklen tolen);
ssize_t sendmsg(int s、const struct msghdr*msg、int flags)
int sendmmsg(int s,struct mmsghdr*msgvec, unsigned int vlen,unsigned int flags);
これらの送信関数にはどんな違いがありますか?
flagsが0の場合、send()とwrite()の機能は同じです.
send(s,buf,len,flags)とsendto(s,buf,len,flags,NULL,0)の機能は同じです.
write()とsend()はソケットが接続状態の時に使用できますが、sendto()、sendmsg()とsendmmsg()はいつでも使えます.
ユーザ層のデータは最後にメッセージヘッダで記述される.
struct msghdr {
void *msg_name; /* optional address, */
socklen_t msg_namelen; /* size of address, */
struct iovec *msg_iov; /* scatter/gather array, */
size_t msg_iovlen; /* #elements in msg_iov, */
void *msg_control; /* ancillary data, */
socklen_t msg_controllen; /* ancillary data buffer len, */
int msg_flags; /* flags on received message */
};
/* Structure for scatter/gather I/O. */
struct iovec {
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
送信のデフォルトはブロック送信であり、非ブロック送信に設定することもできます.
ブロックマークではない:O_NONBBLOCK、MSG_DONTWAIT
When the message does not fit into the send buffer of the socket,send()normally blocks,unless the
socket has been placed in non-blocking I/O mode.
Enbles non-blocking operation;if the operation would block,EAGAIN is returned(this can also be enabled)
using the OuNON-BLOCK with the FuSETEL fcntl(2).
システムの呼び出し
送信関数はglibcによって提供され、ステートメントはinclude/sys/sockett.hにあり、sysdeps/mach/hurd/connect.cに位置し、
主にユーザ空間からsys_という名前に入るために使われます.socketcallのシステム呼び出しとパラメータ伝達.sys.socketcalは実は全部です.
socket関数はカーネル空間の共通入口に入る.
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
...
switch(call) {
...
case SYS_SEND:
err = sys_send(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_SENDTO:
err = sys_sendto(a0, (void __user *)a1 a[2], a[3], (struct sockaddr __user *)a[4], a[5]);
break;
...
case SYS_SENDMSG:
err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
break;
case SYS_SENDMMSG:
err = sys_sendmmsg(a0, (struct msghdr __user *)a1, a[2], a[3]);
break;
...
}
}
send()は実はsendto()の特殊な状況です.SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, unsigned, flags)
{
return sys_sendto(fd, buff, len, flags, NULL, 0);
}
sendto()はメッセージヘッドを初期化し、次にsock_uを呼び出します.sendmsgで処理します./* Send a datagram to a given address. We move the address into kernel space
* and check the user space data area is readable before invoking the protocol.
*/
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, unsigned, flags,
struct sockaddr __user *, addr, int, addr_len)
{
struct socket *sock;
struct sockaddr_storage address;
int err;
struct msghdr msg;
struct iovec iov;
int fput_needed;
if (len > INT_MAX)
len = INT_MAX;
/* fd, socket 。
* fd files_struct file ,
* file private_data socket 。
*/
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (! sock)
goto out;
/* */
iov.iov_base = buff;
iov.iov_len = len;
msg.msg_name = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1; /* */
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_namelen = 0;
if (addr) {
/* */
err = move_addr_to_kernel(addr, addr_len, &address);
if (err < 0)
goto out_put;
msg.msg_name = (struct sockaddr *)&address;
msg.msg_namelen = addr_len;
}
/* */
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;
msg.msg_flags = flags;
/* sock_sendmsg() */
err = sock_sendmsg(sock , &msg, len);
out_put:
fput_light(sock->file, fput_needed);
out:
return err;
}
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
struct iovec *msg_iov; /* scatter/gather array, */
__kernel_size_t msg_iovlen; /* #elements in msg_iov, */
void *msg_control; /* ancillary data, */
__kernel_size_t msg_controllen; /* ancillary data buffer len, */
unsigned int msg_flags; /* flags on received message */
};
/* Structure for scatter/gather I/O. */
struct iovec {
void *iov_base; /* Pointer to data. */
__kernel_size_t iov_len; /* Length of data. */
};
/* For recvmmsg/ sendmmsg */
struct mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
sock_sendmsg()は、非同期IO制御ブロックを初期化した後、__u u uを呼び出します.sock_sendmsg()int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct kiocb iocb;
struct sock_iocb siocb;
int ret;
init_sync_kiocb(&iocb, NULL);
iocb.private = &siocb;
ret = __sock_sendmsg(&iocb, sock, msg, size);
/* iocb queued, will get completion event */
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&iocb);
return ret;
}
/* AIO */
struct kiocb {
struct file *ki_filp;
struct kioctx *ki_ctx; /* NULL for sync ops, NULL */
kiocb_cancel_fn *ki_cancel;
void *private; /* sock_iocb */
union {
void __user *user;
struct task_struct *tsk; /* io */
} ki_obj;
__u64 ki_user_data; /* user's data for completion */
loff_t ki_pos;
size_t ki_nbytes; /* copy of iocb->aio_nbytes */
struct list_head ki_list; /* the aio core uses this for cancellation */
/* If the aio_resfd field of the userspace iocb is not zero,
* this is the underlying eventfd context to deliver events to.
*/
struct eventfd_ctx *ki_eventfd;
};
同前sock_sendmsg()は、Socket層の送信関数を呼び出します.SOCK_であれば.STREAMじゃ、次にinet_を呼びます.sendmsg()処理します
static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
int err = security_socket_sendmsg(sock, msg, size);
return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size);
}
static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
struct sock_iocb *si = kiocb_to_siocb(iocb);
si->sock = sock;
si->scm = NULL;
si->msg = msg;
si->size = size;
/* Socket , SOCK_STREAM, proto_ops inet_stream_ops,
* inet_sendmsg()。
*/
return sock->ops->sendmsg(iocb, sock, msg, size);
}
sendmsg()とsendmmsg()は、システム呼び出し関数でもユーザ空間のデータをカーネルヘッダにコピーし、最後に呼び出します.ソケット層の送信関数inet_sendmsg()は次の処理を行い、ここではもはや説明しない.
ソケット層
SOCK_STREAMセットインターフェースのsocket層操作関数セットの例はinet_である.ストリームopsでは、送信関数はinet_です.sendmsg()
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
...
.sendmsg = inet_sendmsg,
...
};
inetsendmsg()は主にTCP層の送信関数tcp_を呼び出します.sendmsgで処理します.int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
sock_rps_record_flow(sk);
/* We may need to bnd the socket.
* , , 。
* tcp_prot no_autobaind true, TCP 。
*/
if (! inet_sk(sk)->inet_num && ! sk->sk_prot->no_autobind && inet_autobind(s))
return -EAGAIN;
/* TCP, sk_prot tcp_prot,sendmsg tcp_sendmsg() */
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
/* Automatically bind an unbound socket. */
static int inet_autobind(struct sock *sk)
{
struct inet_sock *inet;
/* We may need to bind the socket. */
lock_sock(sk);
/* */
if (! inet->inet_num) {
/* SOCK_STREAM TCP tcp_prot,
* inet_csk_get_port()。
*/
if (sk->sk_prot->get_port(sk, 0)) {
release_sock(sk);
return -EAGAIN;
}
inet->inet_sport = htons(inet->inet_num);
}
release_sock(sk);
return 0;
}