自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux 內(nèi)核網(wǎng)絡(luò)之 connect 的實(shí)現(xiàn)

系統(tǒng) Linux
對(duì)于面向連接的協(xié)議,如 TCP, connect() 建立一條與指定的外部地址的連接。若在connect調(diào)用之前沒(méi)有綁定地址和端口,則會(huì)自動(dòng)綁定一個(gè)地址和端口號(hào)套接口。

對(duì)于面向連接的協(xié)議,如 TCP, connect() 建立一條與指定的外部地址的連接。若在connect調(diào)用之前沒(méi)有綁定地址和端口,則會(huì)自動(dòng)綁定一個(gè)地址和端口號(hào)套接口。

asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr,
int addrlen)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
int err, fput_needed;
//根據(jù)文件描述符獲取套接口指針,并且返回是否需要減少對(duì)文件引用計(jì)數(shù)標(biāo)志。
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
//將用戶空間的uservaddr數(shù)據(jù)復(fù)制到內(nèi)核空間的address
err = move_addr_to_kernel(uservaddr, addrlen, address);
if (err < 0)
goto out_put;

err =
security_socket_connect(sock, (struct sockaddr *)address, addrlen);
if (err)
goto out_put;

//通過(guò)套接口系統(tǒng)調(diào)用的跳轉(zhuǎn)表proto_ops,調(diào)用connect操作。TCP 中為 inet_stream_connect(), UDP 為 inet_dgram_connect()
err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
sock->file->f_flags);
out_put:
// 根據(jù)fput_needed標(biāo)志,調(diào)用fput_light減少對(duì)文件引用計(jì)數(shù)操作
fput_light(sock->file, fput_needed);
out:
return err;
}

通過(guò)套接口系統(tǒng)調(diào)用的跳轉(zhuǎn)表 proto_ops ,調(diào)用 inet_stream_connect。

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
int err;
long timeo;

lock_sock(sk);
/* socket的協(xié)議族錯(cuò)誤 */
if (uaddr->sa_family == AF_UNSPEC) {
/* 如果使用的是TCP,則sk_prot為tcp_prot,disconnect為tcp_disconnect() */
err = sk->sk_prot->disconnect(sk, flags);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}

switch (sock->state) {
default:
err = -EINVAL;
goto out;
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;
/* Fall out of switch with err, set for this state */
break;
case SS_UNCONNECTED: /* 此套接口尚未連接對(duì)端的套接口,即連接尚未建立 */
err = -EISCONN;
if (sk->sk_state != TCP_CLOSE)
goto out;

/* 如果使用的是TCP,則sk_prot為tcp_prot,connect為tcp_v4_connect() */
err = sk->sk_prot->connect(sk, uaddr, addr_len); /* 發(fā)送SYN包 */
if (err < 0)
goto out;

/* 發(fā)出SYN包后socket狀態(tài)設(shè)為正在連接 */
sock->state = SS_CONNECTING;

/* Just entered SS_CONNECTING state; the only
* difference is that return value in non-blocking
* case is EINPROGRESS, rather than EALREADY.
*/
err = -EINPROGRESS;
break;
}

/* sock的發(fā)送超時(shí)時(shí)間,非阻塞則為0 */
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);

/* 發(fā)出SYN包后,等待后續(xù)握手的完成 */
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
/* Error code is set above */
/* 如果是非阻塞的,那么就直接返回錯(cuò)誤碼-EINPROGRESS。
* socket為阻塞時(shí),使用inet_wait_for_connect()來(lái)等待協(xié)議棧的處理:
* 1. 使用SO_SNDTIMEO,睡眠時(shí)間超過(guò)timeo就返回0,之后返回錯(cuò)誤碼-EINPROGRESS。
* 2. 收到信號(hào),就返回剩余的等待時(shí)間。之后會(huì)返回錯(cuò)誤碼-ERESTARTSYS或-EINTR。
* 3. 三次握手成功,被sock I/O事件處理函數(shù)喚醒,之后會(huì)返回0。
*/
if (!timeo || !inet_wait_for_connect(sk, timeo))
goto out;

err = sock_intr_errno(timeo);
/* 進(jìn)程收到信號(hào),如果err為-ERESTARTSYS,接下來(lái)庫(kù)函數(shù)會(huì)重新調(diào)用connect() */
if (signal_pending(current))
goto out;
}

/* Connection was closed by RST, timeout, ICMP error
* or another process disconnected us.
*/
if (sk->sk_state == TCP_CLOSE)
goto sock_error;

/* sk->sk_err may be not zero now, if RECVERR was ordered by user
* and error was received after socket entered established state.
* Hence, it is handled normally after connect() return successfully.
*/
/* 更新socket狀態(tài)為連接已建立 */
sock->state = SS_CONNECTED;
/* 清除錯(cuò)誤碼 */
err = 0;
out:
release_sock(sk);
return err;

sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;
/* 如果使用的是TCP,則sk_prot為tcp_prot,disconnect為tcp_disconnect() */
if (sk->sk_prot->disconnect(sk, flags))
sock->state = SS_DISCONNECTING;
goto out;
}

inet_stream_connect() 主要做了以下事情:

對(duì)協(xié)議族進(jìn)行檢查。

此時(shí)套接口狀態(tài)為 SS_UNCONNECTED, 調(diào)用 tcp_v4_connect() 來(lái)發(fā)送SYN包。

等待后續(xù)握手的完成:

1、如果socket是非阻塞的,那么就直接返回錯(cuò)誤碼 -EINPROGRESS。

2、如果socket為阻塞的,就調(diào)用 inet_wait_for_connect(),通過(guò)睡眠來(lái)等待。在以下三種情況下會(huì)被喚醒:

  • 使用 SO_SNDTIMEO 選項(xiàng)時(shí),睡眠時(shí)間超過(guò)設(shè)定值,返回 0。connect()返回錯(cuò)誤碼 -EINPROGRESS。
  • 收到信號(hào),返回剩余的等待時(shí)間。connect()返回錯(cuò)誤碼 -ERESTARTSYS 或 -EINTR。
  • 三次握手成功,sock的狀態(tài)從 TCP_SYN_SENT 或 TCP_SYN_RECV 變?yōu)門CP_ESTABLISHED,sock I/O事件的狀態(tài)變化處理函數(shù)sock_def_wakeup() 就會(huì)喚醒進(jìn)程。connect() 返回0。

客戶端調(diào)用tcp_v4_connect 發(fā)送SYN包時(shí),設(shè)置客戶端狀態(tài)為 TCP_SYN_SENT。

進(jìn)程休眠

static long inet_wait_for_connect(struct sock *sk, long timeo)
{
/* 初始化等待任務(wù) */
DEFINE_WAIT(wait);
/* 把等待任務(wù)加入到socket的等待隊(duì)列頭部,把進(jìn)程的狀態(tài)設(shè)為TASK_INTERRUPTIBLE */
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);

/* Basic assumption: if someone sets sk->sk_err, he _must_
* change state of the socket from TCP_SYN_*.
* Connect() does not allow to get error notifications
* without closing the socket.
*/
/* 完成三次握手后,狀態(tài)就會(huì)變?yōu)門CP_ESTABLISHED,從而退出循環(huán) */
while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
release_sock(sk);
/* 進(jìn)入睡眠,直到超時(shí)或收到信號(hào),或者被I/O事件處理函數(shù)喚醒。
* 1. 如果是收到信號(hào)退出的,timeo為剩余的jiffies。
* 2. 如果使用了SO_SNDTIMEO選項(xiàng),超時(shí)退出后,timeo為0。
* 3. 如果沒(méi)有使用SO_SNDTIMEO選項(xiàng),timeo為無(wú)窮大,即MAX_SCHEDULE_TIMEOUT,
* 那么返回值也是這個(gè),而超時(shí)時(shí)間不定。為了無(wú)限阻塞,需要上面的while循環(huán)。
*/
timeo = schedule_timeout(timeo);
/* 被喚醒后重新上鎖 */
lock_sock(sk);
/* 如果進(jìn)程有待處理的信號(hào),或者睡眠超時(shí)了,退出循環(huán),之后會(huì)返回錯(cuò)誤碼 */
if (signal_pending(current) || !timeo)
break;
/* 繼續(xù)睡眠 */
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
}
/* 等待結(jié)束時(shí),把等待進(jìn)程從等待隊(duì)列中刪除,把當(dāng)前進(jìn)程的狀態(tài)設(shè)為TASK_RUNNING */
finish_wait(sk->sk_sleep, &wait);
return timeo;
}

當(dāng)前進(jìn)程加入到 socket 的等待隊(duì)列 sk_sleep 中,然后進(jìn)入休眠,直到超時(shí)或接收到信號(hào)。

進(jìn)程被喚醒

在三次握手中,當(dāng)客戶端收到 SYN+ACK、發(fā)出ACK后,連接就成功建立了。此時(shí)連接的狀態(tài)從TCP_SYN_SENT或TCP_SYN_RECV變成了 TCP_ESTABLISHED,表示連接建立成功。最終會(huì)調(diào)用 sock_def_wakeup() 來(lái)處理連接狀態(tài)變化事件,喚醒進(jìn)程,connect()成功返回。

調(diào)用過(guò)程如下

tcp_v4_rcv

-> tcp_v4_do_rcv

-> tcp_rcv_state_process

-> tcp_rcv_synsent_state_process

-> sk_wake_async(sk, 0, POLL_OUT);

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len)
{
...

if (!sock_flag(sk, SOCK_DEAD)) {
/* 指向sock_def_wakeup,會(huì)喚醒調(diào)用connect()的進(jìn)程,完成連接的建立 */
sk->sk_state_change(sk);
/* 若采用異步通知,則發(fā)送SIGIO通知進(jìn)程可寫 */
sk_wake_async(sk, 0, POLL_OUT);
}

...
}

當(dāng)鏈路建立成功后異步發(fā)送SIGIO信號(hào),喚醒阻塞的進(jìn)程并通知 socket 可寫,這也就是為什么非阻塞調(diào)用 connect 時(shí)檢查 socket 是否可寫事件的原因。

static void sock_def_wakeup(struct sock *sk)
{
read_lock(&sk->sk_callback_lock);
/* 有進(jìn)程阻塞在此socket上 */
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
/* 喚醒此socket上的所有睡眠進(jìn)程 */
wake_up_interruptible_all(sk->sk_sleep);
read_unlock(&sk->sk_callback_lock);
}

最終調(diào)用 __wake_up_common(),由于nr_exclusive 為 0,因此會(huì)把此socket 上所有的等待進(jìn)程都喚醒。

責(zé)任編輯:華軒 來(lái)源: 今日頭條
相關(guān)推薦

2023-03-01 23:56:11

2023-03-01 23:53:30

Linuxshutdown進(jìn)程

2023-03-06 15:43:56

2023-03-28 15:51:20

2021-09-08 10:21:33

內(nèi)核網(wǎng)絡(luò)包Tcpdump

2021-09-17 11:59:21

tcpdump網(wǎng)絡(luò)包Linux

2009-07-16 09:02:38

LINUX 2.4.x網(wǎng)絡(luò)安全LINUX開發(fā)

2024-08-22 14:47:50

開源Linux網(wǎng)絡(luò)抓包工具

2025-03-07 08:30:00

pwruLinux網(wǎng)絡(luò)包追蹤

2023-05-12 07:27:24

Linux內(nèi)核網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)

2010-07-19 10:05:52

ibmdwLinux

2021-11-14 07:29:55

Linux 內(nèi)核靜態(tài)追蹤Linux 系統(tǒng)

2021-11-15 04:00:07

Linux 內(nèi)核動(dòng)態(tài)

2013-12-18 14:44:10

2021-04-15 05:51:25

Linux

2021-08-23 06:59:38

Linux內(nèi)核代碼

2021-05-31 07:30:47

Connectsocket函數(shù)

2021-09-30 09:43:11

Linux內(nèi)核Zstd補(bǔ)丁

2023-11-24 11:24:16

Linux系統(tǒng)

2017-01-12 19:15:03

Linux內(nèi)核調(diào)試自構(gòu)proc
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)