sendmsg系統調用,主要工作是將用戶空間的消息頭複製到內核空間中,對消息頭進行檢查。最後逐級調用發包接口發送數據。
https://www.cnblogs.com/wanpengcoder/p/11749313.html
send、sendto、sendmsg區別:
https://www.cnblogs.com/hnrainll/archive/2011/08/16/2141483.html
send只可用於基於連接的套接字,send 和 write唯一的不同點是標誌的存在,當標誌爲0時,send等同於write。sendto 和 sendmsg既可用於無連接的套接字,也可用於基於連接的套接字。
SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
E:\linux-4.1.45\linux-4.1.45\net\socket.c
SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
{
if (flags & MSG_CMSG_COMPAT)
return -EINVAL;
return __sys_sendmsg(fd, msg, flags);
}
__sys_sendmsg()
/*
* BSD sendmsg interface
*/
long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags)
{
int fput_needed, err;
struct msghdr msg_sys;
struct socket *sock;
sock = sockfd_lookup_light(fd, &err, &fput_needed);//根據fd找到對應socket
if (!sock)
goto out;
//發送數據
err = ___sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
fput_light(sock->file, fput_needed);
out:
return err;
}
___sys_sendmsg()
static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags,
struct used_address *used_address)
{
struct compat_msghdr __user *msg_compat =
(struct compat_msghdr __user *)msg;
struct sockaddr_storage address;
struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
unsigned char ctl[sizeof(struct cmsghdr) + 20]
__attribute__ ((aligned(sizeof(__kernel_size_t))));
/* 20 is size of ipv6_pktinfo */
unsigned char *ctl_buf = ctl;
int ctl_len;
ssize_t err;
msg_sys->msg_name = &address;
if (MSG_CMSG_COMPAT & flags)
/* 從64位消息頭拷貝數據到32位消息頭 */
err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
else
err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov);
if (err < 0)
return err;
err = -ENOBUFS;
/* 控制信息長度錯誤 */
if (msg_sys->msg_controllen > INT_MAX)
goto out_freeiov;
/* 拷貝控制信息 */
ctl_len = msg_sys->msg_controllen;
if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
err =
cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
sizeof(ctl));
if (err)
goto out_freeiov;
ctl_buf = msg_sys->msg_control;
ctl_len = msg_sys->msg_controllen;
} else if (ctl_len) {
if (ctl_len > sizeof(ctl)) {
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
if (ctl_buf == NULL)
goto out_freeiov;
}
err = -EFAULT;
/*
* Careful! Before this, msg_sys->msg_control contains a user pointer.
* Afterwards, it will be a kernel pointer. Thus the compiler-assisted
* checking falls down on this.
*/
if (copy_from_user(ctl_buf,
(void __user __force *)msg_sys->msg_control,
ctl_len))
goto out_freectl;
msg_sys->msg_control = ctl_buf;
}
/* 設置發送標記 */
msg_sys->msg_flags = flags;
/* 設置非阻塞標記 */
if (sock->file->f_flags & O_NONBLOCK)
msg_sys->msg_flags |= MSG_DONTWAIT;
/* 如果這次發送的地址跟上次成功發送的一致 */
/*
* If this is sendmmsg() and current destination address is same as
* previously succeeded address, omit asking LSM's decision.
* used_address->name_len is initialized to UINT_MAX so that the first
* destination address never matches.
*/
if (used_address && msg_sys->msg_name &&
used_address->name_len == msg_sys->msg_namelen &&
!memcmp(&used_address->name, msg_sys->msg_name,
used_address->name_len)) {
/* 無需進行檢查,直接發送 */
err = sock_sendmsg_nosec(sock, msg_sys);
goto out_freectl;
}
/* 進行安全模塊檢查後發送 */
err = sock_sendmsg(sock, msg_sys);
/* 發送成功需要更新成功地址記錄 */
/*
* If this is sendmmsg() and sending to current destination address was
* successful, remember it.
*/
if (used_address && err >= 0) {
used_address->name_len = msg_sys->msg_namelen;
if (msg_sys->msg_name)
memcpy(&used_address->name, msg_sys->msg_name,
used_address->name_len);
}
out_freectl:
if (ctl_buf != ctl)
sock_kfree_s(sock->sk, ctl_buf, ctl_len);
out_freeiov:
kfree(iov);
return err;
}
在項目中測試發現,沒有定義CONFIG_COMPAT宏, 用的是#define compat_msghdr msghdr
E:\linux-4.1.45\linux-4.1.45\include\net\compat.h
#if defined(CONFIG_COMPAT)
#include <linux/compat.h>
struct compat_msghdr {
compat_uptr_t msg_name; /* void * */
compat_int_t msg_namelen;
compat_uptr_t msg_iov; /* struct compat_iovec * */
compat_size_t msg_iovlen;
compat_uptr_t msg_control; /* void * */
compat_size_t msg_controllen;
compat_uint_t msg_flags;
};
...
#else /* defined(CONFIG_COMPAT) */
/*
* To avoid compiler warnings:
*/
#define compat_msghdr msghdr
#define compat_mmsghdr mmsghdr
#endif /* defined(CONFIG_COMPAT) */
sock_sendmsg()
int sock_sendmsg(struct socket *sock, struct msghdr *msg)
{
int err = security_socket_sendmsg(sock, msg,
msg_data_left(msg));
return err ?: sock_sendmsg_nosec(sock, msg);
}
sock_sendmsg_nosec()
static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg)
{
int ret = sock->ops->sendmsg(sock, msg, msg_data_left(msg));
BUG_ON(ret == -EIOCBQUEUED);
return ret;
}
inet_sendmsg()
int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
sock_rps_record_flow(sk);
/* We may need to bind the socket. */
if (!inet_sk(sk)->inet_num && !sk->sk_prot->no_autobind &&
inet_autobind(sk))
return -EAGAIN;
return sk->sk_prot->sendmsg(sk, msg, size);
}