如果只買日系車,7-50萬人生不同階段你會選這8台車嗎?

預算15-18萬本田思域辣評:18萬以內最值得年輕人選擇的車型,擁有超越同級別的動力水準,本田大法是無敵的。馬自達昂克賽拉辣評:其實還是推薦2。0L版本,畢竟單純操控好動力跟不上一樣很煩心。如果家中有條件的消費者,銳志、阿特茲(2。

你會發現這樣一個規律:不同地域的消費者在選車時都會有不同程度的系別偏好,譬如我們最常說到的“北方愛德系、南方愛日系”的說法,而且也有人通過一系列的統計調查,也證實了這一句話的真實性,簡單來說,南方人確實偏向於選擇日系車型。

儘管有不少愛國人士在主觀上認為

買日系車=不愛國

但實際上並非如此,消費者受到地域、文化、經濟、交通等多方面影響,導致南方人和北方人購車觀念截然不同。買日系車其實並沒有錯(況且如今大多都是中日合資),拋開所謂的愛國情懷來說,日系車一直以來都是“可靠性高、省油”的代名詞,譬如我們最熟悉的“車到山前必有路,有路必有豐田車”這一類關於質量的廣告語。

在此之前,我們出了一篇文章,在30歲之前如果一定要買大眾車,該怎麼選(傳送門–鏈接到:網傳30歲前該買這9款車!看完淚崩…)?引起了網友的熱議,而這次我們繼續借題發揮:

對於一個南方人來說,在不同的年齡層次應該選擇一台什麼樣的日系車?

18-25歲的年輕消費者

是人生當中最應該衝動的階段

你應該以滿足自己需求為主

簡單來說,年輕人再不瘋狂就要老了!是實話,其實在這個年齡層次能買得起車的消費者主要有兩類:

第一類是家中的經濟條件不錯,畢業工作了,也是應該買台車(普遍的南方家庭都有這樣的想法)。

另外一類就是有獨立的商業頭腦以及機遇、掙錢了自己買。

值得一說的是,剛畢業不久、拿着實習生的工資還打算獨立買車?確實比較困難。對於前者來說,說句實話,應該買什麼車全看家裡給多少錢,但對於普遍家庭來說,一般都會將購車預算控制在18萬以內。針對於此,對於不少年輕消費者而言,其實實際用車環境都是日常出行,而且常載人數大多是1-2人,所以在選車時會更偏向於動力強、操控感強的車型。至於空間,其實關注度並不高。

預算10萬以內

本田飛度

辣評:10級別以內動力表現最佳而且極具改裝潛力的車型,神車地位無可撼動、

預算15萬以內

豐田卡羅拉

辣評:動力表現並不突出,純粹屬於一台家用車型,讓你無比省心,當然也是跑滴滴快車的神器之一。

預算15-18萬

本田思域

辣評:18萬以內最值得年輕人選擇的車型,擁有超越同級別的動力水準,本田大法是無敵的!

馬自達昂克賽拉

辣評:其實還是推薦2.0L版本,畢竟單純操控好動力跟不上一樣很煩心。

如果家中有條件的消費者,銳志、阿特茲(2.5L版本)、思鉑睿(2.4L版本)這一類的運動型B級車也是一個非常不錯的選擇。

針對於另外一部分的人群,在這個階段能夠自己創造出條件購車除了機遇占很大一部分之外,更多的是個人的能力,而相信這部分人群在選車時的起步點也會隨之提高,譬如可以直接考慮更高一個級別的車型。

來到25-30歲的消費者

大多數都有一定的事業基礎

而且大多數都是有家庭之人

針對這部分消費者來說,其實在選車理念方面也會發生相應的變化,譬如在選購時並非以自己為主,而更多的去考慮到家庭的需求,所以在選車方面以舒適、空間為主。另外,根據中國消費者的習性,換車也必須得越換越好。

18-26萬左右居家人士首選

本田雅閣

辣評:在擁有足夠大氣的外觀滿足你日常商務需求的同時,超大的乘坐空間更能夠滿足到你家庭日常所需。宜商宜家,成功人士首選車型。

針對於實用性,不少消費者也將目光投向SUV,譬如

豐田漢蘭達

辣評:一直加價從不停歇,但就是這麼受歡迎,最大的賣點便是空間極大、質量極好、車子極為大氣上檔次。

30歲以上的消費者

其實正處於整個人生的巔峰期

有家庭、有事業

對生活更有追求!

對於這部分消費者來說,隨着生活質量提高也會逐漸變為享受型,到了這時候一輛車子不再是滿足日常出行代步需求,更能與你自身的氣質相輔相成,在這個級別的消費者大多會直接選擇豪華品牌。只是價格越往上走你會發現,可以選擇的豪華日系車並不多!而在很多南方消費者看來,人的一生總應該擁有一輛雷克薩斯!而針對這部分有條件的消費者,選車怎麼也得從ES以上的車型開始選擇!

說到最後,如果你的家底十分豐厚或者突然成了拆遷戶,以上所有推薦都可以直接推翻,至於買什麼車?喜歡就好啦!

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

月銷4萬輛還優惠1多萬元?暗訪4款熱門SUV優惠的真實情況

從上圖中我們可以看到除了一些常規的保險費用以外,多了一項綜合服務費的收費項目。經過我們向銷售諮詢,該項服務費主要包括:pDI檢測費、洗車費等一些雜費。聽完銷售跟我們詳細的講解后,內心是這樣的。所以說4S店的套路永遠都是非常多的,不過只要你詳細問下去,4S店的銷售也並不能說得清楚這筆費用究竟是做什麼的。

無論是買手機還是買電腦,作為消費者的我們肯定非常關注這款手機或者電腦的銷量情況,越多人購買的產品一定是我們需要購買到的好產品。

不同的是,在國內買車是家庭中一件非常大消費的購物行為,所以也有不少精明的消費者參考銷量榜進行買車,再參考各大汽車網站推送出來的優惠幅度,興奮地跑去當地的4S店諮詢,結果是這樣的……

談起買車經歷,很多人都覺得自己被4S店的銷售小賺了一筆,然後羡慕別人拿到一個比自己更低的購車價格,卻不知道應該從哪一項費用上跟銷售談優惠,所以我們以暗訪的形式針對廣州本地的4S店一探究竟。

不愁銷量,價格堅挺!

看完小標題的讀者,肯定能基本猜測出這是哪一款國產SUV,它就是在銷量榜排在第一位的哈弗H6。每個月都能夠獲得不錯的銷量,以至於我們在進入哈弗4S店的時候,被一群哈弗H6圍住,可想而知4S店的老闆笑得多開心。

全新的哈弗H6與老款車型對比,最大的亮點就是十分霸氣的外觀,讓人感覺這款SUV大氣、穩重。不過今天我們主要來調侃銷售,看看如此熱門的一款國產SUV究竟能夠拿到多少優惠。

在諮詢哈弗H6的優惠前,我們先詢問銷售哪一款車型賣得比較好,哪款車身顏色比較熱門,店裡庫存的現車多不多?

在談論價格優惠之前,我們便在網上了解了哈弗H6的價格優惠幅度,根據網上不同的汽車網站显示優惠幅度在1.4萬。不過我們在諮詢銷售的時候,優惠1.4萬的車型並不是全新的哈弗H6,而是舊款車型,否則賣不出去。

由於這款車型屬於2.0T排量車型,所以無法享受購置稅的優惠,廠家為此給這款車型優惠三千。除此以外,並沒有其他現金上的優惠,看來在價格方面還是比較堅挺的,不過我們表示急需用車的時候,銷售突然為我們推薦一個到店活動,優惠當天公布,我們預測現金優惠應該在1-2千左右+大禮包之類。

對於在城市裡面打拚的消費者來說,買車需要家裡人的支持,所以我們這次要求銷售對一次性和貸款兩種方式進行說明,我們可以為讀者提供最划算的買車方式。

不管是全款購車還是分期購車,銷售都是以優惠完3千購置稅的價錢進行接下來的計算。我們先以保險費用一樣的價格進行計算,如果選擇全款購車情況,僅僅在上牌費和貸款手續費上都為自己優惠了接近7千多塊,最後的全款購車價格159819元的基礎上減去七千。

4S店給出的貸款政策優惠的確非常吸引,特別是後面“免息”二字。如果以6萬進行計算,那麼首付需要7.68萬,對於很多消費者來說還想獲得更低的首付,此時銷售告知我們,能貸款到9萬。

與全款購車的價格對比,貸款的費用越高,分期的時間越長,所花費的錢就越多。所以如果需要分期購車,不妨考慮貸款費用和貸款年限在4S店政策支持範圍內。

現金優惠8千,不要精品再優惠五千!

如今在銷量榜上排在第四位的傳祺GS4,優惠幅度還是挺吸引人的。在現金優惠的幅度上與哈弗H6相比,兩者相差接近三倍。我們以同樣的預算,要求銷售給我們推薦店裡最熱銷的車型。

在談論價格優惠之前,我們便在網上同樣參考GS4的價格優惠幅度,根據網上不同的汽車網站显示優惠幅度在1.46萬。同樣這些巨額優惠也就只能吸引客戶到店,實際消費者想購買的車型並沒有這麼大優惠。

在談價格前,銷售非常直接告知我們目前這款車型的優惠是8000元,除此以外就沒有任何優惠了。然後我們便直接開始聊全款和分期的事情。

從上圖中我們可以看到除了一些常規的保險費用以外,多了一項綜合服務費的收費項目。經過我們向銷售諮詢,該項服務費主要包括:pDI檢測費、洗車費等一些雜費。聽完銷售跟我們詳細的講解后,內心是這樣的……

所以說4S店的套路永遠都是非常多的,不過只要你詳細問下去,4S店的銷售也並不能說得清楚這筆費用究竟是做什麼的。那麼就有機會爭取更多的優惠價格了。

相比哈弗H6來說,GS4的手續費便宜500元,同時在按揭的利息上也有一定的區別,GS4貸款年限24期所需利息更低,相反哈弗h6需要12%。等我們聊完分期購車的事情后,故事情節好像發生了改變。

對於購車大禮包這些東西,很多4S店都為他們貼上“原廠”的標籤,瞬間價格翻了幾倍,聽起來也更高大上了。作為唯一一間答應我們不要禮包換取優惠的店,可以得到購車優惠還是能夠繼續砍下去,根據以上銷售提供的優惠,這款車大概優惠在1.4萬左右。

現金優惠+利息補貼,不同的優惠方式!

來到長安的4S店,我們同樣將購車預算購車意向告知銷售顧問,便得到長安CS75比較熱銷的車型以及庫存等情況。

出乎意外的是,到店后銷售所給出的優惠幅度與我們在網上不同的汽車網站上看到的優惠幅度有一定的相差,因為在來之前我們查詢到長安CS75的現金優惠只有2千元,但是到店后所得到的是接近四千元。想必這肯定是國慶庫存太多所導致的結果,否則不會出現高於網上參考的優惠幅度。

為了更切合實際,這次CS75的價格優惠全部以貸款購車形式,對比一下首付多與少哪種更貼合自身的實際情況。

相信現在很多的消費者在購買金額比較大的商品都會有一種習慣,就是最好首付更低、分期時間更長,這樣沒有太大的生活壓力。的確,這次我們採用不同的首付金額,得出的結論就是:首付能給多點盡量給多點,所需貸款的金額也減少,每個月需要還的金額也隨之減低。

同時,4S店的購車優惠除了現金優惠4200元以外,分期購車還有5200元不限年限的利息補貼。由於最後的銷售給出的價格為142000元,我們提議將後面兩千元去掉,銷售也表示能夠向領導申請,所以整體的優惠也同樣接近1萬。

最美的國產SUV,直接1萬現金優惠!

對於國產車,想贏得消費者的芳心,顏值其實是挺重要的。被譽為最美國產SUV的榮威RX5,在優惠幅度上直接給出最高優惠1萬,對於消費者來說也算較大的驚喜,那麼榮威RX5哪款車型更值得我們參考呢?

參考汽車網站提供的優惠信息,與到店提供的優惠相差三千元。其中,同樣存在國慶庫存較多的原因,另外一個原因就是榮威RX5為了獲得更好的銷量表現,以更大的優惠幅度吸引消費者購買。

從上圖可知,現金優惠還是非常大的,對於全款購車的消費者來說的確是一件非常好的事情。不過上牌費高達四千元也是四款車型中最貴的,而且是硬性規定必須在店上牌。此時,銷售推薦我們購買一張國慶剩餘的優惠券,用300元可換購車的交強險和一年保養,整體算下來也就大概優惠一千元左右。

兩種不同的購車方式在總金額上相差6527元,相對其他三款車型來說,榮威RX5的按揭利息還是比較低的,因為需要按揭三年也只是需要6%的利息,對於希望輕鬆買車的消費者來說的確是不錯的貸款政策,唯一不足的地方,就是按揭的手續費收得相對較高。

總結

總之,能夠蹭上熱門的SUV,優惠肯定是比較少,還可能存在加價提車。這是很多消費者普遍存在的想法,但是通過暗訪看來,不一定存在這樣的情況。適合一次性全款買車的車型是:GS4和榮威RX5,同時榮威RX5貸款買車利息也是最低;長安CS75現金優惠適中,如果貸款買車建議選擇一年,因為廠家補貼5200元的利息,所以選擇一年的貸款年限是最划算的;至於哈弗H6,現金優惠幅度最小,如需貸款買車,建議還是選擇貸款6萬18個月免息的政策,否則並不划算。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

第一批90后已經買車了!選車就應該這樣?

但是該選什麼車的權利有可能依然沒有落到你的手中,縱使你已經下定決心,但是家中老婆大人一句不喜歡,足以打亂你所有的計劃。造化弄人這一句話說得沒錯,理想是美好的,但現實總是如此殘酷,其實選什麼車並不是單純地用“喜歡”就能做出抉擇,而更應該是考慮到自己的實際環境以及真實的用車需求,像家庭成員數量、所在地區、愛好等都能直接影響到你的最終選擇。

俗語有說,紙上得來終覺淺 須知此事要躬行這一句話絕對是真理,很多事情縱使別人說得如何天花龍鳳,表現如何搶眼,只要你沒有親身體驗過,你很難了解到个中原因,就像選車一樣,只有親身成為了車主你才會明白到,原來選車就是這樣一回事。

平心而論,雖然每一位消費者對於汽車方面的需求不一樣,但還是根據自身的情況選出以下幾個要點。

別以為你能買到一台自己喜歡的車

從最簡單外觀說起,汽車在這個時代並不僅僅屬於一個交通工具,反而更能彰顯着一個人的品味和身份,所以一輛車造型的好壞也同樣決定着消費者的關注力度,但由於每一個人的審美觀念不一樣,所以在選車時也會與身邊的人產生極大的分歧。

譬如…

剛畢業不久的大學生靠着家中兩老的幫助下購車,簡單來說誰給錢誰說了算,由於主動權在父母手上,在大多數情況下這名大學生會買到一台父母喜歡的車型。

又或者購車的錢終於掙到了…但是該選什麼車的權利有可能依然沒有落到你的手中,縱使你已經下定決心,但是家中老婆大人一句不喜歡,足以打亂你所有的計劃。

造化弄人這一句話說得沒錯,理想是美好的,但現實總是如此殘酷,其實選什麼車並不是單純地用“喜歡”就能做出抉擇,而更應該是考慮到自己的實際環境以及真實的用車需求,像家庭成員數量、所在地區、愛好等都能直接影響到你的最終選擇。

配置不能將就動力更應該不將就

相信在購車時有不少老司機會跟你說,買低配就行性價比最高!其實這樣的說法最主要還是圖它便宜,一旦你成為車主后,假如配置不足,在日後的用車過程中以及在感官上確實會造成一個感官上的落差,簡單來說就是買了高配的車主用得很省心而買了低配的車主卻總在想方設法地給自己的愛車增加所謂高配才有的配置,別不相信,你也不妨去瀏覽一下各個車型的論壇,這樣的情況多不勝數!

同樣對於動力來說也是如此,不少人都在信奉這“夠用”就好的原則,但是何為夠用?其實這必須要通過長時間的磨合你才會感受出來,不過也有一個很簡單的現象就是當你發現你經常需要深踩地板油急加速、日常發動機的轉速老拉得很高、在超車時變得力不從心的時候,那就意味着這台車或許不能夠滿足到你對動力方面的需求了。

空間要符合到家庭所需

認為這必須得看家庭人口基數而定,假如你家中都是兩三口人,其實選個正常的5座小車或者5座SUV就已經足夠了,但倘若你家中人數比較多(5~6人)或者經常滿載,建議考慮7座MpV。

車子並沒有想象中的嬌氣

相信網上也流傳着不少這樣的圖片,某部分車主愛車如命,於是總會做出一點讓人匪夷所思的護車行為,甚至有某些消費者為了保護愛車也同樣會花着大價錢去做上一些專業的汽車美容養護服務、給車用上最好的機油、加最好的汽油等等。說實話,這樣的做法並不是不可取,雖然這是愛,但到頭來都是屬於浪費錢的行為。

而且現在的汽車在机械水平方面其實大部分都做得相當不錯,所以一般只要定期保養,一般不會出現太大的問題,而此時你想要省錢,那你更應該關注的是這台車的零整比,簡單來說零整比越低,汽車配件價格越低,就算日後要涉及到修車也能少花錢,假如選擇進口車、豪華品牌車型的車主你日後可能會碰到的問題便是修車件會比較貴或者等待配件的時間比較長等等。

說起選車,大家都能說出一套一套的道理,但據不完全的調查显示,其實有大部分消費者在購車之後都會表示會多多少少因為自己愛車在某一方面達不到自己的預期而產生一定的後悔感,不過也不用太過害怕,畢竟每個人都有第一次嘛,不過在看來只有真正嘗試過才明白个中的道理,光聽別人說不行,或許在購車前你可以考慮在租車軟件上花上幾百塊錢去把自己心儀的愛車租來開上兩三天,相信你會明白到這車到底適不適合你。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

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

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

闢謠!年底買車最便宜都是騙人的!

第二種,距離指標實在差得太多的。這時候就要小心了,因為銷售口中的“巨額優惠”很有可能就是一個誘餌,你想下始終都是無法達標要受罰了,這時候經銷商就處於破罐破摔的狀態,能掙多少是多少,最好把罰款掙出來。所以看到那種還剩很多庫存車的大促銷就要擦亮眼睛了,必須了解清楚是真的在低價清庫存,還是高價掙罰款,不要最後買貴了還被蒙在鼓裡。

大家好我是阿湯鍋!今天的新車準備跟大家聊聊年底買車這件事。

這轉眼還有一個多月就要過年了,通常在這個時候,大家都認為這是一年當中買車的最好時機!其中既有內因,也有外因。

內因是辛苦吃了大半年泡麵了,大家總想買個大物件回老家去展示自己奮鬥一年的成果,所以在主觀上會更願意花這筆錢。再加上購置稅7.5折的優惠政策將於今年12月底結束,所以今年又給自己多了一個買車的理由。

外因則是大家都想當然地以為年底是經銷商優惠力度最大的時候,因為它們要衝銷量完成廠家的業務指標,所以這個時候消費者就有便宜可撿了。

那麼事實真的是如此嗎?

我告訴你還真不是!並且呢,分分鐘讓你以為是買便宜了,但實際上是更貴了。是不是覺得有點懵?且聽我跟大家分析分析。

首先給大家簡單科普一下廠家和經銷商是如何制定價格方案的。

每一個品牌都會在上年年底或下年年初制定一個年度銷量目標,並且會根據銷量淡旺季月份的區別出台相應的銷售政策。具體來講,廠家通常會在每月1號將當月的銷售政策和促銷方案制定出來,併發放給各個經銷商執行,所以每一款車每個月的終端價格基本都是有差異的。

因此在這個環節就有很多變數了。同樣是經銷商宣稱的“年底大促銷”,卻有可能是完全不同的幾種情況!

第一種,已經達到指標的。這時候經銷商只是象徵性的搞個促銷活動,一般不會有什麼實質性的優惠,所以想要撿便宜的你不要被銷售的幾句甜言蜜語迷惑了。

第二種,距離指標實在差得太多的。這時候就要小心了,因為銷售口中的“巨額優惠”很有可能就是一個誘餌,你想下始終都是無法達標要受罰了,這時候經銷商就處於破罐破摔的狀態,能掙多少是多少,最好把罰款掙出來。

所以看到那種還剩很多庫存車的大促銷就要擦亮眼睛了,必須了解清楚是真的在低價清庫存,還是高價掙罰款,不要最後買貴了還被蒙在鼓裡。

第三種,庫存剩的不多,還比較好賣的。這種大家就可以放心跟銷售扯嘴皮子了,優惠能爭取多一點是一點。因為這種時候經銷商通常寧可自己掏腰包,也要確保完成了廠家指標,不然前期努力就白費力氣了。

所以總結完以上三種情況之後,就告訴大家一個道理:賣的永遠比買的精,大家在各種優惠的誘惑面前一定要保持理性,區分“優惠”的真假。

那怎麼樣在這個節點獲取更大的優惠呢?

牢記四個字,貨比三家!

買車不要嫌麻煩(土豪請無視),看準一款車以後多去幾家店跑跑,表明出你買車的誠意。銷售通常會很頭疼這樣的客戶,因為你對行情心中有數,他們在報價時也會非常謹慎。報高了怕你轉身就走,報低了接下來的生意就不好談。只要你耐心對比幾家價格后,保你買不了吃虧,買不了上當。

另外,重要提醒幾點。

第一是談價格不要只是簡單的車型優惠,你要了解下訂后每一筆具體費用以及相關贈品都有哪些等等,尤其是貸款買車的朋友們更要清楚各項費用,因為最終的落地總價才是最關鍵的;

第二,如果當地有多家同品牌4S店,建議去不同集團下的經銷商談價格,這樣才能確保最終優惠價格的可靠性。

第三,年底買車盡量避免“壓哨”,意思就是說不要拖到快過年了才去下訂,這時候銷售通常會抓住你急於提車過年的心理,然後佔據價格談判的上風,甚至會給出各種讓你加價提車的理由。

以上就是我分享給各位的年底買車小技巧,僅供大家參考。還是那句話,賣的永遠比買的精,汽車價格是很多因素決定的,不要期望所謂的最低價,只要你有需求並且有能力,看準了就下手吧!早買早享受。

最後,祝大家都能買到自己心儀的車,開開心心開車回家過大年!

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

同一款2.0T發動機,價格卻差了10來20萬!買了就坑?

其實大家也可以反過來想一下,如果光是靠發動機可以來衡量一款車是不是好車的話,那麼那麼吉利博越一直都被各路大神吐槽油耗高、小問題多,但依然不愁賣,這又該如何解釋。如今在網上還有一種說法:豪華車型不弄7、8、9個擋,都是為了省成本,不夠高大上,多擋位變速箱理應是豪華車型的標配。

在你心目中,最坑爹的車型是哪一款?



套娃的?

加價提車的?

買發動機送車的?

可是最近在網上竟然看到有人說最坑爹的竟然是一些豪車,說它們的發動機、變速箱都是通過購買而來的,可以說是“高級拼裝車”。其中像路虎極光、捷豹XFL、林肯MKZ等好幾款車型紛紛躺槍,究竟是怎麼回事?

被說“坑爹”的原因很簡單,因為這些車型都搭載了福特全新蒙迪歐的2.0T發動機,同時售價也要比福特全新蒙迪歐更高,因此成了“史上最坑爹的汽車”。

按照這樣的說法,如今在汽車市場上“坑爹”的車型可多了。像寶馬在2006年之前,他們就一直為路虎攬勝和發現3車型提供V8發動機,難不成用了寶馬發動機的路虎就不是路虎?會影響車型檔次?

但如果你想說“他們都是豪華品牌,不至於掉檔次”的話,那現款奧迪Q5又跟大眾途觀L相同的EA888 2.0T發動機,但奧迪Q5的價格要比大眾途觀L高出許多,你又怎麼看呢?

先不說品牌檔次,光是在一些調校、用料等一些內在品質方面,奧迪Q5的表現就比大眾途觀L更為出色,開起來的質感也會更舒適,而且它的安全性能也是得到了市場的檢驗,這就是它的價值所在!

其實大家也可以反過來想一下,如果光是靠發動機可以來衡量一款車是不是好車的話,那麼那麼吉利博越一直都被各路大神吐槽油耗高、小問題多,但依然不愁賣,這又該如何解釋?

如今在網上還有一種說法:豪華車型不弄7、8、9個擋,都是為了省成本,不夠高大上,多擋位變速箱理應是豪華車型的標配?

擋位多與少這個問題,已經不是第一次跟大家科普了,在這裏先不說多擋位的變速箱省不省油,但是拿變速箱擋位來衡量一款車是否上檔次,這完全是一種“病態”。

先來說說路虎極光,不少網友都知道它搭載了9AT變速箱,擋位數夠多吧?可是之前就被曝光過它的變速箱在D擋位存在故障問題,這是一個軟件調校的問題。

同樣,Jeep自由光也搭載了9AT變速箱,可不少車主紛紛表示“沒看見過它的第9擋”,即使把時速跑到120km/h也仍然在第8擋以下,第9擋如同雞肋。很顯然,這同樣是一個匹配、調校的問題。

通過這兩個例子,想說:如果高擋位成為雞肋或者換擋不平順,擋位數量再多都是沒意義的。現款的寶馬X1同為豪華車型,而且現在只搭載了6AT變速箱,加上它空間大、操控好、上檔次,而且口碑還不錯,你還在意它的擋位數量嗎?

再說了,如今像眾泰T700、陸風X7、傳祺GS4等車型,它們雖然並不是什麼高檔車型但都搭載了多擋位變速箱,這很顯然並不是高擋車型的“專屬”,一味的追求是毫無意義的。

此外,有些消費者還會關注到平台的問題,認為一款豪車跟普通車型採用相同的平台打造會顯得“廉價、有失身份”,其實這也是一個誤區!

就拿本田飛度跟謳歌CDX作例子,雖然兩款車都是基於同一個平台打造,但謳歌CDX在用料、舒適性等方面的表現會更加出色,而且它擁有跟飛度一樣的大空間和良好的操控。

但是相反過來,本田飛度能有謳歌CDX那樣舒適的乘坐和駕駛質感嗎?因此,“同平台”的意義並不單純只是降低研發成本,也能對提升車型品質起到一定的作用。

還有奧迪Q7跟大眾途昂,儘管它們都採用了大眾MQB平台進行打造,但是論豪華感、行駛質感等方面,兩款車依然有較大的差距。也就是說“同平台”並不代表工藝、用料、調校都是一樣的,只是共用了部分技術罷了。

如今在汽車市場上,共用發動機、變速箱,甚至採用同一個研發平台的車型還有很多,雖然最大的意義還是在於降低研發成本,但最核心的部分還是在匹配調校、用料方面。

SO~這並不能成為“坑爹、廉價、不上檔次”的代名詞,真正的一款車“好不好”、“值不值”並不能由誰說了算,只有自己親身體驗過,感受過覺得OK,而且價格也能接受,那才是最好、最適合的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

面試:在面試中關於List(ArrayList、LinkedList)集合會怎麼問呢?你該如何回答呢?

前言

在一開始基礎面的時候,很多面試官可能會問List集合一些基礎知識,比如:

  • ArrayList默認大小是多少,是如何擴容的?

  • ArrayListLinkedList的底層數據結構是什麼?

  • ArrayListLinkedList的區別?分別用在什麼場景?

  • 為什麼說ArrayList查詢快而增刪慢?

  • Arrays.asList方法后的List可以擴容嗎?

  • modCount在非線程安全集合中的作用?

  • ArrayListLinkedList的區別、優缺點以及應用場景

 

ArrayList(1.8)

ArrayList是由動態再分配的Object[]數組作為底層結構,可設置null值,是非線程安全的。

ArrayList成員屬性

//默認的空的數組,在構造方法初始化一個空數組的時候使用
private static final Object[] EMPTY_ELEMENTDATA = {};
​
//使用默認size大小的空數組實例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
​
//ArrayList底層存儲數據就是通過數組的形式,ArrayList長度就是數組的長度。
transient Object[] elementData; 
​
//arrayList的大小
private int size;

那麼ArrayList底層數據結構是什麼呢?

很明顯,使用動態再分配的Object[]數組作為ArrayList底層數據結構了,既然是使用數組實現的,那麼數組特點就能說明為什麼ArrayList查詢快而增刪慢?

因為數組是根據下標查詢不需要比較,查詢方式為:首地址+(元素長度*下標),基於這個位置讀取相應的字節數就可以了,所以非常快;但是增刪會帶來元素的移動,增加數據會向後移動,刪除數據會向前移動,導致其效率比較低。

 

ArrayList的構造方法

  • 帶有初始化容量的構造方法

  • 無參構造方法

  • 參數為Collection類型的構造器

//帶有初始化容量的構造方法
public ArrayList(int initialCapacity) {
    //參數大於0,elementData初始化為initialCapacity大小的數組
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    //參數小於0,elementData初始化為空數組
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    //參數小於0,拋出異常
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
​
//無參構造方法
public ArrayList() {
    //在1.7以後的版本,先構造方法中將elementData初始化為空數組DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    //當調用add方法添加第一個元素的時候,會進行擴容,擴容至大小為DEFAULT_CAPACITY=10
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

那麼ArrayList默認大小是多少?

從無參構造方法中可以看出,一開始默認為一個空的實例elementData為上面的DEFAULTCAPACITY_EMPTY_ELEMENTDATA,當添加第一個元素的時候會進行擴容,擴容大小就是上面的默認容量DEFAULT_CAPACITY10

ArrayList的Add方法

  • boolean add(E):默認直接在末尾添加元素

  • void add(int,E):在特定位置添加元素,也就是插入元素

  • boolean addAll(Collection<? extends E> c):添加集合

  • boolean addAll(int index, Collection<? extends E> c):在指定位置后添加集合

boolean add(E)
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

通過ensureCapacityInternal方法為確定容量大小方法。在添加元素之前需要確定數組是否能容納下,size是數組中元素個數,添加一個元素size+1。然後再數組末尾添加元素。

其中,ensureCapacityInternal方法包含了ArrayList擴容機制grow方法,當前容量無法容納下數據時1.5倍擴容,進行:

private void ensureCapacityInternal(int minCapacity) {
    //判斷當前的數組是否為默認設置的空數據,是否取出最小容量
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //包括擴容機制grow方法
    ensureExplicitCapacity(minCapacity);
}
​
private void ensureExplicitCapacity(int minCapacity) {
        //記錄著集合的修改次數,也就每次add或者remove它的值都會加1
        modCount++;
​
        //當前容量容納不下數據時(下標超過時),ArrayList擴容機制:擴容原來的1.5倍
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
​
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //ArrayList擴容機制:擴容原來的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

ArrayList是如何擴容的?

根據當前的容量容納不下新增數據時,ArrayList會調用grow進行擴容:

//相當於int newCapacity = oldCapacity + oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);

擴容原來的1.5倍。

void add(int,E)
public void add(int index, E element) {
    //檢查index也就是插入的位置是否合理,是否存在數組越界
    rangeCheckForAdd(index);
    //機制和boolean add(E)方法一樣
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

ArrayList的刪除方法

  • remove(int):通過刪除指定位置上的元素,

  • remove(Object):根據元素進行刪除,

  • clear():elementData中每個元素都賦值為null,等待垃圾回收將這個給回收掉,

  • removeAll(collection c):批量刪除。

 

remove(int)
public E remove(int index) {
    //檢查下標是否超出數組長度,造成數組越界
    rangeCheck(index);
​
    modCount++;
    E oldValue = elementData(index);
    //算出數組需要移動的元素數量
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //數組數據遷移,這樣會導致刪除數據時,效率會慢
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //將--size上的位置賦值為null,讓gc(垃圾回收機制)更快的回收它。
    elementData[--size] = null; // clear to let GC do its work
    //返回刪除的元素
    return oldValue;
}

為什麼說ArrayList刪除元素效率低?

因為刪除數據需要將數據後面的元素數據遷移到新增位置的後面,這樣導致性能下降很多,效率低。

remove(Object)
public boolean remove(Object o) {
    //如果需要刪除數據為null時,會讓數據重新排序,將null數據遷移到數組尾端
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                //刪除數據,並遷移數據
                fastRemove(index);
                return true;
            }
    } else {
        //循環刪除數組中object對象的值,也需要數據遷移
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

可以看出,arrayList是可以存放null值。

 

LinkedList(1.8)

LinkedList是一個繼承於AbstractSequentialList的雙向鏈表。它也可以被當做堆棧、隊列或雙端隊列進行使用,而且LinkedList也為非線程安全, jdk1.6使用的是一個帶有 header節頭結點的雙向循環鏈表, 頭結點不存儲實際數據 ,在1.6之後,就變更使用兩個節點firstlast指向首尾節點。

LinkedList的主要屬性

//鏈表節點的個數 
transient int size = 0; 
//鏈表首節點
 transient Node<E> first; 
//鏈表尾節點
 transient Node<E> last; 
//Node節點內部類定義
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
​
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

一旦變量被transient修飾,變量將不再是對象持久化的一部分,該變量內容在序列化后無法獲得訪問

LinkedList構造方法

無參構造函數, 默認構造方法聲明也不做,firstlast節點會被默認初始化為null。

*
/** Constructs an empty list. \*/*public LinkedList() {}
​

LinkedList插入

由於LinkedList由雙向鏈表作為底層數據結構,因此其插入無非由三大種

  • 尾插: add(E e)addLast(E e)addAll(Collection<? extends E> c)

  • 頭插: addFirst(E e)

  • 中插: add(int index, E element)

可以從源碼看出,在鏈表首尾添加元素很高效,在中間添加元素比較低效,首先要找到插入位置的節點,在修改前後節點的指針。

 

 

尾插-add(E e)和addLast(E e)
//常用的添加元素方法
public boolean add(E e) {
    //使用尾插法
    linkLast(e);
    return true;
}
​
//在鏈表尾部添加元素
public void addLast(E e) {
        linkLast(e);
    }
​
//在鏈表尾端添加元素
void linkLast(E e) {
        //尾節點
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        //判斷是否是第一個添加的元素
        //如果是將新節點賦值給last
        //如果不是把原首節點的prev設置為新節點
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        //將集合修改次數加1
        modCount++;
    }
​
頭插-addFirst(E e)
public void addFirst(E e) {
    //在鏈表頭插入指定元素
    linkFirst(e);
}
​
private void linkFirst(E e) {
         //獲取頭部元素,首節點
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        //鏈表頭部為空,(也就是鏈表為空)
        //插入元素為首節點元素
        // 否則就更新原來的頭元素的prev為新元素的地址引用
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        //
        size++;
        modCount++;
    }
中插-add(int index, E element)

index不為首尾的的時候,實際就在鏈表中間插入元素。

 // 作用:在指定位置添加元素
    public void add(int index, E element) {
        // 檢查插入位置的索引的合理性
        checkPositionIndex(index);
​
        if (index == size)
            // 插入的情況是尾部插入的情況:調用linkLast()。
            linkLast(element);
        else
            // 插入的情況是非尾部插入的情況(中間插入):linkBefore
            linkBefore(element, node(index));
    }
​
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
​
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }
​
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;  // 得到插入位置元素的前繼節點
        final Node<E> newNode = new Node<>(pred, e, succ);  // 創建新節點,其前繼節點是succ的前節點,後接點是succ節點
        succ.prev = newNode;  // 更新插入位置(succ)的前置節點為新節點
        if (pred == null)
            // 如果pred為null,說明該節點插入在頭節點之前,要重置first頭節點 
            first = newNode;
        else
            // 如果pred不為null,那麼直接將pred的後繼指針指向newNode即可
            pred.next = newNode;
        size++;
        modCount++;
    }

LinkedList 刪除

刪除和插入一樣,其實本質也是只有三大種方式,

  • 刪除首節點:removeFirst()

  • 刪除尾節點:removeLast()

  • 刪除中間節點 :remove(Object o)remove(int index)

在首尾節點刪除很高效,刪除中間元素比較低效要先找到節點位置,再修改前後指針指引。

 

 

刪除中間節點-remove(int index)和remove(Object o)
remove(int index)和remove(Object o)都是使用刪除指定節點的unlink刪除元素

 public boolean remove(Object o) {
     //因為LinkedList允許存在null,所以需要進行null判斷        
     if (o == null) {
         //從首節點開始遍歷
         for (Node<E> x = first; x != null; x = x.next) {
             if (x.item == null) {
                 //調用unlink方法刪除指定節點
                 unlink(x);
                 return true;
             }
         }
     } else {
         for (Node<E> x = first; x != null; x = x.next) {
             if (o.equals(x.item)) {
                 unlink(x);
                 return true;
             }
         }
     }
    return false;
 } 
​
//刪除指定位置的節點,其實和上面的方法差不多
    //通過node方法獲得指定位置的節點,再通過unlink方法刪除
    public E remove(int index) {
        checkElementIndex(index);
       
        return unlink(node(index));
    }
​
 //刪除指定節點
    E unlink(Node<E> x) {
        //獲取x節點的元素,以及它上一個節點,和下一個節點
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        //如果x的上一個節點為null,說明是首節點,將x的下一個節點設置為新的首節點
        //否則將x的上一節點設置為next,將x的上一節點設為null
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        //如果x的下一節點為null,說明是尾節點,將x的上一節點設置新的尾節點
        //否則將x的上一節點設置x的上一節點,將x的下一節點設為null
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        //將x節點的元素值設為null,等待垃圾收集器收集
        x.item = null;
        //鏈表節點個數減1
        size--;
        //將集合修改次數加1
        modCount++;
        //返回刪除節點的元素值
        return element;
    }
刪除首節點-removeFirst()
//刪除首節點
public E remove() {
        return removeFirst();
    }
 //刪除首節點
 public E removeFirst() {
      final Node<E> f = first;
      //如果首節點為null,說明是空鏈表,拋出異常
      if (f == null)
          throw new NoSuchElementException();
      return unlinkFirst(f);
  }
  //刪除首節點
  private E unlinkFirst(Node<E> f) {
      //首節點的元素值
      final E element = f.item;
      //首節點的下一節點
      final Node<E> next = f.next;
      //將首節點的元素值和下一節點設為null,等待垃圾收集器收集
      f.item = null;
      f.next = null; // help GC
      //將next設置為新的首節點
      first = next;
      //如果next為null,說明說明鏈表中只有一個節點,把last也設為null
      //否則把next的上一節點設為null
      if (next == null)
          last = null;
      else
          next.prev = null;
      //鏈表節點個數減1
      size--;
      //將集合修改次數加1
      modCount++;
      //返回刪除節點的元素值
      return element;
 }
刪除尾節點-removeLast()
  
 //刪除尾節點
    public E removeLast() {
        final Node<E> l = last;
        //如果首節點為null,說明是空鏈表,拋出異常
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
    private E unlinkLast(Node<E> l) {
        //尾節點的元素值
        final E element = l.item;
        //尾節點的上一節點
        final Node<E> prev = l.prev;
        //將尾節點的元素值和上一節點設為null,等待垃圾收集器收集
        l.item = null;
        l.prev = null; // help GC
        //將prev設置新的尾節點
        last = prev;
        //如果prev為null,說明說明鏈表中只有一個節點,把first也設為null
        //否則把prev的下一節點設為null
        if (prev == null)
            first = null;
        else
            prev.next = null;
        //鏈表節點個數減1
        size--;
        //將集合修改次數加1
        modCount++;
        //返回刪除節點的元素值
        return element;
    }

其他方法也是類似的,比如查詢方法 LinkedList提供了getgetFirstgetLast等方法獲取節點元素值。

modCount屬性的作用?

modCount屬性代表為結構性修改( 改變list的size大小、以其他方式改變他導致正在進行迭代時出現錯誤的結果)的次數,該屬性被Iterator以及ListIterator的實現類所使用,且很多非線程安全使用modCount屬性。

初始化迭代器時會給這個modCount賦值,如果在遍歷的過程中,一旦發現這個對象的modCount和迭代器存儲的modCount不一樣,Iterator或者ListIterator 將拋出ConcurrentModificationException異常,

這是jdk在面對迭代遍歷的時候為了避免不確定性而採取的 fail-fast(快速失敗)原則:

在線程不安全的集合中,如果使用迭代器的過程中,發現集合被修改,會拋出ConcurrentModificationExceptions錯誤,這就是fail-fast機制。對集合進行結構性修改時,modCount都會增加,在初始化迭代器時,modCount的值會賦給expectedModCount,在迭代的過程中,只要modCount改變了,int expectedModCount = modCount等式就不成立了,迭代器檢測到這一點,就會拋出錯誤:urrentModificationExceptions

 

總結

ArrayList和LinkedList的區別、優缺點以及應用場景

區別:

  • ArrayList是實現了基於動態數組的數據結構,LinkedList是基於鏈表結構。

  • 對於隨機訪問的getset方法查詢元素,ArrayList要優於LinkedList,因為LinkedList循環鏈表尋找元素。

  • 對於新增和刪除操作addremoveLinkedList比較高效,因為ArrayList要移動數據。

優缺點:

  • ArrayListLinkedList而言,在末尾增加一個元素所花的開銷都是固定的。對ArrayList而言,主要是在內部數組中增加一項,指向所添加的元素,偶爾可能會導致對數組重新進行分配;而對LinkedList而言,這個開銷是 統一的,分配一個內部Entry對象。

  • ArrayList集合中添加或者刪除一個元素時,當前的列表移動元素後面所有的元素都會被移動。而LinkedList集合中添加或者刪除一個元素的開銷是固定的。

  • LinkedList集合不支持 高效的隨機隨機訪問(RandomAccess),因為可能產生二次項的行為。

  • ArrayList的空間浪費主要體現在在list列表的結尾預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗相當的空間

應用場景:

ArrayList使用在查詢比較多,但是插入和刪除比較少的情況,而LinkedList用在查詢比較少而插入刪除比較多的情況

各位看官還可以嗎?喜歡的話,動動手指點個,點個關注唄!!謝謝支持!

歡迎掃碼關注,原創技術文章第一時間推出

 

 

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

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  

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

Docker_01

目錄

  • 1.1 Docker簡介
    • 1.1.1 為什麼會有Docker的出現?
    • 1.1.2 Docker理念
    • 1.1.3 Docker or 虛擬機?
  • 2.1 Docker安裝
  • 3.1 Docker基本使用
    • 3.1.1 什麼是鏡像?
    • 3.1.2 Docker鏡像加載原理
    • 3.1.3 常用命令
  • 4.1 重要知識點

1.1 Docker簡介

1.1.1 為什麼會有Docker的出現?

一款產品從開發到上線,從操作系統,到運行環境,再到應用配置。作為開發+運維之間的協作我們需要關心很多東西,這也是很多互聯網公司都不得不面對的問題,特別是各種版本的迭代之後,不同版本環境的兼容,對運維人員都是考驗
Docker之所以發展如此迅速,也是因為它對此給出了一個標準化的解決方案。
環境配置如此麻煩,換一台機器,就要重來一次,費力費時。很多人想到,能不能從根本上解決問題。軟件可以帶環境安裝?1. 也就是說,安裝的時候,把原始環境一模一樣地複製過來。開發人員利用 Docker 可以消除協作編碼時“在我的機器上可正常工作”的問題。

1.1.2 Docker理念

Docker是基於Go語言實現的雲開源項目。
Docker的主要目標是“Build,Ship and Run Any App,Anywhere”,也就是通過對應用組件的封裝、分發、部署、運行等生命周期的管理,使用戶的APP(可以是一個WEB應用或數據庫應用等等)及其運行環境能夠做到“一次封裝,到處運行”。
Linux 容器技術的出現就解決了這樣一個問題,而 Docker 就是在它的基礎上發展過來的。將應用運行在 Docker 容器上面,而 Docker 容器在任何操作系統上都是一致的,這就實現了跨平台、跨服務器。只需要一次配置好環境,換到別的機子上就可以一鍵部署好,大大簡化了操作。

總而言之:解決了運行環境和配置問題軟件容器,方便做持續集成並有助於整體發布的容器虛擬化技術。

1.1.3 Docker or 虛擬機?

虛擬機(virtual machine)就是帶環境安裝的一種解決方案。
它可以在一種操作系統裏面運行另一種操作系統,比如在Windows 系統裏面運行Linux 系統。應用程序對此毫無感知,因為虛擬機看上去跟真實系統一模一樣,而對於底層系統來說,虛擬機就是一個普通文件,不需要了就刪掉,對其他部分毫無影響。這類虛擬機完美的運行了另一套系統,能夠使應用程序,操作系統和硬件三者之間的邏輯不變。 
但是虛擬機有很多缺點:1.資源佔用多2.冗餘步驟多3.啟動慢

由於前面虛擬機存在這些缺點,Linux 發展出了另一種虛擬化技術:Linux 容器(Linux Containers,縮寫為 LXC)。
Linux 容器不是模擬一個完整的操作系統,而是對進程進行隔離。有了容器,就可以將軟件運行所需的所有資源打包到一個隔離的容器中。容器與虛擬機不同,不需要捆綁一整套操作系統,只需要軟件工作所需的庫資源和設置。系統因此而變得高效輕量並保證部署在任何環境中的軟件都能始終如一地運行。
 
比較了 Docker 和傳統虛擬化方式的不同之處:

  • 傳統虛擬機技術是虛擬出一套硬件后,在其上運行一個完整操作系統,在該系統上再運行所需應用進程;
  • 而容器內的應用進程直接運行於宿主的內核,容器內沒有自己的內核,而且也沒有進行硬件虛擬。因此容器要比傳統虛擬機更為輕便。
  • 每個容器之間互相隔離,每個容器有自己的文件系統 ,容器之間進程不會相互影響,能區分計算資源。

對於開發和運維來說Docker的特點:

  • 更快速的應用交付和部署:傳統的應用開發完成后,需要提供一堆安裝程序和配置說明文檔,安裝部署后需根據配置文檔進行繁雜的配置才能正常運行。Docker化之後只需要交付少量容器鏡像文件,在正式生產環境加載鏡像並運行即可,應用安裝配置在鏡像里已經內置好,大大節省部署配置和測試驗證時間。
  • 更便捷的升級和擴縮容:隨着微服務架構和Docker的發展,大量的應用會通過微服務方式架構,應用的開發構建將變成搭樂高積木一樣,每個Docker容器將變成一塊“積木”,應用的升級將變得非常容易。當現有的容器不足以支撐業務處理時,可通過鏡像運行新的容器進行快速擴容,使應用系統的擴容從原先的天級變成分鐘級甚至秒級。
  • 更簡單的系統運維:應用容器化運行后,生產環境運行的應用可與開發、測試環境的應用高度一致,容器會將應用程序相關的環境和狀態完全封裝起來,不會因為底層基礎架構和操作系統的不一致性給應用帶來影響,產生新的BUG。當出現程序異常時,也可以通過測試環境的相同容器進行快速定位和修復。
  • 更高效的計算資源利用: Docker是內核級虛擬化,其不像傳統的虛擬化技術一樣需要額外的Hypervisor支持,所以在一台物理機上可以運行很多個容器實例,可大大提升物理服務器的CPU和內存的利用率。

2.1 Docker安裝

安裝地址有兩個:

  • docker官網:(http://www.docker.com)
  • docker中文官網:(https://www.docker-cn.com/)
    跟github一樣docker也有自己的中心倉庫Docker Hub官網: https://hub.docker.com/
    Linux環境下安裝的話可能要注意套個CDN加速一般選擇阿里雲或者網易的鏡像庫。

安裝命令:

$ sudo yum install -y yum-utils

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
These repositories are included in the docker.repo file above but are disabled by default. You can enable them alongside the stable repository. The following command enables the nightly repository.

$ sudo yum-config-manager --enable docker-ce-nightly
To enable the test channel, run the following command:

$ sudo yum-config-manager --enable docker-ce-test
You can disable the nightly or test repository by running the yum-config-manager command with the --disable flag. To re-enable it, use the --enable flag. The following command disables the nightly repository.

$ sudo yum-config-manager --disable docker-ce-nightly

具體方法參考官網:https://docs.docker.com/engine/install/centos/

3.1 Docker基本使用

3.1.1 什麼是鏡像?

鏡像是一種輕量級、可執行的獨立軟件包,用來打包軟件運行環境和基於運行環境開發的軟件,它包含運行某個軟件所需的所有內容,包括代碼、運行時、庫、環境變量和配置文件。
在Docker打包一個鏡像的時候會使用一個叫UnionFS(聯合文件系統)的文件系統:UnionFS(聯合文件系統):Union文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,它支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)。Union 文件系統是 Docker 鏡像的基礎。鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以製作各種具體的應用鏡像。
特性:一次同時加載多個文件系統,但從外面看起來,只能看到一個文件系統,聯合加載會把各層文件系統疊加起來,這樣最終的文件系統會包含所有底層的文件和目錄。

3.1.2 Docker鏡像加載原理

Docker鏡像加載原理:
docker的鏡像實際上由一層一層的文件系統組成,這種層級的文件系統UnionFS。
bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引導加載kernel, Linux剛啟動時會加載bootfs文件系統,在Docker鏡像的最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot加載器和內核。當boot加載完成之後整個內核就都在內存中了,此時內存的使用權已由bootfs轉交給內核,此時系統也會卸載bootfs。

rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系統中的 /dev, /proc, /bin, /etc 等標準目錄和文件。rootfs就是各種不同的操作系統發行版,比如Ubuntu,Centos等等。

平時我們安裝進虛擬機的CentOS都是好幾個G,為什麼docker這裏才200M??
對於一個精簡的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序庫就可以了,因為底層直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可見對於不同的linux發行版, bootfs基本是一致的, rootfs會有差別, 因此不同的發行版可以公用bootfs。

分層的鏡像:以我們的pull為例,在下載的過程中我們可以看到docker的鏡像好像是在一層一層的在下載.

為什麼 Docker 鏡像要採用這種分層結構呢?
最大的一個好處就是 – 共享資源
 
比如:有多個鏡像都從相同的 base 鏡像構建而來,那麼宿主機只需在磁盤上保存一份base鏡像,
同時內存中也只需加載一份 base 鏡像,就可以為所有容器服務了。而且鏡像的每一層都可以被共享。

3.1.3 常用命令

1.幫助命令:

  • docker version
  • docker info
  • docker –help

2.鏡像命令:

  • docker images OPTIONS說明:-a :列出本地所有的鏡像(含中間映像層)-q :只显示鏡像ID –digests :显示鏡像的摘要信息 –no-trunc :显示完整的鏡像信息。

  • docker search 某個XXX鏡像名字 OPTIONS說明:–no-trunc : 显示完整的鏡像描述 -s : 列出收藏數不小於指定值的鏡像 –automated : 只列出 automated build類型的鏡像;

  • docker pull 某個XXX鏡像名字

  • docker rmi 某個XXX鏡像名字ID 刪除鏡像
    3.容器命令:

  • 新建並啟動容器:
    docker run [OPTIONS] IMAGE [COMMAND] [ARG…]
    OPTIONS說明(常用):有些是一個減號,有些是兩個減號
     
    –name=”容器新名字”: 為容器指定一個名稱;
    -d: 後台運行容器,並返回容器ID,也即啟動守護式容器;
    -i:以交互模式運行容器,通常與 -t 同時使用;
    -t:為容器重新分配一個偽輸入終端,通常與 -i 同時使用;
    -P: 隨機端口映射;
    -p: 指定端口映射,有以下四種格式
          ip:hostPort:containerPort
          ip::containerPort
          hostPort:containerPort
          containerPort

  • 列出當前所有正在運行的容器
    docker ps [OPTIONS]
    OPTIONS說明(常用):
     
    -a :列出當前所有正在運行的容器+歷史上運行過的
    -l :显示最近創建的容器。
    -n:显示最近n個創建的容器。
    -q :靜默模式,只显示容器編號。
    –no-trunc :不截斷輸出。

  • 退出容器
    exit:容器停止退出。 ctrl+P+Q:容器不停止退出

  • 啟動容器
    docker start 容器ID或者容器名

  • 重啟容器
    docker restart 容器ID或者容器名

  • 停止容器
    docker stop 容器ID或者容器名

  • 強制停止容器
    docker kill 容器ID或者容器名

  • 刪除已停止的容器
    docker rm 容器ID(一次性刪除多個容器:1.docker rm -f $(docker ps -a -q)2.docker ps -a -q | xargs docker rm)

4.1 重要知識點

1.關於docker ps -a命令它不會列出你後台啟動的進程也就是說你用守護進程方式啟動容器的話:docker run -d 容器名是不會被ps出來的。
這個是docker的機制問題,比如你的web容器,我們以nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service即可。例如service nginx start但是,這樣做,nginx為後台進程模式運行,就導致docker前台沒有運行的應用,這樣的容器後台啟動后,會立即自殺因為他覺得他沒事可做了.所以,最佳的解決方案是,將你要運行的程序以前台進程的形式運行。
2.如何重新進入已經啟動的容器
有兩個法子:

  • docker exec -it 容器ID bashShell
  • docker attach 容器ID

上述兩個區別:

  • attach 直接進入容器啟動命令的終端,不會啟動新的進程
  • exec 是在容器中打開新的終端,並且可以啟動新的進程
    3.從容器內拷貝文件到主機上
    docker cp 容器ID:容器內路徑 目的主機路徑。

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

【原創】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機制,包括了softirqtaskletworkqueue、以及前文中提到過的中斷線程化處理等,其中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_enterirq_exit分別用於來標識進入和離開硬件中斷上下文處理,這個從preempt_count_add/preempt_count_sub來操作HARDIRQ_OFFSET可以看出來,這也對應到了上文中的Context描述圖;
  • 在離開硬件中斷上下文後,如果!in_interrupt() && local_softirq_pending為真,則進行軟中斷處理。這個條件有兩個含義:1)!in_interrupt()表明不能處在中斷上下文中,這個範圍包括in_nmiin_irqin_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-halfBottom-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_vectasklet_vec_hi,對應兩個不同的優先級,本文以tasklet_vec為例;
  • struct tasklet_structtasklet的抽象,幾個關鍵字段如圖所示,通過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內核機制文章。

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

【其他文章推薦】

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

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

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

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

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