從0實(shí)現(xiàn)基于Linux socket聊天室-多線程服務(wù)器一個(gè)很隱晦的錯(cuò)誤-2

<p>根據(jù) 《<a>0 基于socket和pthread實(shí)現(xiàn)多線程服務(wù)器模型</a>》所述,server創(chuàng)建子線程的時(shí)候用的是以下代碼:</p><pre> pconnsocke?=?(int?*)?malloc(sizeof(int));
*pconnsocke?=?new_fd;

    ret&nbsp;=&nbsp;pthread_create(&amp;tid,&nbsp;NULL,&nbsp;rec_func,&nbsp;(void&nbsp;*)&nbsp;pconnsocke);
    if&nbsp;(ret&nbsp;&lt;&nbsp;0)&nbsp;
    {
        perror(&quot;pthread_create&nbsp;err&quot;);
        return&nbsp;-1;
    }   </pre><p><strong>為什么必須要malloc一塊內(nèi)存專門(mén)存放這個(gè)新的套接字呢产舞?</strong></p><p>要講清楚這個(gè)問(wèn)題的原因需要一些背景知識(shí):</p><ol><li><p>Linux創(chuàng)建一個(gè)新進(jìn)程時(shí)店雅,新進(jìn)程會(huì)創(chuàng)建一個(gè)主線程睹耐;</p></li><li><p>每個(gè)用戶進(jìn)程有自己的地址空間压储,系統(tǒng)為每個(gè)用戶進(jìn)程創(chuàng)建一個(gè)task_struct來(lái)描述該進(jìn)程逝她,

實(shí)際上task_struct 和地址空間映射表一起用來(lái)技竟,表示一個(gè)進(jìn)程局劲;</p></li><li><p>Linux里同樣用task_struct來(lái)描述一個(gè)線程勺拣,線程和進(jìn)程都參與統(tǒng)一的調(diào)度;</p></li><li><p>進(jìn)程內(nèi)的不同線程執(zhí)行是同一程序的不同部分鱼填,各個(gè)線程并行執(zhí)行药有,受操作系統(tǒng)異步調(diào)度;</p></li><li><p>由于進(jìn)程的地址空間是私有的苹丸,因此在進(jìn)程間上下文切換時(shí)愤惰,系統(tǒng)開(kāi)銷比較大;</p></li><li><p>在同一個(gè)進(jìn)程中創(chuàng)建的線程共享該進(jìn)程的地址空間赘理。</p></li></ol><p>明白這些基礎(chǔ)知識(shí)后宦言,下面我來(lái)看下,當(dāng)進(jìn)程創(chuàng)建一個(gè)子線程的時(shí)候商模,傳遞的參數(shù)情況:</p><h1>直接傳遞棧中內(nèi)存地址</h1><p class="image-package">我們首先分析下如果創(chuàng)建子線程傳遞的是局部變量new_fd的地址這種情況奠旺。<img class="uploaded-img" src="https://upload-images.jianshu.io/upload_images/23850874-6d2f53a9690756a4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="auto" height="auto"/></p><p>由上圖所示:</p><ol><li><p>創(chuàng)建一個(gè)線程,如果我們按照?qǐng)D中傳遞參數(shù)方法施流,那么new_fd是在棧中的响疚,創(chuàng)建子線程的時(shí)候我們把new_fd地址傳遞給了thread1,線程回調(diào)參數(shù)arg的地址是new_fd地址瞪醋。</p></li><li><p>因?yàn)橹骱瘮?shù)會(huì)一直循環(huán)不退出忿晕,所以new_fd一直存在棧中。用這種方法的確可以把new_fd的值3傳遞到子線程的局部變量fd银受,這樣子線程就可以使用這個(gè)fd與客戶端通信践盼。</p></li><li><p>但是因?yàn)槲覀冊(cè)O(shè)計(jì)的是并發(fā)服務(wù)器模型,我們沒(méi)有辦法預(yù)測(cè)客戶端什么時(shí)候會(huì)連接我們的服務(wù)器蚓土,假設(shè)遇到一個(gè)極端情況宏侍,在同一時(shí)刻,多個(gè)客戶端同時(shí)連接服務(wù)器蜀漆,那么主線程是要同時(shí)創(chuàng)建多個(gè)子線程的。</p></li></ol><p class="image-package"><strong>多個(gè)客戶端同時(shí)連接服務(wù)器</strong><img class="uploaded-img" src="https://upload-images.jianshu.io/upload_images/23850874-9d13da655775fb2b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="auto" height="auto"/></p><p>如上圖所示咱旱,所有新建的的thread回調(diào)函數(shù)的參數(shù)arg存放的都是new_fd的地址确丢。如果客戶端連接的時(shí)候時(shí)間間隔比較大绷耍,是沒(méi)有問(wèn)題的,但是在一些極端的情況下還是有可能出現(xiàn)由于高并發(fā)引起的錯(cuò)誤鲜侥。</p><p><strong>我們來(lái)捋一下極端的調(diào)用時(shí)序:</strong></p><p/><p class="image-package"><img class="uploaded-img" src="https://upload-images.jianshu.io/upload_images/23850874-9493e6f1b39f668a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="auto" height="auto"/></p><p>如上圖所示:</p><ol><li><p>T1時(shí)刻褂始,當(dāng)客戶端1連接服務(wù)器的時(shí)候,服務(wù)器的accept函數(shù)會(huì)創(chuàng)建新的套接字4描函;</p></li><li><p>T2時(shí)刻崎苗,創(chuàng)建了子線程thread1,同時(shí)子線程回調(diào)函數(shù)參數(shù)arg指向了棧中new_fd對(duì)應(yīng)的內(nèi)存舀寓。</p></li><li><p>假設(shè)胆数,正在此時(shí),又有一個(gè)客戶端要連接服務(wù)器互墓,而且thread1頁(yè)已經(jīng)用盡了時(shí)間片必尼,那么主線程server會(huì)被調(diào)度到。</p></li></ol><p/><p class="image-package"><img class="uploaded-img" src="https://upload-images.jianshu.io/upload_images/23850874-c1a343d46f951776.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="auto" height="auto"/></p><p>如上圖所示:</p><ol><li><p>T3時(shí)刻篡撵,主線程server接受了客戶端的連接判莉,accept函數(shù)會(huì)創(chuàng)建新的套接字5,同時(shí)創(chuàng)建子線程thread2育谬,此時(shí)OS調(diào)度的thread2券盅;</p></li><li><p class="image-package">T4時(shí)刻,thread2通過(guò)arg得到new_fd了的值5,并存入fd膛檀;</p></li><li><p>T5時(shí)刻锰镀,時(shí)間片到了,調(diào)度thread1宿刮,thread1通過(guò)arg去讀取new_fd互站,此時(shí)棧中new_fd的值已經(jīng)修5覆蓋了;</p></li><li><p>所以出現(xiàn)了2個(gè)線程同時(shí)使用同一個(gè)fd的情況發(fā)生僵缺。</p></li></ol><p>這種情況的發(fā)生胡桃,雖然概率很低,但是并不代表不發(fā)生磕潮,該bug就是一口君在解決實(shí)際項(xiàng)目中遇到過(guò)的翠胰。</p><h1>傳遞堆內(nèi)存地址</h1><p>如果采用傳遞堆的地址的方式,我們看下圖:</p><p class="image-package"><img class="uploaded-img" src="https://upload-images.jianshu.io/upload_images/23850874-efa0a0d3c100001b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="auto" height="auto"/></p><ol><li><p>T1時(shí)刻自脯,當(dāng)客戶端1連接服務(wù)器的時(shí)候之景,服務(wù)器的accept函數(shù)會(huì)創(chuàng)建新的套接字4,在堆中申請(qǐng)一塊內(nèi)存膏潮,用指針pconnsocke指向該內(nèi)存锻狗,同時(shí)將4保存到堆中;</p></li><li><p>T2時(shí)刻,創(chuàng)建了子線程thread1轻纪,同時(shí)子線程回調(diào)函數(shù)參數(shù)arg指向了堆中pconnsocke指向的內(nèi)存油额。</p></li><li><p>假設(shè),正在此時(shí)刻帚,又有一個(gè)客戶端要連接服務(wù)器潦嘶,而且thread1頁(yè)已經(jīng)用盡了時(shí)間片,那么主線程server會(huì)被調(diào)度到崇众。</p></li><li><p>T3時(shí)刻掂僵,主線程server接受了客戶端的連接,accept函數(shù)會(huì)創(chuàng)建新的套接字5顷歌,在堆中申請(qǐng)一塊內(nèi)存锰蓬,用指針pconnsocke指向該內(nèi)存,同時(shí)將5保存到堆中衙吩,然后創(chuàng)建子線程thread2互妓;</p></li><li><p>T4時(shí)刻,thread2通過(guò)arg指向了堆中pconnsocke指向的內(nèi)存坤塞,此處值為5,并存入fd冯勉;</p></li><li><p>T5時(shí)刻,時(shí)間片到了摹芙,調(diào)度thread1灼狰,thread1通過(guò)arg去讀取fd,此時(shí)堆中數(shù)據(jù)位5浮禾;</p></li><li><p>就不會(huì)出現(xiàn)了2個(gè)線程同時(shí)使用同一個(gè)fd的情況發(fā)生交胚。</p></li></ol><p>這個(gè)知識(shí)點(diǎn)有點(diǎn)隱蔽,希望讀者在使用的時(shí)候多加小心盈电。
下一章蝴簇,我們要講解如何利用我們現(xiàn)有的代碼實(shí)現(xiàn)登錄注冊(cè)的功能。</p><p>獲取更多關(guān)于Linux的資料匆帚,請(qǐng)關(guān)注公眾號(hào)「一口Linux」</p><p>
</p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末熬词,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吸重,更是在濱河造成了極大的恐慌互拾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚎幸,死亡現(xiàn)場(chǎng)離奇詭異颜矿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嫉晶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)骑疆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)田篇,“玉大人,你說(shuō)我怎么就攤上這事封断∷钩剑” “怎么了舶担?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵坡疼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我衣陶,道長(zhǎng)柄瑰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任剪况,我火速辦了婚禮教沾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘译断。我一直安慰自己授翻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布孙咪。 她就那樣靜靜地躺著堪唐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翎蹈。 梳的紋絲不亂的頭發(fā)上淮菠,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音荤堪,去河邊找鬼合陵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛澄阳,可吹牛的內(nèi)容都是我干的拥知。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼碎赢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼低剔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起揩抡,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤户侥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后峦嗤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蕊唐,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年烁设,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了替梨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钓试。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖副瀑,靈堂內(nèi)的尸體忽然破棺而出弓熏,到底是詐尸還是另有隱情,我是刑警寧澤糠睡,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布挽鞠,位于F島的核電站,受9級(jí)特大地震影響狈孔,放射性物質(zhì)發(fā)生泄漏信认。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一均抽、第九天 我趴在偏房一處隱蔽的房頂上張望嫁赏。 院中可真熱鬧,春花似錦油挥、人聲如沸潦蝇。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)攘乒。三九已至,卻和暖如春翩迈,著一層夾襖步出監(jiān)牢的瞬間持灰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工负饲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堤魁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓返十,卻偏偏與公主長(zhǎng)得像妥泉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子洞坑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355