1. 程式人生 > >Linux核心建立socket的過程

Linux核心建立socket的過程

當我們呼叫socket()這個系統呼叫的時候,Linux核心究竟做了哪些事情呢?
Ok,就讓我們來看看這socket背後的故事吧。

1.使用者程式首先呼叫socket()系統呼叫,其原型為:
int socket(int domain, int type, int protocol)

2.我們呼叫的socket()系統呼叫其實是glic這個庫提供的,glic內部會執行實際的系統呼叫。
我們可以看一下glic的內部實現。
檔案sysdeps/unix/sysv/linux/i386/socket.S

點選(此處)摺疊或開啟

  1. .globl __socket
  2. ENTRY (__socket)
  3. #if defined NEED_CANCELLATION &
    & defined CENABLE
  4.     SINGLE_THREAD_P
  5.     jne 1f
  6. #endif
  7.     /* 儲存ebx暫存器的值 */
  8.     movl %ebx, %edx
  9.     cfi_register (3, 2)
  10.     /* 把系統呼叫的呼叫號放到eax暫存器裡 */
  11.     movl $SYS_ify(socketcall), %eax
  12.     /* 傳給syscall的第一個引數是子code,用來識別是哪一個socket系統呼叫 */
  13.     movl $P(SOCKOP_,socket), %ebx
  14.     /* 第二個引數是使用者態傳遞的引數陣列的地址 */
  15.     lea 4(
    %esp), %ecx
  16.     /* 執行系統呼叫 */
  17.     ENTER_KERNEL
  18.     /* 恢復ebx暫存器 */
  19.     movl %edx, %ebx
  20.     cfi_restore (3)
  21.     /* 如果發生了錯誤,%eax小於0 */
  22.     cmpl $-125, %eax
  23.     jae SYSCALL_ERROR_LABEL
  24.     /* 成功的話返回系統呼叫的結果 */
  25. L(pseudo_end):
  26.     ret
為了節約系統呼叫號,在2.4以後的核心中,已經把所有的socket相關的系統呼叫,比如connect(),send()等等,都統一封裝在一個核心函式sys_socketcall()裡,執行系統呼叫的時候,需要傳遞一個
SOCKOP_XXX(sysdeps/unix/sysv/linux/socketcall.h)來識別是哪個socket函式


3.當核心收到系統呼叫的命令以後,會從eax暫存器中取出系統呼叫號,然後在sys_call_table
這個表中查詢對應的處理系統呼叫的函式
,具體過程不再贅述。
核心會呼叫sys_socketcall()來處理

檔案net/socket.c

點選(此處)摺疊或開啟

  1. asmlinkage long sys_socketcall(int call, unsigned long __user *args)
  2. {
  3.     unsigned long a[6];
  4.     unsigned long a0, a1;
  5.     int err;
  6.     if (call < 1 || call > SYS_ACCEPT4)
  7.         return -EINVAL;
  8.     /* 呼叫copy_from_user函式,從使用者空間的記憶體地址拷貝引數到核心空間 */
  9.     if (copy_from_user(a, args, nargs[call]))
  10.         return -EFAULT;
  11.     err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
  12.     if (err)
  13.         return err;
  14.     a0 = a[0];
  15.     a1 = a[1];
  16.     switch (call) {
  17.     case SYS_SOCKET:
  18.         err = sys_socket(a0, a1, a[2]);
  19.         break;
  20.     case SYS_BIND:
  21.         err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
  22.         break;
  23.     case SYS_CONNECT:
  24.         err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
  25.         break;
  26.     case SYS_LISTEN:
  27.         err = sys_listen(a0, a1);
  28.         break;
  29.     case SYS_ACCEPT:
  30.         err = sys_accept4(a0, (struct sockaddr __user *)a1,
  31.                  (int __user *)a[2], 0);
  32.         break;
  33.     case SYS_GETSOCKNAME:
  34.         err =
  35.          sys_getsockname(a0, (struct sockaddr __user *)a1,
  36.                  (int __user *)a[2]);
  37.         break;
  38.     case SYS_GETPEERNAME:
  39.         err =
  40.          sys_getpeername(a0, (struct sockaddr __user *)a1,
  41.                  (int __user *)a[2]);
  42.         break;
  43.     case SYS_SOCKETPAIR:
  44.         err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
  45.         break;
  46.     case SYS_SEND:
  47.         err = sys_send(a0, (void __user *)a1, a[2], a[3]);
  48.         break;
  49.     case SYS_SENDTO:
  50.         err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
  51.                  (struct sockaddr __user *)a[4], a[5]);
  52.         break;
  53.     case SYS_RECV:
  54.         err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
  55.         break;
  56.     case SYS_RECVFROM:
  57.         err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
  58.                  (struct sockaddr __user *)a[4],
  59.                  (int __user *)a[5]);
  60.         break;
  61.     case SYS_SHUTDOWN:
  62.         err = sys_shutdown(a0, a1);
  63.         break;
  64.     case SYS_SETSOCKOPT:
  65.         err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
  66.         break;
  67.     case SYS_GETSOCKOPT:
  68.         err =
  69.          sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
  70.                  (int __user *)a[4]);
  71.         break;
  72.     case SYS_SENDMSG:
  73.         err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
  74.         break;
  75.     case SYS_RECVMSG:
  76.         err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
  77.         break;
  78.     case SYS_ACCEPT4:
  79.         err = sys_accept4(a0, (struct sockaddr __user *)a1,
  80.                  (int __user *)a[2], a[3]);
  81.         break;
  82.     default:
  83.         err = -EINVAL;
  84.         break;
  85.     }
  86.     return err;
  87. }
可以看到,sys_socketcall會通過call這個引數來判斷執行哪個socket動作。
因為我們呼叫的是socket()系統呼叫,所以會執行
sys_socket()這個函式。

4.核心呼叫sys_socket()來處理建立socket的系統呼叫。
檔案net/socket.c

點選(此處)摺疊或開啟

  1. asmlinkage long sys_socket(int family, int type, int protocol)
  2. {
  3.     int retval;
  4.     struct socket *sock;
  5.     int flags;
  6.     /* Check the SOCK_* constants for consistency. */
  7.     BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
  8.     BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
  9.     BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
  10.     BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
  11.     flags = type & ~SOCK_TYPE_MASK;
  12.     if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
  13.         return -EINVAL;
  14.     type &= SOCK_TYPE_MASK;
  15.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
  16.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
  17.     /* 實際建立socket */
  18.     retval = sock_create(family, type, protocol, &sock);
  19.     if (retval < 0)
  20.         goto out;
  21.     /* 把建立的socket對映到檔案描述符 */
  22.     retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
  23.     if (retval < 0)
  24.         goto out_release;
  25. out:
  26.     /* It may be already another descriptor 8) Not kernel problem. */
  27.     return retval;
  28. out_release:
  29.     sock_release(sock);
  30.     return retval;
  31. }
可以看到,實際建立socket的函式是sock_create(),接下來就讓我們好好研究一下這個函式。

相關推薦

Linux核心建立socket過程

當我們呼叫socket()這個系統呼叫的時候,Linux核心究竟做了哪些事情呢? Ok,就讓我們來看看這socket背後的故事吧。 1.使用者程式首先呼叫socket()系統呼叫,其原型為:int socket(int domain, int type, int proto

Linux作業系統分析》之分析Linux核心建立一個新程序的過程

本篇文章通過fork函式的呼叫,來說明在Linux系統中建立一個新程序需要經歷的過程。 相關知識: 首先關於這篇文章會介紹一些用到的知識。 一、程序、輕量級程序、執行緒 程序是程式執行的一個例項。程序的目的就是擔當分配系統資源的實體。 兩個輕量級程序基本可以共享一些資源,比

arm的2級頁表在Linux核心建立過程解析

系統DDR的基地址為0x0,記憶體為1GB,所以TTB的基地址為0x4000。下面要建立虛擬地址0xfe700000到實體地址0xffff0000之間的對映,對映大小為64KB,即16頁。由於實體地址不是1MB位元組對齊,所以必須建立兩級對映。   使用者空間/核心空間

Linux核心建立一個程序的過程分析

不管在什麼系統中,所有的任務都是以程序為載體的,所以理解程序的建立對於理解作業系統的原理是非常重要的,本文是我在學習linux核心中所做的筆記,如有錯誤還請大家批評指正。注:我所閱讀的核心版本是0.11。 一、關於PCB 對於一個程序來說,PCB就好像是他的

linux核心建立flash上的各分割槽原始碼進行分析

1.注意:核心原始碼版本為4.9 2.首先注意關鍵字串"partitions found on MTD device 這句話在drivers/mtd/mtdpart.c的parse_mtd_partitions()中出現 3.mtd_device_parse_register()呼叫了parse_mtd

linux核心——建立自己的/proc檔案——processinfo

利用proc_mkdir()建立一個mydir,再利用create_proc_read_entry()函式建立一個processinfo檔案。我們從模組裡面獲取的資訊都將寫入到processinfo檔案中。 #include<linux/module.h> #i

linux核心的生成過程, vmlinux除錯分析

1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB;  arm-linux-gnu-ld -EL  -p --no-undefined -X -o vmlinux

基於PPC64架構 Linux 核心訊號處理過程棧處理分析

handle_rt_signal64主要負責訊號處理棧處理分析,如下圖所示 int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user

Linux SSH建立連線過程分析

 SSH建立連線的過程主要分為下面幾個階段: SSH協議版本協商階段。SSH目前包括SSH1和SSH2兩個大版本。金鑰和演算法協商階段,SSH支援多種加密演算法,雙方根據自己和對端支援的演算

Linux核心初始化過程的原始碼分析疑點記錄+好書推薦(附下載)

    這個對基於PowerPC的Linux核心原始碼剖析的文章已經寫了三篇了(見前三篇博文),由於可以找到的關於PowerPC E300處理器的Linux文章基本沒有,這些都是一點點摸索的,可能存在不少的錯誤,特別是第3篇,自我感覺很差,開始計劃寫這個系列的時候,自以為已

Linux核心執行緒(kthread)建立過程

我們在核心中建立並執行核心執行緒,直接呼叫kthread_run巨集就可以實現。其原型為: /** * kthread_run - create and wake a thread. * @threadfn: the function to run unti

linux核心中socket建立過程原始碼分析(總結性質)

http://www.jianshu.com/p/5d82a685b5b6 在漫長地分析完socket的建立原始碼後,發現一片漿糊,所以特此總結,我的部落格中同時有另外一篇詳細的原始碼分析,核心版本為3.9,建議在閱讀本文後若還有興趣再去看另外一篇博文。絕對不要單獨看另外

Linux核心Socket CAN中文文件

轉載地址:https://blog.csdn.net/zhangxiaopeng0829/article/details/7646639 自己在年假中空閒之餘翻譯的核心中Socket CAN的文件,原文地址在: http://lxr.linux.no/linux+v2.6.34/Docume

建立、編輯linux核心工程

用過source insight、vim(未使用外掛)建立編寫linux核心工程,一直想找個更合適的工具。畢竟,source insight是商業軟體,而vim如果不使用外掛還是不怎麼方便。瞭解到vim和emacs都可以用外掛構建出原始碼工程,嘗試過emacs沒能成功,也許是我用vim已經習慣了,最

linux 核心學習過程(1)-硬體特性和核心設計之間的聯絡

該貼用來作為自己學習linux核心的記錄和筆記,很多東西都是自己理解後整理的內容,各位看官若覺得有問題的地方,可以留言或自行查閱。 linux核心在設計的過程中很多都是依據硬體晶片特性來設計,晶片在設計的過程中很多時候需要保持相容性,這樣就留下了很多令人費解的概念,比如分段和分頁機制,

Linux核心啟動過程分析(十)-----RTC驅動分析

參考https://blog.csdn.net/xuao20060793/article/details/46433263這篇博文 RTC驅動分析: Class.c (drivers\rtc):subsys_initcall(rtc_init); static int __init

linux核心編譯過程的最終總結版

一、實驗目的 學習重新編譯Linux核心,理解、掌握Linux核心和發行版本的區別。 二、實驗內容 在Linux作業系統環境下重新編譯核心。實驗主要內容: A. 查詢並且下載一份核心原始碼,本實驗使用最新的Linux核心2.6.36。 B. 配置核心。 C. 

走進Linux核心網路 套接字的祕密—socket與sock

雙十一 今天是雙十一,記得還在念書的時候,每次都會參加京東圖書滿200-100的活動,然後腦子一熱屯一堆書。印象中人文類的書基本都會看,而電子資訊類的可能就只看了一半,甚至買過哪些都忘了。究其原因,主要是人文類的通常都是自己想看的,而技術類的就跟風看哪個銷量大買

Linux核心分析_UDP協議中資料包的收發處理過程

1.前言   實驗基於Linux kernel 3.18.6,實驗內容包括:   (1)編寫UDP客戶端和服務端   (2)將UDP客戶端和服務端整合到MenuOS中   (3)UDP傳送資料的過程   (4)UDP接收資料的過程      本文中完整原始碼:https://github.com

開啟linux核心自帶的模擬i2c-gpio模組過程

首先我們要知道核心的makefile是根據配置檔案,也就是kconfig來決定是否編譯一個檔案的。 如果沒有配置核心編譯它,就不會生成.o檔案。 自然就不會開啟這個模組了。 如下是Kconfig 檔