Linux從用戶層到內核層系列 - TCP/IP協議棧部分系列10:linux內核協議棧中對於socket相關API的實現

題記:本系列文章的目的是拋開書本從源代碼和使用的角度分析Linux內核和相關源代碼,byhankswang和你一起玩轉linux開發


輕鬆搞定TCP/IP協議棧,原創文章歡迎交流, [email protected]微笑

歡迎加入到CHLK - Linux開發交流羣 QQ:327084515 討論Linux開發相關問題


linux內核協議棧中對於socket相關API的實現


首先對於內核中斷向量表不是很熟悉的請先參考一下博文:《TCP/IP協議棧源碼圖解分析系列6:linux 系統調用中斷向量表》 URL:http://blog.csdn.net/byhankswang/article/details/9284023


首先應該做的事情

定義好了內核中斷向量表之後,需要做的就是當用戶層程序陷入到內核態之後,通過內核中斷向量表找到了內核中對於該系統調用的實現。補充一下內核中SYSCALL_DEFINE的用法:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol){ ….}

<=>SYSCALL_DEFINEX(3,_socket,__VA_ARGS__)

<=>_SYSCALL_DEFINE(3,_socket,__VA_ARGS__)

<=>asmlinkage long sys_socket(int family,int type,int protocol)

SYSCALL_DEFINE* 把內核中斷向量表和內核實現完美的銜接了起來。


用戶層API內核是如何實現的

以socket相關的套接字編程接口爲例(linux 3.9.3):

socket.c:1382:SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
socket.c:1423:SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,
socket.c:1519:SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
socket.c:1548:SYSCALL_DEFINE2(listen, int, fd, int, backlog)
socket.c:1581:SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
socket.c:1662:SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
socket.c:1680:SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
socket.c:1712:SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr,
socket.c:1743:SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
socket.c:1775:SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
socket.c:1822:SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len,
socket.c:1834:SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
socket.c:1890:SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
socket.c:1924:SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname,
socket.c:1954:SYSCALL_DEFINE2(shutdown, int, fd, int, how)
socket.c:2096:SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags)
socket.c:2171:SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
socket.c:2269:SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
socket.c:2393:SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
socket.c:2435:SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)


然後我們看相關的源代碼以socket和bind爲例:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
int flags;


/* Check the SOCK_* constants for consistency.  */
BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

flags = type & ~SOCK_TYPE_MASK;
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL;
type &= SOCK_TYPE_MASK;

if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;

retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;

out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval;

out_release:
sock_release(sock);
return retval;
}



SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{
struct socket *sock;
struct sockaddr_storage address;
int err, fput_needed;

sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (err >= 0) {
err = security_socket_bind(sock,
  (struct sockaddr *)&address,
  addrlen);
if (!err)
err = sock->ops->bind(sock,
     (struct sockaddr *)
     &address, addrlen);
}
fput_light(sock->file, fput_needed);
}
return err;
}


我們可以看到,只要抓住了主要的脈絡,分析內核協議棧是很簡單的事情,用侯捷先生的話說“源碼在手,了無祕密”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章