如何不讓車管所黃牛占你便宜?

果不其然。之前說好的400塊只是“裸價”,有的是讓你加錢的地方。第一個是因為我是代辦人而非新車主本人,黃牛說需要50塊代辦費,其實也就是拿張紙寫個書面說明再隨便簽個名就可以了。我想着50塊也不多,加就加吧。第二個是加急費,多收了我150塊,我特么當時心裏一頓MMp,我估摸着就是黃牛知道我們是大老遠從廣州過來的,擔心一天搞不完會增加時間成本,所以就名正言順地給我們辦了所謂的“加急”。

大家好,我是阿湯鍋。

今天準備跟大家聊聊很多人在購買新車或二手車過程中跨不過去的一道坎——車管所黃牛!

由於我一個月前剛入了一輛11年的二手雨燕,並且在外遷和遷入的過程中都跟黃牛打過交道,所以也算是交過學費了,希望看完這篇文章的你能省下幾箱油錢。

大致來講,車管所黃牛可以分為兩大類。

第一種,掙跑腿費的。比如車主不熟悉過戶流程,或者就是懶得動,花個一百兩百讓黃牛幫自己跑;還有車外遷,自己懶得跑去外地上牌,然後花錢找人去辦。

這些是可以接受的,因為這屬於一個願打一個願挨,一個花錢一個出力,都是擺在明面上的。

第二種,“神通廣大”的。比如你這車過戶外檢不可能過、年檢排放不合格、辦不了居住證,或者你今天來晚了手續走不完等等,然後你會發現以上這些只要給錢就都立馬解決了。

這些就是不太能接受的,或者說是出於無奈,因為黃牛無非就是利用自己平時疏通的關係,還有不明真相車主的恐慌心理,然後讓你心甘情願地掏錢。

我相信絕大多數車主都至少經歷過以上兩種黃牛的其中一種,並且多數情況下花出去的錢也確實省下了不少人力和時間成本。所以我想告訴大家的是,如何在節省自己人力、時間的同時少花冤枉錢。

以我自己購買的二手雨燕為例,11年的車國四排放,上線檢車不需要擔心。不過這輛車原本是廣東湛江戶,而我準備上廣東梅州戶,所以我必須先和原車主去湛江提檔過戶,然後再自己回去梅州落戶上牌。

另外,因為某種原因我要把車落在我爸名下,所以全程我都是“代辦人”。於是以上的這些細節,也就成為了我花冤枉錢的伏筆。

先說提檔過戶。

我事先已經聯繫好了湛江車管所的黃牛,價錢也已經談好了,我還砍價砍了100塊,從500講到了400,當時我心裏還樂呵還好不貴。不過我再三強調,一定要在一天內給我辦完,黃牛說沒問題我叫人給你加急。

這個400塊我覺得是不得不出的,畢竟從廣州開去湛江有四百公里的路程,並且還人生地不熟的,再加上這來回八百公里的油錢和過路費就快一千了,所以想快去快回一天內搞完的話我還是乖乖掏錢吧。

於是湊巧在我生日當天就和原車主一起開車到四百公裡外的湛江去提檔過戶了,然後到了車管所之後是黃牛的手下,也就是另一個黃牛接了我們這單“生意”,隱約感覺是安排好的。

果不其然!之前說好的400塊只是“裸價”,有的是讓你加錢的地方。

第一個是因為我是代辦人而非新車主本人,黃牛說需要50塊代辦費,其實也就是拿張紙寫個書面說明再隨便簽個名就可以了。我想着50塊也不多,加就加吧。

第二個是加急費,多收了我150塊,我特么當時心裏一頓MMp,我估摸着就是黃牛知道我們是大老遠從廣州過來的,擔心一天搞不完會增加時間成本,所以就名正言順地給我們辦了所謂的“加急”。(其實快慢都是黃牛自己說了算)

不過必須承認,有黃牛代辦是真的快,如果自己去辦的話就乖乖排隊吧,具體你懂的……於是加起來我一共多花了200塊,後來我總結了,我犯的錯誤主要是事先沒有跟黃牛確認好價格讓他們鑽了空子。

所以這裏要告訴大家的是,當你找黃牛前一定要提前確認好“全部總價”,要不然有的是理由讓你加錢。

另外要告訴大家一個二手車過戶很容易弄錯的點:在原車年檢過期的當月是不能辦理過戶的!必須重新年檢才可以。別問我為什麼知道,都是自己踩過的坑,桑心……

再來說說落戶上牌。

自從經歷過提檔過戶給黃牛多賺了200塊之後,我就下決心自己去上牌了,在梅州自己的地盤總不用再依靠黃牛了吧?

結果證明圖樣圖森破,儘管在此之前還特意問過在相關部門上班的同學,可以自己去辦理上牌,但真正去到現場之後就蒙圈了。

我們都知道,單純上牌只要個牌照手續費,沒記錯的話是120塊左右,但你會絕望地發現,要想全程自己辦理真是一踩一個坑。

首先黃牛插隊就不用說了,必然的,所以時間上沒保障;其次是如果你不熟悉辦理流程的話,你會發現該去哪裡驗車哪裡測尾氣哪裡拍照哪裡拓發動機號等等都足夠把弄得你暈頭轉向的。

於是為了節省時間,我還是說服自己找了黃牛,並且這次我留了個心眼,500一口價不再出任何上牌過程中的費用,不過即便如此我還是覺得自己虧了。

所以事後我自己做了個總結,如果想省下這筆給黃牛賺的錢,就要事先上網了解清楚業務辦理流程,以及需要準備哪些證件,包括車輛證件和新車主證件。如果去到現場不知所措的話,最好的辦法就是問工作人員,記住不要問到黃牛那去了。

不過話說回來,根據我的觀察,我發現發動機拓號、拍照洗出照片,這些都是流動人員在處理的,所以即便是自己去辦理的話也真不一定能快速找到這些流動人員,當然這些也都是要另外收費的(複印身份證特么的收我十塊錢!),於是當你自己辛苦排了一天隊之後可能會發現,其實省下來的那幾百塊錢都是用一肚子委屈換來的,所以說黃牛真是讓人又愛又恨。

最後,希望各位看完我以上兩個經歷后,能夠給今後自己的過戶上牌提供點幫助,能花少一點是一點!如果你也有一些車管所小妙招,歡迎在留言區分享出來,精選可置頂哦!

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

最不顯老的三款家用車 最低7萬起!

寶駿將在2018年第一季度帶來一款全新緊湊型SUV——寶駿530。前衛的外觀,分體式大燈,還有時下流行的懸浮式車頂,相信這樣一款高顏值國產車型絕對不會讓你掉面。寶駿530的內飾也同樣顯得年輕時尚,參考寶駿510,相信其做工和用料不會讓人失望。

大家好,我是阿湯鍋!

就在上周跨年的時候,大家都在朋友圈裡曬自己的18歲,嚇得我也趕緊翻出了18歲的靚照,最後發現自己長得太早熟,也就沒好意思曬出來了。

其實這個梗是來自於步入2018年後,就意味着所有的90后都已經成年了,而像我們這些打頭陣的92、93年的叔叔阿姨們也已經不再是“小鮮肉”了,何其傷感吶!

所以為了證明自己,我相信很多人的18年目標清單中都有“買車”這一項。當然老一輩的90后也是最挑剔的,兜里沒什麼錢要求卻很多,比如選車要個性要運動要有面,最好還能兼顧實用性,最後發現自己的積蓄可能連首付都不夠。

不過別擔心!有些車還是能做到兩全其美的,那麼下面就跟大家聊聊2018年都有哪些既便宜,又能滿足以上條件的新車上市,興許它就是你人生中的第一輛車呦。

1、大眾pOLO

預計售價:8萬起

首先想到的還是離不開大眾神車系,相信很多80后對高爾夫和pOLO都有一種特殊的情感,因為這也是他們年輕時候的第一輛車,而到了今天這兩款車依然深受年輕人的喜愛,尤其是定位更接地氣的pOLO,更容易成為年輕人跨向有車一族的首選。

全新的第六代pOLO最吸引人的毫無疑問就是外觀上的大變,相比於老款的圓潤可愛,新pOLO很識時務地開始變得凌厲帥氣起來,不再是從前那副“女人車”的樣子。

內飾也有了天翻地覆的變化,座椅格子花紋的加入很顯逼格。而配置方面不僅推出與耳機品牌Beats合作的特別版車型,就連奧迪車型上的全液晶儀錶盤也被拿了過來,怎麼能叫年輕人不喜歡?

同時,新車尺寸上也有了大幅提升,軸距增加了94mm后已經完全超越了本田飛度,相比現款足足打了一圈,所以預計會有一個不錯的後排空間。

動力上,海外版車型提供1.0L、1.0T、1.5T汽油發動機及1.6T柴油發動機可選,並將推出普通版、R-Line版和GTI三種版本,滿足不同消費者的需求。而國產後的pOLO也將在今年與國內消費者見面,並且售價可能會略高於現款車型,不過如此全面的它我想已經足夠勝任年輕人對第一輛車的所有期待了。

2、寶駿530

預計售價:7萬起

不得不說現在也有越來越多的年輕人想要選購一台SUV車型,畢竟空間大能裝(震)嘛。而經驗告訴我們,寶駿就是兼顧好看好開和實用的高性價比之選。

寶駿將在2018年第一季度帶來一款全新緊湊型SUV——寶駿530。前衛的外觀,分體式大燈,還有時下流行的懸浮式車頂,相信這樣一款高顏值國產車型絕對不會讓你掉面。

寶駿530的內飾也同樣顯得年輕時尚,參考寶駿510,相信其做工和用料不會讓人失望。另外,豐富的配置也是寶駿530的一大亮點,新車將根據車型的不同提供定速巡航、全景天窗、後排空調出風口、一鍵啟動、主駕駛位電動座椅等豐富配置。

動力方面,新車將搭載1.8L和1.5T兩款發動機,變速箱目前還不得知。同時,該車的后懸架與第6代奧迪A6的后懸架在結構上是非常相像的,因此新車的乘坐表現也非常值得期待。

所以如果預算比較有限,而又想買一台性價比高的SUV車型,我覺得寶駿530會是個不錯的選擇。

3、豐田C-HR

預計售價:12萬起

到了第三款車型,就是一個進階的選項了,因為不排除這款車上市後會成為爆款,也就是關注度極高的豐田C-HR。不過目標總是有的嘛,萬一18年財運旺呢!

可以說,C-HR的到來填補了豐田在小型SUV領域的空缺,並很有希望成為該細分市場的有力競爭者。

國產後的C-HR將被打造成兩款車型,一款交由廣汽-豐田生產,定名為C-HR,另一款則交由一汽-豐田生產,定名IZOA 奕澤,這種雙車策略在國內已經非常常見了。

這樣做的好處就是兩款車型在設計上會有所差別,這就給了消費者多一個選擇,不過可以肯定的是兩款車走的都是個性時尚的設計路線,相信會很受年輕人的喜愛。

動力方面,海外C-HR搭載的是1.2T+6MT/CVT和1.8L混動三套動力總成,這三套動力總成目前也用在國產卡羅拉和雷凌上,因此國產C-HR的動力總成也應該會和海外版的保持一致。

另外值得一提的是,該車的外形提供了一個很好的改裝胚子,所以預計新車的小改裝件會很多,追求個性的朋友完全可以根據自己的喜好裝飾自己的愛車,看來C-HR有機會成為小型SUV領域中的“GK5”。

好了!以上就是我推薦給大家的三款新車,如果其中有你心儀的車型,那就趕快給自己的18年定個小目標吧,爭取能在今年實現自己的買車願望!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

這些低價高配的合資SUV,最低僅11萬不到!

67萬元在我們的印象中,法國人總喜歡在造型設計上大下筆墨,怎麼不實用怎麼來,所以大家都覺得很少會有一款法系車能與“性價比”沾邊。但是。從天逸C5 AIRCROSS開始情況似乎就有點不一樣了。這款緊湊型SUV不僅完美繼承了法系車飄逸的設計風格,並且在配置和價格上都算得上實在,這對於預算吃緊又想得到更多的消費者來說可是個好事。

不知大家有沒有覺得,15萬左右的購車預算是最尷尬的,說多不多,說少不少,尤其是在挑選SUV車型的時候,如今國產+合資一大堆選擇,看得眼花繚亂。

而隨着國產SUV的崛起,不少人也在鼓吹買國產車就是“低價高配”,這也使得很多對汽車不怎麼了解的消費者錯過了一些優秀的合資車型。那麼今天就要給大家介紹幾款極具性價比的合資品牌SUV,讓大家知道所謂的“低價高配”並不是國產車的專屬。

1、北京現代 全新ix35

官方指導價:11.58-18.88萬元

作為一款合資品牌車型,ix35的價格的確是非常給力了,最入門的車型在有些4S店僅11萬不到,實在是令人意想不到。而參考現代車一直以來的耐用性,ix35可以說就是目前市面上你能買到的價格最低,且總體表現最穩定的合資緊湊型SUV了。

從外形上看,新款車型在歷經換代后整體風格更加陽剛硬朗了,甚至還有點小硬漢的感覺。內飾也同樣如此,採用全新的設計和布局,而方正的造型也明顯與外觀呼應。

配置方面,全系標配ESp、上坡輔助、后駐車雷達等,中配及以上車型則相應增加了胎壓監測、陡坡緩降、無鑰匙進入/啟動、9.6英寸中控液晶显示屏(帶倒車雷達/影像)、自動大燈與電加熱/摺疊后視鏡等等。

考慮到其中配車型13.99萬元的售價,以合資品牌的標準看,這配置已經足夠厚道了。

動力方面,ix35全系均搭載了傳統的2.0L自然吸氣發動機,傳動上匹配6速手動或6速手自一體變速箱。雖然數據並不亮眼,但勝在性能成熟穩定,並且後期的使用故障率和維護成本都較低,所以從家用車SUV的角度來看,ix35無疑是一款非常省錢又省心的車型。

2、東風雪鐵龍 天逸C5 AIRCROSS

官方指導價:15.27-23.67萬元

在我們的印象中,法國人總喜歡在造型設計上大下筆墨,怎麼不實用怎麼來,所以大家都覺得很少會有一款法系車能與“性價比”沾邊。

但是!從天逸C5 AIRCROSS開始情況似乎就有點不一樣了。這款緊湊型SUV不僅完美繼承了法系車飄逸的設計風格,並且在配置和價格上都算得上實在,這對於預算吃緊又想得到更多的消費者來說可是個好事。

首先進入到車內,第一感覺就是讓你懷疑這根本就不是一輛售價僅15萬元起的SUV,因為無論是獨具一格的內飾設計,還是雙色皮革的搭配,都讓你感受到實實在在的質感提升。

配置上就更不用說了,除入門版車型外,其餘車型均配備了有多個安全氣囊、胎壓監測、全景天窗、無鑰匙進入/啟動、皮質多功能方向盤、全液晶儀錶盤、全車車窗一鍵升降、電加熱/摺疊后視鏡,自動大燈與自動空調等等,應有盡有!

所以對於一款法系車來說,天逸C5 AIRCROSS的確在“性價比”方面打了一場漂亮的翻身仗,也證明了美貌和實力是可以兼得的。

3、上汽斯柯達 柯珞克

預計售價:13.88萬起

斯柯達也是一個容易被人國內消費者忽略的品牌,所以最後推薦的就是近期即將上市的一款入門緊湊型SUV-柯珞克,而13.88萬的預售價格也十分值得期待。

在外形上,柯珞克採用了與柯迪亞克相同的家族式設計語言,中庸的造型適合絕大部分消費者。而在車身尺寸上,新車長寬高為4432/1841/1614mm,軸距為2688mm,也就是比繽智、昂科拉都要稍大一些。

動力上,新車搭載的是1.2T和1.4T發動機,匹配7速DSG雙離合變速箱,想想不到15萬就能買到這樣一款德系品質(大眾平台)SUV,是不是覺得自己賺了?所以野帝停產後,柯珞克或將成為大眾集團在15萬元左右SUV市場的主力車型。

好了!以上就是今天給大家推薦的三款15萬元左右、極具性價比的合資品牌SUV。大家如果還有其他想了解的購車話題,歡迎留言評論,盡量滿足大家需求。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

8款老闆愛的車最低只要4萬

還有就是形象與同級比起來要更顯莊重,即使去談大買賣,也不至於讓人瞧不起。其實不僅是別克GL8,還有許多定位為商務MpV的車型同樣也充滿了濃郁的商務氣息,而這次推薦GL8主要原因還是因為它空間及乘坐舒適性達到同級一流水平,應有的电子設備都有,相對而言,GL8是現在市面上主流MpV中比較卓越的一款車型。

關於#老闆開什麼車#這樣的話題

相信不少人都有關注過吧

雖然老闆的座駕並沒有什麼標準

可是總有幾款讓人印象深刻

為什麼老闆們都這麼喜歡這些車?

老闆們喜歡的這些車是否適合你?

開上這些車后你是否就能當老闆?

看完這8款老闆車你就會有答案了

首先要排除一個誤區,誰說老闆就一定要開個大奔馳或大賓利的?開五菱宏光的小賣鋪老闆不也是老闆嗎?什麼空間大、油耗低、動力足、操控好、保養便宜、可靠性高等等優點就不多說了,反正銷量說明一切嘛。不過買最便宜的車裝最多的貨,這些簡單的商業意識還是要具備的。

其實能“裝”的確是五菱宏光的核心價值吧,裝人:最多可放置8個座椅,什麼7座SUV都是辣雞;裝貨:只要裝得下,世界就是你的;裝X:方式太多,請自行上論壇選,總有一款適合你。

可能這隻是我的個人印象吧,從小到大,身邊總有一些開着捷達,成天到處跑業務的創業老闆,他們總是會稱讚自己的捷達很省心,很省油,又好開,說這樣的一台車可以完全滿足他們的代步需求,而且外觀低調,卻很有商務范。

其實說到捷達的外觀,雖然現款的捷達也很低調,也很有商務范,同樣也是個“老實”形象,還是以前方頭方腦的造型給我的印象更深刻,可是那個造型放在今天馬路上,也許展現出更多的是“經典”或“復古”吧。

為什麼說開帕薩特的老闆最神秘?首先,一般人根本分不清帕薩特和輝騰,還有就是的確有許多隱形富豪為了低調會選擇帕薩特這款車,首先大眾標就夠低調了,再來就是外觀造型,家族式設計遍布全車身,讓人怎麼看都覺得是一輛不超過20萬的車。

可是20萬要買低調的車選擇很多,為什麼這些隱形富豪要選帕薩特?其實還是圍繞着舒適性吧,德系紮實底盤質感,大空間;還有就是形象與同級比起來要更顯莊重,即使去談大買賣,也不至於讓人瞧不起。

其實不僅是別克GL8,還有許多定位為商務MpV的車型同樣也充滿了濃郁的商務氣息,而這次推薦GL8主要原因還是因為它空間及乘坐舒適性達到同級一流水平,應有的电子設備都有,相對而言,GL8是現在市面上主流MpV中比較卓越的一款車型。

當然,這樣的MpV一般都是公務用車,老闆一般只坐在第二排,坐在舒適的座椅上,有公事要聊的時候,跟助理溝通起來十分方便;沒事聊的時候,放下靠背,也可以睡得很舒服。

作為豐田越野神車蘭德酷路澤的分支車型,普拉多其實已經更傾向於都市SUV,可是在造型方面還是很有越野的味道,這也是許多老闆們喜歡它原因之一,不過實際普拉多還是延續了較好的越野性能。

很多人都說什麼樣的人就會開什麼樣的車,我通過普拉多的車主們就能明顯體現出這點,說不上個個都高大威猛,可是至少也是條硬漢。

雖然說“虎頭奔之後再也沒有S級”,有情懷的車迷都喜歡那個年代的S級,可是現在的S級無疑是更好的S級,動力更好,配置更高,價格更低。雖然造型方面的確是向現在的消費市場妥協了,可是現在的S級仍然是D級車裡最有老闆范的。

但是實際上並不是所有買S級的老闆都會請司機,或者說不是所有買了S級的老闆自己都不開,即使自己開S級的時候的確有點像司機,可是他們還是很願意去駕駛這輛高級車。

與其說開霸道(普拉多)的老闆真霸氣,還不如說開攬勝的人才是真正的“霸道總裁”,說到高大上,縱觀市面上所有高端中大型SUV,好像真沒幾款能媲美路虎攬勝的了。也有人說路虎攬勝根本沒有直接的競爭對手,因為在公路和越野的性能都如此卓越,內飾如此豪華,外觀如此霸氣的SUV屈指可數。

雖然說攬勝也有極高的越野性能,可是車主們更願意把它看作是豪華都市SUV,而且攬勝的形象也似乎更合適出現在都市中。

看到勞斯萊斯,貌似我們的話題就要結束了,我也都相信大家和我一樣,勞斯萊斯才是代表最“尊貴”的老闆車,雖然到了這個等級,選擇還有賓利和邁巴赫,可是這些品牌都不如勞斯萊斯尊貴。

其實到了這個級別的車,更講究的應該就奢華、藝術、品味,多的不說了。

不知道大家看了這麼多老闆車之後,有沒有你心儀的那輛呢?或者在你的印象里,什麼車才算老闆車呢?歡迎把你的所有見解評論到下方留意區哦。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

大廠面試過程復盤(微信/阿里/頭條均拿offer,附答案篇)

背景

本人前端,3年經驗,由於個人的原因,決定跳槽,於是大概3月開始找工作,總歷時大概2個月,面試了微信/阿里/頭條,三家都拿到了offer,來分享一下面經。

問題比較多,而且很多面試題都是跟個人項目相關的,項目相關的問題借鑒意義不大,所以這裏總結一些與項目無絕對關係的問題,歡迎圍觀~

本文答案均以鏈接的形式給出,因為我本人的一個特點就是回答題目一定要夠詳細,而且大部分的題目我都寫過文章或者公眾號轉發過文章了,感興趣可以直接關注公眾號前端Q,希望對你有幫助~

整體流程

這裏,我梳理了一些自己的心得,方便自己後面做直播,這裏就當預告一下,先放個思維導圖。

PS: 圖片有點大,如果打不開,直接訪問我的博客

微信

筆試

  1. 數組交集,編寫一個函數,輸入兩個數組,輸出它們的交集。輸出數組中不含重複的元素,元素排列順序可隨意。

原題地址:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/

  1. 二叉樹的搜索,輸入一個普通二叉樹的根節點,實現一個調度器,調用調度器的next()方法,將返回二叉樹中下一個最小的數;調用迭代器的hasNext()方法,將返回是否存在下一個數。二叉樹節點是整數,無序。

原題地址:https://leetcode-cn.com/problems/binary-search-tree-iterator/

  1. 三角形個數,輸入一個非負整數的數組,如果將數組元素選作三角形的邊長,編寫一個函數,輸出這個數組可構成的三角形數量。

原題地址:https://leetcode-cn.com/problems/valid-triangle-number/

  1. 數組切分問題,輸入一個正序排列的整型數組,如果它可以被切分為1個或多個子序列,輸出True,反之False。子序列需為連續的整型數組,並且長度至少為3。
例1:
輸入: [1,2,3,3,4,5]
輸出: True
解釋:可以切分為2個各自連續的子序列:
1, 2, 3
3, 4, 5
例2:
輸入: [1,2,3,3,4,4,5,5]
輸出: True
解釋:可以切分為2個各自連續的子序列:
1, 2, 3, 4, 5
3, 4, 5
例3:
輸入: [1,2,3,4,4,5]
輸出: False
解釋:無法切分出長度至少為3的子序列。

原題地址:https://leetcode-cn.com/problems/split-array-into-consecutive-subsequences/

  1. 一隻青蛙一次可以跳上1級台階,也可以跳上2級。求該青蛙跳上一個n級的台階總共有多少種跳法.
  2. 求下面代碼的輸出
function test(a,b) {
  console.log(b)
  return {
    test:function(c){
      return test(c,a);
    }
  };
}

var retA = test(0);  
retA.test(2);  
retA.test(4);  
retA.test(8);
var retB = test(0).test(2).test(4).test(8);
var retC = test('good').test('bad');  
retC.test('good');  
retC.test('bad');
  1. top k

原題地址:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

  1. 閉包問題及優化
for (var i = 0; i < 4; i++) {
  setTimeout(function() {
    console.log(i);
  }, 300);
}

PS:一面筆試要求是30分鐘,前4道算法題,二面筆試題是15分鐘

與項目關係不大的問答題

  1. 節流防抖(實現/場景/源碼,如lodash實現原理)(https://www.cnblogs.com/LuckyWinty/p/5949970.html#4564734)
  2. 事件循環(瀏覽器/node/版本差異)(https://mp.weixin.qq.com/s/QgfE5Km1xiEkQqADMLmj-Q)
  3. setTimeout 實現原理(https://mp.weixin.qq.com/s/7qTRSMqaqG8XZ9rpEBhYNQ)
  4. react 和 vue 的區別
  5. Promise 原理(https://juejin.im/post/5d6f7c83e51d4561c541a712)
  6. 前端錯誤監控及容災(https://mp.weixin.qq.com/s/prf-mXexBh1Ie-ctq9FnzA)
  7. 性能優化(https://mp.weixin.qq.com/s/qXFd3f7CkEzz4u_1zl-TSw、https://mp.weixin.qq.com/s/dy1u2g9TeCoq2WOdPLqAXw、https://mp.weixin.qq.com/s/DJ8Fdq1_cIoW0_NYekZwFw、https://mp.weixin.qq.com/s/azeUIx0EA86EFQrtIRUKwQ、https://mp.weixin.qq.com/s/wDKKj5R8SYm-_75Zn1y30A、https://mp.weixin.qq.com/s/BOdnCYbMQfmCpUC3w3KZqw、https://mp.weixin.qq.com/s/FdG7uVIDXltNyskL3qh8Cw、太多了,貼不過來了,自己再總結吧)
  8. 談談 node 的內存泄漏(我的總結暫時沒時間寫,可以看看這篇,https://zhuanlan.zhihu.com/p/25736931)
  9. 開發過程中遇到的最大挑戰是什麼(梳理項目,得出難點)
  10. 學習的動力怎麼來的,如何維持(有興趣可以留意一下我的直播)
  11. 瀏覽器的渲染機制是怎樣的(https://mp.weixin.qq.com/s/wcw30EUb2ds3AoW176WGeg)
  12. SSR 作用及優缺點(seo/首屏快/開發成本高等,我項目用的是vue ssr,沒專門總結,可以先看這個,https://zhuanlan.zhihu.com/p/35871344)
  13. 如何進行狀態管理(vuex/redux)
  14. webpack 及瀏覽器的技術分享目的是什麼,分享了什麼,怎麼做的分享(https://mp.weixin.qq.com/s/1FySzmVrNjS6wjgqALC96g、https://mp.weixin.qq.com/s/wcw30EUb2ds3AoW176WGeg)
  15. 如何進行項目重構(知乎很多討論,直接去參考就可以了,或者看看這個https://www.itzhai.com/refactoring/refactoring-principle.html)
  16. 進程與線程的區別(https://www.zhihu.com/question/25532384)
  17. 說說知道的設計模式(參考掘金小冊子就可以了)
  18. CI/CD(https://mp.weixin.qq.com/s/MbeW8UNZ1fPekWcaNqmsCQ)

PS:由於面試全過程歷時比較長,一些題目已經忘記了。大概記得這麼點,然後很看項目,每一輪都詳細問了項目~

可能感興趣的問答

  1. 面試一共幾輪

我本人一共 4 輪技術面 + 1 輪HR面。目前微信職級為9級以下的技術崗不用經過面委,9級以下的一般4輪技術面,內部轉崗可3輪技術面,9級及以上的需要走面委面試,大概形式是做份ppt去講自己的項目經歷及其他技術項。9級及以上的就5-10輪技術面不等。

  1. 歷時多長

我本人總歷時一個多月(相對來說算很快了),所以如果你要面騰訊,千萬不要裸辭,不對,面什麼都不要裸辭,這年頭不好混,慫一點。

  1. 整體面試流程是怎樣的

內推->一面->二面->三面->四面->HR面->口頭offer->正式offer->背調->入職

每個階段耗時一到兩周的時間

  1. 微信工作地點

目前微信總部在廣州,大部分的組都在廣州,其中微信支付在深圳。

  1. 微信考算法多嗎

多,基本上就是算法+項目

阿里

筆試

  1. n階樓梯問題(尾遞歸的優化[函數最後調用自身可以減少中間變量|化遞歸為循環去優化]/備忘錄優化)
  2. 節流防抖的實現(分析筆試答案時問到,總結差別,說出應用場景)(https://www.cnblogs.com/LuckyWinty/p/5949970.html#4564734)
  3. 深拷貝(類型考慮/Symbol/Date等的構造/循環優化/共用引用優化等),這裏貼2篇文章給大家參考,https://juejin.im/post/5d6aa4f96fb9a06b112ad5b1,https://mp.weixin.qq.com/s/M7KBX3w2KqlWhZFHJSYP6Q)
  4. 封裝一個只能輸入数字的React組件

問答題

  1. 輸入URL后發生了什麼(https://mp.weixin.qq.com/s/DLq_GIkdnuOayThfi3jI0A)
  2. 事件循環說一下(https://mp.weixin.qq.com/s/QgfE5Km1xiEkQqADMLmj-Q)
  3. JS有幾種基本類型,分別是什麼,與對象的區別(不貼鏈接了,我認為是前端的都應該知道)
  4. JS 執行過程中是如何保存上下文的(堆和棧,可以看極客時間的《瀏覽器工作原理》,或者參考我這裏,https://mp.weixin.qq.com/s/klZ6j3Gj9cwHGQFys6Hdjg、https://mp.weixin.qq.com/s/HqADCzMGlIFDNnMRLsPTAA)
  5. 你認為什麼是前端
  6. 為什麼想來阿里
  7. 最近在看什麼書
  8. 微前端了解嗎(https://mp.weixin.qq.com/s/awBqvJmqWc7JGxwgB0QQZA、https://mp.weixin.qq.com/s/80S-RFmoZoq8KfCH6YZ_Tg、https://mp.weixin.qq.com/s/HVwcY8JGyrK8VcN1Ic9sWQ)
  9. 你想成為什麼樣的人(個人成長規劃)
  10. 項目中遇到的最大挑戰是什麼,如何解決的
  11. node 大量日誌怎麼處理的(緩衝隊列)
  12. http2 的特點(https://mp.weixin.qq.com/s/OitrWEosrpuXh19o_TDasg)
  13. node 與其他語言有什麼區別,其優劣勢是什麼
  14. csrf 的核心原理(https://juejin.im/post/5bc009996fb9a05d0a055192)
  15. node 事件循環(https://mp.weixin.qq.com/s/QgfE5Km1xiEkQqADMLmj-Q)
  16. FCP/FMP/FP 分別是怎樣定義,如何統計(https://mp.weixin.qq.com/s/wDKKj5R8SYm-_75Zn1y30A)
  17. MySQL 題目,id 唯一,name 可以重複,求 name 有重複的所有數據
  18. 有沒有推動過什麼東西
  19. 未來的一個發展是否是全棧,如何規劃(可以留意我的直播)
  20. 鏈路錄屏的原理(可以分析rrweb即可,參考例子,https://mp.weixin.qq.com/s/PxEzBrr3YF1jD84c_p1uAA)
  21. 鏈路錄屏法務問題如何解決(屏蔽關鍵信息/用戶授權等)
  22. FMP 定義及統計,w3c的草案你知道嗎(自行谷歌查找)
  23. react 項目的可持續維護性如何體現(自行谷歌查找)
  24. react hooks用過嗎,為什麼要用(沒有專門總結,自行谷歌查找)
  25. h5 和小程序有什麼區別(自行谷歌查找)
  26. https加密原理,中間人攻擊知道嗎(https://mp.weixin.qq.com/s/aMYp6Y5n26r9vdQIom4g0w、https://mp.weixin.qq.com/s/sHtZhRTNOihmxap5sDD6xQ)
  27. ng 負載均衡的方式有哪些(https://mp.weixin.qq.com/s/u-XbBwGxHrhJGiMiiqz26w、https://mp.weixin.qq.com/s/o2Us4Zj6DO2NHGQVaHIa1A)
  28. CDN 有什麼作用(https://github.com/LuckyWinty/fe-weekly-questions/issues/24)
  29. 不可變數據裏面,immutable,刪除數組裡面的列表(https://juejin.im/post/5b9b30a35188255c6418e67c,
    https://juejin.im/post/5e6a14b1f265da572978a1d3)
  30. 垃圾回收中,內部函數返給上層的對象是如何管理的
  31. es 靜態分析原理(https://zhuanlan.zhihu.com/p/33843378)
  32. 有一個很大很大的文件,如何統計文件裏面重複最多的行是哪行(參考git的記錄存儲)

頭條

筆試題

一面:

  1. 執行代碼求輸出,並說明為什麼,嚴格模式下輸出有變化嗎,為什麼
var a = function () {this.b = 3;}
var c = new a();
a.prototype.b = 9;
var b = 7;
a();

console.log(b);
console.log(c.b);
  1. 給定一個升序整型數組[0,1,2,4,5,7,13,15,16],找出其中連續出現的数字區間,輸出為[“0->2″,”4->5″,”7″,”13″,”15->16”]
function summaryRanges(arr){
    //TODO
}
  1. 請實現以下的函數,可以批量請求數據,所有的URL地址在urls參數中,同時可以通過 max 參數控制請求的併發度,當所有請求結束之後,需要執行 callback 回調函數。發請求的函數可以直接使用 fetch 即可
function sendRequest(urls: sring[],max:number,callback:()=>void){
    //TODO
}

二面:

  1. 實現一個字符串反轉:
    輸入:www.toutiao.com.cn
    輸出:cn.com.toutiao.www

要求:
1.不使用字符串處理函數
2.空間複雜度盡可能小

  1. 不藉助變量,交換兩個數。
function swap(a, b) {
    //TODO
}
  1. 觀察者模式與發布訂閱者區別,並寫出其模型

與項目無絕對相關的問答題

  1. vue 事件機制是如何實現的 (https://juejin.im/post/59ca5e975188257a8908959b)
  2. vue 的組件通信方式有哪些
  3. react fiber 的實現原理 (https://juejin.im/post/5dadc6045188255a270a0f85#comment)
  4. vue 響應式數據原理(vue2/vue3/依賴收集/發布訂閱/watcher消息隊列控制/Vue.set實現)(https://mp.weixin.qq.com/s/zDv_IQ36o_rRD25xN9uyuw、https://mp.weixin.qq.com/s/hTygoAan4yH3V4XV9iE1Pw)
  5. vue 轉小程序怎麼實現(ast/生命周期對齊/跨平台模塊兼容/兼容細節點實現過程)
  6. 性能指標,如何理解TTI,如何統計,與FID有什麼區別,如何實現統計,還聊了很多性能的東西(https://mp.weixin.qq.com/s/wDKKj5R8SYm-_75Zn1y30A)
  7. 說說你所了解的安全問題及防護方法(https://mp.weixin.qq.com/s/rU32rVM6Q-ele01ZB3RFzg)
  8. 說說你知道的設計模式,並舉個對應的模式例子(https://mp.weixin.qq.com/s/x3OCgS_knmM4D5zNSIVGRg)
  9. 未來規劃及學習方法
  10. 你覺得你是怎樣拿到京東校招的sp的
  11. 遇到的最大挑戰/過去的最大收穫分別是什麼

我本人的博客/公眾號,似乎面試官都看過,都知道我學過什麼,知道什麼,所以很多我博客上有的東西都沒問,甚至我引申到過去,都不讓說,有種沒機會表現的感覺,哈哈

感受&更多學習資料

其實單看問題,參考性還是比較小的,畢竟面試官主要會根據簡歷去發問。這裏的一個感受就是,項目中遇到的最大挑戰,這個問題真不好答,我當時答得也不夠好,後面又回去總結了一波項目,建議大家都好好總結一下自己平時遇到的問題,尋根究底。

這部分的題目,我會同步到我的每周N題上,歡迎大家來交流答案啊。想刷題的也可以來,地址如下:
https://github.com/LuckyWinty/fe-weekly-questions/issues

有很多知識點,雖然我沒被問到,但是我寫了不少文章,感興趣的話可以關注一下我的博客,地址如下:
https://github.com/LuckyWinty/blog

更多我覺得比較好的博客,地址如下:

  • https://juejin.im/user/5cf288385188254abb110e3b/posts
  • https://juejin.im/user/58d4e734570c350058cbb900/posts
  • http://blog.poetries.top/FE-Interview-Questions/
  • https://juejin.im/user/5bea27965188250edf4ad8b7/posts
  • https://juejin.im/user/5c45ddf06fb9a04a006f5491/posts

交流

  • 歡迎加我微信(winty230),拉你進技術群,長期交流學習…
  • 歡迎關注「前端Q」,認真學前端,做個專業的技術人…

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

QNX Message Passing,一個讓人頭禿的 IPC BUG

問題描述

QNX系統中 Client 與 Server 通過 QNX Message Passing 進行進程間通信。正式開發前,寫過測試程序 (Client),和 Server 通信一切正常。但把同樣的代碼拷貝到正式的 Client 中,結果發現調用 MsgSend() 后無響應,pidin 显示 Client 處於 REPLY PENDING 的狀態。

幾番嘗試,發現一個讓人很難接受的事實:只能在 Client 的主線程中調用 MsgSend() 才能收到 Server 的回復,如果在 Client 的對等線程中調用MsgSend(),則會因為收不到 Server 的回復而 Pending。

問題分析

測試程序能正常工作,所以起初懷疑是 Client 的問題。面對龐大的 Client,始終沒想到突破口。甚至曾一度懷疑是否是 QNX 的系統限制,MsgSend() 只能在主線程中調用,官網文檔翻了一圈並沒有發現這樣的限制。自己寫了個 IPC Server 來測試,發現並沒有這個問題。

Client Server 結果
測試程序,主線程中調用 MsgSend() 正式 Server OK
正式 Client,對等線程中調用 MsgSend() 正式 Server REPLY PENDING
正式 Client,對等線程中調用 MsgSend() 簡化版測試 Server OK

最後不得不懷疑起 Server,莫非 Server 端有什麼機制能檢測到消息是發自主線程還是對等線程,然後只回復來自主線程的 IPC 請求?

一個典型的 IPC Server 示例代碼如下:

While(1) {
   int rcvid = MsgReceive(chid, &recvBuf, sizeof(recvBuf), NULL);
   /* … process the request based on recvBuf … */
   MsgReply(rcvid, EOK, &replyBuf, sizeof(replyBuf)); // reply to unblock the IPC client
}

如果是從 Client 的主線程發送來的消息,rcvid 是一個很小的数字,如 1, 3, 5… 如果是從Client 的對等線程發送來的消息,rcvid 是一個大於 65535 的数字,如 65538, 65540, 65542… 

讓人意外的是 Server 用了一個 int16_t 來保存 rcvid,直接導致後續的 MsgReply() 無法正確的將消息回給 Client,從而導致 Client 一直處於 REPLY PENDING 狀態。

Reference

  • http://www.qnx.com/developers/docs/7.0.0/#com.qnx.doc.neutrino.sys_arch/topic/ipc_Robust.html  

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

【原創】Linux中斷子系統(三)-softirq和tasklet

背景

  • Read the fucking source code! –By 魯迅
  • A picture is worth a thousand words. –By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

中斷子系統中有一個重要的設計機制,那就是Top-half和Bottom-half,將緊急的工作放置在Top-half中來處理,而將耗時的工作放置在Bottom-half中來處理,這樣確保Top-half能儘快完成處理,那麼為什麼需要這麼設計呢?看一張圖就明白了:

  • ARM處理器在進行中斷處理時,處理器進行異常模式切換,此時會將中斷進行關閉,處理完成后再將中斷打開;
  • 如果中斷不分上下半部處理,那麼意味着只有等上一个中斷完成處理后才會打開中斷,下一个中斷才能得到響應。當某个中斷處理處理時間較長時,很有可能就會造成其他中斷丟失而無法響應,這個顯然是難以接受的,比如典型的時鐘中斷,作為系統的脈搏,它的響應就需要得到保障;
  • 中斷分成上下半部處理可以提高中斷的響應能力,在上半部處理完成后便將中斷打開(通常上半部處理越快越好),這樣就可以響應其他中斷了,等到中斷退出的時候再進行下半部的處理;
  • 中斷的Bottom-half機制,包括了softirq、tasklet、workqueue、以及前文中提到過的中斷線程化處理等,其中tasklet又是基於softirq來實現的,這也是本文討論的主題;

在中斷處理過程中,離不開各種上下文的討論,了解不同上下文的區分有助於中斷處理的理解,所以,還是來一張老圖吧:

  • task_struct結構體中的thread_info.preempt_count用於記錄當前任務所處的context狀態;
  • PREEMPT_BITS:用於記錄禁止搶佔的次數,禁止搶佔一次該值就加1,使能搶佔該值就減1;
  • SOFTIRQ_BITS:用於同步處理,關掉下半部的時候加1,打開下半部的時候減1;
  • HARDIRQ_BITS:用於表示處於硬件中斷上下文中;

前戲結束了,直奔主題吧。

2. softirq

2.1 初始化

softirq不支持動態分配,Linux kernel提供了靜態分配,關鍵的結構體描述如下,可以類比硬件中斷來理解:

/* 支持的軟中斷類型,可以認為是軟中斷號, 其中從上到下優先級遞減 */
enum
{
	HI_SOFTIRQ=0,       /* 最高優先級軟中斷 */
	TIMER_SOFTIRQ,      /* Timer定時器軟中斷 */
	NET_TX_SOFTIRQ,     /* 發送網絡數據包軟中斷 */
	NET_RX_SOFTIRQ,     /* 接收網絡數據包軟中斷 */
	BLOCK_SOFTIRQ,      /* 塊設備軟中斷 */
	IRQ_POLL_SOFTIRQ,   /* 塊設備軟中斷 */
	TASKLET_SOFTIRQ,    /* tasklet軟中斷 */
	SCHED_SOFTIRQ,      /* 進程調度及負載均衡的軟中斷 */
	HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on thenumbering. Sigh! */
	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq, RCU相關的軟中斷 */

	NR_SOFTIRQS
};

/* 軟件中斷描述符,只包含一個handler函數指針 */
struct softirq_action {
	void	(*action)(struct softirq_action *);
};
/* 軟中斷描述符表,實際上就是一個全局的數組 */
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

/* CPU軟中斷狀態描述,當某個軟中斷觸發時,__softirq_pending會置位對應的bit */
typedef struct {
	unsigned int __softirq_pending;
	unsigned int ipi_irqs[NR_IPI];
} ____cacheline_aligned irq_cpustat_t;
/* 每個CPU都會維護一個狀態信息結構 */
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;

/* 內核為每個CPU都創建了一個軟中斷處理內核線程 */
DEFINE_PER_CPU(struct task_struct *, ksoftirqd);

來一張圖吧:

  • softirq_vec[]數組,類比硬件中斷描述符表irq_desc[],通過軟中斷號可以找到對應的handler進行處理,比如圖中的tasklet_action就是一個實際的handler函數;
  • 軟中斷可以在不同的CPU上并行運行,在同一個CPU上只能串行執行;
  • 每個CPU維護irq_cpustat_t狀態結構,當某個軟中斷需要進行處理時,會將該結構體中的__softirq_pending字段或上1UL << XXX_SOFTIRQ;

2.2 流程分析

2.2.1 軟中斷註冊

中斷處理流程中設備驅動通過request_irq/request_threaded_irq接口來註冊中斷處理函數,而在軟中斷處理流程中,通過open_softirq接口來註冊,由於它實在是太簡單了,我忍不住想把代碼貼上來:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

也就是將軟中斷描述符表中對應描述符的handler函數指針指向對應的函數即可,以便軟中斷到來時進行回調。

那麼,問題來了,什麼時候進行軟中斷函數回調呢?

2.2.2 軟中斷執行之一:中斷處理后

先看第一種情況,用圖片來回答問題:

  • Linux中斷子系統(二)-通用框架處理文章中講述了整个中斷處理流程,在接收到中斷信號后,處理器進行異常模式切換,並跳轉到異常向量表處進行執行,關鍵的流程為:el0_irq->irq_handler->handle_arch_irq(gic->handle_irq)->handle_domain_irq->__handle_domain_irq;
  • 在__handle_domain_irq函數中,irq_enter和irq_exit分別用於來標識進入和離開硬件中斷上下文處理,這個從preempt_count_add/preempt_count_sub來操作HARDIRQ_OFFSET可以看出來,這也對應到了上文中的Context描述圖;
  • 在離開硬件中斷上下文後,如果!in_interrupt() && local_softirq_pending為真,則進行軟中斷處理。這個條件有兩個含義:1)!in_interrupt()表明不能處在中斷上下文中,這個範圍包括in_nmi、in_irq、in_softirq(Bottom-half disable)、in_serving_softirq,凡是處於這幾種狀態下,軟中斷都不會被執行;2)local_softirq_pending不為0,表明有軟中斷處理請求;

軟中斷執行的入口就是invoke_softirq,繼續分析一波:

  • invoke_softirq函數中,根據中斷處理是否線程化進行分類處理,如果中斷已經進行了強制線程化處理(中斷強制線程化,需要在啟動的時候傳入參數threadirqs),那麼直接通過wakeup_softirqd喚醒內核線程來執行,否則的話則調用__do_softirq函數來處理;
  • Linux內核會為每個CPU都創建一個內核線程ksoftirqd,通過smpboot_register_percpu_thread函數來完成,其中當內核線程運行時,在滿足條件的情況下會執行run_ksoftirqd函數,如果此時有軟中斷處理請求,調用__do_softirq來進行處理;

上圖中的邏輯可以看出,最終的核心處理都放置在__do_softirq函數中完成:

  • local_softirq_pending函數用於讀取__softirq_pending字段,可以類比於設備驅動中的狀態寄存器,用於判斷是否有軟中斷處理請求;
  • 軟中斷處理時會關閉Bottom-half,處理完后再打開;
  • 軟中斷處理時,會打開本地中斷,處理完后關閉本地中斷,這個地方對應到上文中提到的Top-half和Bottom-half機制,在Bottom-half處理的時候,是會將中斷打開的,因此也就能繼續響應其他中斷,這個也就意味着其他中斷也能來打斷當前的Bottom-half處理;
  • while(softirq_bit = ffs(pending)),循環讀取狀態位,直到處理完每一個軟中斷請求;
  • 跳出while循環之後,再一次判斷是否又有新的軟中斷請求到來(由於它可能被中斷打斷,也就意味着可能有新的請求到來),有新的請求到來,則有三個條件判斷,滿足的話跳轉到restart處執行,否則調用wakeup_sotfirqd來喚醒內核線程來處理:
    1. time_before(jiffies, MAX_SOFTIRQ_TIME),軟中斷處理時間小於兩毫秒;
    2. !need_resched,當前沒有進程調度的請求;
    3. max_restart = MAX_SOFTIRQ_RESTART,跳轉到restart循環的次數不大於10次;
      這三個條件的判斷,是基於延遲和公平的考慮,既要保證軟中斷儘快處理,又不能讓軟中斷處理一直佔據系統,正所謂trade-off的藝術;

__do_softirq既然可以在中斷處理過程中調用,也可以在ksoftirqd中調用,那麼softirq的執行可能有兩種context,插張圖吧:

讓我們來思考最後一個問題:硬件中斷觸發的時候是通過硬件設備的電信號,那麼軟中斷的觸發是通過什麼呢?答案是通過raise_softirq接口:

  • 可以在中斷處理過程中調用raise_softirq來進行軟中斷處理請求,處理的實際也就是上文中提到過的irq_exit退出硬件中斷上下文之後再處理;
  • raise_softirq_irqoff函數中,最終會調用到or_softirq_pending,該函數會去讀取本地CPU的irq_stat中__softirq_pending字段,然後將對應的軟中斷號給置位,表明有該軟中斷的處理請求;
  • raise_softirq_irqoff函數中,會判斷當前的請求的上下文環境,如果不在中斷上下文中,就可以通過喚醒內核線程來處理,如果在中斷上下文中處理,那就不執行;
  • 多說一句,在軟中斷整個處理流程中,會經常看到in_interrupt()的條件判斷,這個可以確保軟中斷在CPU上的串行執行,避免嵌套;

2.2.3 軟中斷執行之二:Bottom-half Enable后

第二種軟中斷執行的時間點,在Bottom-half使能的時候,通常用於併發處理,進程空間上下文中進行調用:

  • 在討論併發專題的時候,我們談到過Bottom-half與進程之間能產生資源爭奪的情況,如果在軟中斷和進程之間有臨界資源(軟中斷上下文優先級高於進程上下文),那麼可以在進程上下文中調用local_bh_disable/local_bh_enable來對臨界資源保護;
  • 圖中左側的函數,都是用於打開Bottom-half的接口,可以看出是spin_lock_bh/read_lock_bh/write_lock_bh等併發處理接口的變種形式調用;
  • __local_bh_enable_ip函數中,首先判斷調用該本接口時中斷是否是關閉的,如果已經關閉了再操作BH接口就會告警;
  • preempt_count_sub需要與preempt_count_add配套使用,用於操作thread_info->preempt_count字段,加與減的值是一致的,而在__local_bh_enable_ip接口中,將cnt值的減操作分成了兩步:preempt_count_sub(cnt-1)和preempt_count_dec,這麼做的原因是執行完preempt_count_sub(cnt-1)后,thread_info->preempt_count字段的值保留了1,把搶佔給關閉了,當do_softirq執行完畢后,再調用preempt_count_dec再減去剩下的1,進而打開搶佔;
  • 為什麼在使能Bottom-half時要進行軟中斷處理呢?在併發處理時,可能已經把Bottom-half進行關閉了,如果此時中斷來了后,軟中斷不會被處理,在進程上下文中打開Bottom-half時,這時候就會檢查是否有軟中斷處理請求了;

3. tasklet

從上文中分析可以看出,tasklet是軟中斷的一種類型,那麼兩者有啥區別呢?先說結論吧:

  • 軟中斷類型內核中都是靜態分配,不支持動態分配,而tasklet支持動態和靜態分配,也就是驅動程序中能比較方便的進行擴展;
  • 軟中斷可以在多個CPU上并行運行,因此需要考慮可重入問題,而tasklet會綁定在某個CPU上運行,運行完后再解綁,不要求重入問題,當然它的性能也就會下降一些;

3.1 數據結構

  • DEFINE_PER_CPU(struct tasklet_head, tasklet_vec)為每個CPU都分配了tasklet_head結構,該結構用來維護struct tasklet_struct鏈表,需要放到該CPU上運行的tasklet將會添加到該結構的鏈表中,內核中為每個CPU維護了兩個鏈表tasklet_vec和tasklet_vec_hi,對應兩個不同的優先級,本文以tasklet_vec為例;
  • struct tasklet_struct為tasklet的抽象,幾個關鍵字段如圖所示,通過next來鏈接成鏈表,通過state字段來標識不同的狀態以確保能在CPU上串行執行,func函數指針在調用task_init()接口時進行初始化,並在最終觸發軟中斷時執行;

3.2 流程分析

  • tasklet本質上是一種軟中斷,所以它的調用流程與上文中討論的軟中斷流程是一致的;
  • 調度tasklet運行的接口是tasklet_schedule,如果tasklet沒有被調度則進行調度處理,將該tasklet添加到CPU對應的鏈表中,然後調用raise_softirq_irqoff來觸發軟中斷執行;
  • 軟中斷執行的處理函數是tasklet_action,這個在softirq_init函數中通過open_softirq函數進行註冊的;
  • tasklet_action函數,首先將該CPU上tasklet_vec中的鏈表挪到臨時鏈表list中,然後再對這個list進行遍歷處理,如果滿足執行條件則調用t->func()執行,並continue跳轉遍歷下一個節點。如果不滿足執行條件,則繼續將該tasklet添加回原來的tasklet_vec中,並再次觸發軟中斷;

3.3 接口

簡單貼一下接口吧:

/* 靜態分配tasklet */
DECLARE_TASKLET(name, func, data)

/* 動態分配tasklet */
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

/* 禁止tasklet被執行,本質上是增加tasklet_struct->count值,以便在調度時不滿足執行條件 */
void tasklet_disable(struct tasklet_struct *t);

/* 使能tasklet,與tasklet_diable對應 */
void tasklet_enable(struct tasklet_struct *t);

/* 調度tasklet,通常在設備驅動的中斷函數里調用 */
void tasklet_schedule(struct tasklet_struct *t);

/* 殺死tasklet,確保不被調度和執行, 主要是設置state狀態位 */
void tasklet_kill(struct tasklet_struct *t);

收工!

歡迎關注個人公眾號,不定期分享Linux內核機制文章。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

聽說你的資源被盜用了,那你知道 Nginx 怎麼防盜鏈嗎?

上一篇文章講了 Nginx 中的變量和運行原理,下面就來說一個主要提供變量並修改變量的值的模塊,也就是我們要講的防盜鏈模塊:referer 模塊。

簡單有效的防盜鏈手段

場景

如果做過個人站點的同學,可能會遇到別人盜用自己站點資源鏈接的情況,這就是盜鏈。說到盜鏈就要說一個 HTTP 協議的 頭部,referer 頭部。當其他網站通過 URL 引用了你的頁面,用戶在瀏覽器上點擊 URL 時,HTTP 請求的頭部會通過 referer 頭部將該網站當前頁面的 URL 帶上,告訴服務器本次請求是由誰發起的。

例如,在谷歌中搜索 Nginx 然後點擊鏈接:

在打開的新頁面中查看請求頭會發現,請求頭中包含了 referer 頭部且值是 https://www.google.com/。

像谷歌這種我們是允許的,但是有一些其他的網站想要引用我們自己網站的資源時,就需要做一些管控了,不然豈不是誰都可以拿到鏈接。

目的

這裏目的其實已經很明確了,就是要拒絕非正常的網站訪問我們站點的資源。

思路

  • invalid_referer 變量
    • referer 提供了這個變量,可以用來配置哪些 referer 頭部合法,也就是,你允許哪些網站引用你的資源。

referer 模塊

要實現上面的目的,referer 模塊可得算頭一號,一起看下 referer 模塊怎麼用的。

  • 默認編譯進 Nginx,通過 --without-http_referer_module 禁用

referer 模塊有三個指令,下面看一下。

Syntax: valid_referers none | blocked | server_names | string ...;
Default: —
Context: server, location

Syntax: referer_hash_bucket_size size;
Default: referer_hash_bucket_size 64; 
Context: server, location

Syntax: referer_hash_max_size size;
Default: referer_hash_max_size 2048; 
Context: server, location
  • valid_referers 指令,配置是否允許 referer 頭部以及允許哪些 referer 訪問。
  • referer_hash_bucket_size 表示這些配置的值是放在哈希表中的,指定哈希表的大小。
  • referer_hash_max_size 則表示哈希表的最大大小是多大。

這裏面最重要的是 valid_referers 指令,需要重點來說明一下。

valid_referers 指令

可以同時攜帶多個參數,表示多個 referer 頭部都生效。

參數值

  • none
    • 允許缺失 referer 頭部的請求訪問
  • block:允許 referer 頭部沒有對應的值的請求訪問。例如可能經過了反向代理或者防火牆
  • server_names:若 referer 中站點域名與 server_name 中本機域名某個匹配,則允許該請求訪問
  • string:表示域名及 URL 的字符串,對域名可在前綴或者後綴中含有 * 通配符,若 referer 頭部的值匹配字符串后,則允許訪問
  • 正則表達式:若 referer 頭部的值匹配上了正則,就允許訪問

invalid_referer 變量

  • 允許訪問時變量值為空
  • 不允許訪問時變量值為 1

實戰

下面來看一個配置文件。

server {
	server_name referer.ziyang.com;
    listen 80;

	error_log logs/myerror.log debug;
	root html;
	location /{
		valid_referers none blocked server_names
               		*.ziyang.com www.ziyang.org.cn/nginx/
               		~\.google\.;
		if ($invalid_referer) {
    			return 403;
		}
		return 200 'valid\n';
	}
}

那麼對於這個配置文件而言,以下哪些請求會被拒絕呢?

curl -H 'referer: http://www.ziyang.org.cn/ttt' referer.ziyang.com/
curl -H 'referer: http://www.ziyang.com/ttt' referer.ziyang.com/
curl -H 'referer: ' referer.ziyang.com/
curl referer.ziyang.com/
curl -H 'referer: http://www.ziyang.com' referer.ziyang.com/
curl -H 'referer: http://referer.ziyang.com' referer.ziyang.com/
curl -H 'referer: http://image.baidu.com/search/detail' referer.ziyang.com/
curl -H 'referer: http://image.google.com/search/detail' referer.ziyang.com/

我們需要先來解析一下這個配置文件。valid_referers 指令配置了哪些值呢?

 valid_referers none blocked server_names
        *.ziyang.com www.ziyang.org.cn/nginx/
        ~\.google\.;
  • none:表示沒有 referer 的可以訪問
  • blocked:表示 referer 沒有值的可以訪問
  • server_names:表示本機 server_name 也就是 referer.ziyang.com 可以訪問
  • *.ziyang.com:匹配上了正則的可以訪問
  • www.ziyang.org.cn/nginx/:該頁面發起的請求可以訪問
  • ~\.google\.:google 前後都是正則匹配

下面就實際看下響應:

# 返回 403,沒有匹配到任何規則
  ~ curl -H 'referer: http://www.ziyang.org.cn/ttt' referer.ziyang.com/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
  ~ curl -H 'referer: http://image.baidu.com/search/detail' referer.ziyang.com/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
# 匹配到了 *.ziyang.com
  ~ curl -H 'referer: http://www.ziyang.com/ttt' referer.ziyang.com/
valid
  ~ curl -H 'referer: http://www.ziyang.com' referer.ziyang.com/
valid
# 匹配到了 server name
  ~ curl -H 'referer: http://referer.ziyang.com' referer.ziyang.com/
valid
# 匹配到了 blocked
  ~ curl -H 'referer: ' referer.ziyang.com/
valid
# 匹配到了 none
  ~ curl referer.ziyang.com/
valid
# 匹配到了 ~\.google\.
  ~ curl -H 'referer: http://image.google.com/search/detail' referer.ziyang.com/
valid

防盜鏈另外一種解決方案:secure_link 模塊

referer 模塊是一種簡單的防盜鏈手段,必須依賴瀏覽器發起請求才會有效,如果攻擊者偽造 referer 頭部的話,這種方式就失效了。

secure_link 模塊是另外一種解決的方案。

它的主要原理是,通過驗證 URL 中哈希值的方式防盜鏈。

基本過程是這個樣子的:

  • 由服務器(可以是 Nginx,也可以是其他 Web 服務器)生成加密的安全鏈接 URL,返回給客戶端
  • 客戶端使用安全 URL 訪問 Nginx,由 Nginx 的 secure_link 變量驗證是否通過

原理如下:

  • 哈希算法是不可逆的
  • 客戶端只能拿到執行過哈希算法的 URL
  • 僅生成 URL 的服務器,驗證 URL 是否安全的 Nginx,這兩者才保存原始的字符串
  • 原始字符串通常由以下部分有序組成:
    • 資源位置。如 HTTP 中指定資源的 URI,防止攻擊者拿到一個安全 URI 后可以訪問任意資源
    • 用戶信息。如用戶的 IP 地址,限制其他用戶盜用 URL
    • 時間戳。使安全 URL 及時過期
    • 密鑰。僅服務器端擁有,增加攻擊者猜測出原始字符串的難度

模塊:

  • ngx_http_secure_link_module
    • 未編譯進 Nginx,需要通過 –with-http_secure_link_module 添加
  • 變量
    • secure_link
    • secure_link_expires
Syntax: secure_link expression;
Default: —
Context: http, server, location

Syntax: secure_link_md5 expression;
Default: —
Context: http, server, location

Syntax: secure_link_secret word;
Default: —
Context: location

變量值及帶過期時間的配置示例

  • secure_link
    • 值為空字符串:驗證不通過
    • 值為 0:URL 過期
    • 值為 1:驗證通過
  • secure_link_expires
    • 時間戳的值

命令行生成安全鏈接

  • 生成 md5
echo -n '時間戳URL客戶端IP密鑰' | openssl md5 -binary | openssl base64 | tr +/ - | tr -d =
  • 構造請求 URL
/test1.txt?md5=md5生成值&expires=時間戳(如 2147483647)

Nginx 配置

  • secure_link $arg_md5,$arg_expires;
    • secure_link 後面必須跟兩個值,一個是參數中的 md5,一個是時間戳
  • secure_link_md5 “$secure_link_expires$uri$remote_addr secret”;
    • 按照什麼樣的順序構造原始字符串

實戰

下面是一個實際的配置文件,我這裏就不做演示了,感興趣的可以自己做下實驗。

server {
	server_name securelink.ziyang.com;
    listen 80;
	error_log  logs/myerror.log  info;
	default_type text/plain;
	location /{
		secure_link $arg_md5,$arg_expires;
        secure_link_md5 "$secure_link_expires$uri$remote_addr secret";

        if ($secure_link = "") {
            return 403;
        }

        if ($secure_link = "0") {
            return 410;
        }

		return 200 '$secure_link:$secure_link_expires\n';
	}

	location /p/ {
        secure_link_secret mysecret2;

        if ($secure_link = "") {
            return 403;
        }

        rewrite ^ /secure/$secure_link;
	}

	location /secure/ {
		alias html/;
    	internal;
	}
}

僅對 URI 進行哈希的簡單辦法

除了上面這種相對複雜的方式防盜鏈,還有一種相對簡單的防盜鏈方式,就是只對 URI 進行哈希,這樣當 URI 傳

  • 將請求 URL 分為三個部分:/prefix/hash/link
  • Hash 生成方式:對 “link 密鑰” 做 md5 哈希
  • 用 secure_link_secret secret; 配置密鑰

命令行生成安全鏈接

  • 原請求
    • link
  • 生成的安全請求
    • /prefix/md5/link
  • 生成 md5
    • echo -n 'linksecret' | openssl md5 –hex

Nginx 配置

  • secure_link_secret secret;

這個防盜鏈的方法比較簡單,那麼具體是怎麼用呢?大家都在網上下載過資源對吧,不管是电子書還是軟件,很多網站你點擊下載的時候往往會彈出另外一個頁面去下載,這個新的頁面其實就是請求的 Nginx 生成的安全 URL。如果這個 URL 被拿到的話,其實還是可以用的,所以需要經常的更新密鑰來確保 URL 不會被盜用。

今天這篇文章詳細講了防盜鏈的具體用法,最近的這兩篇文章都是說的已有的變量用法,下一篇文章講一下怎麼生成新的變量。

本文首發於我的個人博客:iziyang.github.io,所有配置文件我已經放在了 Nginx 配置文件,大家可以自取。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

04 . Docker安全與Docker底層實現

Docker安全

Docker安全性時,主要考慮三個方面

# 1. 由內核的名字空間和控制組機制提供的容器內在安全
# 2. Docker程序(特別是服務端)本身的抗攻擊性
# 3. 內核安全性的加強機制對容器安全性的影響
內核命名空間

Docker容器和LXC容器很相似,所提供的安全特性也差不多。當用docker run啟動一個容器時,在後台Docker為容器創建了一個獨立的命名空間和控制組集合。

命名空間提供了最基礎也是最直接的隔離,在容器中運行的進程不會被運行在主機上的進程和其它容器發 現和作用。

每個容器都有自己獨有的網絡棧,意味着它們不能訪問其他容器的sockets或接口。不過,如果主機系統上做了相應的設置,容器可以像跟主機交互一樣的和其他容器交互。當指定公共端口或使用links來連接2個容器時,容器就可以相互通信了(可以根據配置來限制通信的策略).

從網絡架構的角度來看,所有的容器通過本地主機的網橋接口相互通信,就像物理機器通過物理交換機通信一樣。

那麼,內核中實現命名空間和私有網絡的代碼是否足夠成熟?

內核名字空間從2.6.15版本(2008年7月發布)之後被引入,數年間,這些機制的可靠性在諸多大型生產系統中被實踐驗證。

實際上,名字空間的想法和設計提出的時間要更早,最初是為了在內核中引入一種機制來實現 OpenVZ 的特性。而OpenVZ項目早在2005年就發布了,其設計和實現都已經十分成熟。

控制組

控制組是Linux容器機制的另外一個關鍵組件,負責實現資源的審計和限制.

它提供了很多有用的特性;以及確保各個容器可以公平地分享主機的內存、CPU、磁盤IO等資源;當然,更重要的是,控制組確保了當容器內的資源使用產生壓力時不會連累主機系統。

儘管控制組不負責隔離容器之間相互訪問、處理數據和進程,它在防止拒絕服務(DDOS)攻擊方面是必不可少的。尤其是在多用戶的平台(比如公有或私有的PaaS)上,控制組十分重要。例如,當某些應用程序表現異常的時候,可以保證一致地正常運行和性能。

控制組始於2006年,內核從2.6.24版本開始被引入.

Docker服務端的防護

運行一個容器或應用程序的核心是通過Docker服務端。Docker服務的運行目前需要root權限,因此其安全性十分關鍵。

首先,確保只有可信的用戶才可以訪問Docker服務。Docker允許用戶在主機和容器間共享文件夾,同時不需要限制容器的訪問權限,這就容易讓容器突破資源限制。例如,惡意用戶啟動容器的時候將主機的根目錄/映射到容器的 /host目錄中,那麼容器理論上就可以對主機的文件系統進行任意修改了。這聽起來很瘋狂?但是事實上幾乎所有虛擬化系統都允許類似的資源共享,而沒法禁止用戶共享主機根文件系統到虛擬機系統

這將會造成很嚴重的安全後果。因此,當提供容器創建服務時(例如通過一個web服務器),要更加註意進行參數的安全檢查,防止惡意的用戶用特定參數來創建一些破壞性的容器

為了加強對服務端的保護,Docker的REST API(客戶端用來跟服務端通信)在0.5.2之後使用本地的Unix套接字機制替代了原先綁定在 127.0.0.1 上的TCP套接字,因為後者容易遭受跨站腳本攻擊。現在用戶使用Unix權限檢查來加強套接字的訪問安全。

用戶仍可以利用HTTP提供REST API訪問。建議使用安全機制,確保只有可信的網絡或VPN,或證書保 護機制(例如受保護的stunnel和ssl認證)下的訪問可以進行。此外,還可以使用HTTPS和證書來加強 保護。

最近改進的Linux名字空間機制將可以實現使用非root用戶來運行全功能的容器。這將從根本上解決了容器和主機之間共享文件系統而引起的安全問題。

終極目標是改進 2 個重要的安全特性:

  • 將容器的root用戶映射到本地主機上的非root用戶,減輕容器和主機之間因權限提升而引起的安全問題;
  • 允許Docker服務端在非root權限下運行,利用安全可靠的子進程來代理執行需要特權權限的操作。這些子進程將只允許在限定範圍內進行操作,例如僅僅負責虛擬網絡設定或文件系統管理、配置操作等。
  • 最後,建議採用專用的服務器來運行Docker 和相關的管理服務(例如管理服務比如ssh監控和進程監控、管理工具nrpe、collectd等)。其它的業務服務都放到容器中去運行。
內核能力機制

能力機制(Capability)是Linux內核一個強大的特性,可以提供細粒度的權限訪問控制。 Linux內核自2.2版本起就支持能力機制,它將權限劃分為更加細粒度的操作能力,既可以作用在進程上,也可以作用在文件上。

例如,一個Web服務進程只需要綁定一個低於1024的端口的權限,並不需要root權限。那麼它只需要被授權 net_bind_service能力即可。此外,還有很多其他的類似能力來避免進程獲取root權限。

默認情況下,Docker啟動的容器被嚴格限制只允許使用內核的一部分能力.

使用能力機制對加強Docker容器的安全有很多好處。通常,在服務器上會運行一堆需要特權權限的進程,包括有ssh、cron、syslogd、硬件管理工具模塊(例如負載模塊)、網絡配置工具等等。容器跟這些進程是不同的,因為幾乎所有的特權進程都由容器以外的支持系統來進行管理。

# 1. ssh訪問被主機上ssh服務來管理;
# 2. cron通常應該作為用戶進程執行執行,權限交給使用他服務的應用來處理;
# 3. 日誌系統可由Docker或第三方服務管理;
# 4. 硬件管理無關緊要,容器中也就無需執行udevd以及類似服務;
# 5. 網絡管理也都在主機上設置,除非特殊需求,容器不需要對網絡進行配置.

從上面的例子可以看出,大部分情況下,容器並不需要真正的root權限,容器只需要少數的能力即可.為了加強安全,容器可以禁用一些沒必要的權限:

# 1. 完全禁止任何mount操作.
# 2. 禁止直接訪問本地主機的套接字.
# 3. 禁止訪問一些文件系統的操作,比如創建新的設備,修改文件屬性等.
# 4. 禁止模塊加載.

這樣,就算攻擊者在容器中取得了root權限,也不能獲得本地主機的較高權限,能進行的破壞也有限.

默認認情況下,Docker採用 白名單 機制,禁用必需功能之外的其它權限。 當然,用戶也可以根據自身需求來為Docker容器啟用額外的權限。

其他安全特性.

除了能力機制之外,還可以利用一些現有的安全機制來增強docker的安全性,例如TOMOYO,AppArmor,SELinux,GRSEC等.

Docker 當前默認只開啟了能力機制,用戶可以採用多種方案來加強Docker主機的安全,例如:

  1. 在內核中啟用GRSEC和PAX,這將增加很多編譯和運行時的安全檢查,通過地址隨機化避免惡意探測等,並且,啟用該特性不需要Docker進行任何配置.

  2. 使用一些有增強安全特性的容器模板,比如帶AppArmor的模板和Redhat帶Selinux策略的模板.這些模板提供了額外的安全特性.

  3. 用戶可以自定義訪問控制機制來定製安全策略.

跟其他添加Docker容器的第三方工具一樣(比如網絡拓撲和文件系統共享),有很多類似的機制,在不改變Docker內核情況下就可以加固現有的容器.

小結

總體來說,Docker容器還是十分安全的,特別是在容器不使用root權限來運行進程的話.

另外,用戶可以使用現有工具,比如Apparmor,SELinux,GRSEC來增強安全性,甚至自己在內核中實現更複雜的安全機制.

Docker底層實現

Docker底層的核心技術包括Linux上的命名空間(Namespaces)、控制組(ControlGroups),Union文件系統(Union file systems)和容器格式(Container format).

我們知道,傳統的虛擬機通過在宿主主機中運行hypervisor來模擬一套完整的硬件環境提供給虛擬機的操作系統.虛擬機系統看到的環境是可限制的,也是彼此隔離的,這種直接的做法實現了對資源完整的封裝,但很多時候往往意味着系統資源的浪費,例如,以宿主機和虛擬機系統都為linux系統為例,虛擬機中運行的應用其實是可以利用宿主機系統中的運行環境。

我們知道,在操作系統中,包括內核、文件系統、網絡、PID、UID、IPC、內存、硬盤、CPU等等,所有的資源都是應用進程直接共享的,要想實現虛擬化,除了要實現對內存、CPU、網絡IO、硬盤IO、存儲空間等的限制外,還要實現文件系統、網絡、PID、UID、IPC等等的相互隔離,前者相對容易實現一些,後者則需要宿主機系統的深入支持.

隨着Linux系統對於命名空間功能的完善實現,程序員可以實現上面的所有需要,讓某些進程在彼此隔離的命名空間中運行,大家雖然都共用一個內核和某些運行時環境(l例如一些系統命令和系統等),但是彼此卻看不到,大家都以為系統中只有自己的存在,這種機制就是容器,利用命名空間來做權限的隔離控制,利用cgroups來做資源分配.

容器的基本架構

Dcoker採用了c/s架構,包括客戶端和服務端,Docker守護進程(Daemon)作為服務端接受來自客戶端的請求,並處理這些請求(創建、運行、分發容器).

客戶端和服務端既可以運行在一個機器上,也可以通過socket或者RESTful API來進行通信.

Docker守護進程一般在宿主主機後台運行,等待來自客戶端的消息.

Docker客戶端則為用戶提供一系列可執行的命令,用戶用這些命令實現跟Docker守護進程交互.

命名空間

命名空間是Linux內核一個強大的特性,每個容器都有自己單獨的命名空間,運行在其中的應用都像是在獨立的操作系統運行一樣,命名空間保證了容器之間彼此互不影響.

pid命名空間

不同用戶的進程就是通過pid命名空間隔離開的,且不同命名空間可以有相同的pid,所有的LXC進程在Docker中的父進程為Docker進程,每個LXC進程具有不同的命名空間,同時由於嵌套,因此可以很方便的實現嵌套的Docker容器.

net命名空間

有了pid命名空間,每個命名空間的pid能夠實現相互隔離,但是網絡端口還是共享host的端口,網絡隔離是通過net命名空間實現的,每個net命名空間有單獨的網絡設備,IP地址,路由表,/proc/net目錄,這樣每個容器的網絡就能隔離開來,Docker默認採用veth的方式,將容器中的虛擬網卡host上的一個Docker網橋docker0連接在一起.

IPC命名空間

容器中進程交互採用了Linux常見的進程交互方法,包括信號量,消息隊列和共享內存等,然而同VM不同的是,容器的進程交互實際山還是host上具有相同pid命名空間的進程交互,因此需要在IPC資源中申請加入命名空間信息,每個IPC資源有一個唯一的32位id。

mnt命名空間

類似chroot,將一個進程放到一個特定的目錄執行,mnt命名空間允許不同命名空間的進程看到的文件結構不同,這樣每個命名空間中的進程所看到的文件目錄就被隔離開了,同chroot不同,每個命名空間的容器在/proc/mounts的信息只包含所在命名空間的mount point。

uts命名空間

UTS命名空間允許每個容器擁有獨立的hostname和domain name,使其在網絡上可以被視作一個獨立的節點而非主機上的一個進程.

每個容器可以有不同的用戶和組id,也就是說可以在容器內用容器內部的用戶執行程序而非主機上的用戶.

控制組

控制組(cgroups)是一個Linux內核的一個特性,主要用來對資源進行隔離、限制、審計等,只有能控制分配到容器的資源,才能避免當多個容器同時運行時對系統資源的競爭.

控制組技術最早由Google的程序員在2006年提出,Linux內核從2.6.24開始支持.

控制組可以提供對容器的內存、CPU、磁盤IO等資源的限制和審計管理.

聯合文件系統

聯合文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,他支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統.

聯合文件系統是Docker鏡像的基礎,鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以製作各種具體的應用鏡像.

另外,不同Docker容器就可以共享一些基礎的文件系統層,同時加上自己獨有的改動層,大大提高了存儲的效率.

Docker中使用的AUFS就是一種聯合文件系統,AUFS支持為每一個成員目錄(類似Git的分支)設定只讀(readonly)、讀寫(readwrite)和寫出(whiteout-able)權限,同時AUFS里有一個類似分層的概念,對只讀權限的分支可以邏輯上進行增量的修改(不影響只讀部分的).

Docker目前支持的聯合文件系統包括OverlayFS,AUFS,BtrFS,VFS,ZFS和Device Mapper。

有可能的情況下,推薦使用overlay2存儲驅動,overlay2是目前Docker默認的存儲驅動,以前是aufs,可以通過配置以上提到的其他類型存儲驅動.

容器格式

最初,Docker採用了LXC中的容器格式,從0.7版本開始以後去除LXC,轉而使用自行開發的libcontainer,從1.11開始,進一步演進為runC和containerd。

網絡實現

Docker的網絡實現其實就是利用了Linux上的網絡命名空間和虛擬網絡設備(特別是vethpair).

基本原理

首先,要實現網絡通信,機器需要至少一個網絡接口(物理接口或虛擬接口)來收發數據包,此外,如果不同子網之間要進行通信,需要路由機制.

Docker中的網絡接口默認都是虛擬的接口,虛擬接口的優勢之一就是轉發效率較高,Linux通過在內核中進行數據複製來實現虛擬接口之間的數據轉發,發送接口的發送緩存中的數據包直接複製到接收接口的接受緩存中,對於本地系統和容器內系統來看就像是一個正常的以太網卡,只是他不需要真正同外部網絡設備通信,速度要快很多.

Docker容器網絡就是利用了這項技術,他在本地主機和容器內分別創建一個虛擬接口,並讓他們彼此連通(這樣的一對接口叫做veth pair).

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

※網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Java 異常處理的十個建議

前言

Java異常處理的十個建議,希望對大家有幫助~

本文已上傳github:

https://github.com/whx123/JavaHome

公眾號:撿田螺的小男孩

一、盡量不要使用e.printStackTrace(),而是使用log打印。

反例:

try{
  // do what you want  
}catch(Exception e){
  e.printStackTrace();
}

正例:

try{
  // do what you want  
}catch(Exception e){
  log.info("你的程序有異常啦,{}",e);
}

理由:

  • printStackTrace()打印出的堆棧日誌跟業務代碼日誌是交錯混合在一起的,通常排查異常日誌不太方便。
  • e.printStackTrace()語句產生的字符串記錄的是堆棧信息,如果信息太長太多,字符串常量池所在的內存塊沒有空間了,即內存滿了,那麼,用戶的請求就卡住啦~

二、catch了異常,但是沒有打印出具體的exception,無法更好定位問題

反例:

try{
  // do what you want  
}catch(Exception e){
  log.info("你的程序有異常啦");
}

正例:

try{
  // do what you want  
}catch(Exception e){
  log.info("你的程序有異常啦,{}",e);
}

理由:

  • 反例中,並沒有把exception出來,到時候排查問題就不好查了啦,到底是SQl寫錯的異常還是IO異常,還是其他呢?所以應該把exception打印到日誌中哦~

三、不要用一個Exception捕捉所有可能的異常

反例:

public void test(){
    try{
        //…拋出 IOException 的代碼調用
        //…拋出 SQLException 的代碼調用
    }catch(Exception e){
        //用基類 Exception 捕捉的所有可能的異常,如果多個層次都這樣捕捉,會丟失原始異常的有效信息哦
        log.info(“Exception in test,exception:{}”, e);
    }
}

正例:

public void test(){
    try{
        //…拋出 IOException 的代碼調用
        //…拋出 SQLException 的代碼調用
    }catch(IOException e){
        //僅僅捕捉 IOException
        log.info(“IOException in test,exception:{}”, e);
    }catch(SQLException e){
        //僅僅捕捉 SQLException
        log.info(“SQLException in test,exception:{}”, e);
    }
}

理由:

  • 用基類 Exception 捕捉的所有可能的異常,如果多個層次都這樣捕捉,會丟失原始異常的有效信息哦

四、記得使用finally關閉流資源或者直接使用try-with-resource

反例:

FileInputStream fdIn = null;
try {
    fdIn = new FileInputStream(new File("/jay.txt"));
    //在這裏關閉流資源?有沒有問題呢?如果發生異常了呢?
    fdIn.close();
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}

正例1:

需要使用finally關閉流資源,如下

FileInputStream fdIn = null;
try {
    fdIn = new FileInputStream(new File("/jay.txt"));
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}finally {
    try {
        if (fdIn != null) {
            fdIn.close();
        }
    } catch (IOException e) {
        log.error(e);
    }
}

正例2:

當然,也可以使用JDK7的新特性try-with-resource來處理,它是Java7提供的一個新功能,它用於自動資源管理。

  • 資源是指在程序用完了之後必須要關閉的對象。
  • try-with-resources保證了每個聲明了的資源在語句結束的時候會被關閉
  • 什麼樣的對象才能當做資源使用呢?只要實現了java.lang.AutoCloseable接口或者java.io.Closeable接口的對象,都OK。
try (FileInputStream inputStream = new FileInputStream(new File("jay.txt")) {
    // use resources   
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}

理由:

  • 如果不使用finally或者try-with-resource,當程序發生異常,IO資源流沒關閉,那麼這個IO資源就會被他一直佔著,這樣別人就沒有辦法用了,這就造成資源浪費。

五、捕獲異常與拋出異常必須是完全匹配,或者捕獲異常是拋異常的父類

反例:

//BizException 是 Exception 的子類
public class BizException extends Exception {}
//拋出父類Exception
public static void test() throws Exception {}

try {
    test(); //編譯錯誤
} catch (BizException e) { //捕獲異常子類是沒法匹配的哦
    log.error(e);
}

正例:

//拋齣子類Exception
public static void test() throws BizException {}

try {
    test();
} catch (Exception e) {
    log.error(e);
}

六、捕獲到的異常,不能忽略它,至少打點日誌吧

反例:

public static void testIgnoreException() throws Exception {
    try {       
        // 搞事情
    } catch (Exception e) {     //一般不會有這個異常
        
    }
}

正例:

public static void testIgnoreException() {
    try {
        // 搞事情
    } catch (Exception e) {     //一般不會有這個異常
        log.error("這個異常不應該在這裏出現的,{}",e); 
    }
}

理由:

  • 雖然一個正常情況都不會發生的異常,但是如果你捕獲到它,就不要忽略呀,至少打個日誌吧~

七、注意異常對你的代碼層次結構的侵染(早發現早處理)

反例:

public UserInfo queryUserInfoByUserId(Long userid) throw SQLException {
    //根據用戶Id查詢數據庫
}

正例:

public UserInfo queryUserInfoByUserId(Long userid) {
    try{
        //根據用戶Id查詢數據庫
    }catch(SQLException e){
        log.error("查詢數據庫異常啦,{}",e);
    }finally{
        //關閉連接,清理資源
    }
}

理由:

  • 我們的項目,一般都會把代碼分 Action、Service、Dao 等不同的層次結構,如果你是DAO層處理的異常,儘早處理吧,如果往上 throw SQLException,上層代碼就還是要try catch處理啦,這就污染了你的代碼~

八、自定義封裝異常,不要丟棄原始異常的信息Throwable cause

我們常常會想要在捕獲一個異常后拋出另一個異常,並且希望把原始異常的信息保存下來,這被稱為異常鏈。公司的框架提供統一異常處理就用到異常鏈,我們自定義封裝異常,不要丟棄原始異常的信息,否則排查問題就頭疼啦

反例:

public class TestChainException {
    public void readFile() throws MyException{
        try {
            InputStream is = new FileInputStream("jay.txt");
            Scanner in = new Scanner(is);
            while (in.hasNext()) {
                System.out.println(in.next());
            }
        } catch (FileNotFoundException e) {
            //e 保存異常信息
            throw new MyException("文件在哪裡呢");
        }
    }
    public void invokeReadFile() throws MyException{
        try {
            readFile();
        } catch (MyException e) {
            //e 保存異常信息
            throw new MyException("文件找不到");
        }
    }
    public static void main(String[] args) {
        TestChainException t = new TestChainException();
        try {
            t.invokeReadFile();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}
//MyException 構造器
public MyException(String message) {
        super(message);
    }

運行結果如下,沒有了Throwable cause,不好排查是什麼異常了啦

正例:


public class TestChainException {
    public void readFile() throws MyException{
        try {
            InputStream is = new FileInputStream("jay.txt");
            Scanner in = new Scanner(is);
            while (in.hasNext()) {
                System.out.println(in.next());
            }
        } catch (FileNotFoundException e) {
            //e 保存異常信息
            throw new MyException("文件在哪裡呢", e);
        }
    }
    public void invokeReadFile() throws MyException{
        try {
            readFile();
        } catch (MyException e) {
            //e 保存異常信息
            throw new MyException("文件找不到", e);
        }
    }
    public static void main(String[] args) {
        TestChainException t = new TestChainException();
        try {
            t.invokeReadFile();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}
//MyException 構造器
public MyException(String message, Throwable cause) {
        super(message, cause);
    }

九、運行時異常RuntimeException ,不應該通過catch 的方式來處理,而是先預檢查,比如:NullPointerException處理

反例:

try {
  obj.method() 
} catch (NullPointerException e) {
...
}

正例:

if (obj != null){
   ...
}

十、注意異常匹配的順序,優先捕獲具體的異常

注意異常的匹配順序,因為只有第一個匹配到異常的catch塊才會被執行。如果你希望看到,是NumberFormatException異常,就拋出NumberFormatException,如果是IllegalArgumentException就拋出IllegalArgumentException。

反例:

try {
    doSomething("test exception");
} catch (IllegalArgumentException e) {       
    log.error(e);
} catch (NumberFormatException e) {
    log.error(e);
}

正例:

try {
    doSomething("test exception");
} catch (NumberFormatException e) {       
    log.error(e);
} catch (IllegalArgumentException e) {
    log.error(e);
}

理由:

  • 因為NumberFormatException是IllegalArgumentException 的子類,反例中,不管是哪個異常,都會匹配到IllegalArgumentException,就不會再往下執行啦,因此不知道是否是NumberFormatException。所以需要優先捕獲具體的異常,把NumberFormatException放前面~

公眾號

  • 歡迎關注我個人公眾號,交個朋友,一起學習哈~
  • 如果答案整理有錯,歡迎指出哈,感激不盡~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!