關鍵詞:node
一個典型的客戶端/服務器場景中,應用程序使用socket進行通訊的方式以下:服務器
關鍵socket API包括如下下幾種:網絡
socket存在於一個通訊domain中:識別出一個socket的方法(socket地址格式);通訊範圍(是統一主機不一樣應用之間;仍是一個網絡鏈接的不一樣主機上應用之間)。數據結構
domain都是以AF_開頭,表示Address Family;PF_開頭的表示Protocol Family。less
在socket.h中定義以下,能夠看出AF_和PF_基本一對一。dom
/* Protocol families. */ #define PF_UNSPEC 0 /* Unspecified. */ #define PF_LOCAL 1 /* Local to host (pipes and file-domain). */ #define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */ #define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */ #define PF_INET 2 /* IP protocol family. */ #define PF_AX25 3 /* Amateur Radio AX.25. */ #define PF_IPX 4 /* Novell Internet Protocol. */ #define PF_APPLETALK 5 /* Appletalk DDP. */ #define PF_NETROM 6 /* Amateur radio NetROM. */ #define PF_BRIDGE 7 /* Multiprotocol bridge. */ #define PF_ATMPVC 8 /* ATM PVCs. */ #define PF_X25 9 /* Reserved for X.25 project. */ #define PF_INET6 10 /* IP version 6. */ ... #define PF_MAX 44 /* For now.. */ /* Address families. */ #define AF_UNSPEC PF_UNSPEC #define AF_LOCAL PF_LOCAL #define AF_UNIX PF_UNIX #define AF_FILE PF_FILE #define AF_INET PF_INET #define AF_AX25 PF_AX25 #define AF_IPX PF_IPX #define AF_APPLETALK PF_APPLETALK #define AF_NETROM PF_NETROM #define AF_BRIDGE PF_BRIDGE #define AF_ATMPVC PF_ATMPVC #define AF_X25 PF_X25 #define AF_INET6 PF_INET6 ... #define AF_MAX PF_MAX
經常使用的AF_有AF_UNIX、AF_INET、AF_INET6三種。socket
每一個socket實現都至少提供了兩種socket:流和數據報。函數
/* Types of sockets. */ enum __socket_type { SOCK_STREAM = 1, /* Sequenced, reliable, connection-based byte streams. */ SOCK_DGRAM = 2, /* Connectionless, unreliable datagrams of fixed maximum length. */ SOCK_RAW = 3, /* Raw protocol interface. */ SOCK_RDM = 4, /* Reliably-delivered messages. */ SOCK_SEQPACKET = 5, /* Sequenced, reliable, connection-based, datagrams of fixed maximum length. */ SOCK_DCCP = 6, /* Datagram Congestion Control Protocol. */ SOCK_PACKET = 10, /* Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level. */ /* Flags to be ORed into the type parameter of socket and socketpair and used for the flags parameter of paccept. */ SOCK_CLOEXEC = 02000000, /* Atomically set close-on-exec flag for the new descriptor(s). */ SOCK_NONBLOCK = 00004000 /* Atomically mark descriptor(s) as non-blocking. */ };
流socket(SOCK_STREAM)提供了一個可靠的雙向的字節流通訊信道。post
流socket的正常工做須要一對相互鏈接的socket,所以流socket一般被稱爲面向鏈接的。atom
數據報socket(SOCK_DGRAM)容許數據以被稱爲數據報的消息的形式進行交換。
數據報socket是更通常的無鏈接socket概念,一個數據報socket在使用時無需與另外一個socket鏈接。
在Internet domain中,數據報socket使用UDP,流socke通則使用TCP。
各類socket domain使用了不一樣的地址格式,對於各類socket domain都須要定義一個不一樣的結構類型來存儲socket地址。
然而因爲bind()調用適用於全部socket domain,所以他們必需要可以接受任意類型的地址結構。
爲此,socket API定義了一個通用的地址結構struct sockaddr。
這個類型的惟一用途是將各類domain特定的地址結構轉換成單個類型以供socket各個參數使用。
/* Structure describing a generic socket address. */ struct sockaddr { __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ char sa_data[14]; /* Address data. */ };
這個結構是全部domain特定的地址結構的模板,其中每一個地址結構均以與sockadr結構中的sa_family打頭。
經過sa_family字段值足以肯定存儲在這個結構的剩餘部分中地址大小和格式了。
#include <sys/socket.h> int socket(int domain, int type, int protocol); Returns file descriptor on success, or –1 on error
domain指定了socket通訊domain,經常使用的有AF_UNIX、AF_INET、AF_INET6;type指定了socket類型,經常使用的有SOCK_STREAM、SOCK_DGRAM;protocol通常被指爲0。
socket()在成功時會返回一個引用在後續調用中會用到的新建立的socket文件描述符;錯誤則返回-1。
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); Returns 0 on success, or –1 on error
sockfd是由socket()返回的文件描述符。
addr是指向指定socket綁定到的地址的結構體指針,傳輸參數的類型取決於socket domain。
addrlen參數指定了地址結構的大小。
其中addr傳入到內核,最終被不一樣socket domain的proto_ops->bind()調用的時候,會被強制轉換成不一樣數據結構。
好比AF_UNIX、AF_INET、AF_INET6對應的地址結構體分別爲struct sockaddr_un、struct sockaddr_in、struct sockaddr_in6:
#define UNIX_PATH_MAX 108 struct sockaddr_un { __kernel_sa_family_t sun_family; /* AF_UNIX */---------------------填入AF_UNIX。 char sun_path[UNIX_PATH_MAX]; /* pathname */--------------------本地socket的路徑。 }; #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */---填入AF_INET。 __be16 sin_port; /* Port number */--------------端口號。 struct in_addr sin_addr; /* Internet address */---------IPv4的地址。 /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; #define sin_zero __pad /* for BSD UNIX comp. -FvK */ struct sockaddr_in6 { unsigned short int sin6_family; /* AF_INET6 */---------------填入AF_INET6。 __be16 sin6_port; /* Transport layer port # */ __be32 sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ __u32 sin6_scope_id; /* scope id (new in RFC2553) */ };
#include <sys/socket.h> int listen(int sockfd, int backlog); Returns 0 on success, or –1 on error
listen()將sockfd引用的流socket標記爲被動,這個socket後面會被用來接受來自其餘socket鏈接。
若是客戶端在服務器調用accept()以前調用connect(),這將會產生一個未決的鏈接。
內核必須記錄全部未決的鏈接請求,在後續accept()就可以處理這些請求。
backlog參數容許限制這種未決鏈接數量。在這個限制內的鏈接請求會當即成功。以外的鏈接請求就會阻塞直到一個未決鏈接被接受,並從未決隊列中刪除爲止。
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); Returns file descriptor on success, or –1 on error
accept()用在sockfd引用的監聽流socket上接受一個接入鏈接。
若是在accept()時不存在未決的鏈接,那麼調用就會阻塞直到有鏈接請求到達爲止。
理解accept()的關鍵點是它會建立一個新socket,而且這是這個新socket會與執行connect()的對等socket進行鏈接。
accept()返回結果是已經鏈接的socket文件描述符,其會保持打開狀態,而且能夠被用來接受後續的鏈接。
addr參數指向了一個用來返回socket地址的結構。
addrlen在調用以前必需要將其初始化爲addr指向的緩衝區大小;返回以後被設置成實際被複制進緩衝區中的數據的字節數。
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); Returns 0 on success, or –1 on error
connect()將sockfd引用的socket鏈接到地址經過addr和addrlen指定的監聽socket上。
其中addr和addrlen參數指定方式與bind()對應參數指定方式相同。
#include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags,struct sockaddr *src_addr, socklen_t *addrlen); Returns number of bytes received, 0 on EOF, or –1 on error ssize_t sendto(int sockfd, const void *buffer, size_t length, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); Returns number of bytes sent, or –1 on error
flags是一個位掩碼,控制着特定的IO特性。
src_addr和addrlen用來獲取或指定與之同行的對等socket地址。
對於recvfrom()來講,src_addr和addlen會返回用來發送數據報的遠程socket地址。若是不關心,能夠將src_addr和addrlen都指定爲NULL。
對於sendto()來講,dest_addr和addrlen制定了數據報發送到的socket地址。
若是做爲客戶端,socket()返回的sockfd,在connect以後就就能夠對sockfd進行read()/write()/close()操做。
若是做爲服務端,在accept()以後產生新的sockfd,以後sockfd就保持打開狀態。能夠對其進行read()/write()/close()操做。
上面介紹了一系列socket相關API,可是這些C函數並無對應的系統調用。
下面就看看這些scoket API是如何轉到內核調用的,以socket()爲例。
int __socket (int fd, int type, int domain) { #ifdef __ASSUME_SOCKET_SYSCALL return INLINE_SYSCALL (socket, 3, fd, type, domain); #else return SOCKETCALL (socket, fd, type, domain);----------根據SOCKETCALL()定義可知,socket經過鏈接符號便變成其對應的call,做爲socketcall()的第一個參數。 #endif } libc_hidden_def (__socket)-------------------------------對libc以外屏蔽__socket()函數訪問。 weak_alias (__socket, socket)----------------------------若是沒有定義socket()函數,那麼對socket()的調用將會轉到調用__socket()。
從下面SOCKETCALL()宏定義可知,最終是經過socketcall()系統調用實現的。
具體對應connect()對應的是SOCKOP_socket,即socketcall()系統調用的第一個參數爲1。
在socketcall()系統調用中,根據第一個參數執行對應的操做。
因此下面SOCKOP_對應的socket API都是經過socketcall()實現的,而後在socketcall()裏面進行處理。
#define SOCKOP_invalid -1 #define SOCKOP_socket 1 #define SOCKOP_bind 2 #define SOCKOP_connect 3 #define SOCKOP_listen 4 #define SOCKOP_accept 5 #define SOCKOP_getsockname 6 #define SOCKOP_getpeername 7 #define SOCKOP_socketpair 8 #define SOCKOP_send 9 #define SOCKOP_recv 10 #define SOCKOP_sendto 11 #define SOCKOP_recvfrom 12 #define SOCKOP_shutdown 13 #define SOCKOP_setsockopt 14 #define SOCKOP_getsockopt 15 #define SOCKOP_sendmsg 16 #define SOCKOP_recvmsg 17 #define SOCKOP_accept4 18 #define SOCKOP_recvmmsg 19 #define SOCKOP_sendmmsg 20 #define __SOCKETCALL1(name, a1) \ INLINE_SYSCALL (socketcall, 2, name, \ ((long int [1]) { (long int) (a1) })) ... #define __SOCKETCALL6(name, a1, a2, a3, a4, a5, a6) \ INLINE_SYSCALL (socketcall, 2, name, \ ((long int [6]) { (long int) (a1), (long int) (a2), (long int) (a3), \ (long int) (a4), (long int) (a5), (long int) (a6) })) #define SOCKETCALL(name, args...) \ ({ \ long int sc_ret = __SOCKETCALL (SOCKOP_##name, args); \ sc_ret; \ })
weak_alias()是一個宏,其目的是爲函數添加一個「弱」別名,與「強」符號進行區分。
若是調用函數對應的函數無「強」符號對應的函數,則會調用該別名對應的函數。所謂「強」符號的函數名就是普通聲明定義的函數對應的函數名。
這裏若是沒有定義connect()函數,調用connect()實際就會轉到__socket()。
# define weak_alias(name, aliasname) _weak_alias (name, aliasname) # define _weak_alias(name, aliasname) \ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
libc_hidden_def()的定義在libc-symbols.h中。
# define libc_hidden_def(name) hidden_def (name)
能夠說socketcall()是全部socket調用的入口,socketcall()根據call的值switch-case到對應的函數中。這些函數和單獨系統調用基本一致。
下面就先來分析一下socketcall()函數。
#define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ #define SYS_CONNECT 3 /* sys_connect(2) */ ... #define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) static unsigned char nas[21] = {----------------------------------------nas[]將call id做爲下標,獲得對應系統調用參數的總大小。這裏是上面call id和系統調用的一座橋樑。 AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), AL(4), AL(5), AL(4) }; COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) { u32 a[AUDITSC_ARGS]; unsigned int len; u32 a0, a1; int ret; if (call < SYS_SOCKET || call > SYS_SENDMMSG)-----------------------判斷call範圍,從SYS_SOCKET到SYS_SOCKET。 return -EINVAL; len = nas[call];----------------------------------------------------根據call id獲取args大小。 if (len > sizeof(a)) return -EINVAL; if (copy_from_user(a, args, len)) return -EFAULT; ret = audit_socketcall_compat(len / sizeof(a[0]), a);---------------未定義CONFIG_AUDITSYSCALL直接返回0。 if (ret) return ret; a0 = a[0]; a1 = a[1]; switch (call) { case SYS_SOCKET: ret = sys_socket(a0, a1, a[2]); break; case SYS_BIND: ret = sys_bind(a0, compat_ptr(a1), a[2]); break; case SYS_CONNECT: ret = sys_connect(a0, compat_ptr(a1), a[2]); break; ... default: ret = -EINVAL; break; } return ret; }
結合socketcall()系統調用的實現和API單獨系統調用,能夠看出二者的實現是一致的。
struct socket在內核中表示一個socket,struct sock在網絡層表示一個socket。
struct socket { socket_state state;-----------------------------表示當前socket的狀態。 kmemcheck_bitfield_begin(type); short type;---------------------------------對應enum socket_type,等於sys_socket()傳入的type參數。 kmemcheck_bitfield_end(type); unsigned long flags; struct socket_wq __rcu *wq; struct file *file; struct sock *sk; const struct proto_ops *ops;------------------------是family和type二者綜合的操做函數集。 };
sys_socket()建立一個struct socket,根據family從net_families[]找到對應協議族;而後在從type找到具體使用哪一種類型struct proto_ops。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) { int retval; struct socket *sock; int flags; ... flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK;---------------------------------------------經過SOCK_TYPE_MASK將傳入的type分開,一部分是flags,另外一部分是0~15之間的socket type。 if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; retval = sock_create(family, type, protocol, &sock);----------------根據family/type/protocol建立一個struct socket。 if (retval < 0) goto out; retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));-------爲新建立的struct socket分配一個文件描述符。 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; } int sock_create(int family, int type, int protocol, struct socket **res) { return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0); } int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern) { int err; struct socket *sock; const struct net_proto_family *pf; ... if (family == PF_INET && type == SOCK_PACKET) { pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm); family = PF_PACKET; } err = security_socket_create(family, type, protocol, kern); if (err) return err; sock = sock_alloc();--------------------------------------------------建立一個struct inode和struct socket,並將二者綁定起來。 if (!sock) { net_warn_ratelimited("socket: no more sockets\n"); return -ENFILE; /* Not exactly a match, but its the closest posix thing */ } sock->type = type;----------------------------------------------------設置socket類型。 #ifdef CONFIG_MODULES if (rcu_access_pointer(net_families[family]) == NULL) request_module("net-pf-%d", family); #endif rcu_read_lock(); pf = rcu_dereference(net_families[family]);---------------------------net_families使用下標對應AF_XXX,經過net_families[family]能夠得到對應struct net_proto_family。 err = -EAFNOSUPPORT; if (!pf) goto out_release; if (!try_module_get(pf->owner)) goto out_release; rcu_read_unlock(); err = pf->create(net, sock, protocol, kern);--------------------------調用具體AF_XX對應的create成員,好比AF_UNIX對應unix_create()。 if (err < 0) goto out_module_put; if (!try_module_get(sock->ops->owner)) goto out_module_busy; module_put(pf->owner); err = security_socket_post_create(sock, family, type, protocol, kern); if (err) goto out_sock_release; *res = sock; return 0; ... }
net_families[]保存了全部AF_XXX對應的struct net_proto_family。
這些struct net_proto_family經過sock_register()註冊,經過sock_unregister()去註冊。
static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly; int sock_register(const struct net_proto_family *ops) { int err; ... spin_lock(&net_family_lock); if (rcu_dereference_protected(net_families[ops->family], lockdep_is_held(&net_family_lock))) err = -EEXIST; else { rcu_assign_pointer(net_families[ops->family], ops);---------------主要就是講struct net_proto_family賦給net_families[]。 err = 0; } spin_unlock(&net_family_lock); return err; } void sock_unregister(int family) { BUG_ON(family < 0 || family >= NPROTO); spin_lock(&net_family_lock); RCU_INIT_POINTER(net_families[family], NULL); spin_unlock(&net_family_lock); synchronize_rcu(); }
下面以AF_UNIX爲例,看看不一樣type的處理。
static int __init af_unix_init(void) { ... sock_register(&unix_family_ops); ... } static const struct net_proto_family unix_family_ops = { .family = PF_UNIX, .create = unix_create, .owner = THIS_MODULE, }; static int unix_create(struct net *net, struct socket *sock, int protocol, int kern) { if (protocol && protocol != PF_UNIX) return -EPROTONOSUPPORT; sock->state = SS_UNCONNECTED; switch (sock->type) {-------------------------------------------------能夠看出AF_UNIX僅支持SOCK_STREAM、SOCK_STREAM、SOCK_STREAM、SOCK_STREAM幾種形式type。 case SOCK_STREAM: sock->ops = &unix_stream_ops; break; case SOCK_RAW: sock->type = SOCK_DGRAM; case SOCK_DGRAM: sock->ops = &unix_dgram_ops; break; case SOCK_SEQPACKET: sock->ops = &unix_seqpacket_ops; break; default: return -ESOCKTNOSUPPORT; } return unix_create1(net, sock, kern) ? 0 : -ENOMEM;--------------------建立並初始化struct sock。 } static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, .owner = THIS_MODULE, .release = unix_release, ... .set_peek_off = unix_set_peek_off, }; static const struct proto_ops unix_dgram_ops = { .family = PF_UNIX, .owner = THIS_MODULE, .release = unix_release, ... .set_peek_off = unix_set_peek_off, }; static const struct proto_ops unix_seqpacket_ops = { .family = PF_UNIX, .owner = THIS_MODULE, .release = unix_release, ... .set_peek_off = unix_set_peek_off, };
unix_create1()函數分配而且初始化struct sock,而後做爲struct socket的成員sk。
static struct sock *unix_create1(struct net *net, struct socket *sock, int kern) { struct sock *sk = NULL; struct unix_sock *u; atomic_long_inc(&unix_nr_socks); if (atomic_long_read(&unix_nr_socks) > 2 * get_max_files()) goto out; sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_proto, kern); if (!sk) goto out; sock_init_data(sock, sk); lockdep_set_class(&sk->sk_receive_queue.lock, &af_unix_sk_receive_queue_lock_key); sk->sk_allocation = GFP_KERNEL_ACCOUNT; sk->sk_write_space = unix_write_space; sk->sk_max_ack_backlog = net->unx.sysctl_max_dgram_qlen; sk->sk_destruct = unix_sock_destructor; u = unix_sk(sk); u->path.dentry = NULL; u->path.mnt = NULL; spin_lock_init(&u->lock); atomic_long_set(&u->inflight, 0); INIT_LIST_HEAD(&u->link); mutex_init(&u->iolock); /* single task reading lock */ mutex_init(&u->bindlock); /* single task binding lock */ init_waitqueue_head(&u->peer_wait); init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay); unix_insert_socket(unix_sockets_unbound(sk), sk); out: if (sk == NULL) atomic_long_dec(&unix_nr_socks); else { local_bh_disable(); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); local_bh_enable(); } return sk; }
綜上所述,sys_socket() 主要完善內核中struct socket結構體,尤爲是struct proto_ops結構體。而後返回對應文件描述符給用戶空間。
後續關於socket的API都是經過文件描述符找到內核中對應的struct socket,而後調用struct proto_ops中成員來完成工做。
sys_bind()經過入參fd找到內核中表示socket對應的struct socket。
而後調用struct socket->ops->bind()進行umyaddr和fd綁定。在AF_UNIX和SOCK_STREAM狀況下,調用unix_bind()。
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);-------------------------根據fd找到對應的struct socket結構體。以fs爲索引從當前進程的文件描述符表files_struct中找到對應的file實例,而後從file實例中的private_data成員中獲取socket實例。 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);-------------------------------調用struct socket->ops->bind()完成地址與socket的綁定,進而和fd綁定。 } fput_light(sock->file, fput_needed); } return err; } static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) { struct fd f = fdget(fd); struct socket *sock; *err = -EBADF; if (f.file) { sock = sock_from_file(f.file, err); if (likely(sock)) { *fput_needed = f.flags; return sock; } fdput(f); } return NULL; }
相似sys_bind(),sys_listen()也是一樣的經過fd找到struct socket,而後調用struct socket->ops->listen()完成主要工做。
在AF_UNIX和SOCK_STREAM狀況下,調用unix_listen()。
SYSCALL_DEFINE2(listen, int, fd, int, backlog) { struct socket *sock; int err, fput_needed; int somaxconn; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; if ((unsigned int)backlog > somaxconn) backlog = somaxconn; err = security_socket_listen(sock, backlog); if (!err) err = sock->ops->listen(sock, backlog); fput_light(sock->file, fput_needed); } return err; }
服務器端socket使用bind()來綁定IP和端口,客戶端使用connect()讓系統自動選擇IP和端口。
核心也是調用struct socket->ops->connect()。
SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr, int, addrlen) { struct socket *sock; struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed);----------------------------------經過文件描述符fd找到對應的socket實例。 if (!sock) goto out; err = move_addr_to_kernel(uservaddr, addrlen, &address); if (err < 0) goto out_put; err = security_socket_connect(sock, (struct sockaddr *)&address, addrlen);--------------將socket地址從用戶空間拷貝到內核。 if (err) goto out_put; err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,-----------------調用connect()成員函數,對於AF_UNIX和SOCK_STREAM即調用unix_stream_connect()。 sock->file->f_flags); out_put: fput_light(sock->file, fput_needed); out: return err; }
sys_accept()做爲accept()在內核中的實現,返回一個新的句柄,創建新的操做上下文。
SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr, int __user *, upeer_addrlen, int, flags) { struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; struct sockaddr_storage address; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))--------------------------------------不容許使用這兩個flags。 return -EINVAL; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; sock = sockfd_lookup_light(fd, &err, &fput_needed);--------------------------------根據fd找到struct socket。 if (!sock) goto out; err = -ENFILE; newsock = sock_alloc();------------------------------------------------------------建立一個新的struct socket。 if (!newsock) goto out_put; newsock->type = sock->type;--------------------------------------------------------新的socket類型和socket層操做。 newsock->ops = sock->ops; /* * We don't need try_module_get here, as the listening socket (sock) * has the protocol module (sock->ops->owner) held. */ __module_get(newsock->ops->owner); newfd = get_unused_fd_flags(flags);-------------------------------------------------分配一個空閒的文件句柄。 if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); goto out_put; } newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);---------爲新建立的struct socket分配一個文件描述符。 if (IS_ERR(newfile)) { err = PTR_ERR(newfile); put_unused_fd(newfd); sock_release(newsock); goto out_put; } err = security_socket_accept(sock, newsock); if (err) goto out_fd; err = sock->ops->accept(sock, newsock, sock->file->f_flags);-------------------------對於AF_UNIX和SOCK_STREAM則是調用unix_accept()。 if (err < 0) goto out_fd; if (upeer_sockaddr) {----------------------------------------------------------------若是accept須要返回對端socket地址,調用newsock->ops->getname()獲取struct sockaddr並返還給用戶空間upeer_sockaddr。 if (newsock->ops->getname(newsock, (struct sockaddr *)&address, &len, 2) < 0) { err = -ECONNABORTED; goto out_fd; } err = move_addr_to_user(&address, len, upeer_sockaddr, upeer_addrlen); if (err < 0) goto out_fd; } /* File flags are not inherited via accept() unlike another OSes. */ fd_install(newfd, newfile);---------------------------------------------------------以newfd爲索引,把newfile加入當前進程的文件描述符標files_struct中。 err = newfd; out_put: fput_light(sock->file, fput_needed); out: return err; out_fd: fput(newfile); put_unused_fd(newfd); goto out_put; } SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr, int __user *, upeer_addrlen) { return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0); }
因此sys_accept ()主要做用就是:建立新的socket和inode並初始化完成;調用原socket->ops->accept();保存新建立socket的地址到用戶空間。
sys_getsockname()/sys_getpeername()調用相應proto_ops->getname()。
sys_send()/sys_sendto()/sys_sendmsg()/sys_sendmmsg()最終都是經過___sys_sendmsg()實現。
sys_recv()/sys_recvfrom()/sys_recvmsg()/sys_recvmmsg()最終都是經過___sys_recvmsg實現。
sys_setsockopt()/sys_getsockopt()分別調用proto_ops->setsockopt()和proto_ops->getsockopt()。
sys_socketpair()調用proto_ops->socketpair(),sys_shutdown()調用proto_ops->shutdown()。