注:本文分析基於3.10.0-693.el7核心版本,即centos 7.4
int
accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
引數說明:
sockfd:套接字的檔案描述符,socket()系統呼叫返回的檔案描述符fd
可以看出,bind(),connect(),以及accept()的引數都是一致的。
syscall_define3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
int __user *, upeer_addrlen)
最終呼叫的是syscall_define4(accept……
syscall_define4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
int __user *, upeer_addrlen, int, flags)
//建立乙個file結構體,同時將這個file結構體和剛剛建立的socket關聯
//file->private_data指向socket
newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);
if (is_err(newfile))
err = security_socket_accept(sock, newsock);
if (err)
goto out_fd;
//呼叫inet_accept()執行主處理操作
err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err <0)
goto out_fd;
//如果要獲取對端連線資訊,那麼拷貝對應資訊到使用者空間
if (upeer_sockaddr)
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和檔案結構體file關聯到一起
fd_install(newfd, newfile);
err = newfd;//返回新分配的檔案描述符
out_put:
fput_light(sock->file, fput_needed);
out:
return err;
out_fd:
fput(newfile);
put_unused_fd(newfd);
goto out_put;
}
新建socket結構體,然後也分配乙個file結構體,並將這兩個結構體相關聯,這些操作socket()系統呼叫裡也有,包括後面和檔案描述符fd關聯,總的就是將fd和檔案系統以及網路聯絡到一起,形成一切皆檔案的unix理念。有了socket結構體,那肯定需要有對應的sock結構體,這就是sock->ops->accept()所做的了。sock->ops指向inet_stream_ops,
const struct proto_ops inet_stream_ops = ;
所以呼叫的就是inet_accept()。
int inet_accept(struct socket
*sock, struct socket
*newsock, int flags)
然後呼叫對應協議的accept函式,對於tcp協議,
struct proto tcp_prot = ;
因此tcp協議裡,對應的accept函式就是inet_csk_accept()。
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
//從全連線accept佇列裡摘出第乙個request_sock處理
req = reqsk_queue_remove(queue);
//將request_sock和sock關聯,這樣返回後才能繼續處理
//其實也就是替代req接管這個請求了
//req->sk就是在第三次握手後服務端新建立的sock
newsk = req->sk;
//sk_ack_backlog計數減1
sk_acceptq_removed(sk);
//快速開啟選項開啟的情況,
if (sk->sk_protocol == ipproto_tcp &&
queue
->fastopenq !=
null)
spin_unlock_bh(&
queue
->fastopenq->lock);
}out:
release_sock(sk);//listen的sock
//請求已被newsk接管,因此這個請求可以釋放了
if (req)
__reqsk_free(req);
return newsk;
out_err:
newsk =
null;
req =
null;
*err = error;
goto out;
}
獲取可accept的請求,如果此時尚未有客戶端發起連線,那就睡眠直到有請求到來,或者如果使用者設定了超時時間,也會超時返回。另外,也有可能被訊號打斷。
static
int inet_csk_wait_for_connect(struct sock *sk, long timeo)
finish_wait(sk_sleep(sk), &wait);
return err;
}
當有請求到來後,就從全連線佇列裡取出這個請求,返回請求指向的sock結構體,這個結構體也就是在第三次握手中新建的child sock。然後將這個sock結構體和accept最開始建立的socket結構體關聯,通過sock_graft()完成關聯。
static inline
void sock_graft(struct sock *sk, struct socket *
parent)
static inline
void sk_set_socket(struct sock *sk, struct socket *sock)
最後,呼叫fd_install()將fd安裝到對應file結構體中,完成accept()的呼叫。
關於fd、file、socket、sock等結構體的關係可以參考
linux socket系統呼叫(一)。
我們概括下accept的大概流程:
建立乙個socket結構體
獲取乙個未使用的檔案描述符fd
建立乙個file結構體,並和socket關聯
從全連線佇列中獲取客戶端發來的請求
根據請求獲取之前新建的sock結構體返回
將請求中的sock結構體和開始分配的socket結構體關聯
將檔案描述符fd和檔案結構體file關聯,並返回fd供使用者使用
read系統呼叫,mmap系統呼叫
read系統呼叫,mmap系統呼叫 2012 07 23 09 54 28 分類 linux 標籤 linux 檔案系統 虛擬記憶體 儲存系統 字型大小 訂閱 一般情況下,操作檔案既可以使用標準i o,也可直接使用系統呼叫。兩者有何區別呢?在輸入輸出中,直接使用底層的系統呼叫效率是非常低的,為什麼?...
庫呼叫,系統呼叫
通過這個問題,可以判斷候選人是否具有豐富的程式設計經驗以及是否具有找出這類問題答案的敏銳感覺。簡明的回答是 函式庫呼叫是語言或應用程式的一部分,而系統呼叫是作業系統 的一部分。你要確保弄懂 trap 自陷 這個關鍵字的含義。系統呼叫是在作業系統核心發現乙個 trap 或中斷後進行的。函式庫呼叫 vs...
系統呼叫 函式呼叫
linux下對檔案操作有兩種方式 提供了庫函式,如open close read write ioctl 等,需包含標頭檔案unistd.h。以write 函式為例 其函式原型為size t write int fd,const void buf,size t nbytes 其操作物件為檔案控制代碼...