socket系統(tǒng)調(diào)用

Socket套接字

套接字就是支持TCP/IP網(wǎng)絡(luò)通信的基本操作單元,是進(jìn)行TCP/IP通信的接口。套接字Socket看做是不同主機(jī)之間的進(jìn)程進(jìn)行雙向通信的端點约郁,簡單的說就是通信的兩方的一種約定缩挑,用套接字中的相關(guān)函數(shù)來完成通信過程。套接字Socket是連接應(yīng)用程序和網(wǎng)絡(luò)驅(qū)動程序的橋梁鬓梅,套接字Socket在應(yīng)用程序中創(chuàng)建供置,通過綁定與網(wǎng)絡(luò)驅(qū)動建立關(guān)系。此后绽快,應(yīng)用程序送給套接字Socket的數(shù)據(jù)芥丧,由套接字Socket交給網(wǎng)絡(luò)驅(qū)動程序向網(wǎng)絡(luò)上發(fā)送出去。計算機(jī)從網(wǎng)絡(luò)上收到與該套接字Socket綁定IP地址和端口號相關(guān)的數(shù)據(jù)后坊罢,由網(wǎng)絡(luò)驅(qū)動程序交給Socket续担,應(yīng)用程序便可從該Socket中提取接收到的數(shù)據(jù),網(wǎng)絡(luò)應(yīng)用程序就是這樣通過Socket進(jìn)行數(shù)據(jù)的發(fā)送與接收的活孩。
操作系統(tǒng)區(qū)分不同應(yīng)用程序進(jìn)程間的網(wǎng)絡(luò)通信和連接,主要有3個參數(shù):通信的目的IP地址物遇、使用的傳輸層協(xié)議(TCP或UDP)和使用的端口號。

Socket=Ipaddress+TCP/UDP+portSocket=Ipaddress+TCP/UDP+port

系統(tǒng)調(diào)用

對于Linux系統(tǒng)來說憾儒,它分為有用戶態(tài)和內(nèi)核態(tài)询兴,用戶態(tài)只能訪問屬于用戶態(tài)的內(nèi)存空間,內(nèi)核態(tài)的內(nèi)存空間對于用戶態(tài)來說是不可見的航夺。系統(tǒng)調(diào)用則是指通過中斷來向內(nèi)核發(fā)出請求蕉朵,實現(xiàn)內(nèi)核提供的某些服務(wù)。系統(tǒng)調(diào)用的機(jī)制核心就是用了操作系統(tǒng)給用戶留下的一個特別開放的中斷來實現(xiàn)阳掐,在Linux中為int 80h中斷始衅。


Linux系統(tǒng)結(jié)構(gòu)圖解

socket創(chuàng)建

在用戶進(jìn)程中使用下示socket函數(shù)來進(jìn)行socket系統(tǒng)調(diào)用創(chuàng)建屬于tcp的socket

socket(AF_INET, SOCK_STREAM, 0))

AF_INET是指這是ipv4的協(xié)議族,SOCK_STREAM(流式socket缭保,面向連接)是指這是tcp類型的socket汛闸,對應(yīng)的udp的socket類型標(biāo)識為SOCK_DGRAM(數(shù)據(jù)報式socket,無連接)艺骂,該函數(shù)通過系統(tǒng)調(diào)用后如果成功則會返回一個與socket相關(guān)聯(lián)的fd(int類型)诸老,對應(yīng)在系統(tǒng)調(diào)用里面調(diào)用的是sys_socketcall,sys_socketcall幾乎是所有用戶進(jìn)程socket所有操作函數(shù)的入口:

/** sys_socketcall (linux/syscalls.h)*/
asmlinkage long sys_socketcall(int call, unsigned long __user *args);

sys_socketcall實際調(diào)用的是SYSCALL_DEFINE2:

/** SYSCALL_DEFINE2 (net/socket.c)*/
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
    unsigned long a[AUDITSC_ARGS];
    unsigned long a0, a1;
    int err;
    unsigned int len;

    if (call < 1 || call > SYS_SENDMMSG)
        return -EINVAL;
    call = array_index_nospec(call, SYS_SENDMMSG + 1);

    len = nargs[call];
    if (len > sizeof(a))
        return -EINVAL;

    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, len))
        return -EFAULT;

    err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
    if (err)
        return err;

    a0 = a[0];
    a1 = a[1];

    switch (call) {  //通過判斷call指令的類型來進(jìn)入不同的函數(shù)進(jìn)行處理
    case SYS_SOCKET:  //與用戶態(tài)中 socket(int domain, int type, int protocol) 對應(yīng)钳恕,創(chuàng)建socket
        err = sys_socket(a0, a1, a[2]);
        break;
    case SYS_BIND:  //socket綁定
        err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_CONNECT:  //socket建立連接
        err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_LISTEN:
        err = sys_listen(a0, a1);  //socket監(jiān)聽
        break;
    case SYS_ACCEPT: //socket接收系統(tǒng)
        err = sys_accept4(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2], 0);
        break;
    case SYS_GETSOCKNAME:
        err =
            sys_getsockname(a0, (struct sockaddr __user *)a1,
                    (int __user *)a[2]);
        break;
    case SYS_GETPEERNAME:
        err =
            sys_getpeername(a0, (struct sockaddr __user *)a1,
                    (int __user *)a[2]);
        break;
    case SYS_SOCKETPAIR:
        err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
        break;
    case SYS_SEND:
        err = sys_send(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_SENDTO:
        err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
                 (struct sockaddr __user *)a[4], a[5]);
        break;
    case SYS_RECV:
        err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_RECVFROM:
        err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                   (struct sockaddr __user *)a[4],
                   (int __user *)a[5]);
        break;
    case SYS_SHUTDOWN:
        err = sys_shutdown(a0, a1);
        break;
    case SYS_SETSOCKOPT:
        err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
        break;
    case SYS_GETSOCKOPT:
        err =
            sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
                   (int __user *)a[4]);
        break;
    case SYS_SENDMSG:
        err = sys_sendmsg(a0, (struct user_msghdr __user *)a1, a[2]);
        break;
    case SYS_SENDMMSG:
        err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
        break;
    case SYS_RECVMSG:
        err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]);
        break;
    case SYS_RECVMMSG:
        err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
                   (struct timespec __user *)a[4]);
        break;
    case SYS_ACCEPT4:
        err = sys_accept4(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2], a[3]);
        break;
    default:
        err = -EINVAL;
        break;
    }
    return err;
}

在SYSCALL_DEFINE2函數(shù)中别伏,通過call指令判斷進(jìn)入到不同函數(shù)中處理,以創(chuàng)建socket為例忧额,其實際處理時在sys_socket中厘肮,它也是一個系統(tǒng)調(diào)用,對應(yīng)的是SYSCALL_DEFINE3:

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

    TRACE_OUT(tr_sock, ("family=%d type=%d protocol=%d\n", family, type, protocol));

    /* 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;
 //創(chuàng)建socket睦番,關(guān)鍵函數(shù)
    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中主要判斷了設(shè)置的socket類型type类茂,通過調(diào)用sock_create創(chuàng)建socket結(jié)構(gòu)耍属,使用sock_map_fd將socket結(jié)構(gòu)映射為文件描述符并返回,socket結(jié)構(gòu)體如下所示:

struct socket {
    socket_state        state;       // 連接狀態(tài):SS_CONNECTING, SS_CONNECTED 等

    kmemcheck_bitfield_begin(type);
    short           type;        // 類型:SOCK_STREAM, SOCK_DGRAM 等
    kmemcheck_bitfield_end(type);

    unsigned long       flags;       // 標(biāo)志位:SOCK_ASYNC_NOSPACE(發(fā)送隊列是否已滿)等

    struct socket_wq __rcu  *wq; // 等待隊列

    struct file     *file;     // 該socket結(jié)構(gòu)體對應(yīng)VFS中的file指針
    struct sock     *sk;   // socket網(wǎng)絡(luò)層表示巩检,真正處理網(wǎng)絡(luò)協(xié)議的地方
    const struct proto_ops  *ops;  // socket操作函數(shù)集:bind, connect, accept 等
};

socket結(jié)構(gòu)體中定義了socket的基本狀態(tài)厚骗,類型,標(biāo)志兢哭,等待隊列领舰,文件指針,操作函數(shù)集等厦瓢,利用 sock 結(jié)構(gòu)提揍,將 socket 操作與真正處理網(wǎng)絡(luò)協(xié)議相關(guān)的事務(wù)分離。
sock_create函數(shù)調(diào)用__sock_create

/** sock_create (net/socket.c)*/
int sock_create(int family, int type, int protocol, struct socket **res)
{
    return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
EXPORT_SYMBOL(sock_create)

__sock_create函數(shù)如下

/** __sock_create (net/socket.c)*/
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;

    /*
     *      Check protocol is in range
     */
    if (family < 0 || family >= NPROTO)
        return -EAFNOSUPPORT;
    if (type < 0 || type >= SOCK_MAX)
        return -EINVAL;

    /* Compatibility.

       This uglymoron is moved from INET layer to here to avoid
       deadlock in module load.
     */
    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;

    /*
     *  Allocate the socket and allow the family to set things up. if
     *  the protocol is 0, the family is instructed to select an appropriate
     *  default.
     */
    sock = sock_alloc();
    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;

#ifdef CONFIG_MODULES
    /* Attempt to load a protocol module if the find failed.
     *
     * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
     * requested real, full-featured networking support upon configuration.
     * Otherwise module support will break!
     */
    if (rcu_access_pointer(net_families[family]) == NULL)
        request_module("net-pf-%d", family);
#endif

    rcu_read_lock();
    pf = rcu_dereference(net_families[family]);
    err = -EAFNOSUPPORT;
    if (!pf) {
        TRACE_OUT(tr_sock, ("net_proto_family is NULL"));
        goto out_release;
    }
        

    /*
     * We will call the ->create function, that possibly is in a loadable
     * module, so we have to bump that loadable module refcnt first.
     */
    if (!try_module_get(pf->owner))
        goto out_release;

    /* Now protected by module ref count */
    rcu_read_unlock();
/*核心處理煮仇,調(diào)用協(xié)議簇中的create函數(shù)來初始化socket*/
    err = pf->create(net, sock, protocol, kern);
    if (err < 0)
        goto out_module_put;

    /*
     * Now to bump the refcnt of the [loadable] module that owns this
     * socket at sock_release time we decrement its refcnt.
     */
    if (!try_module_get(sock->ops->owner))
        goto out_module_busy;

    /*
     * Now that we're done with the ->create function, the [loadable]
     * module can have its refcnt decremented
     */
    module_put(pf->owner);
    err = security_socket_post_create(sock, family, type, protocol, kern);
    if (err)
        goto out_sock_release;
    *res = sock;

    return 0;

out_module_busy:
    err = -EAFNOSUPPORT;
out_module_put:
    sock->ops = NULL;
    module_put(pf->owner);
out_sock_release:
    sock_release(sock);
    return err;

out_release:
    rcu_read_unlock();
    goto out_sock_release;
}
EXPORT_SYMBOL(__sock_create);

其中核心的處理為pf->create(net, sock, protocol, kern)劳跃,調(diào)用協(xié)議簇中的create函數(shù),ipv4的定義在af_inet.c文件中

/**  (net/ipv4/af_inet.c)*/
static const struct net_proto_family inet_family_ops = {
    .family = PF_INET,
    .create = inet_create,
    .owner  = THIS_MODULE,
};

可以看到.create實際上調(diào)用的是inet_create函數(shù)浙垫,在inet_create中進(jìn)行socket初始化的具體實現(xiàn)刨仑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市夹姥,隨后出現(xiàn)的幾起案子杉武,更是在濱河造成了極大的恐慌,老刑警劉巖辙售,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轻抱,死亡現(xiàn)場離奇詭異,居然都是意外死亡旦部,警方通過查閱死者的電腦和手機(jī)祈搜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來士八,“玉大人容燕,你說我怎么就攤上這事』槎龋” “怎么了蘸秘?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蝗茁。 經(jīng)常有香客問我醋虏,道長,這世上最難降的妖魔是什么哮翘? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任灰粮,我火速辦了婚禮,結(jié)果婚禮上忍坷,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好佩研,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布柑肴。 她就那樣靜靜地躺著,像睡著了一般旬薯。 火紅的嫁衣襯著肌膚如雪晰骑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天绊序,我揣著相機(jī)與錄音硕舆,去河邊找鬼。 笑死骤公,一個胖子當(dāng)著我的面吹牛抚官,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播阶捆,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凌节,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洒试?” 一聲冷哼從身側(cè)響起倍奢,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垒棋,沒想到半個月后卒煞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡叼架,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年畔裕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碉碉。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡柴钻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垢粮,到底是詐尸還是另有隱情贴届,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布蜡吧,位于F島的核電站毫蚓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏昔善。R本人自食惡果不足惜元潘,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望君仆。 院中可真熱鬧翩概,春花似錦牲距、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至评姨,卻和暖如春难述,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吐句。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工胁后, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗦枢。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓攀芯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親净宵。 傳聞我的和親對象是個殘疾皇子敲才,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容