[{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":" 関西旅行記⛺️ # はじめに # 事前に作成した関西旅行プランと関西旅行実行ガイドをもとに、2026年4月18日、私は広州からバスで香港国際空港へ向かい、翌日の人生初フライトに備えました。ここからいよいよ関西へ降り立ち、大阪、京都、奈良を巡り、現地の文化やグルメ、美しい風景を体験する旅の始まりです。\n🔥 尋ね人 — 本気のお願い 京都水族館（2026年4月24日）で見かけた眼鏡をかけた女子高生を探しています。詳細は Day 6 京都水族館 をご覧ください。 nni461904@gmail.com @ice24971676770 Day 0（2026年4月18日） # チェックインと手荷物預け # 18日の夜8時過ぎ、深セン湾からイミグレーションを通過しました。同じく正佳から一緒に出発した方がいたので、その人についていくことにしました。しかし、税関を抜けた後、彼女が左に寄りすぎて運転手さんとすれ違いそうになるハプニングが。幸い人が少なかったおかげで無事に合流でき、車に乗り込むことができました。\n夜9時頃、香港国際空港に到着しました。\nまずは人の流れに沿って中に入り、空港のロビーへ。最初は空港内の環境や設備、人々の様子を観察しながら、全体の手続きの流れを把握しようと30分ほど歩き回りました。どうやらこの階でチェックインと手荷物預けを行うようです。吹き抜けのデザインになっていて、下の階は到着ロビーになっているため、ここからは直接降りられない構造でした。\n最初は「なぜ今の自動チェックイン機で自分の航空会社が見つからないのか？」と戸惑いましたが、スタッフに聞くと、奥の列に行かないといけないとのことでした。\nヒント 空港のチェックインカウンターは航空会社ごとに分かれています。自分の利用する航空会社に対応したエリアを探して手続きを行う必要があります。\nその後、正しい列を見つけ、自動チェックイン機で手続きをしようとしたのですが、エラーが発生。スタッフから有人カウンターへ行くよう指示され、そこで無事にチェックインと手荷物預けを完了しました。その後は保安検査場へと向かいました。\n保安検査と搭乗 # 保安検査の流れは国内の空港とほぼ同じで、手荷物をトレイに乗せ、金属探知機を通るというものです。必要に応じてボディチェックも行われますが、特に問題なくスムーズに通過できました。\n検査後、案内板に従って搭乗口に到着。着いた時は搭乗口にほとんど人がおらず、「このフライト、全然人がいないのかな？」と勘違いしました。後で気づいたのですが、乗客のほとんどが香港の地元の方で、出発ギリギリになってから続々と集まってくるスタイルだったのです。\nそんな感じで空港で一晩を過ごしましたが、快適に眠れる場所もなく、寝入りばなにどんどん気温が下がってきて寒かったため、一睡もできませんでした。\nいよいよ搭乗の朝。搭乗前に香港空港での免税手続きを済ませようと思い、Global Exchangeのカウンターへ。 しかし、地元のスタッフが何を言っているのか分からず、ただ「免税できない」とだけ伝えられました。「どういう意味ですか？」と聞いても、英語の直訳のような返事が返ってくるだけ。 「いやいや、それじゃ意味ないでしょ！」こっちは理由を聞いているのに、そんな返答で問題解決になるわけがありません。これが彼らの“接客態度”のようです。😡\nDay 1（2026年4月19日） # 人生初の飛行機だったので、窓側の席を選びました。飛行機がゆっくりと離陸し、地面から離れて雲の中へ入っていく瞬間。窓から見下ろす街や風景がどんどん小さくなっていく景色は、本当に美しかったです。 空を飛んでいる3時間ちょっとの間、眠ろうとしましたがうまく眠れず、座っているだけで結構疲れました。最初は「飛行機ってこんなもんか、そんなに速くないな」と思っていましたが、後で電車に乗ってから初めて「やっぱり飛行機は速かったんだ」と実感することになります。\n日本の空域に入ると、海と街が山を囲むように広がっているのが見えました。まるでギャルゲーに出てくるような日本の田舎町に戻ってきた感覚があり、とても感動しました。\nPrevious Next 関西国際空港に到着 # 関空に到着後は、人の流れに乗って空港内のシャトルに乗り、入国審査のフロアへ。 ここで事前にネットで登録しておいたVisit Japan Web (VJW)のQRコードの出番です。進む途中に専用の端末があり、QRコードをスキャンして緑色のランプが点灯すれば先に進める仕組み。さらに案内用のディスプレイも至る所にあり、非常に便利で進んでいるなと感じました。手続きはとてもスムーズでした。\nその後、入国審査の列に並びます。ここの流れも基本的には同じです。\nしかし、初めての経験で人の流れにただ付いていってしまったため、外に出る直前になって「あれ？預けた荷物ってどこで受け取るんだっけ？」と完全に忘れていました。スタッフに聞こうにも、私の日本語は壊滅的だったので全く聞き取れず。そのままロビーに出てしまいました。 後になって戻る道を探すも見つからず。スタッフに聞くと「まっすぐ行って左」とのことでしたが、近くに行っても案内所らしきものがありません。仕方なく小紅書（RED）を開いて攻略法を検索。無事に「ある扉の奥に行ってスタッフに助けを求める」という解決策を見つけました。 こうしてスタッフの方に手伝ってもらい、無事に預け荷物を回収。いよいよJRはるかの駅を探して京都へ向かいます。\n無事にJRはるかに乗り込み、1時間ちょっと揺られて京都駅に到着しました。\n京都駅到着 # 京都駅に着いてから、Googleマップを見ながらホテルへ向かいチェックインしようと思ったのですが……。この京都駅、一体どういう設計になっているんでしょうか。まるで巨大な迷宮です。お店、電車の案内板、そして人混みで完全に頭がパニックに。最後は「ええい、マップ通りに進んでやる！」と半ばヤケクソで歩き、なんとか外に出られました。ホテルのチェックイン自体は問題なくスムーズに完了しました。\n前日からお風呂にも入っておらず、何も食べていなかったので限界でした。まずはコンビニでカップ麺を買い、2階の自販機をチェック。バスタブにお湯を張っている間にカップ麺をすすりました。お風呂上がりは最高に気持ちよかったです！ただ、フルーツ牛乳的なものが無かったのが少し心残りですね😂。\nさっぱりした後、夕食を探すことにしました。Geminiにオススメを聞いてみると東洋亭を提案されたので、小紅書でレビューを確認。評判が良かったので、おすすめされていたセットメニューを注文しました。\nハンバーグもあったのですが、写真を撮り忘れました。全体的な感想としては、「まあ普通に美味しいけど、そこまで感動するほどではないかな」というレベル。 おそらく、私がアニメ『食戟のソーマ』を見すぎて、料理へのハードルが異常に上がっていたせいだと思います😂\nお会計はこちら： お店を探したり迷ったりしていたせいで、食べ終わる頃には夜9時近くになっていました。ユニクロで服を買いたかったのですが、またしても迷子に。歩き回っているうちに薬局を見つけたので、口コミで評判の良かったLION PAIRとACNEIGEのニキビ薬を購入しました。効果が楽しみです。\n買い物を終えてホテルへ。帰り道、京都の夜は驚くほど静かで、人通りも少なく少し寂しい雰囲気でした。もっと地元の人が出歩いているのかと思いきや、見かけるのは欧米の観光客ばかり。空港から京都への道のりでも感じましたが、ここに来る観光客の多くは欧米人なんですね。これは私が抱いていた日本の第一印象を大きく覆すものでした。\nDay 2（2026年4月20日） # 貴船神社 # 事前の計画通り、この日の朝は貴船神社（きふねじんじゃ）へ向かいます。 前日早く休んだおかげで目覚めはバッチリ。セブンイレブンで400円くらいのおにぎり🍙とミネラルウォーターを買い、京都駅へ向かって地下鉄に乗ろうとしました。\n最初は本気で地下鉄だと思っていたのですが、駅構内で迷子になりスタッフに聞きながら案内板を追ってようやく理解しました。私が乗ろうとしていたのは地下鉄（メトロ）ではなく電車🚃だったのです。日本では地下鉄も含めて全部「電車」と呼ぶのだと思い込んでいたのですが、京都市内にはちゃんと地下鉄もあるんですね。京都駅にはあらゆる路線が集結していて、最初のうちは本当に混乱しました。さらに、中国の分かりやすすぎる案内に慣れきっていたせいで、日本の案内板のシステムに余計に戸惑ってしまったようです。 なんだかんだで30分も無駄にしましたが、ようやく電車に乗り込み、貴船神社への旅がスタートしました。\nヒント 日本のマナーは非常に良く、公共交通機関ではみんな自発的に並び、割り込みや押し合いはしません。「降りる人優先」「車内では静かにする」のが基本です。\n乗ったのは叡山電鉄。春の息吹を感じる山の中へと入っていく路線で、ものすごくエモい雰囲気でした。途中で中国から来たと思われる女の子2人組がカメラで写真を撮っていましたが、私は周りの迷惑になりたくなかったので撮影は控えました。でも、あの風景はしっかり目に焼き付いています。\n貴船口駅に到着後、バスで直接神社へ向かうのではなく、あえて徒歩で山を登るルートを選びました。 理由の一つは、まだバスの乗り方（支払い方法など）に自信がなくビビっていたから。もう一つは、自分の足で登った方が「旅をしている感」があって神社の雰囲気をより味わえると思ったからです。道のりは長かったですが、沿道の景色がとても美しく、歩く価値は十分にありました。\n通常の参拝ルートは以下の通りです：入口 → 本宮 →（徒歩10～15分）→ 結社 →（さらに上へ）→ 奥宮。 これが基本ですが、途中で出会った日本人の女性は逆ルートで参拝していました。一気に奥宮まで登り、そこから結社、最後に本宮へと降りてくるスタイル。こういう自由な参拝方法も面白いですね。次回は私も試してみたいです。\n写真からも分かる通り、沿道の景色は本当に素晴らしく、歩いて登る価値アリです。\nまず到着したのは神社の本宮です。\n夕暮れ時にライトアップされるとすごく綺麗らしいのですが、夕方にここへ来るのは少し怖かったので断念。日本はよくクマ出没注意の看板があるのでビビってしまいました。\n本宮では有名な「水占みくじ」を体験。おみくじの紙を水に浮かべると文字が浮かび上がってくるというもので、初めて見たのでとても神秘的でした。しかも紙にQRコードが付いていて、スマホで翻訳結果が見られるというハイテク仕様😂\nさらに上へと歩き、結社に到着。ここは人が少なかったので、スマホで参拝の作法を検索して実践してみることにしました。本宮は人が多すぎて、作法を間違えて笑われるのが怖かったんです。\nノート 手水舎を使わない基本的な参拝の作法：二礼 → 二拍手 → 一礼\nそしてついに奥宮へ。雰囲気は下と似ていますが、大きな桜の木がありました。ただ、4月中旬だったため既に散っており、桜吹雪を見られなかったのが残念です。抹茶を楽しめる休憩所もありましたが、後で宇治に行く予定だったので、抹茶は本場で楽しむことにしてここでは我慢しました。\n瑠璃光院 # 貴船神社を出た時点で既にお昼の12時近く。予定ではご飯の時間でしたが、まだ瑠璃光院に行っていません。AIに「無駄のないルート」を相談した結果、「先に瑠璃光院へ行き、その後一乗寺でラーメンを食べる」というプランに決定しました。\n瑠璃光院は以前から動画などで見て知っていました。春の青もみじと秋の紅葉の時期に特別公開される名所です。混雑状況によっては事前予約が必要とのこと。\n私が行った時の拝観料は2000円で、併設の美術館も見学できました。\n靴を脱いで袋に入れ、中へ入ります。\nヒント 瑠璃光院は本当に息を呑むほど美しい場所なのですが、私の撮影技術が壊滅的なため、その美しさを完全には伝えきれません。まずは他の方が撮った素晴らしい写真をご覧ください。\nそしてこちらが、私がお見苦しくも撮影した写真です：\n見学を終えて外に出ると、ある人からすみません、写真を撮っていただけませんか。と声をかけられました。「これ、日本に来る前からずっと自分が言おうと練習してたセリフじゃん！」と内心ツッコミを入れつつ😂\nいいですよ。と答えて、数枚写真を撮ってあげました。\nスマホを返し、立ち去ろうとすると、その方が「あなたも撮りましょうか？」というニュアンスの日本語（正確な言葉は忘れました）をかけてくれました。元々撮るつもりはなかったのですが、せっかくの好意なのでいいですよ。とお願いしました。撮り終わって階段を降りる時、不注意で足を踏み外しそうになり、慌てて大丈夫、大丈夫と言ったら、相手の方に笑われてしまいました（もちろん嫌な笑いではありません）。\nその後、一緒に道を下りながら少しお喋りしました。最初にどこに住んでいますか。と聞くと、\n彼女はフランスと答えました。発音が完全にネイティブだったので驚き、日本人ですかと尋ねると、やはり日本人で、ご家族と一緒にフランスから日本へ旅行に来ているとのことでした。その後「日本には何日いる予定？」と聞かれたのですが、「9日間」の日本語がパッと出てこず、記憶を探りながら適当に答えてしまい間違えました（彼女が訂正してくれましたが、緊張で頭が真っ白でした）。※正解は：ここのかかん\nうまく会話できない申し訳なさから、すみません、私の日本語はあんまり上手です。と言ってしまいました。(後になって気づきましたが、ここでも痛恨のミス。「上手ではありません」と言うべきでした…😭) それでも彼女は（お世辞かもしれませんが）いいえ、上手いよと言ってくれました。（最初は「うまい」の意味がピンときませんでしたが、すぐに褒め言葉だと理解しました）。とても嬉しかったです😊\nその後、彼女は美術館の中へ。もう少しお話したかったのですが、自分にチケットがあるか分からなかったので入りませんでした。後になって「あ、瑠璃光院のチケットに美術館の入場料も含まれてるって言われたわ！」と思い出し、また会えるかもと期待して中に入りました。（入る前にわざわざ「また会いましたね」の日本語を検索したほどです😂）。でも結局彼女は見当たらず、少しだけ見学して出てきました。\nでも、面白いものを見つけました：\n一乗寺 # 次は待ちに待ったご飯！ラーメン激戦区の一乗寺へ向かいます。あまり脂っこいものは避けたかったのでGeminiに相談すると、「中華そば高安」をおすすめされました。ここはスープが比較的あっさりしていてコクがあり、とても美味しかったです（ただ食後はかなり喉が渇きました）。\n一つ心残りだったのは、まだ初日で日本のマナーに慣れておらず、食後にごちそうさまでしたと言う習慣を知らなかったことです。少し勿体ないことをしました。\n食後は周辺を散策。有名な「恵文社」を見つけ、中にお客さんもいたのですが、なんとなくビビってしまい入れませんでした。これも初日ならではのチキンっぷりですね。\nそして次の聖地巡礼スポット、「鴨川の飛び石」へ向かいます。出町柳駅から徒歩10分ほどの場所にあります。\n電車を待っている間、「試運転」の表示を出した電車が来ました。誰も乗らないので不思議に思っていましたが、日本にも試運転の電車があるんですね。面白かったです。\n鴨川の飛び石（出町の飛び石） # 川辺の階段で、高校生の男女二人がトランペット🎺とサックス🎷を練習しているのを見かけました。「えっ、エモすぎん！？これが青春か！！」と感動し、思わず記録。\nPrevious Next この景色を見ていると、『たまこラブストーリー』でたまこと餅蔵が川辺にいるシーンを思い出します。 たまこが飛び石を渡り、その後ろで餅蔵が告白しようか躊躇い、最終的に告白してたまこが川に落ちるあの名シーンです。\n続いて、出町桝形商店街（たまこ商店街）へ。\n巨大なサバのオブジェは見当たりませんでした。食べ歩きできそうなスナックも少なく、外れの方にクッキーを売っているお店があったくらいです。ここの文房具屋さんでペンを何本か買い、日本の文房具屋の雰囲気を味わいました。\nその後、再び飛び石の場所に戻り、ベンチに座って休憩。川のせせらぎと人々の話し声を聞きながら風に吹かれていると、まるで日本アニメの世界に入り込んだような心地よさがありました。これぞ青春！\n京都駅に戻った後、Avantiにあるアニメイトへ。6階まで上がってみたのですが……えっ、狭っ！？ 私の想像とは全く違い、小さなラノベコーナーとグッズ、カードショップが合体しただけの空間でした。正直ガッカリです。 ラノベコーナーで『遊びのカンケイ』を探しましたが見つからず。女性店員に聞いても私の日本語が通じず、ずっと「画像を見せて」と言われ、マジで言葉を失いました。 結局、男性店員が奥から出してきてくれました。最終的に『負けヒロインが多すぎる！』3冊と『遊びのカンケイ』を購入しましたが、免税額に届かず。後ろに人が並んでいたので焦って、適当に『薬屋のひとりごと』を2冊追加しました（本当は『ノーゲーム・ノーライフ』が欲しかったのに）。\nノート 『ノゲノラ』みたいな名作はもっと目立つところに置くべきでは！？ というか、後で大阪の梅田店に行って気づいたのですが、この京都のアニメイトがショボいだけでした。梅田店はビル一棟まるごとアニメイトで、これこそが私の思い描いていたオタクの楽園でした。\nこの日の予定はこれで終了。帰りもまた京都駅で迷子になりました。本当にあの駅は迷宮です。夕食はコンビニや駅構内の売店で適当に買って済ませました。\nDay 3（2026年4月21日） # いよいよこの旅のメインイベント、宇治へ向かいます！『響け！ユーフォニアム』ファンにとって、ここは主人公・久美子たちの舞台となる絶対に外せない聖地です。\n予定より少し遅めに出発したため最初は焦りましたが、時間は十分間に合いました。\nまずは定番の伏見稲荷大社へ。到着した頃には既に人が多く、修学旅行生だらけでした。\n下まで降りてくると、ちょうどお祭りをやっていてとても賑やかでした。 今思えば、夜に来た方が雰囲気があって良かったかもしれません。（初日の京都駅の夜が静かすぎてビビってしまったのが悔やまれます）\n屋台で10円パンを購入。これがめちゃくちゃ美味しくて、忘れられない味になりました。大福も買いましたが、本当はお団子🍡が食べたかったのに売り切れていて残念。\nそしていよいよ、宇治へ向かいます！\n電車を待っている時、女性専用車両というものを初めて見て少し驚きました。\n宇治駅を出てすぐのところに宇治市観光案内所があり、そこで宇治観光マップをもらえます。 最初は勝手に持っていっていいか分からず、すみません、これ、写真を撮っていいですかと聞くと、スタッフさんが「持って行っていいですよ」と教えてくれました。どこから来たの？と軽く立ち話になり、またしても「日本語上手いね、びっくりした！」と褒められ、ニヤニヤが止まりませんでした😊\nこのマップは宇治の観光名所が網羅されていて便利ですが、「ユーフォのキャラが走っていた場所」のようなマニアックな聖地情報は載っていません。\nマップを頼りに宇治橋へ向かいましたが、紙の地図を読むのが苦手な私は結局Google Mapに頼ることに。この日の宇治は風が強く、それがまたアニメチックな雰囲気を醸し出していました。 昼間の宇治橋は撮り忘れましたが、夕暮れの写真はバッチリです：\nまずは宇治川沿いへ向かい、あの久美子ベンチへ！\nそのまま朝霧橋（部長失格橋）へ。\nその後、宇治神社、宇治上神社、平等院、そして大吉山といった王道の聖地を巡りました。\n大吉山を登っている途中、現地の女性からこんにちは。と挨拶され、心がほっこり😊 私も元気よくこんにちは。と返しました。\nそして、どうしても欲しかったのが京阪電車×響け！ユーフォニアムのコラボ一日乗車券。Grokにどこで買えるか調べてもらい、宇治からわざわざ淀屋橋駅まで大移動して1700円のコラボ乗車券をゲット！その後また宇治に戻り、JRで京都へ帰るという強行軍でした。\n乗車券を買いに行く道中で見つけたコラボパネルや自販機の数々。最高です。\nパネル：\nコラボ自販機たち（久美子、麗奈、奏、真由）： 駅へ向かう途中で、王道中の王道であるサイゼリヤや奏のバス停も発見！\nその他のコラボ要素：\nこの日は歩き回った割に観光地らしい場所にはあまり行かず、買い物もしませんでした。我が家で抹茶を飲むのは私だけなので、大行列の中村藤吉本店で抹茶パフェを食べるのは諦めました。でも、せっかくの機会なので並んででも体験しておけばよかったと少し後悔しています。\n京都駅に戻り、昨日と同じスーパーハーベス (Harves)で夜食を購入。店名を見ていなかったので記憶を頼りに探した結果、また迷子になり30分ロスするというアホな展開に。ここのシュークリームとパンを買いましたが、シュークリームが絶品でした！（写真撮り忘れ）\n参考画像： 今日の夕食はパンとシュークリーム、そしてコンビニで買ったお酒。\nこのお酒はアルコール度数が高すぎました。「ほろよい」のような乳酸菌系の甘い味を想像していたのですが、イマイチでした。\n「ほろよい」の白いサワーは本当に美味しいですね。もも味🍑を試さなかったのが心残りです。\n寝る前にユーフォの映画のチケットも無事予約完了！\nDay 4（2026年4月22日） # 本来の予定では豊郷町立豊郷小学校旧校舎群へ『けいおん！』の聖地巡礼に行くはずでした。 しかし、往復3時間という移動時間にビビってしまい、「短い滞在期間なのに、移動ばかりで終わっていいのか？もっと手軽な文化体験にするべきでは？」と迷った挙句、近場の奈良に変更してしまいました。\nノート いや、聖地巡礼こそが最高の文化体験であり没入感を得られる方法なのに、「遠いから」と妥協した時点で私の負けです。今さら言っても遅いですが、本当に行かなかったことを激しく後悔しています😔😔\n奈良駅を出て商店街を歩いていると、中谷堂のような高速餅つきをしているお店がありました（本当に中谷堂だったのかは不明）。外国人が群がって動画を撮っていたので、人混みに飛び込む勇気が出ずスルー。\n奈良はひたすら鹿、鹿、鹿。人馴れしすぎていて全く物怖じしません。\n鹿がお辞儀をする姿には本当に驚きました！動画を撮れなかったのが残念。\n奈良でも修学旅行生に遭遇。これぞ青春です😭😭\nお昼ご飯は、人混みを避けてひたすら歩き回った結果、観光客が全くいない路地裏にある老夫婦が営む定食屋に入りました。\n洋食カツ丼を注文。味は「日本の普通の食堂の800円の味」という感じで、素朴で美味しかったです。\nその後京都に戻り、京都タワーに登って京アニグッズを買うか迷いましたが、前日に1700円の乗車券を買ったこともあり予算をケチって断念。小紅書で調べると「上まで行かなくても買える」という情報があったのですが、それすらもビビって行けませんでした。結局近くのAvantiでGUをぶらぶらして終了。\n夕食は京都駅で九十九うどん (つくもうどん)へ。食券機で買ってカウンターで受け取るシステムです。鶏卵カレーうどん＋鶏天を食べましたが、普通に美味しかったです。\n日本の食事って本当に「炭水化物＋炭水化物」が多くて、野菜🥬が全然ないからカロリー爆弾ですよね。\nDay 5（2026年4月23日） # 嵐山 竹林の小径 # この日は嵐山の竹林の小径へ。ここは『俺ガイル』の八幡が自爆した場所であり、『空の境界』の舞台にもなった立派な聖地です。雨にもかかわらずものすごい人でした。\n適当に撮った写真：\n日本のこういうシトシト降るウザい雨の中でも、修学旅行生は元気に歩き回っていました。\n見かけたお寺：\n周恩来総理の記念詩碑も発見：\n雨宿りしながら歩いていると、湖（川？）のほとりの東屋に到着。「ここ、『俺ガイル』で結衣が八幡に依頼した場所じゃん！」と興奮しましたが、例の小道は撮り忘れました。\n雨のせいかテンションも上がらず、駅周辺の屋台で肉まんじゅう（分厚い水餃子みたいなやつ）を食べて嵐山を後にしました。\n錦市場 # まだ時間が早かったので、「せっかく日本に来たのに何もしてない感」を払拭すべく、祇園四条の錦市場へ。ここは地元民よりも圧倒的に欧米の観光客が多かったです。\n途中でこんなものも見かけました。\nここも錦市場では有名なスポットなのかもしれません。\nここでたこ焼きを買いました。「4個で800円、8個で1000円」という強気な価格設定に「どういう計算！？」とツッコミましたが、いざ食べてみると8個は脂っこすぎてキツかったです。\nたこ焼きを食べた後は京都駅方面へ戻りました。この日は雨でかなり疲れていたので、早めにホテルへ戻ってアニメを見ながら休むことにしました。ただ、今思えば本当にもったいない時間の使い方でした。せっかく日本まで来たのだから、もっとちゃんと歩き回るべきだったと思います。 とはいえ、毎日かなり歩いていたので、8日間の旅程でも体力と精神力は完全には回復しません。あれは休息のために必要なサボりだった、と自分に言い聞かせつつも、やはり少し悔いは残ります。\nDay 6（2026年4月24日） # 京都水族館 # 今日は**『最終楽章 響け！ユーフォニアム 前編』**を見る日です！本当にワクワク！！ 正直に言います。私が今回関西旅行を企画したのは、すべてはこの映画を見るためです。（映画という「餃子のタレ」のために、関西旅行という「餃子本体」をわざわざ用意したようなものです笑）\nホテルから京都水族館への道中：\n道に全然人がいなくて、「日本の社会ってどう回ってるの？みんな仕事してないの？」と本気で不思議でした。\n小紅書のレビューでは「海遊館に比べて小さい」と不評でしたが、そもそも海遊館とはコンセプトが違うので無視して水族館へ。\nここにも修学旅行生が！4校くらい来ていました。\n入場料は2600円と少し高めですが、イルカショーまで見ると全然小さく感じません。オフシーズンに一人で来たら最高に癒やされる空間だと思います。\n水族館＋日本の女子高生の最強の青春コンボ写真：\nここからは水族館で撮った魚たちの写真です。\nPrevious Next ペンギンの写真。\nクラゲの写真。\nPrevious Next その他、館内で撮った写真いろいろ。\nそして、魚を見ている途中で、めちゃくちゃ可愛いメガネっ娘の日本の女子高生を見かけました。あまりにもエモくて青春だったので、思わず後ろ姿を撮ってしまいました。\nこれぞ青春だあああ😭😭😭\n🔥 超重要・本気のお願い！！！ もし、本当に写真に写っている彼女たちの連絡先をご存知の方がいらっしゃいましたら、ぜひ私に紹介していただけませんか？どんな些細な情報でも構いません。以下の連絡先まで、何卒よろしくお願いします！ nni461904@gmail.com @ice24971676770 本当に、本当にお願いします！！！🙇‍♂️🙇‍♂️🙇‍♂️\n最初は思い切って声をかけて連絡先を聞こうか迷いました。日本の女子高生と知り合いになれたら最高じゃないですか。 でも結局チキン発動で声をかけられず。イルカショーの後にもう一度探そうかと思いましたが、映画の時間が迫っていたため諦めました。\nヒント もし映画館で偶然彼女たちに再会するような奇跡（フラグ）が起きていたら、どんなに恥ずかしくても絶対に土下座してでも連絡先を聞きに行ってましたね。\nイルカショーも大満足でした！\nPrevious Next MOVIX 京都 # 水族館の後はバスで四条のMOVIX 京都へ。人生初の日本の路線バスでしたが、iPhoneと小紅書の攻略法のおかげで余裕でした。\n映画の前に腹ごしらえ：\nグッズ売り場で「ボイス付きアクリルスタンド」を探したのですが、売り切れなのか置いておらず、クリアファイルとキーホルダーだけ買いました。\n記念撮影：\nMOVIX京都の周辺。\nコナンくんの映画の宣伝がものすごくて、圧倒的な覇権ジャンルっぷりを見せつけられました。\nユーフォの3期は演奏シーンが少なかったり、原作からの改変（久美子がソリを吹かない）があったりと複雑な心境でしたが、今回の前編は8分間もの濃厚な演奏シーンがあり、本当に最高でした！感動！\n京都大学 # 映画の後は時間が余ったので、バスで京都大学周辺へ。\n華南師範大学より建物が古く、キャンパスが分散している印象を受けました。\n京都大学の百周年時計台記念館は撮り忘れました。\n大学の近く（西部講堂？）でイベントをやっていたので、少し写真を撮りました。\nその後も周辺を少し歩き、何枚か写真を撮りました。\n最後は京都駅へ戻り、コンビニで適当に買い物をしてホテルで休みました。\nDay 7（2026年4月25日） # この日は京都から大阪へ移動する日です。前日に買った京阪電車のコラボ一日乗車券を、ここでようやく使うことになります。 ホテルから京阪電車に乗るには、スーツケースを引きながら1キロ以上歩いて東福寺駅へ向かい、そこから淀屋橋駅まで移動して、さらに大阪市内の地下鉄に乗り換える必要がありました。\n地下鉄の駅を出ると、こんな景色が広がっていました。\nそのまま宿泊先の東横INNへ向かいました。ホテルは大阪のビジネス街にあり、かなり栄えているエリアでした。ただ、梅田の店舗ほどの立地ではないのかもしれません。\nホテル近くの公園 # チェックインと荷物預けを済ませた後、ホテル近くの公園を少し散策しました。\nベンチに座っていると、隣にいたおじいちゃんに話しかけられました。訛りが強くてほとんど聞き取れなかったのですが、いっぱい、WIFI、携帯電話、データ といった単語だけは分かりました。 しばらくしてようやく、スマホのデータ通信量を使い切ってしまい、ネットに繋がらないので近くにWi-Fiがないか聞いているのだと理解しました。私がスマホを2台持っていたので、助けを求めて声をかけてくれたようです。 10分ほどカタコトでやり取りした後、お礼に「ももミルク」をくれました。アニメに出てくるいちごミルクの仲間みたいで、ちょっと嬉しかったです。\n大阪城公園 # その後は大阪城公園へ向かいました。とにかく広く、地元の人がジョギングしたり散歩したりしている姿も多く見かけました。\n途中で見かけた大阪NHKの建物。\n大阪城公園の写真です。\n園内にはコナンのコラボショップもありました。コナン、どこに行っても強すぎます。\n豊臣秀吉像：\n以前、Bilibiliかどこかで見た江ノ島駅の「服を着た小鳥」の大阪版らしきものも発見しました。\n江ノ島駅の小鳥（クリックで参照元へ） なんば # MOVIX京都で買えなかった映画館コラボのボイス付きアクリルスタンドを探すため、大阪の映画館も回ってみることにしました。まず向かったのはなんばパークスシネマです。\n道中では路上ライブが行われていました。\n映画館のグッズ売り場に行ってみると、やはり目当てのアクスタはありませんでした。歩き疲れて、もう他の大阪の映画館を回る気力もなくなり、外のテラス席で休憩することにしました。そこでもライブイベントが行われていて、4月29日に京都駅でTRUEさんが出演するイベントもこんな感じなのかな、と少し想像しながら音楽を聴いて休みました。\n20分ほど休んだ後、今度は5500円の服を買うかどうか迷いながら、もう一度ショップを見に行きました。正直、ユニクロの服が1900円くらいなのにこれは高すぎるだろ、と思ってかなり悩んでいました。\nすると、さっきまでなかったアクスタが1つだけ追加されているではありませんか。しかも最後の1つ。これはもう迷っている場合ではないと思い、即購入しました。4550円と高額でしたが、ボイス付きのアクスタなんて本当に珍しいので、これは取るしかありません。\n心斎橋 # 最後に、オタク仲間に頼まれて近くのアニメイトでCDを探すことにしました。大阪のアニメイトに行って初めて気づきました。「京都のアニメイトが小さかっただけだ。大阪のアニメイト、デカすぎ！！」\n店内の雰囲気はこんな感じです。\nまた、街中では店の宣伝や客引きをしている女の子たちも見かけました。京都では全く見なかったので、関西にはあまりないのかと思っていたのですが、単に京都が伝統的で、大阪の方がこういう繁華街らしい雰囲気が強いだけなのかもしれません。\n昼食をまともに食べていなかったので、早めに何か食べることにしました。AIに聞いたところ、Madras Curry (マドラスカレー)をおすすめされたので行ってみました。 量が本当に多く、小盛りでも十分お腹いっぱいになります。写真は撮り忘れましたが、行くなら普通に小盛りで十分です。隣の人が中盛りを注文して、カレーまできれいに完食していたのにはマジで言葉を失いました🤯。\nその後は大阪の街をぶらぶら。大阪は京都とは全く違い、夜になっても人が多く、街全体に活気がありました。\n最後はホテルに戻って休みました。京都のホテルと比べると、こちらの方が部屋は少し広かったです。ただ、設備の新しさでは京都のホテルの方が上だった気がします。\nDay 8（2026年4月26日） # この日のメインは大阪大学の見学です。\nホテルの朝食 # ホテルの朝食は、小さなパン、おにぎり、コーヒー、少し酸味のあるオレンジ色の飲み物、緑色の野菜ジュースっぽい飲み物、そして味噌汁など。思ったより美味しく、東横INNはかなりおすすめできるホテルだと感じました。\n大阪大学 # 大阪大学へ向かう途中、地下鉄でまた少し混乱しました。電車を待っている間も、「やっぱり奈良の日に豊郷町立豊郷小学校旧校舎群へ聖地巡礼に行くべきだったのでは」と後悔がぶり返していました。\n大阪大学は郊外にあり、中国でいう大学城のような雰囲気でした。ただ、敷地の広さは大学城の大学2つ分くらいありそうです。大学へ向かう道も、とても日本アニメっぽい雰囲気がありました。\n大阪大学の入口。\n掲示板：\n案内板：\nキャンパスの景色と学生運動の名残らしきもの。\n見学後、万博記念公園の太陽の塔に行こうとしたのですが、入り口が分からず、有料っぽいので断念しました。 その後、地下鉄で梅田方面へ向かいます。車内で大学生っぽい人を見かけました。\n梅田 # 梅田に戻ると、ルミシェリー (Lumi Cherie)という地下アイドル？がチェキ会をやっていて大盛況でした。\n親からの「日本に行ったら鰻を食え」というミッションを果たすべく、「鰻の成瀬」でうな重を堪能。\n食後、近くに縁結びの神社があったので少し散策しました。\nその後、梅田スカイビル 空中庭園展望台へ向かいました。\n途中で見かけたもの。\n到着してみると、人はそこまで多くありませんでした。観光客もちらほらいて、インド系の方も見かけました。\n上に登ってもスマホでしか写真を撮れず、入場料もかかるので、結局展望台には上がりませんでした。少し休憩してから、MARUZEN \u0026amp; ジュンク堂書店 梅田店へ向かいます。\nその後、「MARUZEN \u0026amp; ジュンク堂書店 梅田店」へ。 この書店は本当に巨大でした。雑誌、小説、名著、参考書、専門書まで何でも揃っています。コンピュータ書の棚にはCopilotやGPTの使い方を解説した本まであり、少し不思議な気分になりましたが、年配の方などにはこういう本も需要があるのだろうと思います。 それ以上に驚いたのは、RustやLinux eBPFの専門書まで普通に置いてあったことです。O\u0026rsquo;Reillyの本も多く、日本の出版文化の厚みに圧倒されました。 しかも最上階には駿河屋まであり、これは完全に予想外でした。\nその後ホテルに戻り、またコンビニで夜食を買いました。\nこのミルクプリン🍮には本当に驚きました。 蓋にプリンがくっつかない仕様になっていて、舐める必要がない！ 日本の細部へのこだわりに感動。しかもめちゃくちゃ美味しかったです。初日から毎日食べなかったことを激しく後悔しました。\nDay 9（2026年4月27日） # ついに帰国の日です。朝はいつも通りホテルで朝食を食べ、チェックアウトして荷物を預けました。大阪は買い物以外だと、個人的にはそこまで行きたい場所が残っていなかったので、飛行機の時間まで大阪城公園周辺をもう一度散策することにしました。\n道中でまた見かけたNHKの建物。\n近くにあった古い雰囲気の建物。\n芝生では幼稚園の先生が子どもたちを連れて遊んでいました。\n大阪城公園と高校に何か関係があるのでしょうか。\n適当に撮った景色。\n結局、特に行く場所もなく、ホテル近くの公園に戻って日本での最後の時間をぼんやり過ごしました。 この日もまだ、なぜ豊郷町立豊郷小学校旧校舎群へ『けいおん！』の聖地巡礼に行かなかったのかと後悔していました。4時間かかっても今日行けるなら行こうと調べてみましたが、月曜日は休館日。しかも前日はイベントもあったようです。 こんなに後悔するくらいなら、昨日『けいおん！』聖地巡礼に行き、今日大阪大学を見学すればよかったです。月曜日なら大阪大学にも人が多かったはずですし。\nその後も公園やホテル周辺をうろうろしながら、街の様子や食べられそうな店を見て回りました。最終的に、迷った末にごんべという店に入りました。\nここも年配のご夫婦が営むお店でした。店に入っても特に「いらっしゃいませ」がなく、どう座ってどう注文すればいいのか少し不安になりました。とりあえず入口近くの席に座ると、何を食べるか聞かれたので親子丼を注文。お茶を出してもらい、料理を待ちました。\n待っている間にGoogle Mapsの口コミを見てみると、「挨拶がない」「親子丼の量が少なく、鶏肉が数切れしかない」といったコメントがありました。一方で、静かで落ち着く、気を使わずに食べられるという好意的な口コミも多かったです。\n料理が出てきて見てみると、確かに鶏肉は少なめで、4切れほどしかありませんでした。卵は美味しかったのですが、800円でこれだと少し物足りない気もしました。\n仕方なく公園でぼーっとしていると、昼の1時なのにもう小学生が下校していました。早すぎない？\n水鉄砲で遊んでいる子どもたち3人組がいて、水をかけられたので「日本の子どもか…」と思ったら、バリバリの中国語を喋っていてさすがにツッコミそうになりました。\nせっかくなら声をかけて、日本での生活や学校のことを少し聞いてみればよかったかもしれません。\nその後、別の公園にも少し寄りました。\n最後にホテルへ戻って荷物を受け取り、空港へ向かいました。\nところが、天王寺でJRはるかに乗ろうとしたところ、Trip.comで買っていた指定席の時間が過ぎており、指定席が無効になっていました。仕方なく自由席に座れるか賭けることにしました。空港での手続きに不安もあったので、次の便を待たずにそのまま乗車。幸い、料金的にはそこまで損した感じではありませんでした。\n乗車後しばらくすると、車掌さんが6、7人の外国人乗客と何か話していました。なぜか追加料金を払う必要があったようで、30分ほどやり取りしていました。 車内には空港へ向かう中国人も2人ほどいて、その中に白いマスクをしたとても綺麗な女性がいました。写真を撮れなかったのが残念です。空港に着いてから、彼女が春秋航空で上海へ帰る人だと分かり、少し惜しい気持ちになりました。\nいよいよ搭乗です。\nさよなら日本！\nDay 10（2026年4月28日） # 3時間ほどのフライトを経て、深夜0時過ぎに香港空港へ到着しました。そして、ここでまた一晩を過ごすことに。正直、眠るのはかなり難しく、かなりしんどい夜でした。\nヒント 香港空港で荷物受け取りがない場合、急いで入国せず、制限エリア内にあるシャワールームを使うと快適です。外のロビーより環境が良いですよ。\n朝5時半から6時頃、深夜に小紅書で見つけた攻略を頼りに、該当するバス停へ向かいました。私はモバイル通信が使えず、しかも下車地点にも少し不安があったので、近くにいた人に声をかけてみました。\nちょうどその人も広州へ戻るところでした。ただし、彼は深セン湾口岸へ向かう予定で、私は当初、福田口岸から高速鉄道で学校へ戻るつもりでした。それでも親切そうな方だったので、一緒に帰ることにしました。 バスの中で少し話し、うとうとしようとしたところ、気づけば一駅乗り過ごしていました。仕方なく作戦変更して、別のバスに乗り換えることになりました。\nバスを待っている間、近くに「ペットトイレ」と書かれた公園がありました。\n香港の気候は広州とほとんど同じで、蒸し暑くてかなりしんどかったです。\n最終的には永東のバスに乗り、広州へ戻ることができました。\nただ、広州に戻ってから黄埔区で大渋滞に巻き込まれました。帰国早々この光景を見せられて、疲れも相まってかなり参りました。休日でもなく、退勤時間でもないのにここまで渋滞するのかと、帰国早々日本のストレスフリーな交通機関が恋しくなりました。\nおわりに # 午後1時過ぎ、無事に寮に到着し、これにて関西旅行は完結です！\n数々の心残りや後悔はありますが、特に4/29の京都駅でのTRUEさんのライブと宇治・太陽が丘公園のサンフェスを逃したのは痛恨の極みです😭😭😭\n告知が出たのが3月末〜4月中旬で、その頃にはもう航空券もホテルも変更不可でした。もう少し告知が早ければ予定をずらしたのに……！！！😭😭😭\n最後に、今回の戦利品です：\n","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/life/travel/kansai-trip/","section":"生活","summary":"関西旅行記⛺️ # はじめに # 事前に作成した関西旅行プランと関西旅行実行ガイドをもとに、2026年4月18日、私は広州からバスで香港国際空港へ向かい、翌日の人生初フライトに備えました。ここからいよいよ関西へ降り立ち、大阪、京都、奈良を巡り、現地の文化やグルメ、美しい風景を体験する旅の始まりです。\n","title":"関西旅行記⛺️","type":"life"},{"content":"Welcome to my blog!!!\n","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/","section":"響け！ユーフォニアム","summary":"Welcome to my blog!!!\n","title":"響け！ユーフォニアム","type":"page"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/life/","section":"生活","summary":"","title":"生活","type":"life"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/tags/%E6%97%A5%E6%9C%AC/","section":"Tags","summary":"","title":"日本","type":"tags"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/tags/%E6%97%A5%E6%9C%AC%E6%97%85%E8%A1%8C/","section":"Tags","summary":"","title":"日本旅行","type":"tags"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/categories/%E6%97%85%E8%A1%8C/","section":"Categories","summary":"","title":"旅行","type":"categories"},{"content":"","date":"2026年5月2日","externalUrl":null,"permalink":"/ja/life/travel/","section":"生活","summary":"","title":"旅行","type":"life"},{"content":" 🎌 关西 9 天 8 夜圣地巡礼与文化巡游终极手册 # 📌 行前与抵达核心备忘录 # 交通卡充值： 落地前在 Apple Wallet 里给 ICOCA 充值到 15,000 日元（使用 Mastercard/Amex/JCB）。 HARUKA 提票： 关西机场 JR 站绿色售票机 ➔ 选繁体中文 ➔ 扫 QR 码 ➔ 扫护照原件 ➔ 拿纸质票进站。 电影首映划座（核心！）： 4月21日/22日午夜0:00，准时在 MOVIX 京都官网选 4.24 的首映座位。支付时选「ムビチケ」，输入实体卡背面的 10位购买番号和 4位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！ 免税原则： * “一般物品”（衣服、书、手机、风铃、明信片）满 5000 日元免税，可当场拆封使用。 “消耗品”（药妆、食品）免税后绝对不可拆封。 伴手礼大头全留到关西机场安检后买，不占行李额，直接免税。 Tips 终极免税与避坑指南 (Tips)\n1. 两种免税模式对比 # 模式 操作流程 常见场所 优缺点/注意事项 直接免税 (首选) 结账时店员直接扫护照，扣除 10% 消费税。你直接付尾款。 Uniqlo, GU, 大国药妆, 各种药妆店, Yodobashi 最省心，无额外费用。 售后退税 先付含税全额，然后拿着小票和物品去商场的“免税柜台 (Tax Free Counter)”排队领现金或退到卡里。 高岛屋, 大丸, 伊势丹等大型百货商场 商场会收取约 1.55% 的手续费，实际到手退税约 8.45%。人多时可能要排长队。 2. 免税核心门槛 (千万记住) # 金额： 单人在同一家店铺、同一天内消费金额必须满 5,000 日元（不含税）。 证件： 必须携带护照原件！店员要扫上面的入境贴纸。复印件、照片绝对不行。 3. “一般物品” vs “消耗品” 的致命区别 # 一般物品（衣服、轻小说、二手 iPhone、电子产品）： 结算免税后，可以直接拆封使用。买完衣服立马就能穿。 消耗品（药妆、零食、饮料）： 结算免税后，店员会将其装入印有免税字样的透明密封袋中。在日本境内绝对不能拆封！ 一旦拆封，出境查验时会被要求补缴 10% 消费税。 4. 二手 iPhone 避坑特记 # 日本销售的 iPhone 无论是否静音，拍照必定有快门声（防偷拍法律规定）。买之前要有这个心理预期，或者后续在国内使用特定方法规避。 确认机身无严重磕碰，屏幕边缘无老化发黄。 📅 逐日精细行程与实操指南 # Day 1 (4.19)：降落京都，安顿与装备升级 # 08:00 - 12:45： 香港起飞，抵达关西国际机场 (KIX)。 13:30 - 14:30（换票与乘车）： 跟着“铁道”指示牌来到 JR 关西机场站。 跟着这个链接 可以学习扫码进站 主要就是看红色闸机 16:00 入住： 京都第一ホテル (京都第一酒店) ➔ 放下行李，轻装上阵。 17:30 装备采购： * 京都アバンティ (Kyoto Avanti) ➔ 逛 GU 和 Workman。 * イオンモールKYOTO (永旺梦乐城 KYOTO) ➔ 去 Uniqlo 买 U 系列白 T 恤；去大垣书店买几本轻小说，结账时说 Cover onegaishimasu 拿书皮。 19:00 晚餐： 東洋亭 京都ポルタ店 (东洋亭 百年洋食) ➔ 必点招牌锡纸包汉堡排套餐。 20:30 日用品补给： 酒店附近的 ダイコクドラッグ (大国药妆) 买齐 狮王 PAIR Acne 祛痘膏 (ペアアクネクリームW) 等消耗品。 Day 2 (4.20)：叡山电车北线 (自然与日常番巡礼) # 警告 记得去到Movix 京都官网去选座: 优先 E10 or F10，选 4.24 的首映场次！支付时选「ムビチケ」，输入实体卡背面的 10 位购买番号和 4 位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！\n08:30 清晨森系： 貴船神社 (Kifune Jinja) ➔ 拍晨光中的红灯笼。 11:30 春季新绿： 瑠璃光院 (Rurikō-in)。 13:00 极客午餐： 麺屋 極鶏 (Men-ya Gokkei) 或 天天有 本店 (Tentenyu)。 14:30 🎐 文创采购： 恵文社一乗寺店 (Keibunsha) ➔ 在这家全球最美书店挑几张独立画师明信片和精美书签。 15:30 圣地巡礼： 鴨川デルタ (鸭川三角洲) ➔ K-ON! 跳石头。 出町桝形商店街 (Demachi Masugata Shotengai) ➔ 玉子市场原型。 必吃点心：出町ふたば (出町双叶 豆大福)。 18:00 晚餐： 鳥貴族 京都駅前店 (Torikizoku) ➔ 全场统一价居酒屋。 Day 3 (4.21)：京吹重头戏 (宇治深度巡礼) # 笔记 💡 联动任务： 在京阪车站购买“京阪電車×響け！ユーフォニアム2026春 1日乘车券”（1700日元）。全程开启手机 GPS，进入官方特设网页边走边收集数字印章！\n08:00 避开人流： 伏見稲荷大社 (Fushimi Inari Taisha)。 11:30 抵达宇治与午餐： 中村藤吉 本店 (Nakamura Tokichi) ➔ 抹茶荞麦面+芭菲。顺便买几包高级煎茶/抹茶粉送长辈。 14:00 - 17:00 吹奏部巡礼： 购买周边：宇治市観光センター (宇治市观光中心) ➔ 拿纸本“舞台探访MAP”并购买《京吹》限定明信片。 核心场景：宇治橋 (Uji Bridge) ➔ 宇治上神社 (Ujigami Shrine) ➔ 久美子ベンチ (久美子椅)。 17:30 黄昏打卡： 大吉山展望台 (Mt. Daikichi Observatory) ➔ 看日落名场面。 Tips 有什么觉得不够的，遗漏的就看手机上保存过的宇治有网图\n19:30 晚餐： 京都勝牛 京都駅前店 (Kyoto Katsugyu) ➔ 酥炸牛排。 Day 4 (4.22)：奈良千年古都 (文化遗产与境界的彼方) # 09:30 抵达奈良： 近鉄奈良駅 (Kintetsu Nara Station)。 10:00 圣地打卡： 猿沢池 (Sarusawa-ike) ➔ 栗山未来实景地。 10:30 世界遗产： 東大寺 (Todai-ji)。 12:00 特色午餐： 志津香 釜めし 公園店 (Shizuka Kamameshi) ➔ 奈良名物釜饭。 14:00 小鹿与神道： 奈良公園 (Nara Park) ➔ 春日大社 (Kasuga Taisha)。在春日大社求一个学业成就御守，祈愿7月的日语考试以及未来的修士入学考试顺利通过。 16:00 古街漫步： ならまち (Naramachi 奈良町) ➔ 逛逛改建的咖啡馆。 17:30 必吃点心： 中谷堂 (Nakatanidou 艾草麻薯)。 19:00 返回京都车站 ➔ ✨ 限定活动： ニデック京都タワー (京都塔) ➔ 展望室正在举办京吹联动，去拍角色等身大立牌，完成叠色印章。 Day 5 (4.23)：岚山漫步 (《春物》与风景) # 09:00 晨间漫步： 嵐山 竹林の小径 (Arashiyama Bamboo Forest) ➔ 天龍寺 (Tenryu-ji)。 11:00 🎐 寻找风铃： 嵐山 昇龍苑 (Arashiyama Syoryuen) ➔ 挑选一只声音清脆的金属风铃或清水烧风铃。注意：风铃随身带上飞机，切勿托运！ 12:00 豆腐料理： 嵯峨とうふ 稲 (Saga Tofu Ine)。 14:00 大老师打卡： 渡月橋 (Togetsukyo Bridge)。 Day 6 (4.24)：京都市内纵贯线 (剧场版之日) # 💡 票券提示：这一天在地铁站购入“地下铁一日券 (800 日元)”\n08:30 🚨 影院物贩抢夺战： 携带一日券直接冲去 MOVIX京都 (MOVIX Kyoto 电影院) 的周边贩卖区，抢购《最终乐章》场刊与限定周边！买完后在取票机扫手机二维码打出晚上的纸质电影票。 10:00 霙与希美： 京都水族館 (Kyoto Aquarium)。 12:00 ✨ 限定午餐： ホテル京阪 京都八条口 レストラン (京吹联动咖啡厅) ➔ 点一杯「久美子のレッドユーフォニアム」饮品，在等身大挂画包围下沉浸式就餐。 14:00 纵贯线： 京都御所 (Kyoto Imperial Palace) ➔ 京都大学 (Kyoto University) ➔ 禅林寺 永観堂 (Eikando Zenrin-ji)。 18:00 快速晚餐： 餃子の王将 三条店 (Gyoza no Ohsho) ➔ 吃一份热气腾腾的煎饺和天津饭。 18:45 影院补给： 返回 MOVIX京都 柜台购买附带原创提袋和迷你文件夹的京吹限定爆米花套餐。 19:00 终极浪漫： 带着爆米花和电影票进场，观赏《響け！ユーフォニアム》剧场版！ Day 7 (4.25)：转移大阪，拥抱天守阁 # 09:30 🎁 京都收尾采买： 在京都站内购买几盒生八桥 (生八ツ橋)。(保质期短，这时候买正好可以带回家分给朋友)。 10:30 退房转移： 入住 東横INN大阪谷四交差点 (Toyoko INN Osaka Tanimachi)。 13:00 历史名城： 大阪城 (Osaka Castle)。 14:00 咖喱乌冬： 得正 谷町店 (Tokumasa Curry Udon)。 18:00 繁华夜景： 道頓堀 (Dotonbori)。 19:30 大阪烧： 美津の (Mizuno)。 Day 8 (4.26)：硬核淘货与复古大阪 # 10:00 极客时间： 日本橋でんでんタウン (Nipponbashi Den Den Town)。 手机淘货：イオシス 日本橋店 (Iosis) / じゃんぱら (Janpara) ➔ 确认 SIM Free 与 Battery Health。 书籍淘货：周边的书店可以翻阅一下操作系统、内核技术、Rust 语言或 C 语言相关的底层日文图解书籍。 13:00 暴力回血： 天地人 日本橋店 (Tenchijin 炭火豚丼)。 17:00 昭和风情： 新世界 / 通天閣 (Shinsekai / Tsutenkaku)。 18:30 串炸晚餐： 八重勝 (Yaekatsu) ➔ 大阪地道串炸，酱汁严禁蘸第二次。 Day 9 (4.27)：终极扫货与满载返程 # 13:00 机场前哨： 退房，前往 りんくうプレミアム・アウトレット (Rinku Premium Outlets 临空城) 做最后闲逛。 16:00 抵达机场： 関西国際空港 (Kansai International Airport - KIX) ➔ 第一件事：办值机，把沉重的行李全部托运！ 17:00 🎁 终极伴手礼狂欢（免税区）： 过完安检后，在里面的 KIX Duty Free 免税店大肆采购。 必买清单：Royce\u0026rsquo; 生巧克力（加购冰袋）、白色恋人、神户风月堂法兰酥。 结账后直接提着免税购物袋登机，轻松完美收官。 *20:00 满载而归，起飞返航！ ","date":"2026年4月16日","externalUrl":null,"permalink":"/life/travel/kansai-trip-implementation-guide/","section":"生活","summary":"🎌 关西 9 天 8 夜圣地巡礼与文化巡游终极手册 # 📌 行前与抵达核心备忘录 # 交通卡充值： 落地前在 Apple Wallet 里给 ICOCA 充值到 15,000 日元（使用 Mastercard/Amex/JCB）。 HARUKA 提票： 关西机场 JR 站绿色售票机 ➔ 选繁体中文 ➔ 扫 QR 码 ➔ 扫护照原件 ➔ 拿纸质票进站。 电影首映划座（核心！）： 4月21日/22日午夜0:00，准时在 MOVIX 京都官网选 4.24 的首映座位。支付时选「ムビチケ」，输入实体卡背面的 10位购买番号和 4位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！ 免税原则： * “一般物品”（衣服、书、手机、风铃、明信片）满 5000 日元免税，可当场拆封使用。 “消耗品”（药妆、食品）免税后绝对不可拆封。 伴手礼大头全留到关西机场安检后买，不占行李额，直接免税。 Tips 终极免税与避坑指南 (Tips)\n","title":"关西之旅实施攻略","type":"life"},{"content":" 警告 已经决定 4.18 到机场, 4.19 飞去日本这样\n准备工作 # 签证 or 其他准备手续 # 去淘宝找人办理签证 (也就一两百块钱)\n要准备好电子版签证，去程值机要扫码验证\nvjw (Visit Japan Web) 要提前填写好，好快速出关\n教程🔗 VJW网站 机票 (提前三个月看即可) # 目的地: 关西 (关西国际机场)\n选择先坐高铁到达可以直飞并且费用没有这么贵的航空点 (这样一般不是在广州或者深圳起飞) 提前看好，买好票，价格差不多的就直飞 (一般能够在广深起飞) 也可以去香港然后再飞日本, 到时看情况 提示 香港出发的话 考虑一下 香港快速航空 和 乐桃航空, 不过注意行李这些要知道就是是可能会额外收费, 所以选机票的时候要看清楚\n提示 尽量选择早上航班，这样可以下午到达关西，有助于行程安排，不匆匆忙忙\n使用 天巡 和 东航 或者 携程 这种看看机票价钱即可\n住宿 # 警告 保留意见 (毕竟不同时间段会价格不一样) 京都选择这两个:==京都站前卓越酒店==和==樱花台画室饭店==/==京都乌丸五条酒店==\n笔记 京都日本第一酒店 (Kyoto Daiichi Hotel) 已经预订好住六天了\n可以 3+3 天安排\n可能要问一下学长转酒店这些是否能够提前寄存行李 (就是在下午入住前), 怕要提着行李到处走\n樱花台画室饭店\n小红书🍠 # 🏨在哪预定： 价格最低平台➡️booking.com\u0026lt;Agoda\u0026lt;Google map\u0026lt;美团\u0026lt;携程\u0026lt;爱彼迎。\n提示 一定要提前很长时间预定，否则没有房间还有价格偏高\n大阪难波道顿堀阿克罗胶囊旅馆1 (Acro Capsule Hotel Namba Dotonbori) 大阪东急 REI 酒店 (SHIN-OSAKA ESAKA TOKYU REI HOTEL) 京都—Wise Owl Hosteks Kyoto (京都明智猫头鹰青年旅馆) 京都日本第一酒店 (Kyoto Daiichi Hotel) 已经预订好住六天了 京都乌丸五条口袋酒店 (THE POCKET HOTEL Kyoto-Karasumagojo - Private Rooms) 京都-櫻花台畫室飯店 (Sakura Terrace The Gallery -Adult Only) 京都四条鸟丸索特图斯弗雷撒酒店 (Sotetsu Fresa Inn Kyoto-Shijokarasuma) 京都站八条口卓越酒店(Hotel Excellence Kyoto Station) ==住宿时间是多长根据到时讨论商量分别在这个地标居住多久而定==\n交通 # 最好就是问一下学长，地下铁是?\n摘抄自一位学长的攻略 # 携程上购买了 haruka 从关西——京都 (用于机场到京都)(提前 2-2.5 个月预订) 携程说明 在京都站旅客案内所买了两张京都一日地下铁（1100 日元，第一天和去三千院/贵船用）不坐巴士买一日地下铁券会便宜一点（800 日元） 地下铁 地下铁说明 购买西瓜卡 西瓜卡说明 个人出行的话建议试试 LUUP 的共享电车，真的蛮方便，护照和学习答题就好，不过界面是英文的 LUUP🔗 电话卡/流量卡 # 淘宝直接购买 softbank 流量卡 另一张卡开启国际漫游实现接受短信这样 (开启无忧行就可以使用流量这样，看个人选择) 下个无忧行 app 提示 建议先只插这一张卡，重启手机，成功搜索到信号后，再把自己的卡插为卡 2（禁止漫游联网），重启手机。 这样这个卡走流量，自己的卡可以收验证码短信等等。\n游玩路线 # 提示 记得买当地特色产品 (伴手礼)\nkon圣地巡礼\n琉璃光院(以最新消息为准)\n春季特别访问：2025 年 4 月 15 日（星期二）至 5 月 31 日（星期六） 夏季特别参观：2025 年 7 月 1 日（星期二）至 8 月 17 日（星期日） 秋季特别参观：2025 年 10 月 1 日星期三至 2025 年 12 月 14 日星期日 每个时段的参观时间为 10:00 至 17:00（最后入场时间为 16:30）。\n如何购买门票\n在春季和夏季的特殊观赏期，您无需预约即可亲自参观。\n关于秋季特别观赏的预约，详细信息将在决定后立即在官方网站上公布。\n入场费\n成人：2,000 日元 儿童（初中生以上，持学生证）：1,000日元 小学生及学龄前儿童：免费 DAY 0 (上海飞) 2026.04.18 # 提示 因为直飞都比较贵，所以我直接根据学长的攻略，先提前一天飞到上海玩一天，然后明天再坐飞机到关西国际机场 (可以找个有送机的酒店，在去哪儿找都行)\n广州/深圳\u0026ndash;\u0026gt;上海 # 可以简单游玩一下，先基本定在 唯庭轻居酒店(上海虹桥机场国家会展中心店)\n看到时是深圳飞上海便宜还是广州飞便宜 (记得提前一个月多看机票)\n落地先去酒店办理入住，放好东西就玩\n笔记 记得提前预约接送机，告诉行李，航班号，人数时间什么的\n上海\u0026ndash;\u0026gt;关西国际机场 (Day 1) 2026.04.19 # 提示 选择浦东机场🛫\n因为是早上飞机，所以我们是预定接近中午到达 关西国际机场\n到达 关西国际机场之后, 用提前买好的 携程haruka车票 在小绿屋人工兑换或者在绿色机器上兑换 (建议直接人工或者哪里人少去哪里)\n到达 京都站 之后就每个人买两张地下铁 (若决定在京都玩的时间较久可以买三张?!)\n大概是==下午两点半左右==可以到达酒店 (住在京都第一酒店\u0026lt;贵就住在京都乌丸五条先\u0026gt;)，就办理入住这些，就可以寄存行李就可以到处闲逛了\n办理好入住手续之后就先去吃个饭，然后就附近闲逛，看情况是否需要买伞 (雨 or 雪)\nDAY 0 (香港飞) # 提示 香港直飞关西国际机场相对方便，也可以考虑价格，如果直飞便宜就直接飞，否则可以结合广州/深圳方案。\n广州/深圳\u0026ndash;\u0026gt;香港 # 如果你人在广州或深圳，可以先搭高铁或者城际去香港机场（建议提前1–2小时出关），留足过关时间。 提前在 google travel / 携程 / Skyscanner 比价机票，看香港飞关西国际机场哪班更便宜。 建议选择上午或中午航班，这样到达关西后还有时间安排行程。 香港\u0026ndash;\u0026gt;关西国际机场 (Day 1) 2026.04.19 # 抵达香港机场\n办理登机手续（注意护照和港澳通行证/签证） 托运行李（如果有） 飞往关西国际机场\n建议直飞航班（约4小时左右） 如果是廉航，记得提前打印登机牌或下载电子登机牌 到达关西国际机场后\n使用提前购买的 携程Haruka车票 可选择 人工兑换窗口 或 自助绿色机器（人少的地方优先） 乘车前往 京都站（大约1小时15分钟–1小时30分钟车程） 到达京都站后\n每人购买2–3张地下铁票（根据停留时间和游玩计划决定） 前往 京都第一酒店 办理入住并寄存行李 根据时间和体力，可以先去附近吃饭，然后闲逛 若天气不佳（雨或雪），可以顺便购买雨伞或小件防寒物品 笔记 可能是会在机场过夜也说不定\n京都游玩 # 京阿尼实体店铺详情\n京阿尼实体店铺地址\n吃完饭之后就可以看看时间 (ASTY Kyoto 还营不营业，基本这里都有很多商店，可以到处逛逛), 还营业就去看京阿尼实体店，京吹我来了😋, 之后就是买买买了\n然后可以去京都塔逛逛了 (或者夜逛), 先吃了饭再去, 最后再通过附近的京都站乘坐地下铁到达这里附近的鸭川 (不是玉子市场的那里附近)\n最后，就是回酒店好好休息，准备好明天的游玩\nDAY2 2026.04.20 # 酒店\u0026ndash;\u0026gt;贵船神社 # 贵船神社\u0026ndash;\u0026gt;琉璃光院 # 琉璃光院\u0026ndash;\u0026gt;三千院 # 可以在途中到处逛逛\nDAY3 2026.04.21 # 警告 换酒店 (如果不可以先寄存行李的话)\n酒店\u0026ndash;\u0026gt;伏见稻荷神社 # 伏见稻荷神社\u0026ndash;\u0026gt;宇治 # 宇治市观光中心 平等院 宇治神社，宇治上神社 大吉山 宇治桥 久美子椅 莬兔高中看情况去吧 警告 记得买抹茶这些以及吃抹茶小点心😋\nDAY4 2026.04.22 (看推特情况调顺序) # 酒店\u0026ndash;\u0026gt;丰乡小学 (kon) # 笔记 这个其实和 DAY3 或者 DAY5 甚至其他调换顺序，因为要看 kon 圣地巡礼的开放时间\nkon圣地巡礼开放时间\n警告 注意去的是丰乡小学 (旧校舍)\n其实丰乡这个地方没什么太过特别的，这样我们就选择到处逛逛，然后就坐轻轨/地铁回京都\n回到京都之后可以看时间和情况安排到处逛逛\nDAY5 2026.04.23 # 酒店\u0026ndash;\u0026gt;岚山 # 岚山有竹林小径 (大老师自爆点，空之境界) # 岚山\u0026ndash;\u0026gt;天龙寺 # 天龙寺\u0026ndash;\u0026gt;御发神社 # 闲逛 # 回酒店 # DAY6 2026.04.24 # 重要 響け！ユーフォニアム剧场版, 重头戏\n新风馆\n京都水族馆\n京都御所\n京阿尼玉子市场\n鸭川三角洲\n酒店\u0026ndash;\u0026gt;京都水族馆 # 基本一些店什么的都是 10 点开门，看时间安排\n在京都水族馆买票后，看个一两个小时就 run, 坐地铁去下一站\n现在一个早上基本就没了, 吃了饭在出发\n京都水族馆\u0026ndash;\u0026gt;新风馆 # 地铁出发，买一些 Kyoto 当地特色商品就行 (就是逛商店😃)\n新风馆\u0026ndash;\u0026gt;京都御所 # 走路或者地铁都可以，看情况，是想要看看当地街道就走路\n京都御所\u0026ndash;\u0026gt;玉子市场 # 走路过去就可以，就可以看商店街以及三角洲 (玉子掉下水的场景😄)\n鸭川三角洲\u0026ndash;\u0026gt;京都大学 # 走着过去就到了，然后逛逛京都大学，感受下氛围\n可能现在也就五点多的感觉\n京都大学\u0026ndash;\u0026gt;禅林寺 # 禅林寺\u0026ndash;\u0026gt;酒店 # 30分钟 的路程，到了酒店之后就找东西吃\n后面看情况要不要夜行逛逛\nDAY7 2026.04.25 # 在早上10:00前离开京都第一酒店\n京都\u0026ndash;\u0026gt;大阪 # 以下暂定 (因为对大阪不是很熟)\n大阪站\u0026ndash;\u0026gt;心斋桥 # 心斋桥\u0026ndash;\u0026gt;道顿堀 # 吃吃吃😋\n道顿堀\u0026ndash;\u0026gt;大阪城公园/神社 # 夜间瞎逛 (买买买) # 回酒店睡觉，明天到机场回国 # Day8 2026.04.26 # 乘坐飞机离开大阪,返回国内\n参考文献 # 关西游记\n金色标记为首选\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026年2月12日","externalUrl":null,"permalink":"/life/travel/kansai-trip-guide/","section":"生活","summary":" 警告 已经决定 4.18 到机场, 4.19 飞去日本这样\n","title":"关西之旅旅行攻略","type":"life"},{"content":" 連絡先 # GitHub: GitHub ブログ: ブログ QQ: 2145256133 メール: nni461904@gmail.com ","date":"2025年8月11日","externalUrl":null,"permalink":"/ja/about/","section":"響け！ユーフォニアム","summary":"連絡先 # GitHub: GitHub ブログ: ブログ QQ: 2145256133 メール: nni461904@gmail.com ","title":"自己紹介","type":"about"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/categories/alist/","section":"Categories","summary":"","title":"Alist","type":"categories"},{"content":"sudo mount -t glusterfs 192.168.1.108:/myvolume /mnt/gluster 关于 GlusterFS，这是一个分布式文件系统，常用命令主要围绕卷（volume）的管理和查看状态。下面我整理了一些常用的查看卷信息和管理卷的命令。\nGluster 常用命令（卷信息及管理） # 1. 查看卷信息 # 查看所有卷列表： gluster volume list 查看某个卷的详细信息： gluster volume info \u0026lt;卷名\u0026gt; 查看某个卷的状态： gluster volume status \u0026lt;卷名\u0026gt; 查看卷的配置信息（例如副本数、分布等）： gluster volume get \u0026lt;卷名\u0026gt; all 查看所有节点状态： gluster peer status 2. 卷的基本管理命令 # 创建卷： gluster volume create \u0026lt;卷名\u0026gt; \u0026lt;brick1\u0026gt; \u0026lt;brick2\u0026gt; ... 启动卷： gluster volume start \u0026lt;卷名\u0026gt; 停止卷： gluster volume stop \u0026lt;卷名\u0026gt; 删除卷： gluster volume delete \u0026lt;卷名\u0026gt; 3. 节点管理相关 # 查看当前集群中的节点： gluster peer status 移除一个节点： gluster peer detach \u0026lt;hostname|uuid\u0026gt; 添加一个节点（节点本身需先安装好 glusterfs）： gluster peer probe \u0026lt;hostname\u0026gt; 4. 查看卷使用情况和配额 # 查看配额信息： gluster volume quota \u0026lt;卷名\u0026gt; list 启用配额： gluster volume quota \u0026lt;卷名\u0026gt; enable 设置配额： gluster volume quota \u0026lt;卷名\u0026gt; limit-usage /path \u0026lt;size\u0026gt; 5. 查看日志位置 # Gluster 的日志一般在：\n/var/log/glusterfs/ 可以用 tail -f 来实时查看日志：\ntail -f /var/log/glusterfs/glusterd.log 6. 其他实用命令 # 查看卷挂载点： mount | grep glusterfs 查看卷的brick信息： gluster volume status \u0026lt;卷名\u0026gt; detail 一次情况的处理 # 一、检查 brick 挂载及状态相关命令 # 查看 volume 详细状态，包括 brick 状态：\nsudo gluster volume status myvolume detail 查看 volume 简单状态：\nsudo gluster volume status myvolume 查看 brick 对应磁盘设备信息（文件系统、UUID 等）：\nsudo blkid /dev/sdb1 查看挂载点是否挂载成功：\nmount | grep extended_gluster 手动挂载所有 fstab 中定义的设备：\nsudo mount -a 编辑 fstab 文件配置自动挂载：\nsudo nano /etc/fstab 二、处理 brick 离线与恢复 # 强制启动 volume（重启 brick 服务）：\nsudo gluster volume start myvolume force （慎用）强制移除并重新添加 brick：\nsudo gluster volume remove-brick myvolume ice:/mnt/extended_gluster/extended_brick force sudo gluster volume add-brick myvolume ice:/mnt/extended_gluster/extended_brick 三、管理 rebalance 任务 # 查看 rebalance 任务状态：\nsudo gluster volume rebalance myvolume status 启动 rebalance 任务：\nsudo gluster volume rebalance myvolume start 停止（清理）rebalance 任务：\nsudo gluster volume rebalance myvolume stop 实时监控 rebalance 状态（循环查询）：\nwatch -n 2 sudo gluster volume rebalance myvolume status 四、日志排查相关命令 # 查看 GlusterFS 进程日志（全局）：\nsudo less /var/log/glusterfs/glusterfsd.log 查看指定 brick 的日志：\nsudo less /var/log/glusterfs/bricks/extended_brick.log 过滤日志中 rebalance 相关内容：\nsudo grep rebalance /var/log/glusterfs/glusterfsd.log 五、其它实用命令 # 查看 volume 信息（类型，副本数等）：\nsudo gluster volume info myvolume 查看挂载目录文件分布情况（判断数据是否正常）：\nls -lh /mnt/extended_gluster/extended_brick 检查文件系统挂载情况和容量：\ndf -h | grep extended_gluster ","date":"2025年8月10日","externalUrl":null,"permalink":"/notes/tools/alist%E4%BB%A5%E5%8F%8Agluster/","section":"笔记","summary":"sudo mount -t glusterfs 192.168.1.108:/myvolume /mnt/gluster 关于 GlusterFS，这是一个分布式文件系统，常用命令主要围绕卷（volume）的管理和查看状态。下面我整理了一些常用的查看卷信息和管理卷的命令。\n","title":"alist以及gluster","type":"notes"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/categories/gluster/","section":"Categories","summary":"","title":"Gluster","type":"categories"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/","section":"Tags","summary":"","title":"分布式存储","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"文件管理","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E6%8C%82%E8%BD%BD/","section":"Tags","summary":"","title":"挂载","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/brick/","section":"Tags","summary":"","title":"Brick","type":"tags"},{"content":" 节点（Node） 节点指的是参与 GlusterFS 集群的物理或虚拟服务器主机。每个节点上可以运行 GlusterFS 服务，并提供存储资源。\nPeer（对等体） Peer 指的是集群中彼此认识并通信的节点。一个 GlusterFS 集群由多个 peer 组成，peer 之间通过网络互联，形成分布式存储系统。\n在加入集群时，需要执行 gluster peer probe \u0026lt;hostname\u0026gt; 将节点加入集群，建立 peer 关系。 Brick（砖块） Brick 是 GlusterFS 中最小的存储单元，实际是节点上的一个目录或磁盘分区，作为卷（volume）的基本构建块。\n每个 brick 代表一个目录路径，比如 /data/brick1。 一个节点上可以有多个 brick。 Volume（卷） Volume 是对外暴露的逻辑存储池，由一个或多个 brick 组成。它是客户端挂载和使用的存储资源。\nVolume 可以有多种类型，如分布式（distributed）、复制（replicated）、分布式复制（distributed replicated）、条带（striped）等。 Volume 通过将多个 brick 组合起来，实现数据分布、冗余备份和性能提升。 它们之间的关系总结 # 节点 是物理服务器，承载着 GlusterFS 服务和存储资源。 Peer 是节点之间建立的集群关系，节点通过 peer 连接组成集群。 Brick 是节点上的目录或磁盘分区，提供实际的数据存储空间。 Volume 是由多个 brick 组成的逻辑存储单元，供客户端挂载使用。 示意：\n[节点1] -- peer连接 -- [节点2] -- peer连接 -- [节点3] 节点1: brick1 (/data/brick1), brick2 (/data/brick2) 节点2: brick3 (/data/brick3) 节点3: brick4 (/data/brick4), brick5 (/data/brick5) VolumeA = brick1 + brick3 + brick4 （组成分布式卷） 客户端挂载 VolumeA，通过集群访问分布在各节点上的brick数据。 ","date":"2025年8月10日","externalUrl":null,"permalink":"/notes/tools/gluster%E6%A6%82%E5%BF%B5/","section":"笔记","summary":" 节点（Node） 节点指的是参与 GlusterFS 集群的物理或虚拟服务器主机。每个节点上可以运行 GlusterFS 服务，并提供存储资源。\nPeer（对等体） Peer 指的是集群中彼此认识并通信的节点。一个 GlusterFS 集群由多个 peer 组成，peer 之间通过网络互联，形成分布式存储系统。\n","title":"gluster概念","type":"notes"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/volume/","section":"Tags","summary":"","title":"Volume","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E9%9B%86%E7%BE%A4/","section":"Tags","summary":"","title":"集群","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/","section":"Categories","summary":"","title":"分布式存储","type":"categories"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/categories/blog/","section":"Categories","summary":"","title":"Blog","type":"categories"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/categories/university/","section":"Categories","summary":"","title":"University","type":"categories"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E6%88%90%E9%95%BF%E8%AE%B0%E5%BD%95/","section":"Tags","summary":"","title":"成长记录","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E5%AD%A6%E7%94%9F%E6%B4%BB/","section":"Tags","summary":"","title":"大学生活","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E4%BA%8C/","section":"Tags","summary":"","title":"大二","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2/","section":"Tags","summary":"","title":"博客","type":"tags"},{"content":"","date":"2025年8月10日","externalUrl":null,"permalink":"/tags/%E4%B8%AA%E4%BA%BA%E5%9B%9E%E5%BF%86/","section":"Tags","summary":"","title":"个人回忆","type":"tags"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/categories/linux/","section":"Categories","summary":"","title":"Linux","type":"categories"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/tags/ssh/","section":"Tags","summary":"","title":"SSH","type":"tags"},{"content":" 一、在客户端生成 SSH 密钥对 # 在你的本地客户端（例如你的笔记本或开发机器）上：\nssh-keygen -t rsa -b 4096 -C \u0026#34;your_email@example.com\u0026#34; 说明：\n-t rsa：生成 RSA 类型的密钥； -b 4096：密钥长度，建议至少 4096； -C：注释，通常填邮箱方便识别。 系统会提示你保存文件位置，默认是：\n~/.ssh/id_rsa (私钥) ~/.ssh/id_rsa.pub (公钥) 也可以自定义文件名，方便区分不同用途。\n二、将公钥复制到服务器 # 假设你的服务器 IP 是 192.168.1.108，用户名是 ice345：\n方法一（推荐）使用 ssh-copy-id 自动复制： # 提示 这个通常都会无法成功 copy 到服务器上\nssh-copy-id -i ~/.ssh/id_rsa.pub ice345@192.168.1.108 系统会提示输入服务器密码，输入一次即可自动把公钥复制到服务器的 ~/.ssh/authorized_keys 里，并设置好权限。\n方法二：手动复制 # 如果没有 ssh-copy-id：\n查看公钥内容： cat ~/.ssh/id_rsa.pub 登录服务器（使用密码）： ssh ice345@192.168.1.108 在服务器上创建 ~/.ssh/ 目录（如果还没有）： mkdir -p ~/.ssh chmod 700 ~/.ssh 编辑 authorized_keys 文件： nano ~/.ssh/authorized_keys 将你刚才 cat 出来的公钥粘贴进去，保存。\n修改权限： chmod 600 ~/.ssh/authorized_keys 三、测试免密登录 # 回到客户端，执行：\nssh ice345@192.168.1.108 如果一切顺利，不需要输入密码就能登录。\n四、服务器配置 SSH 服务 # 在服务器上编辑 sshd 配置文件：\nsudo nano /etc/ssh/sshd_config 重点配置项：\n# 允许公钥认证（默认通常是yes） PubkeyAuthentication yes # 允许密码登录（如不想允许可以关闭） PasswordAuthentication yes # 或 no # 允许使用 authorized_keys 文件 AuthorizedKeysFile .ssh/authorized_keys # 禁止 root 直接登录（安全建议） PermitRootLogin no 修改后，重启 ssh 服务：\nsudo systemctl restart sshd 五、额外注意事项 # 如果你希望完全关闭密码登录，保证只允许密钥登录，可以把 PasswordAuthentication 改为 no。 确认目录权限正确： 目录/文件 权限 ~/.ssh/ 700 ~/.ssh/authorized_keys 600 防火墙确认开放 22 端口。 六、完整快速版流程总结 # # 1. 客户端生成密钥 ssh-keygen -t rsa -b 4096 # 2. 复制公钥到服务器 ssh-copy-id -i ~/.ssh/id_rsa.pub user@server_ip # 3. 测试免密登录 ssh user@server_ip # 4. 修改服务器 /etc/ssh/sshd_config（可选） sudo nano /etc/ssh/sshd_config # 确认或修改： PubkeyAuthentication yes PasswordAuthentication yes (或 no) # 5. 重启 SSH 服务 sudo systemctl restart sshd ","date":"2025年6月13日","externalUrl":null,"permalink":"/notes/tools/ssh%E7%AE%80%E5%8D%95%E9%85%8D%E7%BD%AE/","section":"笔记","summary":"一、在客户端生成 SSH 密钥对 # 在你的本地客户端（例如你的笔记本或开发机器）上：\n","title":"SSH简单配置","type":"notes"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/tags/%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5/","section":"Tags","summary":"","title":"远程连接","type":"tags"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/categories/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0/","section":"Categories","summary":"","title":"深入学习","type":"categories"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/tags/%E5%AF%86%E9%92%A5%E8%AE%A4%E8%AF%81/","section":"Tags","summary":"","title":"密钥认证","type":"tags"},{"content":"","date":"2025年6月13日","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/","section":"Tags","summary":"","title":"网络安全","type":"tags"},{"content":" 问题描述 # 因为通过上一节的描述, 我们可以通过 cloudflare tunnel 和 alist 结合实现加速访问以及稳定加载, 但是这又导致了一个问题:alist 上传出现了问题.\n经过分析, 有以下原因:\ncloudflare 的 tunnel 的免费服务无法支持不稳定的连接和长时间的连接支持 家里的服务器因为被墙, 所以无法稳定连接 (因为 cloudflare 是在国外) 解决办法 # 所以, 我们想有一种办法能够同时解决这两种问题: 不稳定加载和访问 和 稳定上传 这是无法免费的解决办法可以解决的.\n因此, 我们只好设置两个口来访问 alist 就行.\n通过上一节的办法为 alistip.050626.xyz 再申请一个域名, 然后配置在 /opt/alist/data/config.json 中即可, 然后设置 cloudflare 的 DNS 记录\n这样就可以有域名访问 alist 了, 这个就是直接通过服务器的 ipv6 链路直连的, 稳定性相比配置 cloudflare 的 tunnel 稳定不少\n提示 /opt/alist/data/config.json 里的 force_https 这里仍然是 false, 不然 cloudflare 的 tunnel 绑定的域名会发生问题, 无法访问\n所以, 现在我们就会有两个域名来访问 alist\n但是这两种访问的用途就不一样.\nalist.050626.xyz 就是用来提升下载和访问速度的 alistip.050626.xyz 就是用来上传的 ","date":"2025年6月11日","externalUrl":null,"permalink":"/notes/tools/alist%E9%85%8D%E7%BD%AE%E5%8F%8C%E8%AE%BF%E9%97%AE/","section":"笔记","summary":"问题描述 # 因为通过上一节的描述, 我们可以通过 cloudflare tunnel 和 alist 结合实现加速访问以及稳定加载, 但是这又导致了一个问题:alist 上传出现了问题.\n","title":"alist配置双访问","type":"notes"},{"content":"","date":"2025年6月11日","externalUrl":null,"permalink":"/tags/cloudflare-tunnel/","section":"Tags","summary":"","title":"Cloudflare Tunnel","type":"tags"},{"content":"","date":"2025年6月11日","externalUrl":null,"permalink":"/tags/%E4%B8%8A%E4%BC%A0%E4%BC%98%E5%8C%96/","section":"Tags","summary":"","title":"上传优化","type":"tags"},{"content":"","date":"2025年6月11日","externalUrl":null,"permalink":"/tags/%E5%8F%8C%E5%9F%9F%E5%90%8D/","section":"Tags","summary":"","title":"双域名","type":"tags"},{"content":"","date":"2025年6月11日","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"网络配置","type":"tags"},{"content":" 先前做法 # 先前, 我们是通过脚本来和 cloudflare 配合来不断将域名和 ipv6 进行一个绑定来修改, 但是这样我的服务器就是一个直接的出站, 并没有经过什么加速. 所以相比通过代理的方式, 速度会慢很多.\n脚本如下:\n#!/bin/bash # CHANGE THESE auth_email=\u0026#34;nni461904@gmail.com\u0026#34; #你的CloudFlare注册账户邮箱,your cloudflare account email address auth_key=\u0026#34;188f5f3f1ba73b37fd2a0\u0026#34; #你的cloudflare账户Globel ID ,your cloudflare Globel ID（就是我的个人资料里的global api key，应该就是用来做身份识别用的） zone_name=\u0026#34;050626.xyz\u0026#34; #你的域名,your root domain address record_name=\u0026#34;alist.050626.xyz\u0026#34; #完整域名,your full domain address record_type=\u0026#34;AAAA\u0026#34; #A or AAAA,ipv4 或 ipv6解析 ip_index=\u0026#34;local\u0026#34; #use \u0026#34;internet\u0026#34; or \u0026#34;local\u0026#34;,使用本地方式还是网络方式获取地址 eth_card=\u0026#34;enp1s0\u0026#34; #使用本地方式获取ip绑定的网卡，默认为eth0，仅本地方式有效,the default ethernet card is eth0 ip_file=\u0026#34;ip.txt\u0026#34; #保存地址信息,save ip information in the ip.txt id_file=\u0026#34;cloudflare.ids\u0026#34; log_file=\u0026#34;cloudflare.log\u0026#34; if [ $record_type = \u0026#34;AAAA\u0026#34; ];then if [ $ip_index = \u0026#34;internet\u0026#34; ];then ip=$(curl -6 ip.sb) elif [ $ip_index = \u0026#34;local\u0026#34; ];then if [ \u0026#34;$user\u0026#34; = \u0026#34;root\u0026#34; ];then ip=$(ifconfig $eth_card | grep \u0026#39;inet6\u0026#39; | cut -f2 | awk \u0026#39;{ print $2}\u0026#39; | grep -v \u0026#39;^::1$\u0026#39; | grep -v \u0026#39;^fe80\u0026#39; | grep -v \u0026#39;^f[d|c]\u0026#39; | head -1) else ip=$(/sbin/ifconfig $eth_card | grep \u0026#39;inet6\u0026#39; | cut -f2 | awk \u0026#39;{ print $2}\u0026#39; | grep -v \u0026#39;^::1$\u0026#39; | grep -v \u0026#39;^fe80\u0026#39; | grep -v \u0026#39;^f[d|c]\u0026#39; | head -1) fi else echo \u0026#34;Error IP index, please input the right type\u0026#34; exit 0 fi elif [ $record_type = \u0026#34;A\u0026#34; ];then if [ $ip_index = \u0026#34;internet\u0026#34; ];then ip=$(curl -4 ip.sb) elif [ $ip_index = \u0026#34;local\u0026#34; ];then if [ \u0026#34;$user\u0026#34; = \u0026#34;root\u0026#34; ];then ip=$(ifconfig $eth_card | grep \u0026#39;inet\u0026#39;| grep -v \u0026#39;127.0.0.1\u0026#39; | grep -v \u0026#39;inet6\u0026#39;|cut -f2 | awk \u0026#39;{ print $2}\u0026#39;) else ip=$(/sbin/ifconfig $eth_card | grep \u0026#39;inet\u0026#39;| grep -v \u0026#39;127.0.0.1\u0026#39; | grep -v \u0026#39;inet6\u0026#39;|cut -f2 | awk \u0026#39;{ print $2}\u0026#39;) fi else echo \u0026#34;Error IP index, please input the right type\u0026#34; exit 0 fi else echo \u0026#34;Error DNS type\u0026#34; exit 0 fi # 日志 log file log() { if [ \u0026#34;$1\u0026#34; ]; then echo -e \u0026#34;[$(date)] - $1\u0026#34; \u0026gt;\u0026gt; $log_file fi } # SCRIPT START log \u0026#34;Check Initiated\u0026#34; #判断ip是否发生变化,check the ip had been changed? if [ -f $ip_file ]; then old_ip=$(cat $ip_file) if [ $ip == $old_ip ]; then echo \u0026#34;IP has not changed.\u0026#34; exit 0 fi fi #获取域名和授权 get the domain and authentic if [ -f $id_file ] \u0026amp;\u0026amp; [ $(wc -l $id_file | cut -d \u0026#34; \u0026#34; -f 1) == 2 ]; then zone_identifier=$(head -1 $id_file) record_identifier=$(tail -1 $id_file) else zone_identifier=$(curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones?name=$zone_name\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; | grep -Po \u0026#39;(?\u0026lt;=\u0026#34;id\u0026#34;:\u0026#34;)[^\u0026#34;]*\u0026#39; | head -1 ) record_identifier=$(curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?type=${record_type}\u0026amp;name=$record_name\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; | grep -Po \u0026#39;(?\u0026lt;=\u0026#34;id\u0026#34;:\u0026#34;)[^\u0026#34;]*\u0026#39;) echo \u0026#34;$zone_identifier\u0026#34; \u0026gt; $id_file echo \u0026#34;$record_identifier\u0026#34; \u0026gt;\u0026gt; $id_file fi #更新DNS记录 update the dns update=$(curl -s -X PUT \u0026#34;https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ --data \u0026#34;{\\\u0026#34;type\\\u0026#34;:\\\u0026#34;$record_type\\\u0026#34;,\\\u0026#34;name\\\u0026#34;:\\\u0026#34;$record_name\\\u0026#34;,\\\u0026#34;content\\\u0026#34;:\\\u0026#34;$ip\\\u0026#34;,\\\u0026#34;ttl\\\u0026#34;:1,\\\u0026#34;proxied\\\u0026#34;:false}\u0026#34;) #反馈更新情况 gave the feedback about the update statues if [[ $update == *\u0026#34;\\\u0026#34;success\\\u0026#34;:true\u0026#34;* ]]; then message=\u0026#34;IP changed to: $ip\u0026#34; echo \u0026#34;$ip\u0026#34; \u0026gt; $ip_file log \u0026#34;$message\u0026#34; echo \u0026#34;$message\u0026#34; else message=\u0026#34;API UPDATE FAILED. DUMPING RESULTS:\\n$update\u0026#34; log \u0026#34;$message\u0026#34; echo -e \u0026#34;$message\u0026#34; exit 1 fi 然后, 在 cloudflare 中配置一条 DNS 记录 (先设为 AAAA, 然后就是先输入一次 ipv6, 之后更改就会通过脚本自动推送的了, 不设置代理即云朵为灰色)\n但是, 显然这样的方法, 速度会很慢. 所以就有了以下改进.\n通过 Cloudflare 的 Tunnel 和 alist 绑定 # 提示 因为 alist 一开始是直接通过本服务器的自身 ipv6 链路出站的, 所以速度很慢. 现在换成通过 cloudflare 节点加速的话, 配置就改变一下. (其实就是通过 cloudflare 代理), 所以要删除原先的 DNS记录 才行\n所以, 先设置 /opt/alist/data 的 config.yaml 中的 force_https 为 false\n还有就是因为已经通过 cloudflare 代理了, 所以我们不用自己设置端口号为 443, 我们可以通过设置 http://localhost:5244 这个本地的端口就行, 免得发生冲突\n用 cloudflared 将 Alist 暴露为 https://yourname.cloudflare.workers.dev\nCloudflare 代理可自动走优质线路，无需暴露真实 IPv6\n几乎所有地区都有高质量访问速度\n适合你的场景（远端用户多，原生 IPv6 差）\n非常好，你已经使用了 IPv6 + DDNS 访问 https://alist.050626.xyz，接下来我们要做的是：\n使用 Cloudflare Tunnel 给你的 Alist 提速（绕过 IPv6 出口限制） # 目标： # 通过 Cloudflare Tunnel 实现：\n不暴露真实 IP（保护隐私） 自动走 Cloudflare 高质量 CDN 节点 提升远端访问速度（避免你当前的“IPv6 出站带宽被限速”问题） 步骤一：在服务器上安装 cloudflared # 以 Arch 为例（你是 Arch 用户）：\nsudo pacman -S cloudflared 若是其他系统：\n# Debian/Ubuntu sudo apt install cloudflared # RedHat/CentOS sudo yum install cloudflared 若没有, 就要通过 wget 下载 deb 包然后 dpkg 解压安装\n步骤二：登录并授权 cloudflared # cloudflared tunnel login 这会打开浏览器登录 Cloudflare，授权后你可以创建 tunnel。\n步骤三：创建 tunnel 并绑定 Alist 服务 # cloudflared tunnel create alist-tunnel 会输出一个 tunnel ID，例如：\nCreated tunnel alist-tunnel with ID 12345678-aaaa-bbbb-cccc-123456abcdef 步骤五：创建配置文件 # 默认目录在 ~/.cloudflared/config.yml, 你也要配置在 /etc/cloudflared/ 目录中：\ntunnel: 12345678-aaaa-bbbb-cccc-123456abcdef # 你的 tunnel ID credentials-file: /home/\u0026lt;user\u0026gt;/.cloudflared/12345678-aaaa-bbbb-cccc-123456abcdef.json # 你的json文件 ingress: - hostname: alist.050626.xyz service: http://localhost:5244 # Alist 默认端口 - service: http_status:404 确保 localhost:5244 是 Alist 的监听地址和端口。\n步骤六：创建 DNS 记录 + 启动 tunnel # 将 tunnel 与 Cloudflare DNS 绑定：\ncloudflared tunnel route dns alist-tunnel alist.044454.xyz 然后运行 tunnel：\ncloudflared tunnel run alist-tunnel 如需后台运行，可以用 systemd 或 --no-autoupdate 参数。\n成功后你可以： # 使用 https://alist.044454.xyz 高速访问 Alist（通过 Cloudflare 节点加速） 客户端访问走的是 Cloudflare 的全球加速网络，而不是你服务器自身出站 IPv6 链路 可选：设置为 systemd 后台服务 # sudo cloudflared service install 这样服务器重启后自动启动 tunnel。\n验证： # 用浏览器打开 https://alist.044454.xyz 使用 curl -v https://alist.044454.xyz，应看到 Cloudflare 代理标识 远程测速明显比直接 IPv6 快，尤其是下载时 通过 systemd 来自动运行 # 一键启用后台服务 # sudo cloudflared service install 这个命令会自动为你生成 systemd 服务文件并启用开机启动。\n然后启动服务： # sudo systemctl start cloudflared 设置开机自动运行（通常默认启用）： # sudo systemctl enable cloudflared 检查运行状态： # systemctl status cloudflared 会看到 tunnel 正常运行，输出中包含 alist-tunnel 和你的域名。\n验证 # 浏览器访问你的域名 https://alist.050626.xyz 用命令查看： curl -I https://alist.050626.xyz 看是否有响应头包含：\nHTTP/1.1 200 HTTP/2 200 cf-ray: xxxx server: cloudflare 说明已通过 Cloudflare Tunnel 代理 ✅\n完整示例:\nHTTP/1.1 200 Connection established HTTP/2 200 date: Fri, 06 Jun 2025 05:12:42 GMT content-type: text/html cf-cache-status: DYNAMIC speculation-rules: \u0026#34;/cdn-cgi/speculation\u0026#34; report-to: {\u0026#34;group\u0026#34;:\u0026#34;cf-nel\u0026#34;,\u0026#34;max_age\u0026#34;:604800,\u0026#34;endpoints\u0026#34;:[{\u0026#34;url\u0026#34;:\u0026#34;https://a.nel.cloudflare.com/report/v4?s=UOD6%2Fislw4MqZ951XaKh0jczFwySa4dshAcWMzNleVhuay3NDTkvlRwkqr%2BRv5PBb%2FPMW7aQ2mZFerZSiEZxvZpKE4CPVIpvxGXTdWO5ph4%3D\u0026#34;}]} nel: {\u0026#34;report_to\u0026#34;:\u0026#34;cf-nel\u0026#34;,\u0026#34;success_fraction\u0026#34;:0.0,\u0026#34;max_age\u0026#34;:604800} server: cloudflare cf-ray: 94b56a8e293909f0-HKG alt-svc: h3=\u0026#34;:443\u0026#34;; ma=86400 ❗ 常见问题提示 # 如果你配置了多个 tunnel，可以指定要运行的 tunnel，比如编辑 /etc/default/cloudflared，或手动创建一个 service 文件（进阶使用）。 如果你使用 --config ~/.cloudflared/config.yml，确保 cloudflared 安装路径和 config 路径匹配 ","date":"2025年6月7日","externalUrl":null,"permalink":"/notes/tools/alist%E5%8A%A0%E9%80%9F/","section":"笔记","summary":"先前做法 # 先前, 我们是通过脚本来和 cloudflare 配合来不断将域名和 ipv6 进行一个绑定来修改, 但是这样我的服务器就是一个直接的出站, 并没有经过什么加速. 所以相比通过代理的方式, 速度会慢很多.\n","title":"alist加速","type":"notes"},{"content":"","date":"2025年6月7日","externalUrl":null,"permalink":"/tags/cdn/","section":"Tags","summary":"","title":"CDN","type":"tags"},{"content":"","date":"2025年6月7日","externalUrl":null,"permalink":"/tags/cloudflare/","section":"Tags","summary":"","title":"Cloudflare","type":"tags"},{"content":"","date":"2025年6月7日","externalUrl":null,"permalink":"/tags/ipv6/","section":"Tags","summary":"","title":"IPv6","type":"tags"},{"content":"","date":"2025年6月7日","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E5%8A%A0%E9%80%9F/","section":"Tags","summary":"","title":"网络加速","type":"tags"},{"content":" 在 LazyVim 中自定义插件的快捷键 # 要自定义 LazyVim 插件使用的快捷键，有几种不同的方法，具体取决于你希望快捷键在哪些地方生效以及如何应用。LazyVim 是基于 Neovim 并由 lazy.nvim 插件管理器驱动的，提供了灵活的方式来管理插件的快捷键，同时保持高效的懒加载机制。下面，我将详细介绍几种主要的方法，并提供完整的示例代码，同时解答是否需要手动加载相关文件。\n自定义快捷键的方法 # 1. 在插件规范文件中定义快捷键 (lua/plugins/\u0026lt;plugin\u0026gt;.lua) # 最常见和推荐的方法是在插件的规范文件中使用 keys 字段直接定义快捷键。这种方法可以让 LazyVim 在按下快捷键时才加载插件，从而优化启动时间。\n工作原理： # keys 字段可以指定快捷键，同时设置按键映射，并触发插件加载。 适用于插件提供的命令或功能（例如，切换文件浏览器或打开 Telescope 搜索）。 示例：自定义 nvim-tree 的快捷键 # 假设你想将 \u0026lt;C-n\u0026gt; 绑定为 nvim-tree 文件浏览器的切换键，而不是它的默认快捷键。\n创建或编辑 lua/plugins/nvim-tree.lua： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, dependencies = { \u0026#34;kyazdani42/nvim-web-devicons\u0026#34; }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, -- 可选配置 }) end, keys = { { \u0026#34;\u0026lt;C-n\u0026gt;\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, }, } 解释： \u0026lt;C-n\u0026gt;（Ctrl+n）被映射为 NvimTreeToggle 命令。 desc 字段提供了快捷键的描述（可在 WhichKey 等工具中可见）。 当你按下 \u0026lt;C-n\u0026gt; 时，LazyVim 会加载 nvim-tree（如果尚未加载），并切换文件浏览器。 添加多个快捷键： 如果你还希望 \u0026lt;leader\u0026gt;e 也能切换 nvim-tree，可以这样做： keys = { { \u0026#34;\u0026lt;C-n\u0026gt;\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, { \u0026#34;\u0026lt;leader\u0026gt;e\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, 解释：现在 \u0026lt;C-n\u0026gt; 和 \u0026lt;leader\u0026gt;e 都可以触发相同的操作，并在需要时加载插件。 2. 在插件的 config 函数中自定义快捷键 # 有些插件会在其 setup 配置中定义快捷键（例如 nvim-cmp 这样的补全插件）。对于这些插件，可以直接在 config 函数中修改快捷键。\n工作原理： # config 函数在插件加载后执行，因此可以覆盖插件的默认按键映射。 适用于有自己内部映射系统的插件（例如，补全菜单的导航）。 示例：自定义 nvim-cmp 的快捷键 # 假设你想用 \u0026lt;Tab\u0026gt; 和 \u0026lt;S-Tab\u0026gt;（Shift+Tab）来导航补全菜单，而不是默认的 \u0026lt;C-n\u0026gt; 和 \u0026lt;C-p\u0026gt;。\n编辑 lua/plugins/nvim-cmp.lua： -- lua/plugins/nvim-cmp.lua return { { \u0026#34;hrsh7th/nvim-cmp\u0026#34;, dependencies = { \u0026#34;hrsh7th/cmp-nvim-lsp\u0026#34;, \u0026#34;L3MON4D3/LuaSnip\u0026#34; }, config = function() local cmp = require(\u0026#34;cmp\u0026#34;) cmp.setup({ -- 其他配置（如补全来源、代码片段设置等） mapping = { [\u0026#34;\u0026lt;Tab\u0026gt;\u0026#34;] = cmp.mapping.select_next_item(), [\u0026#34;\u0026lt;S-Tab\u0026gt;\u0026#34;] = cmp.mapping.select_prev_item(), [\u0026#34;\u0026lt;C-y\u0026gt;\u0026#34;] = cmp.mapping.confirm({ select = true }), }, }) end, }, } 解释： mapping 表定义了补全菜单的快捷键。 \u0026lt;Tab\u0026gt; 选择下一个补全项，\u0026lt;S-Tab\u0026gt; 选择上一个，\u0026lt;C-y\u0026gt; 确认补全。 这些快捷键仅在补全菜单打开时生效，并在 nvim-cmp 加载时设置。 3. 在 lua/config/keymaps.lua 中定义快捷键 # 如果你希望所有快捷键集中管理，可以在 lua/config/keymaps.lua 文件中定义它们。但如果插件是懒加载的，必须确保插件在按下快捷键时已加载。\n工作原理： # keymaps.lua 中的快捷键会在 Neovim 启动时生效。 对于懒加载的插件，如果插件尚未加载，命令可能不可用，除非插件已被触发加载。 示例：为 nvim-tree 添加快捷键 # 假设你想使用 \u0026lt;leader\u0026gt;nt 来切换 nvim-tree，并希望在 keymaps.lua 中定义它。\n方案 A：立即加载插件： 在插件配置中设置 lazy = false，确保插件在启动时加载： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, lazy = false, -- 启动时加载 config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, }, } 然后，在 lua/config/keymaps.lua 中： -- lua/config/keymaps.lua vim.keymap.set(\u0026#34;n\u0026#34;, \u0026#34;\u0026lt;leader\u0026gt;nt\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, { desc = \u0026#34;Toggle NvimTree\u0026#34; }) 解释：由于 nvim-tree 在启动时已加载，\u0026lt;leader\u0026gt;nt 可以立即生效。 方案 B：保持懒加载（推荐）： 在插件规范文件的 keys 字段中定义快捷键： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, keys = { { \u0026#34;\u0026lt;leader\u0026gt;nt\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, }, } 解释：这样可以保持懒加载，仅在按下快捷键时加载插件，提高启动速度。 注意：如果在 keymaps.lua 中为懒加载插件定义快捷键，且没有手动触发插件加载，可能会遇到命令不可用的情况。因此，最佳做法是将快捷键放在插件规范文件的 keys 字段中。\n需要手动加载文件吗？ # 不需要！LazyVim 的 lazy.nvim 会自动管理插件加载：\n在 keys 字段中定义快捷键：按下键时加载插件并执行命令。 在 config 函数中定义快捷键：插件加载后按键才会生效。 在 keymaps.lua 中定义快捷键：适用于已加载的插件，否则可能需要 lazy = false。 总结 # 最佳实践：在 lua/plugins/\u0026lt;plugin\u0026gt;.lua 中的 keys 字段定义快捷键，以便懒加载插件。 对于内部映射：在 config 函数中修改插件的默认按键（如 nvim-cmp）。 集中管理快捷键：如果插件已加载（lazy = false），可以使用 keymaps.lua。 按照这些方法，你可以轻松、高效地为任何 LazyVim 插件自定义快捷键\n","date":"2025年6月6日","externalUrl":null,"permalink":"/notes/vim/lazyvim-keymap/","section":"笔记","summary":"在 LazyVim 中自定义插件的快捷键 # 要自定义 LazyVim 插件使用的快捷键，有几种不同的方法，具体取决于你希望快捷键在哪些地方生效以及如何应用。LazyVim 是基于 Neovim 并由 lazy.nvim 插件管理器驱动的，提供了灵活的方式来管理插件的快捷键，同时保持高效的懒加载机制。下面，我将详细介绍几种主要的方法，并提供完整的示例代码，同时解答是否需要手动加载相关文件。\n","title":"lazyvim_keymap","type":"notes"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/categories/neovim/","section":"Categories","summary":"","title":"NeoVim","type":"categories"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/categories/vim/","section":"Categories","summary":"","title":"Vim","type":"categories"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E5%99%A8/","section":"Tags","summary":"","title":"编辑器","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E5%BF%AB%E6%8D%B7%E9%94%AE/","section":"Tags","summary":"","title":"快捷键","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"插件配置","type":"tags"},{"content":" 如何在 LazyVim 中配置插件？ # LazyVim 使用 lazy.nvim 作为插件管理器，允许以模块化和灵活的方式定义和配置插件。要配置插件，您需要创建或修改 Lua 文件来指定要安装的插件及其设置和加载条件。这些文件通常位于 LazyVim 配置的 lua/plugins/ 目录中。\n每个插件配置都写作一个 Lua 表，包含插件的仓库地址、依赖项、配置函数以及加载条件（如立即加载或在特定事件/按键时延迟加载）。LazyVim 的 lazy.nvim 设置（通常在 lua/config/lazy.lua 中定义）会自动发现并加载这些插件配置。\n配置插件的步骤： # 在 lua/plugins/ 中创建或修改文件：通过在此目录中创建新的 .lua 文件或编辑现有文件来添加插件。 定义插件规格：返回一个表（或表列表）来指定插件详细信息。 保存文件：保存后，lazy.nvim 会根据您的配置自动管理插件。 插件配置文件保存在哪里？ # 根据您的目录结构：\n. ├── init.lua ├── lazy-lock.json ├── lazyvim.json ├── LICENSE ├── lua │ ├── config │ │ ├── autocmds.lua │ │ ├── keymaps.lua │ │ ├── lazy.lua │ │ ├── options.lua │ │ └── plugins.lua │ └── plugins │ ├── example.lua │ ├── nvim-cmp.lua │ ├── nvim-tree.lua │ └── treesistter.lua ├── README.md └── stylua.toml 插件配置文件保存在 lua/plugins/ 目录中。该目录中的每个文件（如 nvim-cmp.lua、nvim-tree.lua、treesistter.lua、example.lua）通常定义一个或多个插件。例如：\nnvim-cmp.lua 可能配置 nvim-cmp 自动补全插件 nvim-tree.lua 配置 nvim-tree 文件管理器 treesistter.lua（可能是 treesitter.lua 的拼写错误）可能配置 nvim-treesitter 这些文件会被 lazy.nvim 自动加载，由 lua/config/lazy.lua 文件统筹管理（该文件会导入 lua/plugins/ 的所有模块）。\n注意：您的结构中包含 lua/config/plugins.lua，这不是 LazyVim 的默认配置。通常插件配置应放在 lua/plugins/，而 lua/config/ 存放通用配置文件（如 lazy.lua、options.lua 等）。除非 lua/config/plugins.lua 被显式导入，否则默认不会使用。建议遵循 LazyVim 惯例，将插件配置放在 lua/plugins/。\n详细示例：配置 telescope.nvim 插件 # 以添加强大的模糊搜索插件 nvim-telescope/telescope.nvim 为例：\n步骤 1：创建新文件 # 在 lua/plugins/ 中创建 telescope.lua 文件（路径：~/.config/nvim/lua/plugins/telescope.lua）。\n步骤 2：定义插件规格 # 将以下内容添加到 lua/plugins/telescope.lua：\nreturn { { \u0026#34;nvim-telescope/telescope.nvim\u0026#34;, -- 指定依赖项 dependencies = { \u0026#34;nvim-lua/plenary.nvim\u0026#34; }, -- 插件加载后运行的配置函数 config = function() require(\u0026#34;telescope\u0026#34;).setup({ defaults = { -- 自定义Telescope行为 layout_strategy = \u0026#34;horizontal\u0026#34;, layout_config = { prompt_position = \u0026#34;top\u0026#34;, }, sorting_strategy = \u0026#34;ascending\u0026#34;, }, pickers = { find_files = { hidden = true, -- 显示隐藏文件 }, }, }) end, -- 定义触发插件加载的快捷键 keys = { { \u0026#34;\u0026lt;leader\u0026gt;ff\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope find_files\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;查找文件\u0026#34; }, { \u0026#34;\u0026lt;leader\u0026gt;fg\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope live_grep\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;实时搜索\u0026#34; }, }, -- 当执行Telescope命令时加载插件 cmd = { \u0026#34;Telescope\u0026#34; }, }, } 配置详解： # nvim-telescope/telescope.nvim：插件的 GitHub 仓库 dependencies：Telescope 需要 plenary.nvim 作为依赖 config：插件加载后调用的配置函数，设置布局策略和显示隐藏文件 keys：定义快捷键 \u0026lt;leader\u0026gt;ff（查找文件）和 \u0026lt;leader\u0026gt;fg（实时搜索） cmd：运行 :Telescope 命令时加载插件 步骤 3：保存并同步 # 保存文件后，在 Neovim 中运行 :Lazy 并选择 \u0026ldquo;Sync\u0026rdquo;（或重启 Neovim）来安装插件。LazyVim 会自动处理下载和配置。\n如何使用这些插件？ # 在 lua/plugins/ 中配置插件后，lazy.nvim 会根据您指定的条件（如 lazy = false、keys、cmd、event）管理加载。使用方式如下：\n自动功能：许多插件加载后即用。例如：\n配置 telescope.nvim 后，按 \u0026lt;leader\u0026gt;ff 即可打开文件搜索 nvim-cmp.lua 配置后会在插入模式自动激活补全 命令：插件常提供命令。例如 Telescope 的 :Telescope find_files，配置中的 cmd = { \u0026quot;Telescope\u0026quot; } 确保执行命令时加载插件\n快捷键：使用插件规格中定义的快捷键（如 \u0026lt;leader\u0026gt;ff），或在 lua/config/keymaps.lua 添加自定义快捷键：\nvim.keymap.set(\u0026#34;n\u0026#34;, \u0026#34;\u0026lt;leader\u0026gt;ff\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope find_files\u0026lt;cr\u0026gt;\u0026#34;, { desc = \u0026#34;查找文件\u0026#34; }) 插件文档：通过 :help 插件名（如 :help telescope）查看具体用法\n延迟加载注意事项： # 默认插件会延迟加载（按需加载）以加快启动速度 在 Telescope 示例中，插件会在按下 \u0026lt;leader\u0026gt;ff 或运行 :Telescope 时加载。如需立即加载，可在规格中添加 lazy = false 需要在其他配置文件中加载插件吗？ # 通常不需要。lua/plugins/ 中的插件配置会被 lazy.nvim 自动加载（通过 lua/config/lazy.lua 的设置，通常包含）：\nrequire(\u0026#34;lazy\u0026#34;).setup({ spec = { { import = \u0026#34;plugins\u0026#34; }, -- 导入 lua/plugins/ 所有文件 }, -- 其他性能设置等 }) 这意味着您无需在 init.lua、options.lua 或 keymaps.lua 中手动加载插件。但以下情况需注意：\n在 keymaps.lua 定义快捷键：\n如果在 keymaps.lua 中定义依赖插件的快捷键（如 \u0026lt;leader\u0026gt;ff），lazy.nvim 会在按键时通过 cmd 或 keys 设置自动加载插件 示例：在 keymaps.lua 添加 Telescope 快捷键仍会正常触发加载 选项或自动命令：\n如果插件需要 Neovim 选项或自动命令（如设置 vim.g.some_option），建议将配置放在插件的 config 函数中 或放在 lua/config/options.lua/autocmds.lua，但需确保插件已加载（如设置 lazy = false） 插件间依赖：\n在插件规格中使用 dependencies 字段确保先加载依赖项 示例：配置 nvim-tree 的快捷键 # 在 lua/plugins/nvim-tree.lua 中：\nreturn { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, dependencies = { \u0026#34;kyazdani42/nvim-web-devicons\u0026#34; }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, keys = { { \u0026#34;\u0026lt;leader\u0026gt;e\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;切换文件树\u0026#34; }, }, }, } 按下 \u0026lt;leader\u0026gt;e 时会加载插件，无需在其他地方额外配置。\n总结 # 配置插件：在 lua/plugins/ 中添加/修改 .lua 文件，返回包含插件详情的表 配置文件位置：保存在 lua/plugins/（如 telescope.lua） 使用插件：通过命令、快捷键或插件自动功能访问 其他文件加载：一般不需要，lazy.nvim 通过 lazy.lua 自动管理。如需在快捷键/选项中引用插件，确保配置加载顺序 ","date":"2025年6月6日","externalUrl":null,"permalink":"/notes/vim/lazyvim-plugins/","section":"笔记","summary":"如何在 LazyVim 中配置插件？ # LazyVim 使用 lazy.nvim 作为插件管理器，允许以模块化和灵活的方式定义和配置插件。要配置插件，您需要创建或修改 Lua 文件来指定要安装的插件及其设置和加载条件。这些文件通常位于 LazyVim 配置的 lua/plugins/ 目录中。\n","title":"lazyvim_plugins","type":"notes"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E5%99%A8%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"编辑器配置","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"插件管理","type":"tags"},{"content":"Neovim 的「宏编程」就是通过录制并使用宏（macro）来快速执行重复性操作。它是 Vim/Neovim 中非常实用、效率极高的功能之一。\n✅ 什么是 Neovim 的宏？ # 宏本质上是：你在 Normal 模式下的操作序列的录音回放。\n你可以：\n录制一系列操作 保存到某个寄存器（比如 a） 通过 @a 来重复执行 还可以用 @@ 重复上一次宏调用 📌 宏的基本使用步骤： # 动作 命令 开始录制宏 q\u0026lt;寄存器\u0026gt; 例如 qa 开始录到寄存器 a 执行操作 你正常地输入命令（比如 dw, j, iHello\u0026lt;Esc\u0026gt; 等） 停止录制 q 执行宏 @a 执行寄存器 a 的宏 重复执行上一个宏 @@ 🛠 示例：删除每一行的第一个单词 # qa → 开始录制到 a ^dwj → 移动到行首 (^)，删除一个词 (dw)，跳到下一行 (j) q → 结束录制 @a → 执行一遍宏 @@ → 再次执行刚才的宏 也可以：10@a → 执行 10 次 📎 技巧和注意事项： # 宏只能录制 Normal/Insert/Visual 模式下的操作，不能直接录制 Lua 或 Vimscript。 宏操作受当前光标位置、寄存器内容、编辑器状态影响，操作前最好保证状态一致。 如果你在 Insert 模式下输入内容，宏也会录进去。 宏中的移动建议用固定逻辑（例如 0, $, ^）而非手动左右移动（如 l, h），避免光标状态不同导致行为异常。 🚀 想更强？可以结合： # :normal 批量应用宏到多个行，比如：\n:10,20 normal @a Lua 中调用宏或用 Lua 实现更强的批处理（如使用 vim.api.nvim_feedkeys）\n示例 # test1 1.txt test2 2.txt test3 3.txt ... test10 10.txt 你想借助 Neovim 宏编程（比如使用 @a）自动生成这些行。我们来分两种情况：\n✅ 情况一：你只有第一行 test1 1.txt，想生成接下来的行 # 这种情况，我们可以借助宏 + Ctrl-a（数字加一命令）来自动生成每一行！\n🪄 操作步骤如下： # 手动输入第一行：\ntest1 1.txt 移动光标到行首，执行：\nqa \u0026#34; 开始录制宏到寄存器 a yyp \u0026#34; 复制整行并粘贴 w\u0026lt;C-a\u0026gt; \u0026#34; 移动到数字1，加1（变成2） w\u0026lt;C-a\u0026gt; \u0026#34; 再往后移动到 1.txt 中的1，再加1（变成2.txt） q \u0026#34; 停止录制 然后执行：\n@a \u0026#34; 执行宏一次 @@ \u0026#34; 重复上一次宏（可以多次按） 或者批量执行 9 次（再加9行）：\n9@a 👀 结果你将得到： # test1 1.txt test2 2.txt test3 3.txt test4 4.txt ... test10 10.txt 💡 补充说明 # yyp：复制整行并粘贴\nw\u0026lt;C-a\u0026gt;：先跳到第一个数字（1），然后 Ctrl-a 加1\n再 w\u0026lt;C-a\u0026gt;：继续跳到下一个数字加1（即 1.txt 中的 1）\n只要你的格式是 英文单词+空格+数字.txt，这套宏就能自动推进\n如果你之后格式变复杂，比如：\nprefix-test01 result_001.txt 这种带前导零的情况，也可以用 g\u0026lt;C-a\u0026gt;（更高级的 Vim 自增命令）或 Lua 批量生成\n","date":"2025年6月6日","externalUrl":null,"permalink":"/notes/vim/neovim-macro/","section":"笔记","summary":"Neovim 的「宏编程」就是通过录制并使用宏（macro）来快速执行重复性操作。它是 Vim/Neovim 中非常实用、效率极高的功能之一。\n✅ 什么是 Neovim 的宏？ # 宏本质上是：你在 Normal 模式下的操作序列的录音回放。\n","title":"neovim_macro","type":"notes"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E6%95%88%E7%8E%87/","section":"Tags","summary":"","title":"编辑效率","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E5%AE%8F%E7%BC%96%E7%A8%8B/","section":"Tags","summary":"","title":"宏编程","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/","section":"Tags","summary":"","title":"自动化","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/dhcpcd/","section":"Tags","summary":"","title":"Dhcpcd","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/dns/","section":"Tags","summary":"","title":"DNS","type":"tags"},{"content":"是的，你可以在 /etc/resolv.conf 文件中修改 DNS 服务器。该文件用于配置 Linux 系统使用的 DNS 解析服务器。通过编辑该文件，你可以手动指定 DNS 服务器（例如 Google 的 8.8.8.8 或 Cloudflare 的 1.1.1.1 等）。\n警告 最好使用的是 dhcpcd 服务，这个最容易修改，就在 /etc/dhcpcd.conf 中修改就行\n如何修改 /etc/resolv.conf 文件来更改 DNS 服务器 # 编辑 /etc/resolv.conf 文件\n使用文本编辑器（例如 nano、vim 或 gedit）编辑 /etc/resolv.conf 文件：\nsudo nano /etc/resolv.conf 修改 DNS 服务器\n在文件中，你可以添加或修改 nameserver 行来指定 DNS 服务器。例如：\nnameserver 8.8.8.8 # Google Public DNS nameserver 8.8.4.4 # Google Public DNS (备用) nameserver 2001:4860:4860::8888 nameserver 2001:4860:4860::8844 或者，你可以使用其他 DNS 服务提供商的 IP 地址，例如：\nnameserver 1.1.1.1 # Cloudflare DNS nameserver 1.0.0.1 # Cloudflare DNS (备用) 保存文件并退出编辑器。\n检查 DNS 设置\n编辑完成后，可以使用以下命令检查 DNS 配置是否生效：\ncat /etc/resolv.conf 你应该看到你添加的 DNS 服务器 IP 地址。\n注意事项 # 网络管理工具的干扰\n在某些系统中，/etc/resolv.conf 文件可能会被网络管理工具（如 NetworkManager、systemd-resolved 或 dhclient）覆盖或自动生成。如果你手动修改了该文件，但网络管理工具恢复了默认的 DNS 设置，修改将会被覆盖。\n禁用自动生成的 /etc/resolv.conf\n如果你希望手动管理 DNS 配置并防止它被覆盖，你可以采取以下方法：\n禁用 systemd-resolved（如果在使用 systemd）：\n如果你的系统使用 systemd-resolved 管理 DNS 配置，可以禁用它，然后手动修改 /etc/resolv.conf。\nsudo systemctl disable systemd-resolved.service sudo systemctl stop systemd-resolved.service sudo rm /etc/resolv.conf sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf 修改 NetworkManager 设置：\n如果你使用的是 NetworkManager，可以通过修改其配置文件来防止它覆盖 /etc/resolv.conf。编辑 /etc/NetworkManager/NetworkManager.conf 并添加以下行：\n[main] dns=none 然后重新启动 NetworkManager 服务：\nsudo systemctl reload NetworkManager 然后要重新编辑 resolv.conf 文件\n然后再继续执行\nsudo systemctl reload NetworkManager 通过 DHCP 自动获取 DNS\n如果你的网络通过 DHCP 自动获取 IP 配置，DHCP 服务器也可能会向 /etc/resolv.conf 写入 DNS 配置。你可以禁用 DHCP 配置 DNS 设置来防止这种情况，具体方法依赖于你的网络管理工具。 使用的是 dhcpcd # 编辑 /etc/dhcpcd. conf 的, 找到/添加: static domain_name_server=.....\n使用的是 systemd-networkd # 这个可能就要在 /etc/systemd/network/ 中编辑文件，创建一个文件 ,以. network 结尾例如: 20-wired. network\n[Match] Name=eth0 [Network] Address=192.168.1.100/24 Gateway=192.168.1.1 DNS=8.8.8.8 DNS=1.1.1.1 总结 # 你确实可以手动编辑 /etc/resolv.conf 来设置 DNS 服务器，但要注意一些网络管理工具可能会自动覆盖该文件。如果你想持久化更改，可能需要禁用某些自动管理 DNS 的服务或工具。\n","date":"2025年6月6日","externalUrl":null,"permalink":"/notes/linux/linux-dns%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/","section":"笔记","summary":"是的，你可以在 /etc/resolv.conf 文件中修改 DNS 服务器。该文件用于配置 Linux 系统使用的 DNS 解析服务器。通过编辑该文件，你可以手动指定 DNS 服务器（例如 Google 的 8.8.8.8 或 Cloudflare 的 1.1.1.1 等）。\n警告 最好使用的是 dhcpcd 服务，这个最容易修改，就在 /etc/dhcpcd.conf 中修改就行\n","title":"Linux-DNS网络管理","type":"notes"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/resolv.conf/","section":"Tags","summary":"","title":"Resolv.conf","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"网络管理","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/hyprland/","section":"Tags","summary":"","title":"Hyprland","type":"tags"},{"content":" 提示 首先声明，并不是所有功能都是通过快捷键打开的，就是比如说: Kool_Quick_Settings.sh 这个就是先通过 yad 或者 rofi 这些工具图形化功能栏目，然后就可以通过命令打开文件\n目录结构 # 省流版 # .config/hypr/ . ├── animations ├── application-style.conf ├── configs ├── hypridle.conf ├── hyprland.conf ├── hyprlock-2k.conf ├── hyprlock.conf ├── hyprpaper.conf ├── initial-boot.sh ├── Monitor_Profiles ├── monitors.conf ├── scripts ├── UserConfigs ├── UserScripts ├── v2.3.15 ├── wallpaper_effects ├── wallust └── workspaces.conf 大概说明 # 最重要文件: hyprland.conf, 这个文件就是一个相当于 hyprland 的根配置文件，窗口管理(WM)的根基配置\nanimations 是控制窗口、工作区、图层等 UI 元素的动画效果的一个目录的配置文件. (可以通过 SUPER SHIFT A 来选择)\napplication-style.conf: hyprland-qt-support provides a QML style for hypr qt6 apps\nconfigs 是项目 Default config 目录, 最好不要改动. scripts 同样是\nUserConfigs 则不一样，是表示用户级别的配置目录，可以自定义设置. UserScripts 同样\nhypridle.conf :\nhyprlock.conf 是与锁屏有关的配置文件\nhyprpaper.conf 是显示器 wallpaper 设置的配置文件\ninitial-boot.sh 是 hyprland 启动的时候，如何渲染就是通过这个脚本文件来配置的\nmonitors.conf 是由 Monitor_Profile 这个与monitor配置有关的目录里的 default.conf 生成的\nwallpaper_effects 是 wallpaper 改变之后生成的一个目录，有两个隐藏文件: .wallpaper_current 和 .wallpaper_modified\nworkspaces.conf 是一个由 Userconfigs 里的 WorkSpaceRules 文件生成的一个文件，是一个工作区如何定义和管理的配置文件\nwallust 是颜色定义目录，里面有个文件 (相当于一个常量头文件)\nUserConfigs/WindowRules.conf 这个文件就是管理窗口的，定义规则看如何给窗口贴标签，分配 workspace 这些等1\n例如:\nwindowrulev2 = tag +projects, class:^(codium|codium-url-handler|VSCodium)$ windowrulev2 = tag +projects, class:^(VSCode|code-url-handler)$ windowrulev2 = tag +projects, class:^(jetbrains-.+)$ # 这个可以实现在规定workspace启动应用窗口 windowrulev2 = workspace 4, tag:projects 详细版目录结构 # .config/hypr . ├── animations │ ├── 00-default.conf │ ├── 01-default - v2.conf │ ├── 03- Disable Animation.conf │ ├── END-4.conf │ ├── HYDE - default.conf │ ├── HYDE - minimal-1.conf │ ├── HYDE - minimal-2.conf │ ├── HYDE - optimized.conf │ ├── HYDE - Vertical.conf │ ├── Mahaveer - me-1.conf │ ├── Mahaveer - me-2.conf │ ├── ML4W - classic.conf │ ├── ML4W - dynamic.conf │ ├── ML4W - fast.conf │ ├── ML4W - high.conf │ ├── ML4W - moving.conf │ └── ML4W - standard.conf ├── application-style.conf ├── configs │ └── Keybinds.conf ├── hypridle.conf ├── hyprland.conf ├── hyprlock-2k.conf ├── hyprlock.conf ├── hyprpaper.conf ├── initial-boot.sh ├── Monitor_Profiles │ ├── default.conf │ └── README ├── monitors.conf ├── scripts │ ├── AirplaneMode.sh │ ├── Animations.sh │ ├── BrightnessKbd.sh │ ├── Brightness.sh │ ├── ChangeBlur.sh │ ├── ChangeLayout.sh │ ├── ClipManager.sh │ ├── DarkLight.sh │ ├── Distro_update.sh │ ├── GameMode.sh │ ├── Hypridle.sh │ ├── KeyBinds.sh │ ├── KeyHints.sh │ ├── KillActiveProcess.sh │ ├── Kitty_themes.sh │ ├── Kool_Quick_Settings.sh │ ├── KooLsDotsUpdate.sh │ ├── LockScreen.sh │ ├── MediaCtrl.sh │ ├── MonitorProfiles.sh │ ├── Polkit-NixOS.sh │ ├── Polkit.sh │ ├── PortalHyprland.sh │ ├── RefreshNoWaybar.sh │ ├── Refresh.sh │ ├── RofiEmoji.sh │ ├── RofiSearch.sh │ ├── RofiThemeSelector-modified.sh │ ├── RofiThemeSelector.sh │ ├── ScreenShot.sh │ ├── Sounds.sh │ ├── SwitchKeyboardLayout.sh │ ├── TouchPad.sh │ ├── UptimeNixOS.sh │ ├── Volume.sh │ ├── WallustSwww.sh │ ├── WaybarCava.sh │ ├── WaybarLayout.sh │ ├── WaybarScripts.sh │ ├── WaybarStyles.sh │ └── Wlogout.sh ├── UserConfigs │ ├── 00-Readme │ ├── 01-UserDefaults.conf │ ├── ENVariables.conf │ ├── LaptopDisplay.conf │ ├── Laptops.conf │ ├── Startup_Apps.conf │ ├── UserAnimations.conf │ ├── UserDecorations.conf │ ├── UserKeybinds.conf │ ├── UserSettings.conf │ ├── WindowRules.conf │ ├── WindowRules-new.conf │ └── WorkSpaceRules ├── UserScripts │ ├── 00-Readme │ ├── RainbowBorders.sh │ ├── RofiBeats.sh │ ├── RofiCalc.sh │ ├── WallpaperAutoChange.sh │ ├── WallpaperEffects.sh │ ├── WallpaperRandom.sh │ ├── WallpaperSelect.sh │ ├── Weather.py │ ├── Weather.sh │ └── ZshChangeTheme.sh ├── v2.3.15 ├── wallpaper_effects ├── wallust │ └── wallust-hyprland.conf └── workspaces.conf 配置 # 应用自启动配置 # 在 hyprland.conf 这个文件里自定义软件的自启动2\nexamples:\nexec-once = [workspace 1 silent] kitty exec-once = [workspace 1 silent] subl exec-once = [workspace 3 silent] mailspring exec-once = [workspace 4 silent] firefox exec VS exec-once # You can execute a shell script on:3\nstartup of the compositor every time the config is reloaded. shutdown of the compositor exec-once = command will execute only on launch support rules\nexecr-once = command will execute only on launch\nexec = command will execute on each reload support rules\nexecr = command will execute on each reload\nexec-shutdown = command will execute only on shutdown\n工作区 (Workspace) # 就是如何自定义和管理 workspaces 的，所以都是在 UserConfigs/WorkspaceRules 来设置来管理(不过这个一旦设置就是全部通用)，或者通过 workspaces.conf 这个文件来定义4\n显示器 (Monitor) # 可以直接通过 nwg-displays 来快速设定 (可以 SUPER SHIFT E 打开功能栏目然后搜索就可以了)\n也可以直接自定义 monitors.conf 文件5\n可以通过 nwg-display 工具来设定显示器\n快捷键 (keybinds) # rules: bind = MODS, key, dispatcher, params6\nexamples: bind = SUPER, B, exec, firefox\n根据上面目录结构所说的，可以在 UserConfigs/UserKeybinds.conf 里定义，也有 UserScripts/UserKeybinds.sh 里定义脚本，还有一个 UserScripts/UserKeyHints.sh 有常用快捷键提示\n这里说到快捷键要说明一下，渲染问题 (有 yad 和 rofi 这两个实现脚本的 GUI)\n简单对比:\n功能 yad rofi 图形风格 GTK，传统 GUI 样式 极简、dmenu 风格 多列显示 ✅ 原生支持 🚫 不支持多列（需手动 hack） 自定义交互复杂度 高（按钮、表单、进度条等） 中（菜单交互 + 自定义脚本） 外观主题支持 GTK 主题 rofi 主题（通过 config 设置） 使用场景 配置工具、提示菜单、设置界面 启动器、搜索菜单、快速选择器 环境变量 ENV # 在 /etc/environment 加载 fcitx5\n笔记 下面的注释是 fcitx5 的 wayland 前端好像是会自动加载这些，所以还添加的话，这样每次启动都会有提示消息\n#GTK_IM_MODULE=fcitx #QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus 加载 nvidia (还是怕 nvidia 驱动没有加载，所以在系统级别的 evn 中添加)\n__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia Nvidia # 警告 首先禁用 nouveau 这个开源驱动，fedora 和 arch 方法不一样，arch 可以直接在 /etc/mkinitcpio.conf 里的 HOOK 字段修改 (删除 kms 就行), 然后就mkinitcpio -P\n禁用 nouveau 驱动 # 在 /etc/modprobe.d 这个目录里编写一个文件:blacklist-nouveau. conf(文件名可以自己自定义)\nblacklist nouveau options nouveau modeset=0 然后, 我们要加载这个禁用的文件配置\n/etc/default/grub 文件中:\nGRUB_CMDLINE_LINUX=\u0026#34;rd.driver.blacklist=nouveau modprobe.blacklist=nouveau\u0026#34; 然后就要: sudo dracut --force 重新生成 grub 引导文件\n以防 nvidia 驱动没有启动，我们在很多方面都写了相关环境变量\n在 /etc/modprobe.d 编写一个nvidia. conf文件7\noptions nvidia_drm modeset=1 UserConfigs/ENVariables.conf 定义各种 env 包括nvidia的 (所以基本我们为了项目化，都在这个改动 env)\n输入法设置 # Fcitx5 各种配置以及问题 # Rime 的大写模式下的问题 # 在 Rime 的中文输入法下按下 capslock键 你就处于大写模式无法输入中文了,然后就各种文档参看, 找到2013 年的的 issue8, 这种应该按道理不管在哪个 DE 或者 WM 下都应该修复了才对的啊!!🤔\nI\u0026rsquo;ll look into this problem tonight.\nYou can temporarily disable mode switching with Caps Lock by patching default.yaml:\n# default.custom.yaml patch: { ascii_composer/switch_key/Caps_Lock: noop } 软件窗口里的输入法 # 笔记 不知道是 electron 应用的问题还是 hyprland 和 fcitx5 还不是很兼容的问题。我们需要在 desktop file 里的Exec字段手动定义实现可以用 fcitx5 的中文输入\n补充如下配置:\n--enable-wayland-ime 如果是用户级别的软件，我们需要在 .local/share/applications 里的 desktop file 里定义。否则，我们要在 /usr/share/applications 里查找 desktop file 来修改\n因为有些应用会出现输入中文的拼音会漏词 (就是明明想要输入 shuru 但是可能会漏出 h 到 fcitx5 的候选词栏中), 有些博客说可以:\n在 ~/.config/gtk-3.0/config 文件中添加:9\n[Settings] gtk-im-module = fcitx Electron 相关应用参考官方链接 # 比如软件:obsidian,vscode,chrome等10\n因为在 linux+nvidia+wayland 这种情况下，nvidia 这个 gpu 加速不一定适配，所以可能就无法实现很流畅地渲染，但是为了进可能地渲染\n我们在 desktop file 里也定义:\n--enable-features=UseOzonePlatform --ozone-platform=wayland StartupWMClass=obsidian # 这个field就是添加窗口class属性(可以更好在窗口管理管理) 代理问题: 这个也是要在 .desktop 里根据各个 applications 的要求设置 proxy\n奇奇怪怪的问题 # firefox 的 class 问题 # 目的: 想要实现在打开某些窗口的时候打开到固定的工作区\n.config/hypr/UserConfigs/WindowRules.conf 这个文件里定义窗口规则的时候，发现如下配置:\nwindowrule2 = tag +browser, class:^([Ff]irefox|org.mozilla.firefox|[Ff]irefox-esr|[Ff]irefox-bin) windowrule2 = workspace 4, tag: browser 这样的配置根本没有在 workspace 4 打开 firefox, 我使用 hyprctl clients 命令查看属性的时候，发现是正常的.\n但是听从 gpt 意见写成:\nwindowrele2= workspace 4, tag +browser, class:^([Ff]irefox|org.mozilla.firefox|[Ff]irefox-esr|[Ff]irefox-bin) 这样的配置却可以生效，问了一下 gpt, 就说是如下原因:\n窗口创建时机：某些应用程序在启动时可能会先创建窗口，然后再设置类名（class）等属性。如果 tag 是基于 class 设置的，而 class 尚未被识别，tag 就不会被应用，导致依赖于该 tag 的规则无法生效。(比如 firefox)\nwaybar 的 icon 显示问题: 比如 waybar 的 rose 的主题，有时候会出现蓝牙图标没有显示(==我想到的可能性是蓝牙启动比 waybar 慢，所以无法显示==)，只有重新 refresh waybar 才行，所以可以尝试在 hyprland.conf 中设置 exec-once = blueman-applet 我们手动设置让蓝牙启动\n结论:\nHyprland 的 windowrulev2 规则在处理标签（tag）和工作区（workspace）时，应用顺序和窗口创建时机非常关键。​如果窗口在创建时尚未被赋予标签，那么依赖于该标签的工作区规则可能不会生效。​ spotify 也是这样，wiki 里也有提及11\n为确保窗口在创建时立即被分配到正确的工作区，建议将标签和工作区的设置合并为一条规则，并确保窗口在创建时就匹配该规则。\n参考🔗 # hyprland wiki workspace rules\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki autostartup apps\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki executing\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki workspaces\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki monitors\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki keybinds\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki nvidia\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nrime-issue\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nfcitx5漏字\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki electron\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki spotify\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025年6月6日","externalUrl":null,"permalink":"/notes/tools/hyprland%E7%AE%80%E6%98%8E%E6%8C%87%E5%8C%97/","section":"笔记","summary":" 提示 首先声明，并不是所有功能都是通过快捷键打开的，就是比如说: Kool_Quick_Settings.sh 这个就是先通过 yad 或者 rofi 这些工具图形化功能栏目，然后就可以通过命令打开文件\n","title":"Hyprland简明指北","type":"notes"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/wayland/","section":"Tags","summary":"","title":"Wayland","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83/","section":"Tags","summary":"","title":"桌面环境","type":"tags"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/categories/%E6%A1%8C%E9%9D%A2%E8%AE%BE%E8%AE%A1/","section":"Categories","summary":"","title":"桌面设计","type":"categories"},{"content":"","date":"2025年6月6日","externalUrl":null,"permalink":"/tags/%E7%AA%97%E5%8F%A3%E7%AE%A1%E7%90%86%E5%99%A8/","section":"Tags","summary":"","title":"窗口管理器","type":"tags"},{"content":" 杂谈 # alist 的相关目录配置全部都在 /opt/alist 目录下，这里可以配置相关的比如:\nhttps 设置 (即证书的设置) 端口设置 密码设置 (高版本已经不行了) 警告 这个脚本是可以使用缓存的文件的, 所以配置新的域名或者什么的, 请先删除 ip.txt, cloudflare.log, cloudflare.ids 文件\n谈到有关证书的事情，我这个 alist 服务器有关 https 访问，就要申请 ssl 证书。那么就要将相关的情况分类说明:\n情况一 # 你的 cloudflare 代理不是仅 DNS 的 (即你的云朵是橙色的), 这就要看你的加密方式是什么了 (在 边缘证书 那里可以看到相关设置)\nCloudflare 提供三种主要的 SSL/TLS 模式，决定了 Cloudflare 与源站之间的连接方式：\n灵活（Flexible）：​浏览器与 Cloudflare 之间使用 HTTPS，但 Cloudflare 与源站之间使用 HTTP。​\n完全（Full）：​浏览器与 Cloudflare 之间使用 HTTPS，Cloudflare 与源站之间也使用 HTTPS，但不验证源站证书的有效性。​\n完全（严格）（Full (Strict)）：​浏览器与 Cloudflare 之间使用 HTTPS，Cloudflare 与源站之间也使用 HTTPS，并且验证源站证书的有效性（必须是受信任的 CA 签发的有效证书）。\n在“完全（严格）”模式下，Cloudflare 会验证你服务器上的 SSL 证书是否有效且由受信任的证书颁发机构（CA）签发。​如果你使用 Let’s Encrypt 证书，并确保其未过期，Cloudflare 将接受该证书。​\n所以，如果你想设置 cloudflare 的证书，你就要自己手动设置相关的还有续期也要分不同情况来决定是否会自动续期.\n情况二 # 你的 cloudflare 代理是仅 DNS 的 (即你的云朵是灰色的)\n我的服务器是通过 cerbot 这个来申请 ssl 证书的，他的相关配置文件存储在 /etc/letsencrypt 下\n/etc/letsencrypt/live/yourdomain/：包含实际使用的证书文件的符号链接。​ 以下是先骨干目录结构内容:\nfullchain.pem：完整的证书链，供服务器使用。​\nprivkey.pem：私钥文件。​\n/etc/letsencrypt/archive/yourdomain/：存储所有历史版本的证书和私钥。​\n/etc/letsencrypt/renewal/yourdomain.conf：包含续期所需的配置信息。​\n/var/log/letsencrypt/letsencrypt.log：Certbot 的日志文件，记录操作过程和错误信息。\n以下是通过 cerbot 申请 ssl 证书步骤:\n申请 SSL 证书: 通过 standalone 模式申请证书：(因为我不是使用 nginx 的，所以是 standalone 模式) sudo cerbot certonly --standalone -d alist.050626.xyz 验证过程: Certbot 会启动一个临时的 web 服务器，向 Let\u0026rsquo;s Encrypt 证明你拥有该域名。验证完成后，它会生成证书并保存在 /etc/letsencrypt/live/yourdomain.com/ 目录下。 证书自动续期: Certbot 会自动配置一个 cron 任务来定期检查并续期证书。验证证书是否会自动续期: sudo cerbot renew --dry-run 配置定时任务: sudo crontab -e 每天检查证书并进行续期 (如果需要), 然后重启 alist\n0 0 * * * certbot renew --quiet \u0026amp;\u0026amp; systemctl restart alist 然后有了证书之后，就在 /opt/alist/data 目录下:\n在 cert_file 以及 key_file 上放置相关配置文件\n例如:\n\u0026#34;cert_file\u0026#34;: \u0026#34;/etc/letsencrypt/live/alist.050626.xyz/fullchain.pem\u0026#34;, \u0026#34;key_file\u0026#34;: \u0026#34;/etc/letsencrypt/live/alist.050626.xyz/privkey.pem\u0026#34;, ","date":"2025年4月20日","externalUrl":null,"permalink":"/notes/tools/alist%E9%85%8D%E7%BD%AE/","section":"笔记","summary":"杂谈 # alist 的相关目录配置全部都在 /opt/alist 目录下，这里可以配置相关的比如:\nhttps 设置 (即证书的设置) 端口设置 密码设置 (高版本已经不行了) 警告 这个脚本是可以使用缓存的文件的, 所以配置新的域名或者什么的, 请先删除 ip.txt, cloudflare.log, cloudflare.ids 文件\n","title":"alist配置","type":"notes"},{"content":"","date":"2025年4月20日","externalUrl":null,"permalink":"/tags/https/","section":"Tags","summary":"","title":"HTTPS","type":"tags"},{"content":"","date":"2025年4月20日","externalUrl":null,"permalink":"/tags/ssl%E8%AF%81%E4%B9%A6/","section":"Tags","summary":"","title":"SSL证书","type":"tags"},{"content":"","date":"2025年4月20日","externalUrl":null,"permalink":"/tags/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"配置管理","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"零宽断言（lookahead 和 lookbehind）、负向断言、后向引用以及平衡组是正则表达式中的高级特性，能够处理更复杂的匹配需求。以下是它们的功能和在 Python 中的具体应用。\n1. 零宽断言 # 正向零宽断言（Positive Lookahead） # 匹配某个位置后面紧跟着特定内容，但不会把这个内容包括在结果中。\n语法： # (?=pattern) 示例： # 提取所有后面跟着数字的单词：\nimport re text = \u0026#34;apple 123, banana456, cherry 789.\u0026#34; # 匹配后面紧跟数字的单词 pattern = r\u0026#39;\\b\\w+(?=\\d)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;banana\u0026#39;] 负向零宽断言（Negative Lookahead） # 匹配某个位置后面不跟特定内容。\n语法： # (?!pattern) 由感叹号都知道这是表示非\n示例： # 提取所有后面不跟数字的单词：\nimport re text = \u0026#34;apple 123, banana456, cherry 789.\u0026#34; # 匹配后面不跟数字的单词 pattern = r\u0026#39;\\b\\w+(?!\\d)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;apple\u0026#39;, \u0026#39;cherry\u0026#39;] 2. 后向零宽断言 # 正向后向断言（Positive Lookbehind） # 匹配某个位置前面紧跟着特定内容。\n语法： # (?\u0026lt;=pattern) 示例： # 判断条件是 \u0026ldquo;@\u0026rdquo; ：\nimport re text = \u0026#34;Contact us at support@example.com or sales@company.com.\u0026#34; # 匹配 \u0026#34;@\u0026#34; 后的部分 pattern = r\u0026#39;(?\u0026lt;=@)\\w+\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;example\u0026#39;, \u0026#39;company\u0026#39;] 负向后向断言（Negative Lookbehind） # 匹配某个位置前面不紧跟特定内容。\n语法： # (?\u0026lt;!pattern) 示例： # import re text = \u0026#34;Contact us at support@example.com or sales@company.com.\u0026#34; # 匹配前面不是 @ 的单词 pattern = r\u0026#39;(?\u0026lt;!@)\\b\\w+\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;Contact\u0026#39;, \u0026#39;us\u0026#39;, \u0026#39;at\u0026#39;, \u0026#39;support\u0026#39;, \u0026#39;or\u0026#39;, \u0026#39;sales\u0026#39;] 3. 后向引用 # 后向引用是指在正则表达式中引用之前捕获的内容，可以用 \\1, \\2, 等等表示。\n示例： # 匹配文本中的重复单词：\nimport re text = \u0026#34;I saw a dog and a cat, but I didn\u0026#39;t see the dog dog.\u0026#34; # 匹配重复单词 pattern = r\u0026#39;\\b(\\w+)\\b\\s+\\1\\b\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;dog\u0026#39;] 4. 平衡组 # 平衡组主要用于匹配嵌套结构（例如括号对等结构）。Python 的标准库 re 不直接支持平衡组操作，但可以通过复杂的逻辑来实现。例如，使用 pyparsing 或 regex 模块可以更好地处理。\n示例：使用 regex 匹配嵌套括号：\nimport regex text = \u0026#34;(a(b(c)d)e)f(g(h)i)\u0026#34; # 匹配嵌套括号 pattern = r\u0026#39;\\((?\u0026gt;[^\\(\\)]+|(?R))*\\)\u0026#39; matches = regex.findall(pattern, text) print(matches) # 输出：[\u0026#39;(a(b(c)d)e)\u0026#39;, \u0026#39;(b(c)d)\u0026#39;, \u0026#39;(c)\u0026#39;, \u0026#39;(g(h)i)\u0026#39;, \u0026#39;(h)\u0026#39;] 解释：\n(?R) 是递归调用整个正则表达式本身。 (?\u0026gt;...) 是原子组，确保括号内的内容是完整匹配。 组合练习：解析复杂数据 # 任务： # 提取所有括号中的内容，但要求括号内不含特定关键词（如 \u0026ldquo;skip\u0026rdquo;）。\n示例代码： # import re text = \u0026#34;Match (this), (skip this), and (not skip).\u0026#34; # 匹配括号中的内容，但不包含 \u0026#34;skip\u0026#34; pattern = r\u0026#39;\\((?!.*skip).*?\\)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;(this)\u0026#39;, \u0026#39;(not skip)\u0026#39;] 任务：验证嵌套括号结构是否匹配 # 例如：\n输入 \u0026quot;(a(b)c)d\u0026quot; 是有效的。 输入 \u0026quot;a(b(c)d\u0026quot; 是无效的。 示例代码： # import regex def is_valid_parentheses(s): # 匹配嵌套括号 pattern = r\u0026#39;^\\((?\u0026gt;[^\\(\\)]+|(?R))*\\)$\u0026#39; return bool(regex.match(pattern, s)) print(is_valid_parentheses(\u0026#34;(a(b)c)d\u0026#34;)) # False print(is_valid_parentheses(\u0026#34;(a(b)c)d)\u0026#34;)) # False print(is_valid_parentheses(\u0026#34;(a(b)c)\u0026#34;)) # True 总结 # 零宽断言：用于检查前后内容是否满足条件，但不消耗匹配的内容。 后向引用：引用前面的捕获内容，特别适合处理重复和结构化数据。 平衡组：用于复杂嵌套结构匹配（需借助 regex 模块）。 通过练习这些场景，你可以熟悉它们的强大功能，并灵活应用到数据处理、文件操作和复杂匹配任务中！\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/regex/regrex%E8%A1%A5%E5%85%85/","section":"笔记","summary":"零宽断言（lookahead 和 lookbehind）、负向断言、后向引用以及平衡组是正则表达式中的高级特性，能够处理更复杂的匹配需求。以下是它们的功能和在 Python 中的具体应用。\n","title":"regrex补充","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%90%8E%E5%90%91%E5%BC%95%E7%94%A8/","section":"Tags","summary":"","title":"后向引用","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","section":"Tags","summary":"","title":"正则表达式","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%B9%B3%E8%A1%A1%E7%BB%84/","section":"Tags","summary":"","title":"平衡组","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E9%9B%B6%E5%AE%BD%E6%96%AD%E8%A8%80/","section":"Tags","summary":"","title":"零宽断言","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/regrex/","section":"Categories","summary":"","title":"Regrex","type":"categories"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%AE%9E%E6%88%98%E5%BA%94%E7%94%A8/","section":"Tags","summary":"","title":"实战应用","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E7%BB%83%E4%B9%A0%E9%A2%98/","section":"Tags","summary":"","title":"练习题","type":"tags"},{"content":"以下是一些精心设计的正则表达式练习，结合了 Python 编程 和 文件操作，帮助你熟悉正则表达式在不同场景中的实际应用。\n练习一：基本字符匹配 # 目标：掌握正则表达式的基础功能。\n任务：\n提取字符串中的所有电话号码（格式如：123-456-7890 或 1234567890）。 提取所有以 \u0026ldquo;test\u0026rdquo; 开头的单词。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; 联系人1：123-456-7890 联系人2：9876543210 测试数据：test1, test2, testing \u0026#34;\u0026#34;\u0026#34; # 匹配电话号码 phone_pattern = r\u0026#39;\\d{3}-\\d{3}-\\d{4}|\\d{10}\u0026#39; phones = re.findall(phone_pattern, text) print(\u0026#34;电话号码列表：\u0026#34;, phones) # 匹配以 test 开头的单词 test_pattern = r\u0026#39;\\btest\\w*\\b\u0026#39; test_words = re.findall(test_pattern, text) print(\u0026#34;以 test 开头的单词：\u0026#34;, test_words) 练习二：处理文件名 # 目标：熟悉正则表达式操作文件名。\n任务：\n在指定文件夹中，找到所有以 test_ 开头的文件。 将这些文件重命名为去掉 test_ 的新名字。 代码模板：\nimport os import re folder_path = r\u0026#39;F:\\your_folder\u0026#39; # 替换为你的文件夹路径 # 正则匹配以 test_ 开头的文件 pattern = re.compile(r\u0026#39;^test_(.+)$\u0026#39;) for root, dirs, files in os.walk(folder_path): for file in files: match = pattern.match(file) if match: new_name = match.group(1) os.rename(os.path.join(root, file), os.path.join(root, new_name)) print(f\u0026#34;重命名：{file} -\u0026gt; {new_name}\u0026#34;) 练习三：文本清洗 # 目标：使用正则表达式清洗文本数据。\n任务：\n移除文本中的所有 HTML 标签。 替换所有连续的空白字符为单个空格。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt;\u0026lt;title\u0026gt;测试页面\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;标题\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;这是一个段落。\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; 还有一些 多余的空格。\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; \u0026#34;\u0026#34;\u0026#34; # 移除 HTML 标签 html_pattern = r\u0026#39;\u0026lt;[^\u0026gt;]+\u0026gt;\u0026#39; clean_text = re.sub(html_pattern, \u0026#39;\u0026#39;, text) print(\u0026#34;去掉 HTML 标签后的文本：\\n\u0026#34;, clean_text) # 替换连续空格为单个空格 space_pattern = r\u0026#39;\\s+\u0026#39; clean_text = re.sub(space_pattern, \u0026#39; \u0026#39;, clean_text) print(\u0026#34;清理空格后的文本：\\n\u0026#34;, clean_text) 练习四：验证用户输入 # 目标：使用正则表达式对用户输入进行格式验证。\n任务：\n验证一个输入是否是有效的电子邮件地址。 验证一个输入是否是有效的 URL。 代码模板：\nimport re # 验证电子邮件 email = input(\u0026#34;请输入一个电子邮件地址：\u0026#34;) email_pattern = r\u0026#39;^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$\u0026#39; if re.match(email_pattern, email): print(\u0026#34;有效的电子邮件地址\u0026#34;) else: print(\u0026#34;无效的电子邮件地址\u0026#34;) # 验证 URL url = input(\u0026#34;请输入一个 URL：\u0026#34;) url_pattern = r\u0026#39;^https?://[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}(/.*)?$\u0026#39; if re.match(url_pattern, url): print(\u0026#34;有效的 URL\u0026#34;) else: print(\u0026#34;无效的 URL\u0026#34;) 练习五：提取日志中的 IP 地址 # 目标：从日志文件中提取所有的 IP 地址。\n任务：\n打开一个日志文件。 提取其中的所有 IPv4 地址。 代码模板：\nimport re # 假设日志内容 log_data = \u0026#34;\u0026#34;\u0026#34; 192.168.0.1 - - [24/Nov/2024:14:15:22] \u0026#34;GET /index.html HTTP/1.1\u0026#34; 200 10.0.0.5 - - [24/Nov/2024:14:16:00] \u0026#34;POST /form.html HTTP/1.1\u0026#34; 404 Invalid log entry 123.456.789.0 \u0026#34;\u0026#34;\u0026#34; # 匹配 IPv4 地址 ip_pattern = r\u0026#39;((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\u0026#39; # 使用 finditer 获取迭代器 matches = re.finditer(ip_pattern, log_data) ips = [match.group(0) for match in matches] print(\u0026#34;提取的 IP 地址：\u0026#34;, ips) 练习六：复杂文件名处理 # 目标：掌握文件名匹配和高级正则表达式。\n任务：\n查找文件夹中的所有符合以下模式的文件名：课程编号_章节名_说明_时间戳.ext。 从文件名中提取 课程编号 和 章节名，生成新的文件名格式：课程编号-章节名.ext。 代码模板：\nimport os import re folder_path = r\u0026#39;F:\\your_folder\u0026#39; # 替换为你的文件夹路径 # 匹配复杂文件名 pattern = re.compile(r\u0026#39;^(课程\\d+)_(章节\\d+)_.+?_(\\d+)\\.(.+)$\u0026#39;) for root, dirs, files in os.walk(folder_path): for file in files: match = pattern.match(file) if match: course = match.group(1) chapter = match.group(2) ext = match.group(4) new_name = f\u0026#34;{course}-{chapter}.{ext}\u0026#34; os.rename(os.path.join(root, file), os.path.join(root, new_name)) print(f\u0026#34;重命名：{file} -\u0026gt; {new_name}\u0026#34;) 练习七：动态生成正则表达式 # 目标：掌握在代码中动态构造正则表达式。\n任务：\n根据用户输入的关键词列表，生成一个动态正则表达式，用来查找文本中包含任意关键词的句子。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; 今天我学习了正则表达式，它非常有用。 Python 编程语言让我感觉很强大。 我还想学习更多关于人工智能的知识。 \u0026#34;\u0026#34;\u0026#34; # 用户输入关键词 keywords = input(\u0026#34;请输入关键词，用空格分隔：\u0026#34;).split() keyword_pattern = \u0026#39;|\u0026#39;.join(map(re.escape, keywords)) # 动态构造正则表达式 sentence_pattern = fr\u0026#39;\\b.*?({keyword_pattern}).*?\\.\u0026#39; sentences = re.findall(sentence_pattern, text, re.IGNORECASE) print(\u0026#34;匹配的句子：\u0026#34;, sentences) 通过以上练习，你可以逐步熟悉 正则表达式的匹配规则，以及如何在 Python 文件操作 中使用正则表达式。完成每个任务后，试着改动规则或扩展功能，进一步加深理解！\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/regex/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%BB%83%E4%B9%A0/","section":"笔记","summary":"以下是一些精心设计的正则表达式练习，结合了 Python 编程 和 文件操作，帮助你熟悉正则表达式在不同场景中的实际应用。\n练习一：基本字符匹配 # 目标：掌握正则表达式的基础功能。\n","title":"正则表达式练习","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C/","section":"Tags","summary":"","title":"文件操作","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%85%83%E5%AD%97%E7%AC%A6/","section":"Tags","summary":"","title":"元字符","type":"tags"},{"content":"版本：v2.4.1 (2019-11-15) 作者：deerchao 转载请注明来源\n目录 # 本文目标 如何使用本教程 正则表达式到底是什么东西？ 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 # 30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。\n如何使用本教程 # 别被下面那些复杂的表达式吓倒，只要跟着我一步一步来，你会发现正则表达式其实并没有想像中的那么困难。当然，如果你看完了这篇教程之后，发现自己明白了很多，却又几乎什么都记不得，那也是很正常的——我认为，没接触过正则表达式的人在看完这篇教程后，能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理，以后你还需要多练习，多使用，才能熟练掌握正则表达式。\n除了作为入门教程之外，本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说，这个目标还是完成得不错的——你看，我自己也没能把所有的东西记下来，不是吗？\n清除格式 文本格式约定：专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串 对正则表达式或其中一部分的说明\n隐藏边注 本文右边有一些注释，主要是用来提供一些相关信息，或者给没有程序员背景的读者解释一些基本概念，通常可以忽略。\n本文介绍的大部分正则语法，在不同的正则表达式引擎中都可以使用，但也有一些会有所差异。本文介绍的是 .Net 下的正则表达式，其它环境下的具体情况可以在读完本文后去参考官方文档，或者查看正则表达式引擎特性对比。\n最重要的是——请给我30分钟，如果你没有使用正则表达式的经验，请不要试图在30秒内入门——除非你是超人 :)\n正则表达式到底是什么东西？ # 在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。\n很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard)，也就是*和?。如果你想查找某个目录下的所有的Word文档的话，你会搜索*.doc。在这里，*会被解释成任意的字符串。和通配符类似，正则表达式也是用来进行文本匹配的工具，只不过比起通配符，它能更精确地描述你的需求——当然，代价就是更复杂——比如你可以编写一个正则表达式，用来查找所有以0开头，后面跟着2-3个数字，然后是一个连字号“-”，最后是7或8位数字的字符串(像010-12345678或0376-7654321)。\n字符是计算机软件处理文字时最基本的单位，可能是字母，数字，标点符号，空格，换行符，汉字等等。字符串是0个或更多个字符的序列。文本也就是文字，字符串。说某个字符串匹配某个正则表达式，通常是指这个字符串里有一部分（或几部分分别）能满足表达式给出的条件。\n入门 # 学习正则表达式的最好方法是从例子开始，理解例子之后再自己对例子进行修改，实验。下面给出了不少简单的例子，并对它们作了详细的说明。\n假设你在一篇英文小说里查找hi，你可以使用正则表达式hi。\n这几乎是最简单的正则表达式了，它可以精确匹配这样的字符串：由两个字符组成，前一个字符是h,后一个是i。通常，处理正则表达式的工具会提供一个忽略大小写的选项，如果选中了这个选项，它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。\n不幸的是，很多单词里包含hi这两个连续的字符，比如him,history,high等等。用hi来查找的话，这里边的hi也会被找出来。如果要精确地查找hi这个单词的话，我们应该使用\\bhi\\b。\n\\b是正则表达式规定的一个特殊代码（好吧，某些人叫它元字符，metacharacter），代表着单词的开头或结尾，也就是单词的分界处。虽然通常英文的单词是由空格，标点符号或者换行来分隔的，但是\\b并不匹配这些单词分隔字符中的任何一个，它只匹配一个位置。\n如果需要更精确的说法，\\b匹配这样的位置：它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\\w。\n假如你要找的是hi后面不远处跟着一个Lucy，你应该用\\bhi\\b.*\\bLucy\\b。\n这里，.是另一个元字符，匹配除了换行符以外的任意字符。*同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此，.*连在一起就意味着任意数量的不包含换行的字符。现在\\bhi\\b.*\\bLucy\\b的意思就很明显了：先是一个单词hi,然后是任意个任意字符(但不能是换行)，最后是Lucy这个单词。\n换行符就是\u0026rsquo;\\n\u0026rsquo;,ASCII编码为10(十六进制0x0A)的字符。\n如果同时使用其它元字符，我们就能构造出功能更强大的正则表达式。比如下面这个例子：\n0\\d\\d-\\d\\d\\d\\d\\d\\d\\d\\d匹配这样的字符串：以0开头，然后是两个数字，然后是一个连字号“-”，最后是8个数字(也就是中国的电话号码。当然，这个例子只能匹配区号为3位的情形)。\n这里的\\d是个新的元字符，匹配一位数字(0，或1，或2，或……)。-不是元字符，只匹配它本身——连字符(或者减号，或者中横线，或者随你怎么称呼它)。\n为了避免那么多烦人的重复，我们也可以这样写这个表达式：0\\d{2}-\\d{8}。这里\\d后面的{2}({8})的意思是前面\\d必须连续重复匹配2次(8次)。\n测试正则表达式 # 如果你不觉得正则表达式很难读写的话，要么你是一个天才，要么，你不是地球人。正则表达式的语法很令人头疼，即使对经常使用它的人来说也是如此。由于难于读写，容易出错，所以找一种工具对正则表达式进行测试是很有必要的。\n不同的环境下正则表达式的一些细节是不相同的，本教程介绍的是微软 .Net Framework 4.x 下正则表达式的行为，所以，我向你推荐我编写的.Net下的工具 Regester。请参考该页面的说明来安装和运行该软件。\n下面是Regester运行时的截图：\n你也可以试试这个在线测试工具：Wegester, JavaScript正则表达式测试器。\n现在你已经知道几个很有用的元字符了，如\\b,.,*，还有\\d.正则表达式里还有更多的元字符，比如\\s匹配任意的空白符，包括空格，制表符(Tab)，换行符，中文全角空格等。\\w匹配字母或数字或下划线或汉字等。\n对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的，其它环境下的具体情况请查看相关文档。\n下面来看看更多的例子：\n\\ba\\w*\\b匹配以字母a开头的单词——先是某个单词开始处(\\b)，然后是字母a,然后是任意数量的字母或数字(\\w*)，最后是单词结束处(\\b)。\n\\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符，不同的是*匹配重复任意次(可能是0次)，而+则匹配重复1次或更多次。\n\\b\\w{6}\\b 匹配刚好6个字符的单词。\n好吧，现在我们说说正则表达式里的单词是什么意思吧：就是不少于一个的连续的\\w。不错，这与学习英文时要背的成千上万个同名的东西的确关系不大 :)\n代码 说明 . 匹配除换行符以外的任意字符 \\w 匹配字母或数字或下划线或汉字 \\s 匹配任意的空白符 \\d 匹配数字 \\b 匹配单词的开始或结束 ^ 匹配字符串的开始 $ 匹配字符串的结束 元字符^（和数字6在同一个键位上的符号）和$都匹配一个位置，这和\\\\b有点类似。^匹配你要用来查找的字符串的开头，$匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的QQ号必须为5位到12位数字时，可以使用：^\\d{5,12}$。\n这里的{5,12}和前面介绍过的{2}是类似的，只不过{2}匹配只能不多不少重复2次，{5,12}则是重复的次数不能少于5次，不能多于12次，否则都不匹配。\n因为使用了^和$，所以输入的整个字符串都要用来和\\d{5,12}来匹配，也就是说整个输入必须是5到12个数字，因此如果输入的QQ号能匹配这个正则表达式的话，那就符合要求了。\n和忽略大小写的选项类似，有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项，^和$的意义就变成了匹配行的开始处和结束处。\n正则表达式引擎通常会提供一个“测试指定的字符串是否匹配一个正则表达式”的方法，如JavaScript里的RegExp.test()方法或.NET里的Regex.IsMatch()方法。这里的匹配是指是字符串里有没有符合表达式规则的部分。如果不使用^和$的话，对于\\d{5,12}而言，使用这样的方法就只能保证字符串里包含5到12连续位数字，而不是整个字符串就是5到12位数字。\n字符转义 # 如果你想查找元字符本身的话，比如你查找.,或者*,就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用\\来取消这些字符的特殊意义。因此，你应该使用\\.和\\*。当然，要查找\\本身，你也得用\\\\.\n例如：deerchao\\.cn匹配deerchao.cn，C:\\\\Windows匹配C:\\Windows。\n重复 # 你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码，例如*,{5,12}等)：\n代码/语法 说明 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 下面是一些使用重复的例子：\nWindows\\d+匹配Windows后面跟1个或更多数字\n^\\w+匹配一行的第一个单词(或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)\n字符类 # 要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？\n很简单，你只需要在方括号里列出它们就行了，像\n\\[aeiou\\]就匹配任何一个英文元音字母，\n\\[.?!\\]匹配标点符号(.或?或!)。\n我们也可以轻松地指定一个字符范围，像\n\\[0-9\\]代表的含意与\\d就是完全一致的：一位数字；同理\n\\[a-z0-9A-Z\\_\\]也完全等同于\\w（如果只考虑英文的话）。\n下面是一个更复杂的表达式：\\(?0\\d{2}\n\\[) -\\]?\\d{8}。\n这个表达式可以匹配几种格式的电话号码，像(010)88886666，或022-22334455，或02912345678等。我们对它进行一些分析吧：首先是一个转义字符\\(,它能出现0次或1次(?),然后是一个0，后面跟着2个数字(\\d{2})，然后是)或-或空格中的一个，它出现1次或不出现(?)，最后是8个数字(\\d{8})。\n“(”和“)”也是元字符，后面的分组节里会提到，所以在这里需要使用转义。\n分枝条件 # 不幸的是，刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题，我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用|把不同的规则分隔开。听不明白？没关系，看例子：\n0\\d{2}-\\d{8}|0\\d{3}-\\d{7}这个表达式能匹配两种以连字号分隔的电话号码：一种是三位区号，8位本地号(如010-12345678)，一种是4位区号，7位本地号(0376-2233445)。\n\\(0\\d{2}\\)\n\\[- \\]?\\d{8}|0\\d{2}\n\\[- \\]?\\d{8}这个表达式匹配3位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。\n\\d{5}-\\d{4}|\\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字，或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题：使用分枝条件时，要注意各个条件的顺序。如果你把它改成\\d{5}|\\d{5}-\\d{4}的话，那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。\n分组 # 我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定子表达式(也叫做分组)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。\n(\\d{1,3}\\.){3}\\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式，请按下列顺序分析它：\\d{1,3}匹配1到3位的数字，(\\d{1,3}\\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次，最后再加上一个一到三位的数字(\\d{1,3})。\n不幸的是，它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的IP地址：((2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?)\\.){3}(2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?)。\n理解这个表达式的关键是理解2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?，这里我就不细说了，你自己应该能分析得出来它的意义。\nIP地址中每个数字都不能大于255. 经常有人问我, 01.02.03.04 这样前面带有0的数字, 是不是正确的IP地址呢? 答案是: 是的, IP 地址里的数字可以包含有前导 0 (leading zeroes).\n反义 # 有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到反义：\n代码/语法 说明 \\W 匹配任意不是字母，数字，下划线，汉字的字符 \\S 匹配任意不是空白符的字符 \\D 匹配任意非数字的字符 \\B 匹配不是单词开头或结束的位置 \\[^x\\] 匹配除了x以外的任意字符 \\[^aeiou\\] 匹配除了aeiou这几个字母以外的任意字符 例子：\\S+匹配不包含空白符的字符串。\n\u0026lt;a\n\\[^\u003e\\]+\u0026gt;匹配用尖括号括起来的以a开头的字符串。\n以下是将你提供的内容修改成适合 Markdown 格式的笔记版本：\n后向引用 # 使用小括号指定一个子表达式后，匹配这个子表达式的文本（也就是此分组捕获的内容）可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个组号，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为 1，第二个为 2，以此类推。\n实际上，组号分配过程要从左向右扫描两遍：第一遍只给未命名组分配，第二遍只给命名组分配——因此所有命名组的组号都大于未命名的组号。你可以使用 (?:exp) 这样的语法来剥夺一个分组对组号分配的参与权。\n后向引用示例 # 后向引用用于重复搜索前面某个分组匹配的文本。例如，\\\\1 代表分组 1 匹配的文本。以下正则表达式可以用来匹配重复的单词：\n\\\\b(\\\\w+)\\\\b\\\\s+\\\\1\\\\b 这个表达式首先匹配一个单词，然后是 1 个或几个空白符，最后是分组 1 中捕获的内容（也就是前面匹配的那个单词）。\n你也可以自己指定子表达式的组名。要指定一个子表达式的组名，请使用这样的语法：\n(?\u0026lt;Word\u0026gt;\\\\w+) 然后可以使用 \\\\k\u0026lt;Word\u0026gt; 来反向引用这个分组捕获的内容，所以上一个例子可以改写为：\n\\\\b(?\u0026lt;Word\u0026gt;\\\\w+)\\\\b\\\\s+\\\\k\u0026lt;Word\u0026gt;\\\\b 常用分组语法 # 分类 代码/语法 说明 捕获 (exp) 匹配 exp 并捕获文本到自动命名的组里 捕获命名组 (?\u0026lt;name\u0026gt;exp) 匹配 exp 并捕获文本到名称为 name 的组里 非捕获分组 (?:exp) 匹配 exp，不捕获匹配的文本，也不给此分组分配组号 零宽断言 (?=exp) 匹配 exp 前面的位置 零宽回顾 (?\u0026lt;=exp) 匹配 exp 后面的位置 负向零宽断言 (?!exp) 匹配后面跟的不是 exp 的位置 负向零宽回顾 (?\u0026lt;!exp) 匹配前面不是 exp 的位置 注释 (?#comment) 这类分组不会影响正则表达式的处理，用于注释 零宽断言 # 零宽断言用于查找在某些内容（但并不包括这些内容）之前或之后的东西。它们像 \\\\b, ^, $ 那样用于指定一个位置，这个位置应该满足一定的条件（即断言），因此它们也被称为零宽断言。\n正向先行断言 # (?=exp) 也叫零宽度正预测先行断言，它断言当前位置后面能匹配 exp。\n例如：\n\\\\b\\\\w+(?=ing\\\\b) 它会匹配以 ing 结尾的单词的前部分。\n正回顾后发断言 # (?\u0026lt;=exp) 也叫零宽度正回顾后发断言，它断言当前位置前面能匹配 exp。\n例如：\n(?\u0026lt;=\\\\bre)\\\\w+\\\\b 它会匹配以 re 开头的单词的后半部分。\n负向零宽断言 # 如果你想要确保某个字符没有出现，可以使用负向零宽断言。例如，匹配包含字母 q 且后面不是字母 u 的单词：\n\\\\b\\\\w*q[^u]\\\\w*\\\\b 如果 q 是单词的最后一个字符，可以使用负向先行断言解决问题：\n\\\\b\\\\w*q(?!u)\\\\w*\\\\b 示例 # \\\\d{3}(?!\\\\d) 匹配三位数字，且后面不能是数字。 (?\u0026lt;=\u0026lt;(\\\\w+)\u0026gt;).*?(?=\u0026lt;\\\\/\\\\1\u0026gt;) 匹配 HTML 标签内的内容。 贪婪与懒惰 # 贪婪匹配表示尽可能多地匹配字符，而懒惰匹配表示尽可能少地匹配字符。\n贪婪匹配 # 例如：\na.*b 它会匹配以 a 开头，以 b 结尾的最长字符串。\n懒惰匹配 # 懒惰匹配通过在量词后面加上问号 ? 来实现：\na.*?b 它会匹配以 a 开头，以 b 结尾的最短字符串。\n处理选项 # 在正则表达式中，你可以使用一些选项来改变处理方式，例如忽略大小写、处理多行等。\n名称 说明 IgnoreCase 匹配时不区分大小写 Multiline 使 ^ 和 $ 匹配任意一行的行首和行尾 Singleline 使 . 匹配所有字符（包括换行符） IgnorePatternWhitespace 忽略表达式中的空白符并启用注释 ExplicitCapture 仅捕获显式命名的组 平衡组/递归匹配 # 平衡组语法用于匹配嵌套的层次结构，如括号之间的内容。以下是一个使用平衡组的正则表达式示例：\n\u0026lt; # 最外层的左括号 [^\u0026lt;\u0026gt;]* # 非括号的内容 ( ( (?\u0026#39;Open\u0026#39;\u0026lt;) # 左括号，压入 \u0026#34;Open\u0026#34; [^\u0026lt;\u0026gt;]* # 左括号后面的内容 )+ ( (?\u0026#39;-Open\u0026#39;\u0026gt;) # 右括号，弹出 \u0026#34;Open\u0026#34; [^\u0026lt;\u0026gt;]* # 右括号后面的内容 )+ )* (?(Open)(?!)) # 检查是否存在未弹出的 \u0026#34;Open\u0026#34; \u0026gt; # 最外层的右括号 这个示例可以匹配嵌套的 \u0026lt;div\u0026gt; 标签。\n其他未提到的内容 # 代码/语法 说明 \\\\a 报警字符（电脑发出嘀声） \\\\b 单词边界（在字符类里是退格符） \\\\t 制表符（Tab） \\\\r 回车符 \\\\n 换行符 \\\\xnn 十六进制表示的 ASCII 字符 \\\\unnnn Unicode 字符 \\\\A 字符串开头（类似 ^） \\\\Z 字符串结尾或行尾（不受多行选项影响） ","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/regex/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F30%E5%88%86%E9%92%9F%E5%85%A5%E9%97%A8/","section":"笔记","summary":"版本：v2.4.1 (2019-11-15) 作者：deerchao 转载请注明来源\n目录 # 本文目标 如何使用本教程 正则表达式到底是什么东西？ 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 # 30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。\n","title":"正则表达式30分钟入门","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"入门教程","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%8C%B9%E9%85%8D%E6%A8%A1%E5%BC%8F/","section":"Tags","summary":"","title":"匹配模式","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%88%86%E7%BB%84/","section":"Tags","summary":"","title":"分组","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/pip/","section":"Tags","summary":"","title":"Pip","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/python/","section":"Categories","summary":"","title":"Python","type":"categories"},{"content":" 1. 基础结构 # setup.py 是 Python 打包的核心配置文件，需使用 setuptools 库定义包的元数据和构建规则。\n基本模板 # from setuptools import setup, find_packages setup( name=\u0026#34;your-package-name\u0026#34;, # 包名（PyPI唯一标识） version=\u0026#34;0.1.0\u0026#34;, # 版本号 author=\u0026#34;Your Name\u0026#34;, author_email=\u0026#34;your@email.com\u0026#34;, description=\u0026#34;Short description\u0026#34;, long_description=open(\u0026#34;README.md\u0026#34;).read(), # 长描述（README内容） long_description_content_type=\u0026#34;text/markdown\u0026#34;, # README格式 url=\u0026#34;https://github.com/yourusername/your-package\u0026#34;, # 项目地址 packages=find_packages(), # 自动发现包目录 install_requires=[], # 依赖库列表 entry_points={}, # 命令行工具入口 classifiers=[], # PyPI分类标签 python_requires=\u0026#34;\u0026gt;=3.6\u0026#34;, # Python版本要求 ) 2. 关键参数详解 # (1) name # 作用：包在 PyPI 上的唯一标识符。 规则： 必须全小写，可用连字符（-），如 my-package。 需在 PyPI 上未被占用（上传前检查）。 (2) version # 作用：包的版本号，每次更新必须递增。 格式：推荐语义化版本（如 1.0.0, 0.2.5）。 (3) packages # 作用：指定包含的 Python 包目录。 自动发现： packages=find_packages(), # 自动搜索所有含 __init__.py 的目录 手动指定： packages=[\u0026#34;my_package\u0026#34;, \u0026#34;my_package.submodule\u0026#34;], (4) install_requires # 作用：定义包的依赖库及版本约束。 示例： install_requires=[ \u0026#34;requests\u0026gt;=2.25.1\u0026#34;, # 最低版本 \u0026#34;numpy\u0026lt;1.22,\u0026gt;=1.20\u0026#34;, # 版本范围 \u0026#34;pandas\u0026#34;, # 不限制版本 ] (5) entry_points # 作用：定义命令行工具入口，生成可执行命令。 示例： entry_points={ \u0026#34;console_scripts\u0026#34;: [ \u0026#34;my-command=my_package.module:main\u0026#34;, # 格式：命令名=包.模块:函数 ], } 用户安装后可直接运行 my-command。 (6) package_data # 作用：包含包内的非代码文件（如数据、配置文件）。 示例： package_data={ \u0026#34;my_package\u0026#34;: [\u0026#34;data/*.csv\u0026#34;, \u0026#34;templates/*.html\u0026#34;], }, (7) include_package_data # 作用：自动包含 MANIFEST.in 中指定的文件。 用法： include_package_data=True, # 需配合 MANIFEST.in 文件 (8) classifiers # 作用：为 PyPI 提供包的分类标签（便于搜索）。 常用选项： classifiers=[ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, \u0026#34;Topic :: Software Development :: Libraries\u0026#34;, ] 完整列表见：PyPI Classifiers 3. 完整示例 # 假设项目结构如下：\nmy_project/ ├── my_package/ │ ├── __init__.py │ ├── main.py │ └── data/ │ └── config.json ├── README.md ├── LICENSE └── setup.py 对应的 setup.py：\nfrom setuptools import setup, find_packages setup( name=\u0026#34;my-awesome-tool\u0026#34;, version=\u0026#34;0.1.0\u0026#34;, author=\u0026#34;Jane Doe\u0026#34;, author_email=\u0026#34;jane@example.com\u0026#34;, description=\u0026#34;A tool to do awesome things.\u0026#34;, long_description=open(\u0026#34;README.md\u0026#34;).read(), long_description_content_type=\u0026#34;text/markdown\u0026#34;, url=\u0026#34;https://github.com/janedoe/my-awesome-tool\u0026#34;, packages=find_packages(), install_requires=[ \u0026#34;requests\u0026gt;=2.25.1\u0026#34;, \u0026#34;click\u0026gt;=8.0.0\u0026#34;, # 命令行库 ], entry_points={ \u0026#34;console_scripts\u0026#34;: [ \u0026#34;awesome-tool=my_package.main:cli\u0026#34;, # 假设 main.py 有 cli() 函数 ], }, package_data={ \u0026#34;my_package\u0026#34;: [\u0026#34;data/*.json\u0026#34;], # 包含 data 目录下的 JSON 文件 }, classifiers=[ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, ], python_requires=\u0026#34;\u0026gt;=3.7\u0026#34;, ) 4. 包含额外文件：MANIFEST.in # 如果需要包含包外的文件（如文档、测试数据），需创建 MANIFEST.in 文件：\ninclude LICENSE README.md recursive-include docs * recursive-include tests * 5. 验证与打包 # (1) 生成分发包 # pip install setuptools wheel # 确保工具已安装 python setup.py sdist bdist_wheel # 生成源码包和二进制包 生成文件在 dist/ 目录。 (2) 本地测试安装 # pip install . # 在当前目录安装包 awesome-tool # 测试命令行工具 6. 关键注意事项 # 包名唯一性：在 PyPI 搜索确认包名未被占用。 版本号递增：每次更新代码后需修改 version。 依赖管理：精确指定依赖版本，避免未来更新导致兼容性问题。 许可证：必须包含 LICENSE 文件，并在 classifiers 中声明。 入口函数：确保 entry_points 指向的函数已正确定义。 通过以上步骤，你可以编写一个完整的 setup.py 文件，为发布到 PyPI 做好准备。\n7. 提一嘴之生成可执行文件 # 使用工具如 PyInstaller 或 cx_Freeze 将Python脚本转换为独立的可执行文件：\n1. 使用 PyInstaller # 安装： pip install pyinstaller 生成可执行文件： pyinstaller --onefile your_script.py --onefile 生成单个可执行文件。\n输出在 dist/ 目录中。\n跨平台：需在目标操作系统下分别打包。\n2. 注意事项 # 依赖处理：PyInstaller会自动分析脚本依赖，但某些复杂情况（如动态导入）可能需要手动配置（使用 .spec 文件）。\n文件大小：因包含Python解释器和依赖库，生成文件较大。\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/tools/python%E4%BD%BF%E7%94%A8pip%E4%B9%8Bsetup-py%E7%BC%96%E5%86%99/","section":"笔记","summary":"1. 基础结构 # setup.py 是 Python 打包的核心配置文件，需使用 setuptools 库定义包的元数据和构建规则。\n基本模板 # from setuptools import setup, find_packages setup( name=\"your-package-name\", # 包名（PyPI唯一标识） version=\"0.1.0\", # 版本号 author=\"Your Name\", author_email=\"your@email.com\", description=\"Short description\", long_description=open(\"README.md\").read(), # 长描述（README内容） long_description_content_type=\"text/markdown\", # README格式 url=\"https://github.com/yourusername/your-package\", # 项目地址 packages=find_packages(), # 自动发现包目录 install_requires=[], # 依赖库列表 entry_points={}, # 命令行工具入口 classifiers=[], # PyPI分类标签 python_requires=\"\u003e=3.6\", # Python版本要求 ) 2. 关键参数详解 # (1) name # 作用：包在 PyPI 上的唯一标识符。 规则： 必须全小写，可用连字符（-），如 my-package。 需在 PyPI 上未被占用（上传前检查）。 (2) version # 作用：包的版本号，每次更新必须递增。 格式：推荐语义化版本（如 1.0.0, 0.2.5）。 (3) packages # 作用：指定包含的 Python 包目录。 自动发现： packages=find_packages(), # 自动搜索所有含 __init__.py 的目录 手动指定： packages=[\"my_package\", \"my_package.submodule\"], (4) install_requires # 作用：定义包的依赖库及版本约束。 示例： install_requires=[ \"requests\u003e=2.25.1\", # 最低版本 \"numpy\u003c1.22,\u003e=1.20\", # 版本范围 \"pandas\", # 不限制版本 ] (5) entry_points # 作用：定义命令行工具入口，生成可执行命令。 示例： entry_points={ \"console_scripts\": [ \"my-command=my_package.module:main\", # 格式：命令名=包.模块:函数 ], } 用户安装后可直接运行 my-command。 (6) package_data # 作用：包含包内的非代码文件（如数据、配置文件）。 示例： package_data={ \"my_package\": [\"data/*.csv\", \"templates/*.html\"], }, (7) include_package_data # 作用：自动包含 MANIFEST.in 中指定的文件。 用法： include_package_data=True, # 需配合 MANIFEST.in 文件 (8) classifiers # 作用：为 PyPI 提供包的分类标签（便于搜索）。 常用选项： classifiers=[ \"Programming Language :: Python :: 3\", \"License :: OSI Approved :: MIT License\", \"Operating System :: OS Independent\", \"Topic :: Software Development :: Libraries\", ] 完整列表见：PyPI Classifiers 3. 完整示例 # 假设项目结构如下：\n","title":"python使用pip之setup.py编写","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/setup.py/","section":"Tags","summary":"","title":"Setup.py","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/setuptools/","section":"Tags","summary":"","title":"Setuptools","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/%E7%BC%96%E7%A8%8B/","section":"Categories","summary":"","title":"编程","type":"categories"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%8C%85%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"包管理","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/android/","section":"Tags","summary":"","title":"Android","type":"tags"},{"content":" 3. Add a provider that offers secure DNS # This is where it can get a bit tricky. You need to have the address of a provider that offers Private DNS. Here\u0026rsquo;s my provider of choice: 1dot1dot1dot1.cloudflare-dns.com Some other possible hostnames you can use: # Google DNS: dns.google\nQuad9: dns.quad9.net\nCleanbrowsing DNS: security-filter-dns.cleanbrowsing.org\nOpen DNS: 208.67.222.222\nNextDNS: 45.90.28.0\nComodo Secure 8.26.56.26\nOpenNIC: 192.95.54.3\nNote: Although the above free DNS services are all worth trying, I recommend going with Cloudflare (1dot1dot1dot1.cloudflare-dns.com). I find it the fastest and the most secure of the bunch. On top of the speed, Cloudflare adds DNS filtering into the mix, which can help prevent email from being sent from malicious IP addresses.\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/tools/android-privatedns/","section":"笔记","summary":"3. Add a provider that offers secure DNS # This is where it can get a bit tricky. You need to have the address of a provider that offers Private DNS. Here’s my provider of choice: 1dot1dot1dot1.cloudflare-dns.com Some other possible hostnames you can use: # Google DNS: dns.google\n","title":"Android-PrivateDNS","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/%E6%9D%82/","section":"Categories","summary":"","title":"杂","type":"categories"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/","section":"Categories","summary":"","title":"计算机","type":"categories"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E9%9A%90%E7%A7%81/","section":"Tags","summary":"","title":"隐私","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E7%A7%BB%E5%8A%A8%E8%AE%BE%E5%A4%87/","section":"Tags","summary":"","title":"移动设备","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C/","section":"Tags","summary":"","title":"网络","type":"tags"},{"content":"You can find my dotfiles here.\nOpen a terminal window # Open a terminal window on your macOs or linux machine.\nI’m using Alacritty on macOs and I’m using the zsh shell.\nInstall Homebrew # Run the following command:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; If necessary, when prompted, enter your password here and press enter. If you haven’t installed the XCode Command Line Tools, when prompted, press enter and homebrew will install this as well.\nAdd To Path (Only Apple Silicon Macbooks) # After installing, add it to the path. This step shouldn’t be necessary on Intel macs.\nRun the following two commands to do so:\necho \u0026#39;eval \u0026#34;$(/opt/homebrew/bin/brew shellenv)\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zprofile eval \u0026#34;$(/opt/homebrew/bin/brew shellenv)\u0026#34; #1: fzf (command line fuzzy finder) # fzf is an amazing fuzzy finder for the command line.\nInstall fzf with homebrew:\nThis will vary slightly depending on the shell that you’re using, I’m using zsh.\nOpen ~/.zshrc file with your favorite editor. I personally use Neovim.\nWith TextEdit:\nWith VSCode (you have to have it installed):\nWith Neovim (what I use):\nTo the bottom of the file add:\n# ---- FZF ----- # Set up fzf key bindings and fuzzy completion eval \u0026#34;$(fzf --zsh)\u0026#34; After saving, in your terminal reload the zsh configuration:\nNow you can use fzf!\nExamples of what you can do with it:\nExample Description CTRL-t Look for files and directories CTRL-r Look through command history Enter Select the item Ctrl-j or Ctrl-n or Down arrow Go down one result Ctrl-k or Ctrl-p or Up arrow Go up one result Tab Mark a result Shift-Tab Unmark a result cd **Tab Open up fzf to find directory export **Tab Look for env variable to export unset **Tab Look for env variable to unset unalias **Tab Look for alias to unalias ssh **Tab Look for recently visited host names kill -9 **Tab Look for process name to kill to get pid any command (like nvim or code) + **Tab Look for files \u0026amp; directories to complete command Use fd with fzf # fzf by default uses the find command to look for files and directories.\nLet’s replace that with fd for better functionality, like ignoring files ignored by git (with .gitignore).\nInstall fd:\nOpen ~/.zshrc with preferred text editor.\nAdd to the bottom of this file:\n# -- Use fd instead of fzf -- export FZF_DEFAULT_COMMAND=\u0026#34;fd --hidden --strip-cwd-prefix --exclude .git\u0026#34; export FZF_CTRL_T_COMMAND=\u0026#34;$FZF_DEFAULT_COMMAND\u0026#34; export FZF_ALT_C_COMMAND=\u0026#34;fd --type=d --hidden --strip-cwd-prefix --exclude .git\u0026#34; # Use fd (https://github.com/sharkdp/fd) for listing path candidates. # - The first argument to the function ($1) is the base path to start traversal # - See the source code (completion.{bash,zsh}) for the details. _fzf_compgen_path() { fd --hidden --exclude .git . \u0026#34;$1\u0026#34; } # Use fd to generate the list for directory completion _fzf_compgen_dir() { fd --type=d --hidden --exclude .git . \u0026#34;$1\u0026#34; } After saving, in your terminal do:\nSetup fzf-git # fzf-git is a really nice script to look for git related things (commits, hashes, files and more) with fzf.\nNavigate to your home directory:\nClone the repository:\ngit clone https://github.com/junegunn/fzf-git.sh.git Open ~/.zshrc with preferred text editor and add to the bottom:\nsource ~/fzf-git.sh/fzf-git.sh Save and in terminal run:\nNow you can use the following:\nKeybind Description CTRL-GF Look for git files with fzf CTRL-GB Look for git branches with fzf CTRL-GT Look for git tags with fzf CTRL-GR Look for git remotes with fzf CTRL-GH Look for git commit hashes with fzf CTRL-GS Look for git stashes with fzf CTRL-GL Look for git reflogs with fzf CTRL-GW Look for git worktrees with fzf CTRL-GE Look for git for-each-ref with fzf Hold down CTRL and then press GF or one of the others consecutively and quickly.\nSetup a custom theme for fzf # You can also setup a custom theme for fzf.\nTo use mine you can add this to ~/.zshrc:\n# --- setup fzf theme --- fg=\u0026#34;#CBE0F0\u0026#34; bg=\u0026#34;#011628\u0026#34; bg_highlight=\u0026#34;#143652\u0026#34; purple=\u0026#34;#B388FF\u0026#34; blue=\u0026#34;#06BCE4\u0026#34; cyan=\u0026#34;#2CF9ED\u0026#34; export FZF_DEFAULT_OPTS=\u0026#34;--color=fg:${fg},bg:${bg},hl:${purple},fg+:${fg},bg+:${bg_highlight},hl+:${purple},info:${blue},prompt:${cyan},pointer:${cyan},marker:${cyan},spinner:${cyan},header:${cyan}\u0026#34; If you want to make your own check out the theme generator\n#2: bat (better cat) # bat is a really nice alternative to cat to display file contents in the terminal with syntax highlighting.\nInstall with homebrew:\nNow you can start using it!\nExample:\nThe example above would print out the contents of a file called filename.txt.\nTo see examples of all of the themes you can use:\nbat --list-themes | fzf --preview=\u0026#34;bat --theme={} --color=always /path/to/file\u0026#34; Make sure to replace /path/to/file with the path to an actual file.\nInstall a custom theme # If you find a theme on github for example, you can follow these steps.\nThe first time you do this create the following directory:\nmkdir -p \u0026#34;$(bat --config-dir)/themes\u0026#34; Then move into it:\ncd \u0026#34;$(bat --config-dir)/themes\u0026#34; Then you can download the file for the theme you want to use into this directory.\nThe file should use the .tmTheme extension.\nFor example:\ncurl -O https://raw.githubusercontent.com/folke/tokyonight.nvim/main/extras/sublime/tokyonight_night.tmTheme This will install the night version of the tokyonight theme.\nThen to use it run:\nAnd then add the following to your ~/.zshrc:\n# ----- Bat (better cat) ----- export BAT_THEME=tokyonight_night Replace tokyonight_night with the name of the theme you would like to use.\nThen save this file and run:\n#3: delta (better git diff) # delta is a great way to produce better git diffs using the syntax highlighting functionality of bat.\nInstall it:\nThen open your ~/.gitconfig file.\nWith TextEdit:\nWith VSCode (you have to have it installed):\nWith Neovim (what I use):\nAdd the following to it:\n[core] pager = delta [interactive] diffFilter = delta --color-only [delta] navigate = true # use n and N to move between diff sections side-by-side = true # delta detects terminal colors automatically; set one of these to disable auto-detection # dark = true # light = true [merge] conflictstyle = diff3 [diff] colorMoved = default You can remove side-by-side=true if you don’t want side by side diffs.\nAfter saving this file your git diffs should look a lot nicer.\nSetup fzf previews # Now that you have eza and bat setup you can setup some really nice previews for fzf.\nIn your ~/.zshrc, where you’re configuring fzf, add the following:\nexport FZF_CTRL_T_OPTS=\u0026#34;--preview \u0026#39;bat -n --color=always --line-range :500 {}\u0026#39;\u0026#34; export FZF_ALT_C_OPTS=\u0026#34;--preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39;\u0026#34; # Advanced customization of fzf options via _fzf_comprun function # - The first argument to the function is the name of the command. # - You should make sure to pass the rest of the arguments to fzf. _fzf_comprun() { local command=$1 shift case \u0026#34;$command\u0026#34; in cd) fzf --preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39; \u0026#34;$@\u0026#34; ;; export|unset) fzf --preview \u0026#34;eval \u0026#39;echo $\u0026#39;{}\u0026#34; \u0026#34;$@\u0026#34; ;; ssh) fzf --preview \u0026#39;dig {}\u0026#39; \u0026#34;$@\u0026#34; ;; *) fzf --preview \u0026#34;bat -n --color=always --line-range :500 {}\u0026#34; \u0026#34;$@\u0026#34; ;; esac } This will setup file previews with bat as well as directory previews with eza and some other previews as well.\nThere is one slight issue with this code though. When we are looking for files and directories with Ctrl-T or doing something like nvim **Tab, we are using bat to generate the preview but this won’t work for directories and we’ll get an error.\nWe can fix this by using an if statement that checks whether the path to what we should preview is a directory or not and use eza instead for the preview in this case.\nTo do this you can replace the previous code with something like the following:\nshow_file_or_dir_preview=\u0026#34;if [ -d {} ]; then eza --tree --color=always {} | head -200; else bat -n --color=always --line-range :500 {}; fi\u0026#34; export FZF_CTRL_T_OPTS=\u0026#34;--preview \u0026#39;$show_file_or_dir_preview\u0026#39;\u0026#34; export FZF_ALT_C_OPTS=\u0026#34;--preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39;\u0026#34; # Advanced customization of fzf options via _fzf_comprun function # - The first argument to the function is the name of the command. # - You should make sure to pass the rest of the arguments to fzf. _fzf_comprun() { local command=$1 shift case \u0026#34;$command\u0026#34; in cd) fzf --preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39; \u0026#34;$@\u0026#34; ;; export|unset) fzf --preview \u0026#34;eval \u0026#39;echo ${}\u0026#39;\u0026#34; \u0026#34;$@\u0026#34; ;; ssh) fzf --preview \u0026#39;dig {}\u0026#39; \u0026#34;$@\u0026#34; ;; *) fzf --preview \u0026#34;$show_file_or_dir_preview\u0026#34; \u0026#34;$@\u0026#34; ;; esac } #4: eza (better ls) # eza is a better version of ls with lots of different options.\nInstall it:\nNow you can start using it!\nYou can create an alias for it in ~/.zshrc like so:\n# ---- Eza (better ls) ----- alias ls=\u0026#34;eza --color=always --long --git --no-filesize --icons=always --no-time --no-user --no-permissions\u0026#34; #5: tldr (user-friendly man pages) # tldr is a huge collection of community maintained help pages with helpful summaries for cli tools. You can use this as an alternative to man pages.\nInstall the rust client with homebrew:\nNow you can use it just like man to learn more about a tool.\nFor example to learn more about eza:\n#6: thefuck # thefuck is a really nice tool to auto correct mistyped commands.\nInstall it like so:\nThen open your ~/.zshrc file and add:\n# thefuck alias eval $(thefuck --alias) This will setup the alias which is fuck.\nYou can also add your own custom alias:\n# thefuck alias eval $(thefuck --alias) eval $(thefuck --alias fk) Save your zshrc file and run:\nNow if mistype a command you can run fuck afterwards.\nIf there are is more than one result you can use your up and down arrow keys and then enter to select the one you want.\n#7: zoxide (better cd) # zoxide is an amazing alternative to cd.\nIt will remember the directories you’ve visited in the past and make it really easy to navigate back to them by just typing out a portion of the name of the directory you want to visit.\nInstall it like so:\nThen add the following to ~/.zshrc:\n# ---- Zoxide (better cd) ---- eval \u0026#34;$(zoxide init zsh)\u0026#34; If you want to keep using cd then create an alias in ~/.zshrc:\n# ---- Zoxide (better cd) ---- eval \u0026#34;$(zoxide init zsh)\u0026#34; alias cd=\u0026#34;z\u0026#34; Save and then run:\nNow you can use z as a much smarter replacement to cd.\nIf you want to trigger fzf when you’re moving to a directory with z you can do space + tab.\nThat’s it! 🚀 # ","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/tools/7-amazing-cli-tools/","section":"笔记","summary":"You can find my dotfiles here.\nOpen a terminal window # Open a terminal window on your macOs or linux machine.\nI’m using Alacritty on macOs and I’m using the zsh shell.\nInstall Homebrew # Run the following command:\n","title":"7-Amazing-CLI-Tools","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/cli%E5%B7%A5%E5%85%B7/","section":"Tags","summary":"","title":"CLI工具","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/categories/cli%E7%9B%B8%E5%85%B3/","section":"Categories","summary":"","title":"CLI相关","type":"categories"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/homebrew/","section":"Tags","summary":"","title":"Homebrew","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E7%BB%88%E7%AB%AF/","section":"Tags","summary":"","title":"终端","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E8%A1%8C/","section":"Tags","summary":"","title":"命令行","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/gnome/","section":"Tags","summary":"","title":"GNOME","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/kde/","section":"Tags","summary":"","title":"KDE","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/xorg/","section":"Tags","summary":"","title":"Xorg","type":"tags"},{"content":"Wayland、Xorg、GNOME、KDE 这些术语在 Linux 图形系统和桌面环境中扮演着不同的角色。它们分别负责不同的功能，彼此之间相互协作，提供完整的图形用户体验。下面我将解释它们的关系以及各自的定义：\n笔记 省流:\n用户与桌面环境(KDE,GNOME)交互 桌面环境和显示服务器协议(wayland,xrog)交互 显示服务器和内核交互 内核与硬件交互 1. Xorg 和 Wayland: 显示服务器协议 # Xorg 和 Wayland 是两种不同的 显示服务器协议，它们负责在 Linux 系统上管理图形显示、处理输入设备（如鼠标、键盘）的交互，协调窗口的绘制。\nXorg（X Window System 或 X 11）:\n概念: Xorg 是最早的、也是最广泛使用的显示服务器协议，它在 Linux 和类 Unix 系统上运行，已经有几十年的历史。 功能: Xorg 提供了客户端-服务器架构，应用程序通过 X 协议与显示服务器通信，显示服务器负责处理输入设备、管理窗口绘制、以及将图形内容显示在屏幕上。 优缺点: 优点: 经过多年开发，兼容性好，支持广泛的硬件和软件。 缺点: 架构较为老旧，性能问题和安全性不足（如通过网络远程访问图形界面时可能有性能损失）。 使用场景: Xorg 仍然是大多数 Linux 系统的默认显示服务器，特别是在不支持 Wayland 的情况下。 Wayland:\n概念: Wayland 是一个较新的显示服务器协议，旨在替代 Xorg，它简化了架构，提供更现代化的性能和安全性。 功能: Wayland 将显示管理和窗口管理的许多任务移交给应用程序，由窗口管理器（即 Wayland Compositor）来完成，减少了传统 Xorg 的复杂性和延迟问题。 优缺点: 优点: 架构简洁，性能更好，延迟更低，尤其是针对现代硬件的支持更完善，提供更好的图形效果和安全性。 缺点: 虽然已经发展了多年，但在某些方面的兼容性（如某些老应用程序或图形驱动程序）可能不如 Xorg 完善。 使用场景: GNOME 和 KDE 等现代桌面环境越来越多地采用 Wayland，尤其在高性能、高安全性要求的场景下。 2. GNOME 和 KDE: 桌面环境 # GNOME 和 KDE 是 Linux 上的 桌面环境（Desktop Environment），它们提供了图形用户界面（GUI）以及与用户交互的各种工具和应用程序。\nGNOME:\n概念: GNOME 是一个广泛使用的桌面环境，以简洁、高效、现代的设计为特点。 功能: GNOME 提供了窗口管理器、文件管理器、应用程序启动器、系统设置等图形界面工具，帮助用户管理系统和应用程序。它默认使用 Wayland 作为显示协议（但也兼容 Xorg），并且在一些 Linux 发行版（如 Fedora、Ubuntu GNOME）中默认采用。 特性: 用户界面简洁，强调简化操作，减少复杂的设置选项。 默认使用 Wayland，提供现代图形处理和显示性能。 窗口管理器: GNOME 使用 Mutter 作为窗口管理器，它同时也是 Wayland 的合成器。 KDE Plasma:\n概念: KDE Plasma 是另一个流行的桌面环境，以高度可定制化和丰富的功能著称。它适合那些希望更多自定义选项和控制的用户。 功能: KDE Plasma 提供了丰富的桌面定制工具和应用程序，用户可以根据需要调整外观、行为和设置。KDE Plasma 也支持 Wayland 和 Xorg。 特性: 提供丰富的配置选项，用户可以对桌面环境的每个部分进行自定义。 支持 Wayland，但在某些情况下仍使用 Xorg，尤其是在 Wayland 兼容性不足的情况下。 窗口管理器: KDE Plasma 使用 KWin 作为窗口管理器，它同时支持 Xorg 和 Wayland 协议。 3. 关系总结 # 显示服务器协议（Xorg 和 Wayland）: 负责处理系统的图形输出和输入设备。它们是桌面环境的底层技术，直接与操作系统的内核和硬件交互。\nXorg 是老式的、功能成熟的显示服务器协议，兼容性广泛。 Wayland 是现代的显示服务器协议，提供更好的性能和安全性，正逐步取代 Xorg。 桌面环境（GNOME 和 KDE）: 是在显示服务器之上运行的图形用户界面，它们提供了一整套交互工具和应用程序，让用户可以方便地操作和管理系统。\nGNOME 提倡简洁、现代的界面设计，默认支持 Wayland。 KDE 强调可定制性和丰富功能，同时支持 Xorg 和 Wayland。 4. 工作方式举例： # 当你启动一个 Linux 系统时，显示服务器协议（如 Xorg 或 Wayland）首先负责启动和管理显示设备。 然后，桌面环境（如 GNOME 或 KDE Plasma）会通过显示服务器协议与操作系统交互，提供你所看到的图形界面、窗口管理、任务栏等。 桌面环境的具体表现、功能和风格由其内部的组件（如 Mutter 和 KWin）决定，而这些组件则通过 Xorg 或 Wayland 来与硬件互动。 你目前使用的 GNOME 桌面环境 是基于 Wayland 的，但也可以切换回 Xorg。如果你有其他桌面环境（如 KDE Plasma），可以根据自己的需求选择不同的图形协议和桌面环境。\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/tools/%E5%9B%BE%E5%BD%A2%E5%8C%96%E7%9B%B8%E5%85%B3%E7%A7%91%E6%99%AE/","section":"笔记","summary":"Wayland、Xorg、GNOME、KDE 这些术语在 Linux 图形系统和桌面环境中扮演着不同的角色。它们分别负责不同的功能，彼此之间相互协作，提供完整的图形用户体验。下面我将解释它们的关系以及各自的定义：\n","title":"图形化相关科普","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/inode/","section":"Tags","summary":"","title":"Inode","type":"tags"},{"content":" 文件描述符 # 文件在 Linux 系统中通过文件描述符与内核进行交互。打开文件时，Linux 内核会执行以下步骤：\n系统调用：当用户程序调用 open() 打开文件时，它会触发一个系统调用，通过内核与硬件进行交互。 文件系统操作：内核会查找文件路径对应的 inode 结构，inode 存储文件的元数据（如文件大小、权限、数据块位置等）。 缓存与内存映射：内核会将文件数据从磁盘加载到内存中的缓存（page cache）中，以加速后续的文件读取操作。如果文件很大，内核可能会使用内存映射（mmap()）将文件映射到进程的虚拟地址空间。 在 Linux 中，文件共享主要指多个进程或多个线程对同一文件的并发访问。文件共享不仅仅包括并发访问文件内容，还涉及对文件描述符和文件锁定的管理。\n3.1 文件描述符和共享 # 每个进程在打开文件时，都会获取一个文件描述符（file descriptor）。文件描述符是内核中的一个索引，它指向一个 file 结构，这个结构包含了文件的状态和偏移量等信息。多个进程如果打开相同的文件，它们会共享文件描述符，指向同一个文件结构。\n内核在处理文件描述符时会采用 引用计数，即多个进程或线程共享文件描述符时，只要有一个进程关闭文件描述符，内核才会真正释放相关资源。\n3.2 文件锁定与并发访问 # 为了避免文件的并发写入产生冲突，Linux 提供了文件锁（flock）机制，进程可以对文件加锁，确保在某一时刻只有一个进程对文件进行写操作。\n文件锁的类型： 共享锁（Shared Lock）：多个进程可以同时获得共享锁，适用于只读操作。 独占锁（Exclusive Lock）：只有一个进程可以获得独占锁，适用于写操作。 通过 flock() 或 fcntl() 系统调用，程序可以请求对文件的共享或独占锁。\n3 .3 内核中的文件共享实现 # 内核通过 struct file 结构来实现文件共享。每当一个进程打开一个文件时，内核会为该文件分配一个 file 结构体，并将该文件的描述符映射到进程的文件表中。如果多个进程打开相同的文件，它们共享这个 file 结构体。进程间共享文件时，内核会确保数据的同步性和一致性，避免数据竞争和冲突。\n内存映射文件（mmap）：如果多个进程需要访问同一个文件的同一部分，Linux 提供了内存映射文件机制（mmap()），这使得进程可以将文件内容映射到进程的地址空间，进程之间可以直接通过共享内存区域访问文件数据。内存映射文件适合高效的文件共享和进程间通信（IPC）。\n读写锁（Read-Write Locks）：内核也提供了一些同步机制，如读写锁，确保对文件的并发访问不会引起数据不一致。\nindoe # 是的，正如你所说，在 Linux 中，几乎所有的资源都被视为文件，这包括普通文件、目录、设备文件、管道、套接字等。这种“万物皆文件”的设计理念使得 Linux 操作系统对文件的管理变得统一且高效。与文件紧密相关的一个重要概念是 inode。\n什么是 inode？ # inode 是 Linux 文件系统中的一个重要数据结构，它存储了文件的元数据（metadata），即与文件内容无关的信息。每个文件（或目录）在文件系统中都有一个唯一的 inode 来描述该文件的属性。inode 并不包含文件的名称或文件的实际数据内容，而是包含了指向数据块的指针、文件的大小、权限、创建时间、修改时间等重要信息。\ninode 的主要内容 # 一个 inode 通常包含以下内容：\n文件类型：例如普通文件、目录、符号链接、设备文件等。 文件权限：文件的读、写、执行权限（例如 755、644 等）。 文件拥有者：文件的所有者 UID 和 GID（用户 ID 和组 ID）。 文件大小：文件的字节数。 时间戳： 创建时间（ctime）：inode 被创建或修改的时间。 修改时间（mtime）：文件内容最后一次修改的时间。 访问时间（atime）：文件最后一次被访问的时间。 数据块指针：指向文件数据的指针，表示该文件存储的数据位于磁盘的哪个位置。这些指针分为直接指针、间接指针等。 硬链接计数：指示指向此 inode 的硬链接数量。 文件与 inode 的关系 # 每个文件（无论是普通文件还是目录文件）都有一个 唯一的 inode，这是文件的核心标识。文件名只是文件系统中某个目录项（directory entry）中的一部分，它与 inode 关联起来。在 Linux 中，文件名和 inode 之间的映射关系是通过 目录项（directory entry）来实现的。\n目录项（如 readdir() 返回的内容）将文件名与其对应的 inode 号关联起来。文件名是目录中的一个条目，而 inode 号则指向存储文件元数据的 inode 结构。 inode 如何工作 # 当你执行以下操作时，操作系统如何利用 inode 来实现文件访问：\n打开文件：当你打开一个文件时，操作系统会先查找该文件的目录项，找到文件名对应的 inode 号。\n读取文件内容：一旦找到 inode，操作系统就知道文件数据的存储位置（通过 inode 中的块指针）。它会使用这些指针从磁盘中加载文件内容到内存中。\n文件写入：当文件内容被修改时，内核会更新 inode 中的时间戳，并可能修改数据块指针。如果文件增长或减少，内核会根据需要更新 inode 的大小字段。\n文件和 inode 的生命周期 # 文件创建：当你创建一个新文件时，内核会为该文件分配一个新的 inode，并在磁盘上为文件分配数据块。文件名会被存储在某个目录中，并与 inode 号关联。\n文件删除：当删除文件时，文件名会从目录中移除，内核会减少 inode 的硬链接计数。当硬链接计数为零时，inode 和数据块会被释放。\n硬链接：硬链接是一个文件名和一个 inode 号的额外映射。硬链接创建后，多个文件名可以指向同一个 inode。这时，删除某个文件名不会立即释放 inode 或数据块，只有当所有硬链接都删除后，inode 才会被释放。\n软链接（符号链接）：符号链接（symlink）是一种特殊类型的文件，它包含指向另一个文件路径的字符串，而不是直接指向 inode。通过符号链接，可以间接访问文件。\ninode 与目录的关系 # 目录也是文件。在 Linux 中，目录实际上是一个特殊的文件，它的内容是目录项，每个目录项包含一个文件名和其对应的 inode 号。因此，目录与文件的关系也是通过 inode 来建立的。\n目录的 inode：目录本身也有一个 inode，它包含了目录的元数据（如权限、拥有者等）。然而，目录的内容并不是文件数据，而是目录项，这些目录项将文件名映射到其对应的 inode。\n目录项与 inode：当你查看一个目录时，内核会通过目录项中的 inode 号来访问每个文件的 inode，进而获得文件的元数据和实际数据。\ninode 的限制与优化 # 文件系统的 inode 数量：每个文件系统在格式化时会指定一个 inode 的数量。这个数量决定了文件系统最多能创建多少个文件。通常情况下，inode 的数量与磁盘的容量相关，但它也受到文件系统设计的影响。\n文件系统优化：为了解决 inode 数量不足的问题，许多现代文件系统（如 ext4、XFS 等）采用了动态 inode 分配机制，也就是说，在文件系统创建后可以根据需要扩展 inode 的数量。\n总结 # inode 是 Linux 文件系统中每个文件（或目录）都有的一个数据结构，存储了文件的元数据（如权限、大小、时间戳等）以及指向文件数据块的指针。 文件名 和 inode 是通过 目录项 关联起来的。文件名只是一个标识符，而 inode 存储了实际的文件信息。 目录文件 本身也是特殊的文件，包含了文件名和 inode 号的映射关系，目录中的每个条目指向一个文件的 inode。 硬链接 允许多个文件名指向同一个 inode，而 符号链接 则是指向另一个文件路径的引用，不直接指向 inode。 通过 inode，Linux 文件系统能够高效地管理文件的存储和访问，提供了文件的元数据和数据的分离，极大地提升了文件管理的灵活性和性能。\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/linux/linux%E6%96%87%E4%BB%B6file/","section":"笔记","summary":"文件描述符 # 文件在 Linux 系统中通过文件描述符与内核进行交互。打开文件时，Linux 内核会执行以下步骤：\n","title":"Linux文件FILE","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E5%85%B1%E4%BA%AB/","section":"Tags","summary":"","title":"文件共享","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/","section":"Tags","summary":"","title":"文件系统","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/","section":"Tags","summary":"","title":"文件描述符","type":"tags"},{"content":"作者: 杰克小麻雀\n原文链接: https://blog.csdn.net/yushuaigee/article/details/107883964\n1、从一个最常见的例子说起 # 在使用Linux的过程中， 我们平时经常看到下面这样的用法：\n1 echo log \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026gt; ：表示将输出结果重定向到哪里，例如：echo “123” \u0026gt; /home/123.txt /dev/null ：表示空设备文件 所以 echo log \u0026gt; /dev/null 表示把日志输出到空文件设备，也就是将打印信息丢弃掉，屏幕上什么也不显示。\n1 ：表示stdout标准输出 2 ：表示stderr标准错误 \u0026amp; ：表示等同于的意思 所以 2\u0026gt;\u0026amp;1 表示2的输出重定向等同于1，也就是标准错误输出重定向到标准输出。因为前面标准输出已经重定向到了空设备文件，所以标准错误输出也重定向到空设备文件。\n这个用法平时很常见，重点是为什么这里是用 2 和 1 ，不是3456什么的呢？这要从 Linux 中的文件描述符说起。\n2、Linux中的文件描述符（file descriptor） # 我们知道在Linux系统中一切皆可以看成是文件，文件又可分为：普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候，我们每操作一次就找一次名字，这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引，这样要操作文件的时候，我们直接找到索引就可以对其进行操作了。\n文件描述符（file descriptor）就是内核为了高效管理这些已经被打开的文件所创建的索引，其是一个非负整数（通常是小整数），用于指代被打开的文件，所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候，0是标准输入，1是标准输出，2是标准错误。这意味着如果此时去打开一个新的文件，它的文件描述符会是3，再打开一个文件文件描述符就是4……\nLinux内核对所有打开的文件有一个文件描述符表格，里面存储了每个文件描述符作为索引与一个打开文件相对应的关系，简单理解就是下图这样一个数组，文件描述符（索引）就是文件描述符表这个数组的下标，数组的内容就是指向一个个打开的文件的指针。\n上面只是简单理解，实际上关于文件描述符，Linux内核维护了3个数据结构：\n进程级的文件描述符表 系统级的打开文件描述符表 文件系统的i-node表 一个 Linux 进程启动后，会在内核空间中创建一个 PCB 控制块，PCB 内部有一个文件描述符表（File descriptor table），记录着当前进程所有可用的文件描述符，也即当前进程所有打开的文件。进程级的描述符表的每一条记录了单个进程所使用的文件描述符的相关信息，进程之间相互独立，一个进程使用了文件描述符3，另一个进程也可以用3。除了进程级的文件描述符表，系统还需要维护另外两张表：打开文件表、i-node 表。这两张表存储了每个打开文件的打开文件句柄（open file handle）。一个打开文件句柄存储了与一个打开文件相关的全部信息。\n系统级的打开文件描述符表：\n当前文件偏移量（调用read()和write()时更新，或使用lseek()直接修改） 打开文件时的标识（open()的flags参数） 文件访问模式（如调用open()时所设置的只读模式、只写模式或读写模式） 与信号驱动相关的设置 对该文件i-node对象的引用，即i-node 表指针 文件系统的i-node表：\n文件类型（例如：常规文件、套接字或FIFO）和访问权限 一个指针，指向该文件所持有的锁列表 文件的各种属性，包括文件大小以及与不同类型操作相关的时间戳 文件描述符、打开的文件句柄以及i-node之间的关系如下图：\n在进程 A 中，文件描述符 1 和 20 都指向了同一个打开文件表项，标号为 23（指向了打开文件表中下标为 23 的数组元素），这可能是通过调用 dup()、dup2()、fcntl() 或者对同一个文件多次调用了 open() 函数形成的。 进程 A 的文件描述符 2 和进程 B 的文件描述符 2 都指向了同一个文件，这可能是在调用 fork() 后出现的（即进程 A、B 是父子进程关系），或者是不同的进程独自去调用 open() 函数打开了同一个文件，此时进程内部的描述符正好分配到与其他进程打开该文件的描述符一样。 进程 A 的描述符 0 和进程 B 的描述符 3 分别指向不同的打开文件表项，但这些表项均指向 i-node 表的同一个条目（标号为 1976）；换言之，它们指向了同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了 open() 调用。同一个进程两次打开同一个文件，也会发生类似情况。 这就说明：同一个进程的不同文件描述符可以指向同一个文件；不同进程可以拥有相同的文件描述符；不同进程的同一个文件描述符可以指向不同的文件（一般也是这样，除了 0、1、2 这三个特殊的文件）；不同进程的不同文件描述符也可以指向同一个文件。\n3、Linux上打开文件举例 # 比如在Linux上用 vim test.py 打开一个文件，保持打开状态，再新打开一个新的shell，输入命令pidof vim 获取vim进程的pid号，然后 ll /proc/$pid/fd 查看vim 进程所使用的文件描述符列表。\n/dev/pts是远程登陆(telnet,ssh等)后创建的控制台设备文件所在的目录。因为我是通过Xshell远程登录的，所以标准输入0，标准输出1，标准错误2的文件描述符都指向虚拟终端控制台 /dev/pts/6 。 再看下面是新打开的 test.py 的文件描述符，竟然是4，说好的从3开始呢？\n这个我也困扰了好久，查了各种资料，终于在一个大佬的帮助下在一个论坛找到原因，有时候中文查不到还是要试试英文搜索啊。因为vim这种编辑器的原理是先打开源文件并拷贝，然后关闭源文件再打开自己的副本，修改完文件保存的时候直接将副本重命名覆盖源文件。所以打开源文件的时候用的文件描述符3，然后打开自己的副本是时候就该用文件描述符4了，然后关闭源文件，文件描述符3就被释放了，我们查看的时候就只剩下了4，这里它指向的是vim创建的副本文件。这里只是说个大概意思，具体深究要去深入了解一下 vim的实现原理——奥尔特星云大使，下面是当时我看到的论坛上的资料截图，链接在这：StackOverFlow。\n如果不相信可以试一试别的进程，比如 tail。\n在Linux上用 tail -f test.py 打开一个文件，保持打开状态，再新打开一个新的shell，输入命令pidof tail 获取tail进程的pid号，然后ll /proc/$pid/fd查看tail进程所使用的文件描述符列表，可以看到文件描述符确实是从3开始使用的。tail不是编辑器不存在修改文件的情况，所以直接文件描述符直接打开的源文件。实际上可以使用 ll /proc/$pid/fd命令获取当前运行的任意进程的文件描述符使用情况。\n4、C语言中文件描述符的使用 # C语言中可以通过 open 函数返回一个文件的文件描述符，首先创建一个 test.py 文件用于打开，然后创建一个 test.c 文件，输入下面代码保存。 编译后执行，发现新打开文件的文件描述符是3。\n1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main(int argc, char* argv[]) { int fd = open(\u0026quot;test.py\u0026quot;, O_RDONLY); if (fd == -1) { return -1; } printf(\u0026quot;test.py fd = %d \\n\u0026quot;, fd); close(fd); return 0; } 5、Python中文件描述符的使用 # Python中通过 sys 模块封装了标准输入、标准输出和错误输出。通过我们平时常用的内建函数 open 可以获取一个文件的文件描述符，首先创建一个 test.py 文件用于打开，然后创建一个 test2.py 文件，输入下面代码保存。 执行，发现新打开文件的文件描述符是3。\n1 2 3 4 5 6 7 8 import sys print('stdin fd = ', sys.stdin.fileno()) print('stdout fd = ', sys.stdout.fileno()) print('stderr fd = ', sys.stderr.fileno()) with open(\u0026quot;test.py\u0026quot;, \u0026quot;w\u0026quot;) as f: print('test.py fd = ', f.fileno()) 6、Linux配置系统最大打开文件描述符个数 # （1）系统级限制\n理论上系统内存有多少就可以打开多少的文件描述符，但是在实际中内核是会做相应的处理，一般最大打开文件数会是系统内存的10%（以KB来计算），称之为系统级限制。这个数字可以通过 cat /proc/sys/fs/file-max 或者 sysctl -a | grep fs.file-max 命令查看。\n更改系统级限制有临时更改和永久更改两种方式：\n临时更改：session断开或者系统重启后会恢复原来的设置值。使用命令 sysctl -w fs.file-max=xxxx，其中xxxx就是要设置的数字。 永久更改：vim编辑 /etc/sysctl.conf 文件，在后面添加 fs.file-max=xxxx，其中xxxx就是要设置的数字。保存退出后还要使用sysctl -p 命令使其生效。 （2）用户级限制\n同时为了控制每个进程消耗的文件资源，内核也会对单个进程最大打开文件数做默认限制，即用户级限制。32位系统默认值一般是1024，64位系统默认值一般是65535，可以使用 ulimit -n 命令查看。\n7、参考链接 # 每天进步一点点——Linux中的文件描述符与打开文件之间的关系——cywosp Linux文件描述符到底是什么？——C语言中文网 句柄和文件描述符（FD）——阳光丶不锈 带你破案：文件描述符到底是什么？——vran Linux配置调优：最大打开文件描述符个数——Idea Buffer 修改Linux系统下的最大文件描述符限制——BlueguyChui ","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/linux/linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6-fd/","section":"笔记","summary":"作者: 杰克小麻雀\n原文链接: https://blog.csdn.net/yushuaigee/article/details/107883964\n1、从一个最常见的例子说起 # 在使用Linux的过程中， 我们平时经常看到下面这样的用法：\n","title":"Linux文件描述符(fd)","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/stderr/","section":"Tags","summary":"","title":"Stderr","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/stdout/","section":"Tags","summary":"","title":"Stdout","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E9%87%8D%E5%AE%9A%E5%90%91/","section":"Tags","summary":"","title":"重定向","type":"tags"},{"content":" 需求 # 最近我们在开发一个基于树莓派的小盒子，我们会采购一些树莓派，装好外盒，装好arm版本的【Ubuntu Server 20.04】系统，并且装上我们开发的配套软件，对接我们云端的服务，最终把小盒子卖给用户并提供一些收费的服务。我们在安装系统的时候使用了Ubuntu自带的磁盘加密功能，确保机器在不开机的情况下不能被轻易的取出TF卡读取数据。\n另一方面，我们创建了一个低权限的Linux用户【user】，方便用户进行一些基本操作。\n默认情况下【Ubuntu Server 20.04】系统开机后，会要求你输入账号和密码来登录，但我们希望用户开机后，系统自动以【user】身份登录，并且自动运行一些脚本。\n实现 # 在网络搜索了一下，资料很多，但是坑也很多，最终总结出一个最简单的办法。\n先创建一个用户【user】\nsudo adduser user\n然后一路回车，这时候发现无法给这个用户指定空密码，提示\nNo password supplied\n不用担心，先胡乱设一个密码，一路回车，创建完毕后，删除该用户的密码\nsudo passwd -d user\n接下来就是配置终端的自动登录\nsudo vim /etc/systemd/system/getty.target.wants/getty@tty1.service\n修改【getty@tty1.service】文件的【ExecStart】这一行内容，从\nExecStart=-/sbin/agetty -o ‘-p – \\u’ \u0026ndash;noclear %I $TERM\n修改为\nExecStart=-/sbin/agetty -a user -o ‘-p – \\u’ \u0026ndash;noclear %I $TERM\n其实是就是加了-a \\[用户名\\]最后让systemd重新加载一下配置文件\nsudo systemctl daemon-reload\n然后重启系统\nsudo reboot\n便可实现Ubuntu Server的无密码开机自动登录\n","date":"2025年2月4日","externalUrl":null,"permalink":"/notes/linux/linux%E6%97%A0%E9%9C%80%E5%AF%86%E7%A0%81%E7%99%BB%E9%99%86-%E9%80%82%E5%90%88%E6%9C%8D%E5%8A%A1%E5%99%A8/","section":"笔记","summary":"需求 # 最近我们在开发一个基于树莓派的小盒子，我们会采购一些树莓派，装好外盒，装好arm版本的【Ubuntu Server 20.04】系统，并且装上我们开发的配套软件，对接我们云端的服务，最终把小盒子卖给用户并提供一些收费的服务。我们在安装系统的时候使用了Ubuntu自带的磁盘加密功能，确保机器在不开机的情况下不能被轻易的取出TF卡读取数据。\n","title":"Linux无需密码登陆-适合服务器","type":"notes"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/ubuntu-server/","section":"Tags","summary":"","title":"Ubuntu Server","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E8%87%AA%E5%8A%A8%E7%99%BB%E5%BD%95/","section":"Tags","summary":"","title":"自动登录","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"服务器管理","type":"tags"},{"content":"","date":"2025年2月4日","externalUrl":null,"permalink":"/tags/%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"用户管理","type":"tags"},{"content":"","date":"2025年1月27日","externalUrl":null,"permalink":"/tags/hexo/","section":"Tags","summary":"","title":"Hexo","type":"tags"},{"content":"你提供的代码中已经添加了显示网站存活时间的功能，接下来可以根据你的需求来添加访问人数统计的功能。以下是详细步骤：\n1. 不蒜子访问量统计的代码添加 # 不蒜子是一个非常简单、免费且无需配置的访问量统计工具，可以直接嵌入你的 Hexo 网站。\n步骤： # 在你的 \u0026lt;div class=\u0026quot;footer-inner\u0026quot;\u0026gt; 中，找到合适的位置（例如网站存活时间下方），然后添加以下不蒜子的统计代码： \u0026lt;!-- 访问人数统计 --\u0026gt; \u0026lt;span id=\u0026#34;busuanzi_container_site_pv\u0026#34; style=\u0026#34;display: none;\u0026#34;\u0026gt; 本站总访问量：\u0026lt;span id=\u0026#34;busuanzi_value_site_pv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 次 \u0026lt;/span\u0026gt; \u0026lt;script async src=\u0026#34;https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 代码解释： # id=\u0026quot;busuanzi_container_site_pv\u0026quot; 和 id=\u0026quot;busuanzi_value_site_pv\u0026quot; 是不蒜子提供的 ID，它们会在页面加载后自动统计并显示站点的总访问量。 通过 busuanzi.pure.mini.js 引入不蒜子统计脚本，它会自动计算并更新访问量。 修改后的文件： # \u0026lt;div class=\u0026#34;footer-inner\u0026#34;\u0026gt; \u0026lt;% if (theme.footer.content) { %\u0026gt; \u0026lt;div class=\u0026#34;footer-content\u0026#34;\u0026gt; \u0026lt;%- theme.footer.content %\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if (theme.footer.statistics.enable) { %\u0026gt; \u0026lt;%- partial(\u0026#39;_partials/footer/statistics.ejs\u0026#39;) %\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if(theme.footer.beian.enable) { %\u0026gt; \u0026lt;!-- 备案信息 ICP for China --\u0026gt; \u0026lt;%- partial(\u0026#39;_partials/footer/beian.ejs\u0026#39;) %\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if(theme.web_analytics.cnzz) { %\u0026gt; \u0026lt;!-- cnzz Analytics Icon --\u0026gt; \u0026lt;span id=\u0026#34;cnzz_stat_icon_\u0026lt;%= theme.web_analytics.cnzz %\u0026gt;\u0026#34; style=\u0026#34;display: none\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;!-- 网站存活时间计时功能 --\u0026gt; \u0026lt;div id=\u0026#34;site-lifetime\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; // 设置网站的创建日期 const creationDate = new Date(\u0026#39;2024-04-21\u0026#39;); // 请根据实际情况修改日期 // 计算当前日期和网站创建日期之间的天数差 function calculateDaysSinceCreation() { const now = new Date(); const timeDifference = now - creationDate; const daysSinceCreation = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); return daysSinceCreation; } // 页面加载完成后执行 document.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, function() { // 计算存活天数 const daysSinceCreation = calculateDaysSinceCreation(); // 更新存活时间 const lifetimeElement = document.getElementById(\u0026#39;site-lifetime\u0026#39;); lifetimeElement.textContent = `网站存活了 ${daysSinceCreation} 天`; }); \u0026lt;/script\u0026gt; \u0026lt;!-- 访问人数统计 --\u0026gt; \u0026lt;span id=\u0026#34;busuanzi_container_site_pv\u0026#34; style=\u0026#34;display: none;\u0026#34;\u0026gt; 本站总访问量：\u0026lt;span id=\u0026#34;busuanzi_value_site_pv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 次 \u0026lt;/span\u0026gt; \u0026lt;script async src=\u0026#34;https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/div\u0026gt; 2. 使用插件方式显示访问量 # 如果你想使用插件（例如 hexo-wordcount）来统计和显示页面访问量，你可以按照以下步骤进行配置。\n步骤： # 安装插件：\n首先，安装 hexo-wordcount 插件：\nnpm install hexo-wordcount --save 修改模板文件：\n找到你的主题文件夹中的页面模板（例如 themes/your-theme/layout/_partial/footer.ejs 或 footer.ejs），然后在适当位置（例如网站存活时间和备案信息之后）添加以下代码：\n\u0026lt;!-- 访问人数统计 --\u0026gt; 本站总访问量：\u0026lt;%= page.total_count %\u0026gt; 注意： 这个插件可以统计字数、阅读时间等信息，也可以结合其他统计功能使用。\n更新 Hexo 并生成静态文件：\n完成后，执行以下命令生成更新后的静态文件：\nhexo clean hexo generate 然后部署网站：\nhexo deploy 3. Moe-Counter 动漫式计数 # github🔗\n主要思路就是跟着文档操作就行了的，先生成代码 (就是一个 img 格式的代码)\n然后，如果你想在底部 (footer) 插入，你就要在 /home/ice/my-blog/themes/fluid(your-theme)/layout/_partials/footer.ejs 插入代码，具体示例如下:\n\u0026lt;!-- Moe-Counter 访问人数统计 --\u0026gt; \u0026lt;div style=\u0026#34;text-align: center; margin-top: 10px;\u0026#34;\u0026gt; \u0026lt;span style=\u0026#34;font-size: 16px; font-weight: bold;\u0026#34;\u0026gt;访问人数统计: \u0026lt;/span\u0026gt; \u0026lt;img src=\u0026#34;https://count.getloli.com/@:ice345?name=%3Aice345\u0026amp;theme=gelbooru\u0026amp;padding=7\u0026amp;offset=0\u0026amp;align=bottom\u0026amp;scale=1\u0026amp;pixelated=1\u0026amp;darkmode=auto\u0026#34; alt=\u0026#34;visitor count\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; 这个你想在底部生成邮箱，github 链接，QQ这些就仿照如下:\n\u0026lt;!-- 添加社交链接 --\u0026gt; \u0026lt;div style=\u0026#34;font-size: 13px; font-weight: bold; text-align: center;\u0026#34;\u0026gt; \u0026lt;div style=\u0026#34;display: flex; justify-content: center; gap: 20px;\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;https://github.com/ice345\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fab fa-github\u0026#34; style=\u0026#34;color:#409EFF;\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; GitHub \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;mailto:nni461904@gmail.com\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fa fa-envelope\u0026#34; style=\u0026#34;color:#409EFF\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; 邮箱 \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;https://alist.050626.xyz\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fa-solid fa-cloud-download\u0026#34; style=\u0026#34;color:#409EFF;\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; Alist \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;br /\u0026gt; \u0026lt;/div\u0026gt; 4. 存活时间 # 这个的添加就仿照访问人数即可\n","date":"2025年1月27日","externalUrl":null,"permalink":"/misc/hexo%E6%B7%BB%E5%8A%A0%E5%AD%98%E6%B4%BB%E6%97%B6%E9%97%B4%E5%92%8C%E8%AE%BF%E9%97%AE%E4%BA%BA%E6%95%B0/","section":"杂项","summary":"你提供的代码中已经添加了显示网站存活时间的功能，接下来可以根据你的需求来添加访问人数统计的功能。以下是详细步骤：\n1. 不蒜子访问量统计的代码添加 # 不蒜子是一个非常简单、免费且无需配置的访问量统计工具，可以直接嵌入你的 Hexo 网站。\n","title":"hexo添加存活时间和访问人数","type":"misc"},{"content":"","date":"2025年1月27日","externalUrl":null,"permalink":"/tags/%E8%AE%BF%E9%97%AE%E7%BB%9F%E8%AE%A1/","section":"Tags","summary":"","title":"访问统计","type":"tags"},{"content":"","date":"2025年1月27日","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E4%BC%98%E5%8C%96/","section":"Tags","summary":"","title":"博客优化","type":"tags"},{"content":"","date":"2025年1月27日","externalUrl":null,"permalink":"/tags/%E4%B8%8D%E7%AE%97%E5%AD%90/","section":"Tags","summary":"","title":"不算子","type":"tags"},{"content":"","date":"2025年1月27日","externalUrl":null,"permalink":"/categories/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/","section":"Categories","summary":"","title":"个人博客","type":"categories"},{"content":"","date":"2025年1月15日","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E4%B8%80/","section":"Tags","summary":"","title":"大一","type":"tags"},{"content":"","date":"2024年12月20日","externalUrl":null,"permalink":"/tags/.desktop%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":".Desktop文件","type":"tags"},{"content":" 笔记 好用的工具: desktop-file-validate 这个来检查编写的错误\n例子 # [Desktop Entry] Version=1.0 Name=Todoist Comment=This is balabala(填任何东西都可以) Exec=/home/ice/.... Icon=/home/ice/..... Terminal=false Type=Application Categories=Utility 这个就是典型的 .desktop 的编写例子, 其余什么的 .desktop 文件都可以参考这个来编写\n错误 # 最近在 arch 中编写这个发现出错，但是想破了脑袋都没有想明白哪里出了问题，问 chatgpt, 我就简单进行比对，发现一模一样就没有想清楚。\n我就只好总结询问 Google, 功夫不负有心人，在 reddit 中，看到这个 question 发现可以通过 desktop-file-validate \u0026lt;.desktop file name\u0026gt; 来检查编写出现的错误.\n最后，给我这样的信息:\ntodoist.desktop: error: value \u0026#34;Application \u0026#34; for key \u0026#34;Type\u0026#34; in group \u0026#34;Desktop Entry\u0026#34; is not a registered type value (\u0026#34;Application\u0026#34;, \u0026#34;Link\u0026#34; and \u0026#34;Directory\u0026#34;) 但是我看我的 Type 行没有发现有什么空格，我就不能理解.\n但是最后我在 Application 后发现这个后面有个空格导致了出现这样的错误\nTips 我会在这个后面出现空格，是因为 nvim 编写时，如果你不加空格，你按 enter 就会接受提示词，因此才会导致这样的问题的😠\n参考 # reddit\n","date":"2024年12月20日","externalUrl":null,"permalink":"/notes/linux/linux%E4%B8%8B-desktop%E5%88%9B%E5%BB%BA%E6%97%B6%E5%8F%91%E7%94%9F%E7%9A%84%E9%94%99%E8%AF%AF/","section":"笔记","summary":" 笔记 好用的工具: desktop-file-validate 这个来检查编写的错误\n","title":"Linux下.desktop创建时发生的错误","type":"notes"},{"content":"","date":"2024年12月20日","externalUrl":null,"permalink":"/tags/%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87/","section":"Tags","summary":"","title":"桌面图标","type":"tags"},{"content":"","date":"2024年12月20日","externalUrl":null,"permalink":"/tags/%E9%94%99%E8%AF%AF%E6%8E%92%E6%9F%A5/","section":"Tags","summary":"","title":"错误排查","type":"tags"},{"content":"","date":"2024年12月20日","externalUrl":null,"permalink":"/tags/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":"配置文件","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/cpu%E6%BC%8F%E6%B4%9E/","section":"Tags","summary":"","title":"CPU漏洞","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/meltdown/","section":"Tags","summary":"","title":"Meltdown","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/spectre/","section":"Tags","summary":"","title":"Spectre","type":"tags"},{"content":"就利用到了 cache 的缓存的时间不一样 (访问某些位置时会发生访问时间快的问题使得可以通过这个来得到其他进程的数据内容)\n分支预测 (speculative execution) 侧信道攻击 (side-channel attack) 访问时间差异获取数据内容 通过下面的这张图基本也可以理解个大概了\n","date":"2024年12月13日","externalUrl":null,"permalink":"/notes/system/spectre%E5%92%8Cmeltdown/","section":"笔记","summary":"就利用到了 cache 的缓存的时间不一样 (访问某些位置时会发生访问时间快的问题使得可以通过这个来得到其他进程的数据内容)\n分支预测 (speculative execution) 侧信道攻击 (side-channel attack) 访问时间差异获取数据内容 通过下面的这张图基本也可以理解个大概了\n","title":"spectre和meltdown","type":"notes"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E4%BE%A7%E4%BF%A1%E9%81%93%E6%94%BB%E5%87%BB/","section":"Tags","summary":"","title":"侧信道攻击","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/","section":"Tags","summary":"","title":"分支预测","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/cpu%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"CPU管理","type":"tags"},{"content":" 00:00 - ⏰ 进程调度 # 总结： # 进程调度的目的是在多个程序或进程之间合理分配 CPU 时间。最初，程序通过主动调用 API 函数交出 CPU，但有时进程不合作，比如陷入死循环或长时间占用 CPU，导致其他进程无法执行。\n为了避免这些问题，系统引入了时钟中断机制，在每个时钟周期结束时，强制切换进程，这就是抢占式调度的基础。每个进程被分配一个固定的时间片（比如 100 毫秒），时间片用完时，调度器会选择其他进程执行。\n随着进程数量增加，系统通过就绪队列管理进程（即正在等待 CPU 执行的进程）。此外，将阻塞的进程（如等待 I/O 或锁的进程）移到等待队列，避免 CPU 空转。\n示例： # 假设你有 5 个进程在运行，每个进程执行 100ms，调度器每 100ms 中断一次。如果进程 A 被选中运行，但它一直在等待 I/O，系统就会把它移入等待队列，调度下一个进程。这样，CPU 就不会空闲，而是去执行其他进程。\n2:09 - 🤔 进程过多导致系统卡顿 # 总结： # 初始时系统进程较少，运行流畅，但随着进程增多（达到数百个），即使每个进程的执行时间很短（例如 10 毫秒），每个进程上下文切换的开销依然很大。\n上下文切换包括保存和加载进程的状态，这些操作有较高的开销。如果进一步缩短每个进程的执行时间，虽然每个进程的 CPU 时间减少了，但上下文切换的开销却增加了，反而导致系统卡顿。\n示例： # 假设有 1000 个进程，每个进程分配 10ms 执行时间。理论上每个进程执行 10ms 后进行一次上下文切换，但上下文切换本身需要时间（假设每次切换花费 1ms），如果系统做过多上下文切换，反而会导致效率低下，系统变得卡顿。\n2:47 - ⏳ 双队列进程调度 # 总结： # 为了解决高进程数量带来的效率问题，系统采用了双队列调度： active 队列：用于存放正在运行的进程。 expi 队列：用于存放高优先级的进程，暂时不执行。 这种方法的关键是避免高优先级进程长期占用 CPU 资源，导致低优先级进程**“饿死”。在某一轮调度后，系统将高优先级进程移到 expi 队列**，然后继续执行 active 队列中的进程，直到所有进程都执行完后，交换两个队列指针，确保所有优先级的进程都有机会执行。 示例： # 假设有两个队列：\nactive 队列：[进程 A, 进程 B, 进程 C] expi 队列：[进程 D, 进程 E] 调度时，高优先级进程（D, E）会被暂时放入 expi 队列，只有 active 队列中的进程（A, B, C）能被调度。待 active 队列中的进程执行完后，交换队列指针，使得 expi 队列中的进程得以执行。\n5:33 - 优先级调度改进 # 总结： # 为了解决优先级调度中出现的问题，采用了动态时间片分配算法。该算法根据进程的优先级来分配时间片：优先级较高的进程拥有较短的时间片，优先级较低的进程则拥有较长的时间片。\n举例来说，优先级为 20 的进程的时间片是 100 毫秒，每升高或降低一级，时间片增加或减少 5 毫秒。然而，存在缺陷：如果两个进程优先级相差 1，CPU 占用的差距可能非常大，有时能达到几倍的差异。\n示例： # 假设有两个进程，进程 A 和进程 B，优先级分别为 20 和 19：\n进程 A 时间片是 100ms，进程 B 时间片是 105ms。理论上，进程 B 会占用更多的 CPU 时间，但如果系统频繁切换进程，B 却可能因为时间片过长而阻塞过久，A 反而会更频繁地被调度，影响公平性。 7:01 - ⏳ 权重优先调度 # 总结： # 权重优先调度算法基于进程优先级的权重来分配 CPU 时间。每个进程的时间片根据其权重进行调整，权重越大的进程获得更多的 CPU 时间。\n例如，进程 A 权重为 1，进程 B 权重为 2，进程 A 每次获得 100 毫秒的时间片，而进程 B 则获得 200 毫秒的时间片。为了避免过度切换，算法还设置了最小时间片。\n如果进程的时间片没有完全用完，调度器会考虑优先执行那些运行时间最短的进程。通过引入虚拟运行时间，算法能够补偿权重差异所导致的时间片分配不均，确保系统公平。\n示例： # 假设进程 A 的权重是 1，进程 B 的权重是 2。假如它们的时间片分别为 100ms 和 200ms。 如果进程 A 没有完全用完 100ms，它会较快地再次获得 CPU，进程 B 则较慢。为了补偿进程 B 因为权重较大而执行时间过长，调度器会让进程 A 在某些时刻得到更多的 CPU 时间。 9:02 - ⏱️ 虚拟时间调度 # 总结： # 这种调度算法的核心思想是通过进程的虚拟时间来决定其执行顺序。虚拟时间是指进程已消耗的“公平时间”，进程的虚拟时间越短，它会越早被选中执行。\n为了高效管理这些进程，系统使用红黑树来维护进程。通过缓存最左节点（虚拟时间最短的进程），调度器能够快速找到下一个应该执行的进程。\n这种方式与 Linux 的 O(1) 调度算法和 CFS 调度算法类似，但做了一些简化，仍然能保证高效且公平的调度。\n示例： # 假设有 3 个进程 A、B 和 C，它们的虚拟时间分别为 5ms、10ms 和 15ms。调度器会根据虚拟时间来选择最短的进程（A）执行。 红黑树用于快速查找最短虚拟时间的进程，进程 A 会被选中执行。 ","date":"2024年12月13日","externalUrl":null,"permalink":"/notes/linux/linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6/","section":"笔记","summary":"00:00 - ⏰ 进程调度 # 总结： # 进程调度的目的是在多个程序或进程之间合理分配 CPU 时间。最初，程序通过主动调用 API 函数交出 CPU，但有时进程不合作，比如陷入死循环或长时间占用 CPU，导致其他进程无法执行。\n","title":"linux进程调度","type":"notes"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E6%8A%A2%E5%8D%A0%E5%BC%8F%E8%B0%83%E5%BA%A6/","section":"Tags","summary":"","title":"抢占式调度","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E6%97%B6%E9%97%B4%E7%89%87/","section":"Tags","summary":"","title":"时间片","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6/","section":"Tags","summary":"","title":"进程调度","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/","section":"Tags","summary":"","title":"多线程","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E8%BF%9B%E7%A8%8B/","section":"Tags","summary":"","title":"多进程","type":"tags"},{"content":" 1. 多进程 (Multiprocessing) # 示例：假设你需要进行大量的文件处理任务，比如读取多个文件、处理内容并写入新文件。\n如何使用多进程： 你可以为每个文件创建一个独立的进程，每个进程处理一个文件。 每个进程在操作系统层面上拥有独立的内存空间，互不干扰。 如果你有四核 CPU，操作系统可以将这些进程分配给不同的核心进行并行处理，提高效率。 实际应用：如图像处理、视频编码等计算密集型任务，它们可以通过多进程来分摊计算负载，每个进程执行不同的计算任务。\n2. 多线程 (Multithreading) # 示例：你正在开发一个 web 服务器，服务器需要同时处理多个用户的请求（比如访问不同的网页）。\n如何使用多线程： 每一个用户请求可以由一个线程来处理。所有线程共享服务器的内存和资源，例如共享缓存数据。 线程间的通信和数据共享更容易（通过共享内存），但也需要注意同步，以避免并发问题。 如果是多核 CPU，多个线程可以同时在不同核心上执行，增强服务器的响应能力。 实际应用：如网络服务、数据库连接池等场景，线程之间需要频繁的资源共享，适合使用多线程来处理高并发请求。\n3. 并发 (Concurrency) # 示例：你有一个任务是同时进行多个 I/O 操作（如网络请求、文件读写等），但系统只有一个 CPU 核心。\n如何使用并发： 虽然系统只有一个核心，但通过操作系统的调度，可以在短时间内交替执行多个任务。例如，任务 1 正在等待网络响应，操作系统可以切换到任务 2，继续执行文件操作。这样看起来像是同时进行，但实际上是任务间的切换。 操作系统通过时间片轮转等机制，实现任务的“并发”，即在一个 CPU 上交替执行多个任务。 实际应用：如高并发的网络爬虫，爬虫需要同时发送多个请求，虽然只有一个 CPU 核心，但通过任务调度（并发）能有效地提高 I/O 操作的效率。\n4. 并行 (Parallelism) # 示例：假设你需要处理一个大规模的图像处理任务，比如对大量图片进行滤镜处理。\n如何使用并行： 如果你的系统有多个 CPU 核心（例如四核），你可以将任务分成 4 个子任务，每个子任务分别在一个核心上处理不同的图片。 这样所有图片的处理是同时进行的，充分利用了多核 CPU 的计算能力，显著提高处理速度。 实际应用：如深度学习模型训练、科学计算、视频渲染等场景，这些任务往往计算密集型，并且可以通过并行化加速处理过程。\nTips 并发和并行的区别就是: 并行就是多个任务同时进行;并发并不是多个任务同时进行，而是多个任务轮流进行，因为调度得好，从而看上去是同步进行的. (==并发就是多进程通过进程调度算法调度从而实现的==)\n总结：具体例子对比 # 任务类型 举例：图片处理任务 举例：Web 服务器 举例：网络爬虫 举例：科学计算 多进程 每个进程处理一张图片（独立进程） 每个用户请求一个进程处理（多个用户请求并行） 不常见，进程间通信开销大 每个进程处理一个计算任务（分布式计算） 多线程 不适用（图片处理通常不需要共享资源） 每个请求一个线程处理，线程共享数据 合适，线程共享内存、频繁I/O 适合计算任务的拆分（如大规模模拟） 并发 不常见（计算任务通常不能并发） 多个请求轮流处理（在一个核心上交替执行） 多个网络请求交替执行 适合 I/O 密集型任务（单核） 并行 同时处理多个图片（多核 CPU） 多个请求同时处理（多核 CPU） 适合在多核机器上同时发起请求 多个计算任务同时执行（多核 CPU） 每种方法都适用于不同的场景：多进程适合任务独立且计算密集型；多线程适合任务间需要频繁交互的场景；并发强调任务的调度和交替执行；而并行则适合计算任务的加速和负载分配。\n","date":"2024年12月13日","externalUrl":null,"permalink":"/notes/linux/%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%92%8C%E5%A4%9A%E7%BA%BF%E7%A8%8B-%E5%B9%B6%E5%8F%91%E5%92%8C%E5%B9%B6%E8%A1%8C/","section":"笔记","summary":"1. 多进程 (Multiprocessing) # 示例：假设你需要进行大量的文件处理任务，比如读取多个文件、处理内容并写入新文件。\n","title":"多进程和多线程,并发和并行","type":"notes"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E5%B9%B6%E5%8F%91/","section":"Tags","summary":"","title":"并发","type":"tags"},{"content":"","date":"2024年12月13日","externalUrl":null,"permalink":"/tags/%E5%B9%B6%E8%A1%8C/","section":"Tags","summary":"","title":"并行","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/aes/","section":"Tags","summary":"","title":"AES","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/categories/algorithm/","section":"Categories","summary":"","title":"Algorithm","type":"categories"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/rsa/","section":"Tags","summary":"","title":"RSA","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"对称加密","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/%E6%B7%B7%E5%90%88%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"混合加密","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"非对称加密","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/categories/%E5%AF%86%E7%A0%81%E5%AD%A6/","section":"Categories","summary":"","title":"密码学","type":"categories"},{"content":"在处理大文件或大量数据时，流行的加密方法通常是采用 混合加密方案，即结合了 对称加密（如 AES）和 非对称加密（如 RSA）的方法。这样可以在保证安全性的同时，提高效率，避免直接对大文件进行加密所带来的性能瓶颈。\n1. 混合加密方案 # 混合加密方案利用了对称加密和非对称加密各自的优势：\n对称加密（如 AES）：处理速度快，适合加密大数据。 非对称加密（如 RSA）：安全性高，适合用来加密对称加密的密钥。 典型流程 # 生成对称密钥：首先，生成一个随机的对称密钥（如 AES 密钥），这个密钥用来加密文件。对称加密通常很快，适用于处理大量数据。\n用对称密钥加密文件：使用 AES 或其他对称加密算法将大文件或大量数据加密。因为对称加密效率高，可以迅速加密大文件。\n加密对称密钥：用接收方的公钥（RSA）加密对称密钥。这样，只有持有私钥的接收方才能解密对称密钥，从而解密文件。\n传输加密数据：将加密后的文件和加密后的对称密钥一起发送给接收方。\n接收方解密：接收方首先用自己的私钥解密对称密钥，然后使用解密得到的对称密钥解密文件。\n这种方案的优点：\n效率高：对称加密（AES）非常快速，适合处理大文件。 安全性高：非对称加密（RSA）保证了对称密钥的安全传输。 灵活性：适用于大文件、流媒体、云存储等场景。 2. 分块加密 # 对于一些特殊应用，分块加密也是一种流行的方法，尤其是在直接使用非对称加密时：\n将大文件分成多个较小的块（通常每个块的大小限制在 RSA 加密能够处理的范围内，比如 256 字节）。 对每个块使用 RSA 或其他加密算法进行加密。 这种方法适用于一些小型系统或者需要直接加密而没有对称加密需求的场景，但效率比混合加密方案低。 3. 端到端加密 (End-to-End Encryption) # 端到端加密也在加密大量数据时广泛使用，特别是用于云存储或消息传递服务中。以下是典型的端到端加密方案：\n数据加密：客户端对数据进行加密，然后发送到服务器。 密钥管理：密钥管理是关键，通常使用公私钥对来加密通信密钥。 解密：只有授权的接收方能够解密数据。 在很多应用场景中，端到端加密结合了对称加密和非对称加密（类似混合加密方案），确保通信的安全性和效率。\n4. 使用专用加密工具 # 许多加密工具和协议已经优化了大文件的加密过程，例如：\nGPG (GNU Privacy Guard)：基于 OpenPGP 标准，常用于文件加密，支持对称和非对称加密。 SSL/TLS：用于保护网络传输中的数据，特别适合流媒体、实时通信等大数据量传输。 5. 硬件加速 # 为了进一步提高加密效率，很多系统使用硬件加速：\n硬件加速的对称加密算法：如 AES-NI（Intel 提供的 AES 加速指令），可以显著加速 AES 加密过程，尤其是在加密大文件时。 硬件安全模块 (HSM)：用于生成和存储密钥，确保加密操作的安全性和高效性。 6. 云服务加密 # 对于云存储和云计算中的大文件加密，很多云服务提供商会自动处理加密：\n例如，Amazon S 3、Google Cloud Storage 等会自动对存储在云中的数据进行加密，通常使用 AES-256（对称加密算法）。 同时，密钥管理服务（如 AWS KMS 或 Google Cloud KMS）提供了加密密钥的管理和访问控制。 7. 流式加密 # 对于实时数据流的加密（如视频流、音频流、实时通信数据），流式加密算法（如 RC 4、ChaCha 20）常常被使用。这些算法专门针对数据流进行加密，能够在流数据传输过程中保证安全性。\n总结 # 处理大文件或大量数据时，最流行的加密方法是采用 混合加密方案，即结合 对称加密（如 AES）和 非对称加密（如RSA）的优点。对称加密用于高效地加密数据，而非对称加密用于加密对称密钥，确保密钥的安全性。这种方法广泛应用于云存储、文件加密、数据传输等场景，同时，使用硬件加速、分块加密、端到端加密等技术可以进一步提高效率和安全性。\n","date":"2024年11月18日","externalUrl":null,"permalink":"/notes/security/%E6%B5%81%E8%A1%8C%E7%9A%84%E5%A4%A7%E9%87%8F%E6%95%B0%E6%8D%AE%E6%88%96%E8%80%85%E5%A4%A7%E6%96%87%E4%BB%B6%E5%8A%A0%E5%AF%86/","section":"笔记","summary":"在处理大文件或大量数据时，流行的加密方法通常是采用 混合加密方案，即结合了 对称加密（如 AES）和 非对称加密（如 RSA）的方法。这样可以在保证安全性的同时，提高效率，避免直接对大文件进行加密所带来的性能瓶颈。\n","title":"流行的大量数据或者大文件加密","type":"notes"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/hash%E7%AE%97%E6%B3%95/","section":"Tags","summary":"","title":"Hash算法","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/rsa%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"RSA加密","type":"tags"},{"content":" 情境概述 # Alice 和 Bob 是通信双方。 目标：确保消息 \\( M \\) 的机密性、完整性和来源可验证。 使用的技术：RSA 加密、数字签名和 Hash 算法。 双方应持有的密钥 # Alice:\n私钥$( d_{A})$ ：用来签名消息。 公钥 $(e_{A})$：用来加密消息（如果 Alice 要接收加密消息）。 Bob:\n私钥 $( d_{B})$ ：用来解密消息。 公钥 $(e_{B})$：用来加密消息（如果 Bob 要接收加密消息）。 注意：双方的公钥是公开的，任何人都可以获取，而私钥则是保密的，只能由对应的密钥持有者使用。\n已知信息 # Alice 和 Bob 都知道对方的 公钥。即： Alice 知道 Bob 的公钥 $(e_{B})$。 Bob 知道 Alice 的公钥 $(e_{A})$。 过程说明 # 1. Alice 准备消息 # Alice 要向 Bob 发送一条消息 \\( M \\)，比如： $$ M = \\text{\"Hello, Bob!\"} $$ 2. Alice 对消息进行 RSA 加密 # Alice 使用 Bob 的公钥 $(e_{B})$ 对消息 \\( M \\) 进行加密，得到密文 \\( C \\)： $$ C = M^{e_{B}} \\mod n_{B} $$这里，$( n_{B} )$ 是 Bob 公钥的一个组成部分。\n密文 \\( C \\) 是加密后的消息，只有拥有 Bob 私钥 $( d_{B})$ 的 Bob 才能解密。\n3. Alice 计算消息的 Hash 值并生成数字签名 # 为了确保消息的完整性和来源，Alice 使用 Hash 算法（如 SHA-256）计算消息 \\( M \\) 的 Hash 值 \\( H (M) \\)： $$ H(M) = \\text{SHA-256}(M) $$ 然后，Alice 使用 自己的私钥$( d_{A})$ 对 Hash 值 \\( H (M) \\) 进行签名，生成数字签名 \\( S \\)： $$ S = H(M)^{d_{A}} \\mod n_{A} $$这里，$( n_{A} )$ 是 Alice 私钥的一个组成部分。\n4. Alice 发送加密消息和签名 # Alice 将加密后的消息 \\( C \\) 和数字签名 \\( S \\) 一同发送给 Bob。 $$ \\text{发送数据} = (C, S) $$ 5. Bob 接收消息和签名 # Bob 收到 Alice 发送的加密消息 \\( C \\) 和签名 \\( S \\)。 6. Bob 使用私钥解密消息 # Bob 使用 自己的私钥 $( d_{B})$ 解密密文 \\( C \\)，得到原始消息 \\( M \\)： $$ M = C^{d_{B}} \\mod n_{B} $$ 解密后，Bob 得到原始的消息 $( M = \\text{\"Hello, Bob!\"} )$。\n7. Bob 使用 Alice 公钥验证签名 # Bob 使用 Alice 的公钥 $(e_{A})$ 对签名 \\( S \\) 进行验证： $$ H'(M) = S^{e_{A}} \\mod n_{A} $$这里，\\( H' (M) \\) 是通过公钥解密签名后得到的 Hash 值。\n然后，Bob 对收到的消息 \\( M \\) 使用相同的 Hash 算法计算 Hash 值 \\( H (M) \\)，即： $$ H(M) = \\text{SHA-256}(M) $$ 8. Bob 验证签名的有效性 # 如果计算得到的 Hash 值 \\( H (M) \\) 与通过公钥解密得到的 Hash 值 \\( H' (M) \\) 相同： $$ H(M) = H'(M) $$那么消息 \\( M \\) 没有被篡改，且确实是由 Alice 发送的，签名有效。\n如果 $( H (M) \\neq H' (M) )$，则说明消息可能已被篡改，或者签名无效。\n9. Bob 进一步处理消息 # 如果签名验证成功，Bob 知道消息完整且真实，接下来可以继续处理消息内容（比如显示消息：“Hello, Bob!”）。 总结 # 在这个过程中，RSA 加密和数字签名共同作用：\nRSA 加密：保护消息的机密性，确保只有 Bob（拥有私钥）才能解密消息。 数字签名：确保消息的完整性和来源，只有 Alice（拥有私钥）才能生成有效的签名，Bob 可以用 Alice 的公钥验证签名。 双方持有的密钥 # Alice： 私钥$( d_{A})$ 公钥 $(e_{A})$ Bob： 私钥 $( d_{B})$ , 公钥 $( e_{B} )$ 双方应知的内容 # Alice 知道：Bob 的公钥 $(e_{B})$（用于加密消息） Bob 知道：Alice 的公钥 $(e_{A})$（用于验证签名） 通过这种方式，RSA 加密保证了消息的机密性，数字签名确保了消息的完整性和来源的真实性。\n","date":"2024年11月18日","externalUrl":null,"permalink":"/notes/security/rsa%E5%8A%A0%E5%AF%86-%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E5%92%8Chas%E7%AE%97%E6%B3%95%E7%BB%93%E5%90%88%E4%BE%8B%E5%AD%90/","section":"笔记","summary":"情境概述 # Alice 和 Bob 是通信双方。 目标：确保消息 \\( M \\) 的机密性、完整性和来源可验证。 使用的技术：RSA 加密、数字签名和 Hash 算法。 双方应持有的密钥 # Alice:\n","title":"RSA加密,数字签名和HAS算法结合例子","type":"notes"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/%E5%AE%89%E5%85%A8%E9%80%9A%E4%BF%A1/","section":"Tags","summary":"","title":"安全通信","type":"tags"},{"content":"","date":"2024年11月18日","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D/","section":"Tags","summary":"","title":"数字签名","type":"tags"},{"content":" RSA 加密算法解释 # 一、RSA 加密算法的数学原理 # 1. 生成密钥对 # 步骤如下：\n选择两个大素数：\\( p \\) 和 \\( q \\)。 计算 \\( n \\)： $$ n = p \\times q $$ 计算欧拉函数 ($\\varphi(n)$)： $$ \\varphi (n) = (p-1) \\times (q-1) $$ 选择一个公钥指数 ( e )，要求 $1","date":"2024年11月18日","externalUrl":null,"permalink":"/notes/security/rsa%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/","section":"笔记","summary":"RSA 加密算法解释 # 一、RSA 加密算法的数学原理 # 1. 生成密钥对 # 步骤如下：\n","title":"RSA加密算法","type":"notes"},{"content":"sudo mount /dev/sdXX /mnt sudo mount /dev/sdaXX /mnt/home(有这个分区的话) sudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp sudo chroot /mnt 什么是 chroot？ # chroot（Change Root）是 Linux/Unix 系统中的一种工具，它允许你将当前的根文件系统切换到指定的目录，使该目录成为虚拟机或类似于容器的环境。简单来说，chroot 可以将系统的根目录 / 改变为某个其他的目录，这样你可以在这个隔离的环境下操作，不影响主系统。这种方式通常用于恢复系统、测试环境、构建软件或者运行独立的系统。\n但是，chroot 环境并不自动包含宿主机所有功能。为了确保虚拟机能够正常使用类似宿主机的功能，比如设备访问、进程管理和网络功能，你需要手动挂载一些关键文件系统。\n挂载的原理： # 这些挂载操作是通过 --bind 选项来实现的，它允许将宿主机的某个目录或文件系统“绑定”到 chroot 环境的某个位置。这样一来，虚拟机就可以直接访问宿主机上的这些资源，达到正常运行系统和网络功能的目的。\n这些文件系统本身大多是虚拟的（例如 /proc 和 /sys），因此它们不是实际的存储内容，而是动态生成的系统信息。通过将它们挂载到虚拟机，虚拟机就可以和宿主机共享这些动态系统信息。\n挂载的文件系统有哪些？为什么挂载？ # 为了让 chroot 环境有完整的系统功能，你需要挂载以下文件系统或文件：\n/proc （进程文件系统） # /proc （进程文件系统） 作用：提供系统和进程的信息，如 CPU、内存使用情况、挂载点等。 为什么挂载：许多命令依赖 /proc 来获取系统状态（如 ps, top 等）。挂载后，chroot 可以访问宿主机的系统和进程信息。 挂载命令： sudo mount --bind /proc /mnt/proc /sys （系统文件系统） # /sys （系统文件系统） 作用：提供关于系统硬件设备的信息，包括驱动程序、设备状态等。 为什么挂载：系统管理工具（如 udevadm）和硬件相关的功能需要访问 /sys。 挂载命令： sudo mount --bind /sys /mnt/sys /dev （设备文件系统） # /dev （设备文件系统） 作用：提供设备节点，比如硬盘、终端设备、USB 等。 为什么挂载：/dev 让 chroot 环境可以访问实际的硬件设备，包括硬盘、鼠标、键盘等，以及管理这些设备。 挂载命令： sudo mount --bind /dev /mnt/dev /run （运行时信息） # /run （运行时信息） 作用：包含系统运行时生成的临时文件，比如 PID 文件、socket 文件等。 为什么挂载：一些系统进程和服务需要 /run 来存储它们的运行状态或通信文件。 挂载命令： sudo mount --bind /run /mnt/run /dev/pts （伪终端 # /dev/pts （伪终端） 作用：提供伪终端（pseudo-terminal），用于创建多个终端会话，比如 ssh 或 sudo 需要使用它。 为什么挂载：在 chroot 中执行需要伪终端的命令时（如 sudo），必须挂载 /dev/pts。 挂载命令： sudo mount --bind /dev/pts /mnt/dev/pts /etc/resolv.conf （DNS 配置） # /etc/resolv.conf （DNS 配置） 作用：配置系统如何解析域名，通过 DNS 服务器转换域名为 IP 地址。 为什么挂载：如果 chroot 环境中需要访问网络（如更新软件），必须能解析域名，因此要挂载 /etc/resolv.conf。 挂载命令： sudo mount --bind /etc/resolv.conf /mnt/etc/resolv.conf /tmp （临时文件） # /tmp （临时文件） 作用：用于存储临时文件和目录。 为什么挂载：某些程序可能需要 /tmp 目录来存储临时数据。 挂载命令： sudo mount --bind /tmp /mnt/tmp 挂载顺序 # 一般来说，挂载这些目录的顺序不是特别严格，只要你确保所有必要的目录都已经挂载。可以按照如下步骤进行：\nsudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp 然后进入 chroot：\nsudo chroot /mnt ","date":"2024年10月14日","externalUrl":null,"permalink":"/notes/system/chroot/","section":"笔记","summary":"sudo mount /dev/sdXX /mnt sudo mount /dev/sdaXX /mnt/home(有这个分区的话) sudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp sudo chroot /mnt 什么是 chroot？ # chroot（Change Root）是 Linux/Unix 系统中的一种工具，它允许你将当前的根文件系统切换到指定的目录，使该目录成为虚拟机或类似于容器的环境。简单来说，chroot 可以将系统的根目录 / 改变为某个其他的目录，这样你可以在这个隔离的环境下操作，不影响主系统。这种方式通常用于恢复系统、测试环境、构建软件或者运行独立的系统。\n","title":"chroot","type":"notes"},{"content":"","date":"2024年10月14日","externalUrl":null,"permalink":"/tags/chroot/","section":"Tags","summary":"","title":"Chroot","type":"tags"},{"content":"","date":"2024年10月14日","externalUrl":null,"permalink":"/tags/%E9%9A%94%E7%A6%BB%E7%8E%AF%E5%A2%83/","section":"Tags","summary":"","title":"隔离环境","type":"tags"},{"content":"","date":"2024年10月14日","externalUrl":null,"permalink":"/tags/%E7%B3%BB%E7%BB%9F%E6%81%A2%E5%A4%8D/","section":"Tags","summary":"","title":"系统恢复","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/bios/","section":"Tags","summary":"","title":"BIOS","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/categories/boot/","section":"Categories","summary":"","title":"BOOT","type":"categories"},{"content":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\n笔记 A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. ==It operates after the system firmware has selected a boot device but before the operating system is loaded.==\n引导加载程序 和 引导管理器 是系统引导过程中密切相关的组件，但它们的用途不同。下面详细介绍了两者之间的关系和区别：\n1. 引导加载程序(bootloader) # 引导加载程序 是一个负责将操作系统内核加载到内存中并启动操作系统的程序。它在低级运行，直接与系统的固件（例如 BIOS/UEFI）交互。\n引导加载程序的主要功能： # 低级功能：引导加载程序的主要功能是将操作系统加载到内存中并将控制权移交给它。它是系统固件（BIOS/UEFI）完成硬件初始化后运行的第一个软件。 位置：在 MBR 系统中，引导加载程序存储在主引导记录（磁盘的前 446 个字节）中。在 GPT 系统中，引导加载程序存储在 EFI 系统分区 (ESP) 中。 通常没有用户界面：引导加载程序的作用主要是功能性的。其主要任务是加载操作系统，而不一定提供用户选择（除非它还充当引导管理器）。 示例： GRUB（GRand Unified Bootloader）是 Linux 系统中最常用的引导加载程序之一。 Windows Bootloader 从磁盘加载 Windows。 LILO（Linux Loader）是 Linux 引导加载程序的另一个示例。 2. 引导管理器（boot manager） # 引导管理器是一个高级程序，允许用户从多个操作系统或内核中进行选择以进行引导。它在系统固件选择引导设备之后但在加载操作系统之前运行。\n引导管理器的主要功能： # 提供操作系统选择菜单：引导管理器允许用户在安装了多个操作系统时选择从哪个操作系统或内核进行引导。它为此显示菜单或界面。 可与多个引导加载程序配合使用：引导管理器可以指示系统为不同的操作系统加载特定的引导加载程序。例如，GRUB 可以加载 Linux 和 Windows 的引导加载程序，让用户可以选择要引导的引导加载程序。 位于 EFI 系统分区或 MBR 中：在 UEFI 系统中，引导管理器文件存储在 EFI 系统分区 (ESP) 中。在 MBR 系统中，像 GRUB 这样的引导管理器可以驻留在 MBR 中。 可以成为引导加载程序的一部分：某些引导加载程序（如 GRUB）也可用作引导管理器。GRUB 不仅加载操作系统，还提供菜单供用户在多个操作系统或内核配置之间进行选择。 示例： GRUB：GRUB 既是引导加载程序又是引导管理器，让用户可以从不同的操作系统或内核版本中进行选择。 Windows 引导管理器：Windows 系统的默认引导管理器，如果安装了多个版本的 Windows，则会显示菜单。 rEFInd：UEFI 系统的独立启动管理器，可以直接从其引导加载程序加载操作系统。 3. 引导加载程序和引导管理器之间的关系 # 相互依赖：引导管理器与引导加载程序协同工作。引导管理器负责向用户提供选项（例如要引导哪个操作系统），而引导加载程序则执行实际加载所选操作系统的任务。例如，GRUB（可充当引导加载程序和引导管理器）将显示一个菜单，供用户在 Linux 和 Windows 之间进行选择。一旦用户做出选择，GRUB 将加载相应的引导加载程序以引导到所选操作系统。\n引导顺序：\n系统固件 (BIOS/UEFI) 启动引导过程，并根据配置的引导设备顺序查找引导加载程序或引导管理器。 引导管理器（如果存在）向用户提供操作系统选择。 执行所选操作系统的 引导加载程序，并将该操作系统的内核加载到内存中。 控制权移交给操作系统，操作系统完成启动过程。 4. 引导加载程序和引导管理器之间的主要区别： # 功能 引导加载程序 引导管理器 主要角色 将操作系统内核加载到内存中 管理用户选择要运行的操作系统或引导加载程序 运行时 在系统固件 (BIOS/UEFI) 之后立即运行 在引导加载程序之前运行以提供操作系统选择 功能 直接加载并启动操作系统 显示用于选择要运行的操作系统或引导加载程序的菜单 用户界面 通常没有用户界面（除非集成） 提供用于选择操作系统的菜单 示例 GRUB（引导加载程序功能）、Windows 引导加载程序 GRUB（启动管理器功能）、Windows 启动管理器、rEFInd 存储位置 MBR 或 EFI 系统分区 (ESP) EFI 系统分区 (ESP) 或有时在 MBR 中 多操作系统支持 直接加载一个操作系统 管理多个引导加载程序或操作系统选择 5. 摘要： # 引导加载程序：加载操作系统并负责启动引导过程。它是系统开始加载操作系统所需的关键低级程序。 引导管理器：允许用户在不同的操作系统或配置之间进行选择。它提供了可以在安装多个操作系统时提供选择的用户界面。 在现代系统中，GRUB 是一种常用工具，它结合了引导管理器和引导加载程序的角色，提供操作系统选择和操作系统的实际加载。但是，某些系统可能会为这两个角色使用单独的程序，尤其是在更复杂的多引导环境中。\n","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/bootloader-%E5%92%8C-boot-manager%E7%9A%84%E5%8C%BA%E5%88%AB%E4%B8%AD%E6%96%87/","section":"笔记","summary":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system’s firmware (e.g., BIOS/UEFI).\n","title":"bootloader 和 boot manager的区别（中文）","type":"notes"},{"content":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\n笔记 A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. ==It operates after the system firmware has selected a boot device but before the operating system is loaded.==\nThe bootloader and the boot manager（不仅可以引导多系统，还可以引导多个 bootloader） are closely related components in the system boot process, but they serve different purposes. Here\u0026rsquo;s a breakdown of the relationship and differences between the two:\n1. Bootloader # A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\nKey Features of a Bootloader: # Low-level function: The bootloader\u0026rsquo;s primary function is to load the operating system into memory and hand over control to it. It is the first software that runs after the system firmware (BIOS/UEFI) has done its hardware initialization. Location: In MBR systems, the bootloader is stored in the Master Boot Record (the first 446 bytes of the disk). In GPT systems, bootloaders are stored in the EFI System Partition (ESP). Doesn’t typically have a user interface: The bootloader’s role is primarily functional. Its main task is to load the OS without necessarily providing user choices (unless it also serves as a boot manager). Examples: GRUB (GRand Unified Bootloader) is one of the most common bootloaders used in Linux systems. Windows Bootloader loads Windows from the disk. LILO (Linux Loader) is another example of a Linux bootloader. 2. Boot Manager # A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. It operates after the system firmware has selected a boot device but before the operating system is loaded.\nKey Features of a Boot Manager: # Presents a menu for OS selection: A boot manager allows users to choose which operating system or kernel to boot from when multiple OSes are installed. It displays a menu or interface for this purpose. Works with multiple bootloaders: The boot manager can direct the system to load specific bootloaders for different operating systems. For example, GRUB can load the bootloaders for both Linux and Windows, allowing the user to choose which one to boot. Located in the EFI System Partition or MBR: In UEFI systems, the boot manager files are stored in the EFI System Partition (ESP). In MBR systems, a boot manager like GRUB can reside in the MBR. Can be part of a bootloader: Some bootloaders, like GRUB, also serve as boot managers. GRUB not only loads the OS but also presents a menu to choose between multiple operating systems or kernel configurations. Examples: GRUB: GRUB is both a bootloader and a boot manager, allowing users to choose from different operating systems or kernel versions. Windows Boot Manager: The default boot manager for Windows systems that presents a menu if multiple versions of Windows are installed. rEFInd: A standalone boot manager for UEFI systems that can load operating systems directly from their bootloaders. 3. Relationship Between Bootloader and Boot Manager # Interdependency: The boot manager works with bootloaders. The boot manager is responsible for presenting the user with options (like which OS to boot), while the bootloader executes the task of actually loading the chosen OS. For example, GRUB (which can act as both a bootloader and a boot manager) will present a menu to choose between Linux and Windows. Once the user makes a selection, GRUB will load the corresponding bootloader to boot into the chosen operating system.\nBoot Sequence:\nSystem firmware (BIOS/UEFI) starts the boot process and looks for a bootloader or boot manager based on the configured boot device order. The boot manager (if present) presents the user with a choice of operating systems. The bootloader for the selected OS is executed, and it loads the kernel of that OS into memory. Control is handed over to the OS, which completes the boot process. 4. Key Differences Between Bootloader and Boot Manager: # Feature Bootloader Boot Manager Primary Role Loads the operating system kernel into memory Manages user selection of which OS or bootloader to run When It Runs Runs immediately after system firmware (BIOS/UEFI) Runs before the bootloader to offer OS selection Functionality Directly loads and starts the OS Presents a menu for choosing which OS or bootloader to run User Interface Typically no user interface (unless integrated) Provides a menu for OS selection Examples GRUB (bootloader function), Windows Bootloader GRUB (boot manager function), Windows Boot Manager, rEFInd Storage Location MBR or EFI System Partition (ESP) EFI System Partition (ESP) or sometimes in the MBR Multiple OS Support Directly loads one OS Manages multiple bootloaders or OS choices 5. Summary: # Bootloader: Loads the operating system and is responsible for starting the boot process. It’s a critical low-level program that the system needs to begin loading the OS. Boot Manager: Allows the user to select between different operating systems or configurations. It provides the user interface that can offer choices when there are multiple OSes installed. In modern systems, GRUB is a common tool that combines the roles of boot manager and bootloader, offering both OS selection and actual loading of the OS. However, some systems may use separate programs for these two roles, especially in more complex multi-boot environments.\n","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/bootloader%E5%92%8Cboot-manager%E7%9A%84%E5%8C%BA%E5%88%ABenglish/","section":"笔记","summary":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system’s firmware (e.g., BIOS/UEFI).\n","title":"BOOTLOADER和BOOT MANAGER的区别（English）","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/gpt/","section":"Tags","summary":"","title":"GPT","type":"tags"},{"content":" GPT（GUID 分区表） 分区方案在结构、功能以及与引导加载程序和系统引导的交互方式方面与 MBR（主引导记录） 方案有很大不同。让我们比较一下这两者，并了解 GPT 在系统引导环境中的工作原理，尤其是对于双引导场景。\n1. GPT 和 MBR 分区方案之间的差异： # 方面 MBR GPT 分区表大小 64 字节（限制为 4 个主分区） 将多个分区存储在更大的表中 分区数 最多 4 个主分区（或 3 个主分区 + 1 个带逻辑分区的扩展分区） 默认支持最多 128 个分区（可以添加更多分区） 磁盘大小支持 最大 2 TB 支持大于 2 TB 的磁盘（最多 9.4 ZB） 引导代码 包含 446 字节的引导加载程序代码（主引导代码） GPT 不以相同的方式存储引导加载程序。引导加载程序存储在 EFI 系统分区 (ESP) 中 冗余 无冗余，分区表仅存储在磁盘的开头 GPT 存储分区表的多个副本以实现冗余（一个在开头，一个在磁盘的末尾） 损坏恢复 更容易损坏；没有自动恢复 更强大，可以使用备份表从损坏中恢复 兼容性 适用于旧式 BIOS 启动模式 专为 UEFI 设计，但可以模拟 MBR 以实现向后兼容（通过“保护性 MBR”） 2. 使用 GPT 启动： # 使用 GPT 启动与 MBR 不同，因为 UEFI（统一可扩展固件接口） 是传统 BIOS 的现代替代品。让我们来探索一下启动差异：\n使用 GPT 和 UEFI 启动： # EFI 系统分区 (ESP)：\n在使用 UEFI 的基于 GPT 的系统中，引导加载程序存储在称为 EFI 系统分区 (ESP) 的特殊分区中。\nESP 包含各种操作系统的引导加载程序文件。这些引导加载程序文件采用 EFI 可执行文件 (. efi) 的形式（可以用 c语言 来写），UEFI 可以直接加载。\n您安装的每个操作系统都可以将其自己的引导加载程序放置在 ESP 中。例如，GRUB 可能位于 /EFI/grub 中，而 Windows 启动管理器位于 /EFI/Microsoft/Boot 中。\n没有主引导记录 (MBR) 代码：\n与 MBR 不同，GPT 中没有单个“主引导代码”。相反，UEFI 根据引导顺序设置或用户输入（通过引导菜单 grub）从 ESP 加载适当的引导加载程序。\n引导管理器：\nUEFI 本身可以充当基本引导管理器。它可以提供一个菜单（取决于 uefi 的固件支不支持），列出存储在 ESP 中的可用操作系统或引导加载程序。这允许您选择从哪个操作系统引导，而无需单独的引导管理器（如 GRUB）。\n但是，对于更复杂的设置（例如，使用 Linux 和 Windows 双引导），仍然可以安装 GRUB 或其他引导管理器以提供更多灵活性（例如，自定义引导选项、在不同的 Linux 内核之间进行选择等）。\n使用 GPT 和传统 BIOS（CSM 模式）启动： # 兼容性支持模块 (CSM)： 一些支持 UEFI 的系统还提供兼容性支持模块 (CSM)，允许它们在传统 BIOS 模式下启动。 如果您使用的是 GPT 磁盘，但想要在传统 BIOS 模式下启动，则可以将系统配置为通过 CSM 模拟 BIOS。在这种情况下，您可能需要“保护性 MBR”以避免与不理解 GPT 的旧工具发生兼容性问题。 但是，使用 GPT 在传统模式下启动通常不太常见，并且不是 GPT 的预期设计，GPT 旨在与 UEFI 配合使用。 3. 使用 GPT 进行双启动： # 在使用 GPT 的双启动场景中，所涉及的过程和工具与基于 MBR 的系统略有不同：\n安装多个操作系统：\n两个操作系统（例如 Linux 和 Windows）都将其引导加载程序文件放在 EFI 系统分区 (ESP) 中。每个操作系统都会在 ESP 中创建一个目录来存储其引导加载程序。\nUEFI 启动菜单：\nUEFI 可以识别 ESP 中的多个引导加载程序条目，并且通常提供基本启动菜单。您可以在启动期间按下某个键（通常是 F 12、ESC 或根据制造商的不同而不同的其他键）来访问该菜单，以手动选择要加载哪个引导加载程序（例如 Windows 启动管理器、GRUB）。\nGPT 上的 GRUB：\n如果您在具有 UEFI 的 GPT 磁盘上安装 GRUB，它仍将用作启动管理器，允许您在多个操作系统之间进行选择。GRUB 将检测 Windows 启动管理器和其他可启动条目，并在其启动菜单中显示它们。\nGRUB 的引导加载程序安装在 EFI 系统中分区（不在 MBR 中，因为 GPT 不使用 MBR 引导代码），UEFI 将从 ESP 加载 GRUB。\n没有 GRUB？：\n如果您不安装 GRUB 或任何其他引导管理器，UEFI 将从 ESP 启动默认操作系统引导加载程序（例如，Windows 引导管理器）。如果只有一个操作系统的引导加载程序存在或配置为默认，则不会有任何引导菜单，系统将直接启动到该操作系统。\n4. MBR 和 GPT 之间双引导设置的主要区别： # MBR：\n引导加载程序存储在 MBR 中。\n有限的分区支持。\n需要在 MBR 中安装 GRUB（或其他引导管理器）以提供双引导系统的引导菜单。\nGPT：\n引导加载程序存储在 EFI 系统分区中。\n没有像 MBR 中的单个“主引导代码”； UEFI 直接从 ESP 加载引导加载程序。\n如果 ESP 中存在多个引导加载程序，UEFI 可以提供基本启动菜单，但可以安装 GRUB 或其他启动管理器以实现更高级的控制。\n支持更多分区和更大的磁盘大小。\n摘要： # MBR 依赖于主引导记录中的单个引导加载程序，并支持有限的分区和较小的磁盘大小。 GPT 专为 UEFI 系统设计，使用 EFI 系统分区 (ESP) 来存储多个引导加载程序，并允许使用许多分区和更大的磁盘进行灵活的启动管理。 使用 GPT，UEFI 可以提供启动菜单以在不同的操作系统之间进行选择（如果已配置），但更高级的启动管理（如 GRUB）仍可用于复杂的双启动设置。 ","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/gpt%E5%88%86%E5%8C%BA%E6%96%B9%E6%A1%88/","section":"笔记","summary":" GPT（GUID 分区表） 分区方案在结构、功能以及与引导加载程序和系统引导的交互方式方面与 MBR（主引导记录） 方案有很大不同。让我们比较一下这两者，并了解 GPT 在系统引导环境中的工作原理，尤其是对于双引导场景。\n","title":"GPT分区方案","type":"notes"},{"content":"是的，GRUB 不会将其所有引导加载程序代码都存储在 /boot/grub 中。它使用 多阶段架构，其中引导加载程序的不同部分存储在不同的位置，这对于使用 MBR 和 GPT 分区方案的系统至关重要，尤其是在 BIOS 模式 下启动时。\nGRUB 的多阶段架构说明 # GRUB 的启动过程分为几个阶段，每个阶段都存储在不同的位置，具体取决于分区方案（MBR 或 GPT）和启动模式（BIOS 或 UEFI）。以下是细分：\n1. GRUB 第 1 阶段 # 位置：存储在 MBR（主引导记录）或 GPT 系统中的 保护性 MBR（protective MBR） 中。 大小：仅 446 字节（磁盘前 512 字节的一部分）。 功能：阶段 1 的唯一目的是加载 GRUB 的下一阶段（阶段 1.5 或阶段 2）。由于阶段 1 非常小，因此它无法执行读取文件系统等复杂任务。 2. GRUB 阶段 1.5 # 位置： 在 MBR 磁盘 上：存储在 后 MBR 间隙（post-mbr gap）（MBR 和磁盘上第一个分区之间的小空间）中。 在 GPT 磁盘 上：存储在专用的 BIOS 启动分区（通常大小为 1-2 MB）中。 功能：阶段 1.5 包含必要的 文件系统驱动程序 和从分区（例如 /boot/grub）读取所需的其他代码。它弥补了阶段 1（最小）和阶段 2（功能齐全）之间的差距。 如果没有阶段 1.5，GRUB 将无法找到并读取存储在 /boot/grub 目录中的实际文件，因为阶段 1 本身缺乏理解文件系统（例如 ext 4、Btrfs 等）的能力。 3. GRUB 阶段 2 # 位置：存储在分区（通常是 /boot 分区或操作系统的根分区）上的 /boot/grub/ 目录中。 功能：阶段 2 是完整的引导加载程序，它： 显示 GRUB 菜单（如果有多个操作系统或内核）。 允许用户选择要引导的操作系统或内核。 将 操作系统内核 加载到内存中（例如 Linux 内核）。 将控制权传递给操作系统。 当我们在多启动系统上看到 GRUB 菜单时，我们经常与 Stage 2 交互。它可以自定义，可以提供图形界面，并处理启动操作系统的更复杂任务。\nGRUB 为什么需要 Stage 1.5？ # Stage 1.5 的主要原因是 Stage 1 太小，无法包含对不同文件系统的支持。如果没有 Stage 1.5，GRUB 的 Stage 1 将无法理解从分区中加载 Stage 2 的位置或方式，因为分区通常使用复杂的文件系统（如 ext 4、XFS 等）。Stage 1.5 包括文件系统驱动程序，允许 GRUB 在分区上找到 /boot/grub 目录，并从中加载 Stage 2。\n带有 Stage 1.5 的 MBR：在 MBR 系统上，Stage 1.5 存储在 MBR 后间隙中，这为其提供了足够的空间来存储必要的文件系统驱动程序。 带有 Stage 1.5 的 GPT：在 GPT 系统上，不存在 MBR 后间隙，因此 Stage 1.5 存储在 BIOS 启动分区中。这是 **GRUB 在 GPT 上以 BIOS 模式运行所必需的。 摘要 # Stage 1（存储在 MBR 中）非常小，仅用于加载 Stage 1.5 或 Stage 2。 Stage 1.5（存储在 MBR 的 MBR 后间隙中或 GPT 的 BIOS 启动分区中）是加载文件系统驱动程序和继续启动过程所必需的。 第 2 阶段（存储在 /boot/grub/ 中）是功能齐全的引导加载程序，它显示 GRUB 菜单、允许选择操作系统并加载操作系统内核。 因此，GRUB 不仅将引导代码存储在 /boot/grub/ 中；它还将关键的引导加载程序代码存储在 MBR（第 1 阶段）中，如果是 GPT 磁盘上的 BIOS 模式，则存储在 BIOS 引导分区（第 1.5 阶段）中。这种架构使 GRUB 能够正确加载和引导操作系统。\n","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/grub/","section":"笔记","summary":"是的，GRUB 不会将其所有引导加载程序代码都存储在 /boot/grub 中。它使用 多阶段架构，其中引导加载程序的不同部分存储在不同的位置，这对于使用 MBR 和 GPT 分区方案的系统至关重要，尤其是在 BIOS 模式 下启动时。\n","title":"GRUB","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/grub/","section":"Tags","summary":"","title":"GRUB","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/mbr/","section":"Tags","summary":"","title":"MBR","type":"tags"},{"content":" 笔记 MBR（主引导记录） 结构就是主引导代码+分区表（+post-mbr gap），主引导代码 就是专门负责用来加载系统到内存中的，分区表 就是一个磁盘的整体布局。post-mbr gap 就是用来过渡引导的阶段 1 和阶段 2 的。如果就是，主引导记录用来引导 GRUB，==则就会出现 grub 的代码覆盖了原本的主引导的原先代码。==\n1. MBR 结构： # 主引导记录 (MBR) 由两个主要部分组成： 主引导代码 (446 字节)：此部分包含负责将操作系统或其他引导加载程序（如 GRUB）加载到内存中的引导加载程序代码。 分区表 (64 字节)：此部分包含磁盘分区的布局，包括其起始和结束位置。 在 MBR（主引导记录） 设置中，磁盘布局在 MBR（占用前 512 个字节）和磁盘上的第一个分区之间留下一小块未使用的空间。此空间通常称为 后 MBR 间隙（post-mbr gap），通常约为 31 KB。此间隙用于存储 BIOS 在启动过程中加载的额外引导加载程序代码。(==所以从这里可知: 单纯靠 mbr 无法完整引导系统，所以要靠 post-mbr gap 来将阶段 1 过渡到阶段 2) 如果 MBR 的主引导代码部分为空或已损坏，则系统将无法加载任何操作系统，因为没有代码来指示系统如何继续引导过程。分区表可能仍然完好无损，但如果没有引导代码，系统将不知道如何从这些分区启动引导。\n2. 启动具有双操作系统（GRUB 和 MBR）的系统： # 当您拥有双系统（例如 Linux 和 Windows）时，引导加载程序是必需的，以便提供选择要启动哪个操作系统的方法。 如果 MBR 包含标准引导加载程序（如默认的 Windows 引导加载程序），它将仅启动默认操作系统（在本例中为 Windows），而不提供选择其他操作系统的菜单。 另一方面，GRUB 是一种更高级的引导加载程序，可以检测多个操作系统。如果 MBR 中安装了 GRUB，它将加载并提供启动菜单，允许您选择要启动哪个操作系统。 如果 MBR 不包含 GRUB（或任何启动管理器）： # 没有启动管理器菜单：如果 MBR 中没有安装 GRUB（或其他启动管理器，如 rEFInd 或 LILO），您将没有菜单来选择要启动哪个操作系统。系统将直接启动到与已安装的引导加载程序关联的默认操作系统。 Windows 引导加载程序：例如，如果 Windows 引导加载程序 位于 MBR 中，它将直接启动到 Windows，而不提供启动 Linux 的选项。 3. GRUB 和引导管理器角色： # 当 GRUB 安装在 MBR 中时，它会用自己的代码替换默认引导代码，该代码可以： 检测多个操作系统安装（例如 Linux 和 Windows）。 在启动期间显示启动菜单，允许您选择要启动的操作系统。 如果您在设置双系统后安装 GRUB，GRUB 通常会扫描其他已安装的操作系统（如 Windows）并将其添加到其启动菜单中。如果 MBR 不包含 GRUB 或任何其他引导管理器，您将只能启动与 MBR 中存在的引导加载程序关联的操作系统（例如，如果 Windows 引导加载程序存在，则为 Windows）。\n结论： # 如果您的 MBR 为空，您将根本无法启动系统。 如果您有 双系统，但 GRUB（或其他启动管理器） 未安装在 MBR 中，您将无法获得启动菜单来选择要启动的操作系统。系统将默认使用现有的任何引导加载程序（例如，Windows 引导加载程序），这可能会自动加载一个操作系统而不显示另一个系统的任何选项。 要启用双启动功能，在 MBR 中安装像 GRUB 这样的启动管理器是必不可少的。\n补充说明 post-mbr gap # 流程 # 第 1 阶段引导加载程序执行： 第 1 阶段引导加载程序（MBR 中的小引导代码）非常有限。它的工作是定位和加载引导加载程序的下一阶段，通常称为第 1.5 阶段或直接称为第 2 阶段。 由于 MBR 只有 446 字节的空间，因此它无法容纳完整的引导加载程序（如 GRUB 或其他引导管理器），也无法容纳直接引导操作系统所需的复杂代码。 MBR 后间隙（也称为“嵌入区域”）： 在 MBR 之后，MBR 与磁盘上的第一个分区之间通常存在间隙。此间隙通常称为 MBR 后间隙 或 嵌入区域。 在 传统 BIOS-MBR 引导 中，此间隙为引导加载程序的 第 1.5 阶段 代码提供了额外的空间。第 1.5 阶段代码是必需的，因为： 它包含理解磁盘上文件系统的代码。 它提供加载完整 第 2 阶段引导加载程序 所需的额外功能。 此间隙通常介于 30 KB 到 60 KB 之间，具体取决于分区方案和引导加载程序。 阶段 1.5 引导加载程序： 阶段 1.5 是 MBR 中最小 阶段 1 引导加载程序 与完整 阶段 2 引导加载程序 之间的桥梁。 它位于 MBR 后间隙中，包含理解存储 阶段 2 引导加载程序的文件系统（例如 ext 4、NTFS 等）所需的基本驱动程序或代码。 例如，在 GRUB 的情况下，阶段 1.5 允许 GRUB 访问包含完整 /boot/grub 目录的分区。 阶段 2 引导加载程序： 一旦加载 阶段 1.5，它就会找到完整的 阶段 2 引导加载程序，该程序通常存储在操作系统分区上的 /boot 目录中。 第 2 阶段 是完整的引导加载程序（例如 GRUB 或 LILO），它： 显示引导菜单（如果配置了多个操作系统）。 将所选操作系统的内核加载到内存中。 将控制权移交给内核以引导系统。 操作系统内核： 一旦 第 2 阶段引导加载程序 将内核加载到内存中，它就会开始执行内核，从而接管引导过程、初始化硬件、加载驱动程序并最终引导系统。 为什么后 MBR 间隙很重要？ # MBR 引导代码 只有 446 个字节，非常小，不足以加载完整的引导加载程序（如 GRUB）。 后 MBR 间隙 为引导加载程序的代码（第 1.5 阶段）提供了额外的空间，而这些空间无法容纳在 MBR 本身中。 使用 MBR 磁盘 时，此间隙对于 基于 BIOS 的启动 至关重要，因为它包含读取磁盘文件系统和加载完整引导加载程序所需的代码。 ","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/mbr%E5%88%86%E5%8C%BA%E6%96%B9%E6%A1%88/","section":"笔记","summary":" 笔记 MBR（主引导记录） 结构就是主引导代码+分区表（+post-mbr gap），主引导代码 就是专门负责用来加载系统到内存中的，分区表 就是一个磁盘的整体布局。post-mbr gap 就是用来过渡引导的阶段 1 和阶段 2 的。如果就是，主引导记录用来引导 GRUB，==则就会出现 grub 的代码覆盖了原本的主引导的原先代码。==\n","title":"MBR分区方案","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/post/","section":"Tags","summary":"","title":"POST","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/sysfs/","section":"Tags","summary":"","title":"Sysfs","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/uefi/","section":"Tags","summary":"","title":"UEFI","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%90%AF%E5%8A%A8/","section":"Tags","summary":"","title":"启动","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%BC%80%E6%9C%BA%E6%B5%81%E7%A8%8B/","section":"Tags","summary":"","title":"开机流程","type":"tags"},{"content":" boot 方式和分区方案的最佳配合 # uefi+gpt bios+mbr BIOS+MBR # 笔记 MBR (主引导记录)的整体详细结构简单介绍就是：主引导代码+分区表+post-mbr gap\n单纯靠 主引导代码 无法完整引导系统，所以要有 post-mbr gap （MBR 与第一个磁盘分区之间的间隙）来辅助引导系统，可以说是一个过渡用的。\n第一阶段就是主引导代码，主要作用就是定位和加载下一阶段的引导 第 1.5 阶段就是（post-mbr gap 做的）主要作用就是理解存储阶段 2 引导加载程序的文件系统（例如 ext 4、NTFS 等）所需的基本驱动程序或代码, 就是可能也有一部分加载程序在文件系统(grub additional code)中，这样就可以读取文件 (一些加载程序文件从而辅助 boot) 第 2 阶段就是引导加载程序了（bootloader boot manager） 到此，基本就可以引导完系统了，然后操作系统就加载在内存中了，控制权就交给系统内核了\nUEFI+GPT # 这个基本就没有什么，就主要在分区时，有个 ESP 分区 就可以了，这个引导程序（bootloader boot manager）就存储在 ESP 分区\n一些 linux 中的命令：\n下载配置文件 grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=ARCH 生成配置文件并放置于/boot 分区（这个 ESP 分区） grub-mkconfig -o /boot/grub/grub.cfg 有多系统的记得下载 os-prober, 并 vim /etc/default/grub 添加 GRUB_DISABLE_OS_PROBER=false 这一行 :::tips **注意：**这里只要是将提前装好系统并使用上述命令生成了grub的，你将这个硬盘换到另一个机子上来运行OS的话。这是你要 重新用live-usb这些启动重新再新机子上重新使用上述命令来生成grub，否则无法启动系统 :::\nBIOS+GPT # 首先声明，这种情况，你要添加一个 1 M-2 M 的 BIOS 分区才行\n理由 # gpt 磁盘本身第一个扇区（磁盘的前 512 个字节）就是一个 protective mbr 分区，这是为了向只能 BIOS 引导的旧机子兼容而产生的。\n讲述 protective mbr 的文章。大致重要内容如下：\n大概内容说白了就是为了兼容和保护 gpt 磁盘\n==省流: 就是这个 BIOS boot 分区相当于 mbr 的 post-mbr gap 用来辅助 bootloader 的加载的==\n第 1 阶段引导加载程序加载 BIOS 引导分区： # 由于系统使用的是 GPT 分区方案，因此不存在 后 MBR 间隙（MBR 和第一个分区之间的空间），MBR 系统通常会使用该间隙来存储其他引导加载程序代码（如 GRUB Stage 1.5）。 相反，在磁盘上创建了一个专用的 BIOS 引导分区（通常大小为 1-2 MB）。此分区不包含文件系统，也不用于存储常规文件 - 它仅用于保存 GRUB Stage 1.5。 来自保护性 MBR 的 第 1 阶段代码定位 BIOS 引导分区并从中加载 第 1.5 阶段引导加载程序代码。 GRUB Stage 1.5 处理文件系统访问： # Stage 1.5 是至关重要的一步，因为它包含 GRUB 理解磁盘文件系统（例如 ext 4、Btrfs 等）所需的驱动程序和代码。 加载 BIOS 启动分区后，Stage 1.5 现在可以读取磁盘中的文件，例如 GRUB 配置和Stage 2 引导加载程序代码，这些代码存储在 /boot/grub/ 中。 Stage 2 引导加载程序从 /boot/grub 加载： # Stage 1.5 加载后，它会找到Stage 2 引导加载程序，这些代码存储在其中一个分区上的 /boot/grub/ 目录中。 Stage 2 是功能齐全的 GRUB 引导加载程序。它： 如果您安装了多个操作系统（双启动设置），则显示 GRUB 启动菜单。 允许您选择要启动的操作系统或内核。 将所选操作系统的 内核 加载到内存中。 ","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/%E7%AE%80%E5%8D%95%E6%80%BB%E7%BB%93mbr%E5%92%8Cgpt%E5%88%86%E5%8C%BA/","section":"笔记","summary":"boot 方式和分区方案的最佳配合 # uefi+gpt bios+mbr BIOS+MBR # 笔记 MBR (主引导记录)的整体详细结构简单介绍就是：主引导代码+分区表+post-mbr gap\n","title":"简单总结MBR和GPT分区","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"驱动程序","type":"tags"},{"content":"当然！启动计算机的过程，从开机到加载操作系统并进入桌面，涉及多个阶段和组件。下面详细介绍了整个启动过程，包括 BIOS/UEFI、引导加载程序、启动管理器的作用以及操作系统的加载。\n1. 开机自检 (POST) # 开机： 当您打开计算机电源时，电源会向主板和所有连接的组件供电。 POST： 固件（BIOS 或 UEFI）运行一系列称为开机自检 (POST) 的诊断测试。这会检查必要的硬件组件（CPU、RAM、存储设备等）以确保它们正常运行。如果发生任何错误，系统可能会显示错误消息或蜂鸣代码。 2. 固件初始化 # BIOS/UEFI 初始化： 完成 POST 后，固件初始化硬件并为启动过程设置环境。 BIOS（基本输入/输出系统）是旧系统中使用的传统固件，而 UEFI（统一可扩展固件接口）是现代替代品，可提供增强的功能，包括支持更大的磁盘和更快的启动时间。 UEFI 固件会初始化storage（SSD, HDD 等）, cpu, memory（RAM） 基本外围设备。一开始，主机控制权在 UEFI 固件中，然后如果有 bootloader 或者 boot manager，控制权可能就转交给他们。系统加载完成后，控制权就交给系统了。 3. 启动设备选择 # 启动顺序： 固件查看其设置中配置的启动顺序以确定从哪个设备启动（例如硬盘、SSD、USB、网络）。 UEFI 通常具有图形界面来配置启动选项，而 BIOS 可能具有基于文本的界面。 4. 加载引导加载程序（bootloader） # 引导设备访问： 固件访问所选的引导设备并查找引导加载程序。 如果 GPT 磁盘带有 UEFI，它将扫描 EFI 系统分区 (ESP) 以查找引导条目。 加载引导加载程序： UEFI 根据引导顺序找到适当的 .efi 文件（例如，GRUB 的 grubx64.efi）并将其加载到内存中。 5. 引导管理器（如果适用） # 引导管理器角色： 如果引导加载程序（如 GRUB）包含引导管理器功能，则在检测到多个操作系统时，它将向用户显示菜单。例如，GRUB 可以显示 Linux 和 Windows 的选项。 如果没有显示菜单，它将自动加载默认操作系统。 6. 加载操作系统 # 操作系统内核加载： 一旦用户选择操作系统或选择默认操作系统，引导加载程序就会执行必要的命令，将操作系统的内核加载到内存中。 引导加载程序将控制权交给内核，初始化内核的子系统并加载运行操作系统所需的基本驱动程序。 用户空间初始化： 内核初始化系统进程和服务，挂载根文件系统，并为用户空间应用程序准备环境。 这包括加载必要的驱动程序和系统服务，使计算机能够与键盘、鼠标和显示器等硬件进行通信。 启动过程摘要： # 固件 (BIOS/UEFI) 执行开机自检 (POST)。 固件初始化硬件组件并检查启动顺序。 找到并从EFI 系统分区或 MBR 加载引导加载程序（如 GRUB）。 如果适用，启动管理器会显示一个菜单供操作系统选择。 引导加载程序将操作系统内核加载到内存中。 操作系统初始化系统进程并准备用户环境。 从开机到进入桌面的整个过程通常只需几秒到一分钟，具体取决于硬件性能以及操作系统及其配置的复杂性。\n","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/%E4%B8%80%E4%B8%AA%E6%95%B4%E4%BD%93%E7%9A%84%E5%BC%80%E6%9C%BA%E6%B5%81%E7%A8%8B/","section":"笔记","summary":"当然！启动计算机的过程，从开机到加载操作系统并进入桌面，涉及多个阶段和组件。下面详细介绍了整个启动过程，包括 BIOS/UEFI、引导加载程序、启动管理器的作用以及操作系统的加载。\n","title":"一个整体的开机流程","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E5%8A%A0%E8%BD%BD/","section":"Tags","summary":"","title":"引导加载","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E5%8A%A0%E8%BD%BD%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"引导加载程序","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"引导程序","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%9B%BA%E4%BB%B6/","section":"Tags","summary":"","title":"固件","type":"tags"},{"content":" 固件 # 定义：固件是嵌入硬件或由系统加载以直接控制硬件内部操作的低级软件。它通常存储在硬件设备本身的非易失性存储器（如 ROM、EEPROM 或闪存）中。\n角色：固件提供允许硬件正常运行的基本控制指令。它非常接近硬件并且通常在最基本的层面上运行。这意味着它控制硬件的基本行为和特性，例如网卡如何处理数据传输或显卡如何处理渲染。\n直接控制：在某些情况下，固件直接与硬件通信，而不依赖于操作系统 (OS)。例如，BIOS/UEFI 固件甚至在操作系统启动之前就控制硬件。某些设备可以仅使用其固件（例如嵌入式系统）运行，但如果没有来自操作系统的高级控制，它们可能会受到限制。\n示例：\nBIOS/UEFI，用于在计算机启动时初始化和配置系统硬件。\n显卡固件（如 VBIOS），用于设置 GPU 以进行基本渲染和与操作系统通信。\nSSD 固件，用于管理存储设备的内部磨损均衡、垃圾收集和错误更正过程。\n驱动程序 # 定义：驱动程序是一种软件，允许操作系统（如 Linux、Windows 或 macOS）与硬件设备通信。驱动程序充当操作系统和硬件之间的中介。\n角色：驱动程序使操作系统和更高级别的软件能够控制和使用硬件。固件控制硬件的内部操作，而驱动程序解释来自操作系统的命令并将其转换为硬件可以理解和执行的指令。驱动程序还抽象硬件复杂性，允许操作系统和应用程序与硬件交互，而无需了解其低级细节。\n操作系统控制：与固件不同，驱动程序由操作系统在运行时加载。如果没有驱动程序，操作系统就无法直接控制硬件，但一旦安装了驱动程序，操作系统就可以通过驱动程序向硬件发送请求（例如，读取/写入数据、显示图形、发送网络数据包）。驱动程序还使操作系统能够以标准化的方式访问硬件资源。\n示例：\n图形驱动程序（如 NVIDIA、AMD 或 Intel 驱动程序）允许操作系统和应用程序渲染复杂的图形并访问 GPU 功能。 网络驱动程序使操作系统能够与系统的网卡通信并处理网络上的数据传输。 存储驱动程序使操作系统能够与硬盘驱动器、SSD 和 USB 设备交互。 固件和驱动程序之间的主要区别 # 方面 固件 驱动程序 位置 嵌入硬件或存储在 /lib/firmware 安装在操作系统中（例如，在 Linux 上存储在 /lib/modules/ 中） 控制级别 直接控制硬件的内部工作 为操作系统提供与硬件通信的接口 加载时间 在操作系统启动前加载（例如，BIOS/UEFI）或由操作系统加载以初始化硬件 由操作系统在运行时加载以管理硬件功能 用途 管理基本的低级硬件功能 使操作系统能够访问和控制硬件 与操作系统的交互 通常独立于操作系统；在硬件级别工作 需要操作系统才能运行；作为操作系统和硬件之间的中介 示例 GPU 固件、SSD 固件、UEFI 固件 图形驱动程序、网络驱动程序、声音驱动程序 固件和驱动程序如何协同工作 # 固件控制硬件的内部操作：例如，网卡上的固件管理卡在网络上物理发送和接收数据包的方式。它在非常低的级别处理数据包缓冲和错误检查等事情。\n驱动程序在硬件（由固件控制）和操作系统之间进行接口：操作系统中的网卡驱动程序从操作系统接收更高级别的请求（如“发送此数据包”或“连接到此网络”）并将其传递给网卡，网卡使用其固件执行这些指令。\n固件 + 驱动程序示例：\n显卡上的固件设置其低级操作并确保它可以渲染基本图像，但操作系统中的图形驱动程序通过在应用程序和硬件之间提供接口，允许用户充分利用 GPU 的游戏、3 D 建模或视频渲染功能。\n摘要 # 固件直接控制硬件的内部、低级操作，使其正常运行。 驱动程序通过抽象硬件复杂性并提供标准化接口，允许操作系统与硬件进行通信和控制。 固件和驱动程序共同确保硬件设备正常工作，并可由操作系统和用户级应用程序充分利用。\n","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/%E5%9B%BA%E4%BB%B6-%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F/","section":"笔记","summary":"固件 # 定义：固件是嵌入硬件或由系统加载以直接控制硬件内部操作的低级软件。它通常存储在硬件设备本身的非易失性存储器（如 ROM、EEPROM 或闪存）中。\n","title":"固件 驱动程序","type":"notes"},{"content":"在 Linux 中，固件 是指与特定硬件组件紧密相关的低级软件，通过提供指令和控制机制使它们能够正常运行。此固件在内核之下运行，但对于硬件与操作系统交互至关重要。\n让我们分解一下问题的每个部分：\n1. Linux 中的固件 # 在 Linux 系统中，固件通常是指在以下硬件设备上运行的微代码或二进制代码：\nCPU（例如，Intel 或 AMD 微代码） GPU（显卡固件） 网卡（Wi-Fi 或以太网适配器固件） 存储控制器（例如，SSD 固件） 外围设备（例如，USB 设备、声卡） ==一些固件内置于硬件本身，而其他固件则由操作系统在启动期间加载。==Linux 使用存储在 /lib/firmware 目录中的文件来处理固件加载。\n2. /sys 目录中的固件 # Linux 中的 /sys 目录是 sysfs 虚拟文件系统的一部分，它提供了内核和用户空间之间的接口，公开了有关硬件设备和内核子系统的详细信息。尽管 sysfs 主要处理内核和设备信息，但它也可以公开固件相关数据。\n/sys/firmware：此目录包含与系统固件相关的信息和接口，例如： /sys/firmware/efi：如果您的系统以 UEFI 模式启动，则此目录包含有关 UEFI 固件的详细信息，例如配置表、运行时变量和内存映射。 /sys/firmware/acpi：与 ACPI（高级配置和电源接口）固件相关的信息，可帮助进行电源管理和硬件配置。 /sys/firmware/dmi：DMI（桌面管理接口）表提供系统信息，例如主板型号、系统制造商和 BIOS/UEFI 版本。 3. 固件、内核、硬件和系统之间的关系 # 这些组件中的每一个都在计算机的整体运行中发挥着关键作用：\n固件 # 它是什么：固件是嵌入硬件或由系统在运行时加载的低级软件。它提供了硬件应如何运行和与操作系统通信的基本指令。 角色：固件直接控制硬件，使其能够运行。如果没有固件，网卡、CPU 和 GPU 等硬件组件可能无法正常工作或根本无法工作。 位置：固件要么预装在硬件中（非易失性存储器），要么从操作系统加载（来自 Linux 中的“/lib/firmware”）。 内核 # 它是什么：Linux 内核是操作系统的核心。它管理系统资源，包括内存、进程和硬件。 角色：内核负责通过驱动程序与硬件通信，驱动程序充当内核和硬件设备之间的中介。在需要时，内核还可以加载固件来初始化和控制特定硬件。 与固件的关系：内核与固件交互以初始化设备。它在需要时将固件加载到设备中，并处理固件与更高级别系统进程之间的通信。 硬件 # 它是什么：计算机的物理组件，例如 CPU、GPU、网卡、硬盘和外围设备。 角色：硬件执行系统所需的物理任务（例如计算、存储、输入/输出）。但是，硬件需要固件和内核驱动程序才能正常运行并与系统的其余部分交互。 系统 # 它是什么：整个计算机，包括硬件、固件、内核和用户级软件（应用程序、实用程序等）。 角色：系统作为一个整体，提供用户可以与应用程序和服务交互的环境，而应用程序和服务又依赖内核来访问硬件资源。如果没有固件，系统可能无法正确初始化硬件组件。 交互示例： # 当 Linux 系统启动时：\n固件 (BIOS/UEFI) 初始化硬件，执行 POST（开机自检），然后将控制权传递给引导加载程序（如 GRUB）。 引导加载程序将 内核 加载到内存中。 加载内核后，它开始初始化设备（如网卡或 GPU）。如果硬件需要额外的固件，内核将从 /lib/firmware 加载它。 内核使用 驱动程序 来管理硬件和操作系统之间的通信。 硬件现在已正确初始化并由其固件和内核驱动程序控制，可以执行其任务（例如，网络传输、渲染图形等）。 关系摘要： # 固件是直接控制硬件组件的低级软件。==可以与硬件直接通信，不依赖于操作系统。== 内核是操作系统的核心，它使用驱动程序管理系统资源和硬件，并在必要时加载固件。 硬件依靠固件和内核驱动程序与操作系统通信。 系统包含所有这些组件，共同为用户提供功能环境。==系统要控制硬件，必须要通过驱动程序。== ","date":"2024年10月10日","externalUrl":null,"permalink":"/notes/system/%E5%9B%BA%E4%BB%B6-%E7%A1%AC%E4%BB%B6-%E7%B3%BB%E7%BB%9F/","section":"笔记","summary":"在 Linux 中，固件 是指与特定硬件组件紧密相关的低级软件，通过提供指令和控制机制使它们能够正常运行。此固件在内核之下运行，但对于硬件与操作系统交互至关重要。\n","title":"固件 硬件 系统","type":"notes"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/categories/%E7%A1%AC%E7%9B%98/","section":"Categories","summary":"","title":"硬盘","type":"categories"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E7%A1%AC%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"硬件管理","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E7%A1%AC%E4%BB%B6%E6%8E%A7%E5%88%B6/","section":"Tags","summary":"","title":"硬件控制","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/","section":"Tags","summary":"","title":"操作系统","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8/","section":"Tags","summary":"","title":"多系统启动","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%8C%BA/","section":"Tags","summary":"","title":"分区","type":"tags"},{"content":"","date":"2024年10月10日","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%8C%BA%E5%AF%B9%E6%AF%94/","section":"Tags","summary":"","title":"分区对比","type":"tags"},{"content":"","date":"2024年9月27日","externalUrl":null,"permalink":"/tags/lvm/","section":"Tags","summary":"","title":"LVM","type":"tags"},{"content":"","date":"2024年9月27日","externalUrl":null,"permalink":"/tags/%E9%80%BB%E8%BE%91%E5%8D%B7/","section":"Tags","summary":"","title":"逻辑卷","type":"tags"},{"content":" 大致流程 # 流程如下：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 在逻辑卷上创建文件系统（如 Ext4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 文件系统介绍 # 1. 文件系统 # 文件系统说白了就是操作系统管理硬盘中数据的一个方式。 详细点就是：问阿金系统可以管理 硬盘，SSD，u盘 等存储设备的数据布局，提供读写文件的接口。文件系统负责存储文件、文件的元数据（如文件名称、权限、时间戳等），以及组织文件所在的路径结构（如文件夹层级）。\n2. 文件系统格式 # 主流格式如下：\nNTFS（Windows默认文件系统） FAT32（一种兼容性较好的文件系统，用于U盘等设备） exFAT（常用于大容量的外部存储设备） XFS（高性能的文件系统，适合大数据存储） Btrfs（现代Linux文件系统，提供高级功能，如快照、子卷等） ZFS（跨平台、具有高数据完整性和快照功能） 笔记 不同的文件系统格式的区别就是：性能，容量，恢复 等的区别。\n硬盘操作和文件系统关系 # 回顾 # 上一节的硬盘相关-卷这个操作流程：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 其实还不能达到我们平时打开文件资源管理器中点击文件夹就可以实现跳转并显示下一层的文件内容。（说白了这个逻辑卷还只是一个没有关联的存储空间，无法读取和写入数据）我们还要在逻辑卷上操作然后才可以实现如上效果。 要进行的操作就是：\n在逻辑卷上创建文件系统（如 Ext 4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 Tips 这个文件系统的格式是可以自己选择的，就是我们安装系统的时候，选择自定义安装或者高级安装方式，可以手动选择文件系统格式，手动划分硬盘，位每个分区或者逻辑卷选择文件系统格式。例如：\n你可以选择在 / 根目录分区上使用 Ext4。 在 /home 分区上使用 XFS。 在 /boot 分区上使用 Ext 3，等等。 创建好文件系统格式后，我们就要将其挂载到系统目录下了。原因如下：\n原因 # ==我们创建好文件系统格式后，是可以让硬盘知道怎么管理数据了，但是这个数据是怎么来的，就是从操作系统上来的。既然是从操作系统上来的，我们就要挂载到操作系统上才可以来让数据写入到硬盘中，从而文件系统可以按照格式来管理数据==\n让操作系统识别并访问数据： 挂载的作用是将存储设备（如硬盘分区或逻辑卷）关联到操作系统中的某个目录。\n例如，执行一下挂载命令：\nsudo mount /dev/myvg/mylv /mnt/mydata（/mnt一般是挂载目录） 文件系统的入口点： 文件系统是用于组织数据的，而挂载点（如 /mnt/mydata）是文件系统与操作系统交互的入口。 多卷管理：挂载可以实现在不同目录下挂载多个存储设备或逻辑卷。类比于：就是在电脑上可以同时挂载个 u 盘或者挂载个移动硬盘。 动态扩展和迁移：就是继续上面的例子，u 盘和移动硬盘可以随时卸载（umount），插在别的电脑上。 ","date":"2024年9月27日","externalUrl":null,"permalink":"/notes/system/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%92%8C%E5%8D%B7%E7%9A%84%E5%85%B3%E7%B3%BB/","section":"笔记","summary":"大致流程 # 流程如下：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 在逻辑卷上创建文件系统（如 Ext4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 文件系统介绍 # 1. 文件系统 # 文件系统说白了就是操作系统管理硬盘中数据的一个方式。 详细点就是：问阿金系统可以管理 硬盘，SSD，u盘 等存储设备的数据布局，提供读写文件的接口。文件系统负责存储文件、文件的元数据（如文件名称、权限、时间戳等），以及组织文件所在的路径结构（如文件夹层级）。\n","title":"文件系统和卷的关系","type":"notes"},{"content":" LVM（逻辑卷管理器）的概念 # LVM（Logical Volume Manager，逻辑卷管理器）是一种用于管理计算机存储设备上磁盘空间分配的方法。它提供了一种更灵活和动态的磁盘管理方式，与传统的分区方式相比，更加灵活和便于调整。\n1. 物理卷（Physical Volume，PV） # 物理卷是 LVM 的基本存储单元，通常对应于物理硬盘分区或整个硬盘。一个物理卷可以包含在一个或多个卷组中。\n物理卷的创建： 你可以使用 pvcreate 命令将一个物理分区或硬盘初始化为物理卷。例如：\nsudo pvcreate /dev/sda3 物理卷的信息： 通过 pvs 命令可以查看所有物理卷的信息。\nsudo pvs 2. 卷组（Volume Group，VG） # 卷组是 LVM 的第二个层次，包含了一个或多个物理卷。一个卷组可以看作是一个存储池，可以在其上创建逻辑卷。\n卷组的创建： 可以使用 vgcreate 命令将一个或多个物理卷组合成一个卷组。例如：\nsudo vgcreate ubuntu-vg /dev/sda3 卷组的信息： 通过 vgdisplay 命令可以查看卷组的详细信息。\nsudo vgdisplay 3. 逻辑卷（Logical Volume，LV） # 逻辑卷是在卷组之上创建的，可以看作是传统的分区。它提供了实际的存储空间，可以像普通分区一样使用（格式化、挂载等）。\n逻辑卷的创建： 可以使用 lvcreate 命令在卷组上创建逻辑卷。例如：\nsudo lvcreate -L 100G -n ubuntu-lv ubuntu-vg 逻辑卷的信息： 通过 lvdisplay 命令可以查看逻辑卷的详细信息。\nsudo lvdisplay 它们之间的关系 # 物理卷（PV）： 物理卷是 LVM 管理的最基本单位，它们是硬盘分区或整块硬盘。多个物理卷可以加入到一个卷组中。\n卷组（VG）： 卷组由一个或多个物理卷组成，它们将多个物理卷的空间整合在一起，形成一个大的存储池。卷组提供了逻辑卷创建和管理的基础。\n逻辑卷（LV）： 逻辑卷是在卷组之上创建的虚拟分区，它们可以动态调整大小，并且可以像普通的分区一样进行格式化和挂载。\n与硬盘的关系及使用 # 硬盘分区（或整块硬盘）： 首先将硬盘分区（如 /dev/sda1、/dev/sda2 等）初始化为物理卷。\n创建卷组： 将一个或多个物理卷添加到卷组中，形成一个统一的存储池。\n创建逻辑卷： 在卷组上创建一个或多个逻辑卷，逻辑卷将使用卷组中的空间。\n格式化和挂载： 创建好的逻辑卷可以进行格式化，如 ext 4 文件系统，并挂载到文件系统路径上。\n使用硬盘空间的方式 # 初始化物理卷：\n例如，使用 pvcreate /dev/sda3 将物理分区初始化为物理卷。 创建卷组：\n使用 vgcreate 命令将一个或多个物理卷组合成一个卷组，如 vgcreate ubuntu-vg /dev/sda3。 创建逻辑卷：\n使用 lvcreate 命令在卷组上创建逻辑卷，如 lvcreate -L 100G -n ubuntu-lv ubuntu-vg。 格式化逻辑卷：\n例如，使用 mkfs.ext4 /dev/ubuntu-vg/ubuntu-lv 格式化逻辑卷。 挂载逻辑卷：\n使用 mount 命令挂载逻辑卷，如 mount /dev/ubuntu-vg/ubuntu-lv /mnt。 通过这些步骤，你可以动态地管理和调整存储空间，而不必像传统分区那样需要在创建时确定固定的大小。这种灵活性对于服务器和大规模存储管理尤其重要。\n命令及其解释 # sudo vgdisplay： 显示卷组（Volume Group，VG）的详细信息。\nsudo vgdisplay 输出解释：\n--- Volume group --- VG Name ubuntu-vg # 卷组的名称 System ID Format lvm2 # 卷组使用的LVM版本 Metadata Areas 1 # 元数据区域数量 Metadata Sequence No 2 # 元数据序列号 VG Access read/write # 卷组的访问权限 VG Status resizable # 卷组的状态 MAX LV 0 # 最大逻辑卷数量（0表示没有限制） Cur LV 1 # 当前逻辑卷数量 Open LV 1 # 当前打开的逻辑卷数量 Max PV 0 # 最大物理卷数量（0表示没有限制） Cur PV 1 # 当前物理卷数量 Act PV 1 # 当前活动的物理卷数量 VG Size \u0026lt;462.71 GiB # 卷组的总大小 PE Size 4.00 MiB # 每个物理扩展区（PE）的大小 Total PE 118453 # 总的物理扩展区数量 Alloc PE / Size 25600 / 100.00 GiB # 已分配的物理扩展区数量及大小 Free PE / Size 92853 / \u0026lt;362.71 GiB # 未分配的物理扩展区数量及大小 VG UUID xDrQn5-82mb-GSRY-IqDV-VR4V-fNOk-JodsIE # 卷组的唯一标识符 sudo lvdisplay： 显示逻辑卷（Logical Volume，LV）的详细信息。\nsudo lvdisplay 输出解释：\n--- Logical volume --- LV Path /dev/ubuntu-vg/ubuntu-lv # 逻辑卷的路径 LV Name ubuntu-lv # 逻辑卷的名称 VG Name ubuntu-vg # 逻辑卷所属的卷组名称 LV UUID OeCqDd-O039-JMhk-TWaJ-og8T-BPgi-rL6hcd # 逻辑卷的唯一标识符 LV Write Access read/write # 逻辑卷的访问权限 LV Creation host, time ubuntu-server, 2024-07-13 10:44:07 +0000 # 逻辑卷创建的主机和时间 LV Status available # 逻辑卷的状态 # open 1 # 当前打开的逻辑卷数量 LV Size 100.00 GiB # 逻辑卷的大小 Current LE 25600 # 当前逻辑扩展区数量 Segments 1 # 逻辑卷的段数 Allocation inherit # 分配策略 Read ahead sectors auto # 预读扇区数 - currently set to 256 # 当前设置的预读扇区数 Block device 252:0 # 逻辑卷的块设备号 sudo pvs： 显示物理卷（Physical Volume，PV）的信息。\nsudo pvs 输出解释：\nPV VG Fmt Attr PSize PFree /dev/sda3 ubuntu-vg lvm2 a-- \u0026lt;462.71g \u0026lt;362.71g PV: 物理卷的路径。 VG: 所属的卷组名称。 Fmt: 物理卷的格式（LVM 版本）。 Attr: 属性（a-- 表示激活状态）。 PSize: 物理卷的总大小。 PFree: 物理卷的空闲大小。 sudo vgs： 显示卷组的摘要信息。\nsudo vgs 输出解释：\nVG #PV #LV #SN Attr VSize VFree ubuntu-vg 1 1 0 wz--n- \u0026lt;462.71g \u0026lt;362.71g VG: 卷组的名称。 #PV: 物理卷数量。 #LV: 逻辑卷数量。 #SN: 快照数量。 Attr: 属性（wz--n- 表示可写状态、可调整大小、未关闭的卷组）。 VSize: 卷组的总大小。 VFree: 卷组的空闲大小。 sudo lvs： 显示逻辑卷的摘要信息。\nsudo lvs 输出解释：\nLV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert ubuntu-lv ubuntu-vg -wi-ao---- 100.00g LV: 逻辑卷的名称。 VG: 所属的卷组名称。 Attr: 属性（-wi-ao---- 表示写入状态、初始化状态、可用状态）。 LSize: 逻辑卷的大小。 Pool: 池（如果是精简卷池）。 Origin: 源（如果是快照）。 Data%: 已使用数据的百分比（对于精简卷池）。 Meta%: 已使用元数据的百分比（对于精简卷池）。 Move Log: 移动日志。 Cpy%Sync: 同步的复制百分比。 Convert: 转换状态。 这些命令及其输出提供了详细的 LVM 布局和卷的状态信息，帮助你管理和监控你的存储配置。如果有其他问题或需要进一步的解释，请告诉我。\n命令 # 在 Linux 系统中，逻辑卷管理（LVM，Logical Volume Manager）是一种用于管理磁盘存储设备的工具。LVM 使得系统管理员可以更灵活地管理文件系统和磁盘分区。以下是一些常用的 LVM 命令以及它们的详细介绍：\n1. 物理卷 (Physical Volume, PV) # 物理卷是 LVM 的基本存储单元。物理卷可以是整个硬盘，硬盘分区，或者是 RAID 设备。\n创建物理卷:\npvcreate /dev/sda1 查看物理卷:\npvdisplay 查看物理卷信息:\npvscan 删除物理卷:\npvremove /dev/sda1 2. 卷组 (Volume Group, VG) # 卷组是由一个或多个物理卷组成的逻辑集合。逻辑卷是在卷组上创建的。\n创建卷组:\nvgcreate my_vg /dev/sda1 /dev/sdb1 扩展卷组:\nvgextend my_vg /dev/sdc1 查看卷组:\nvgdisplay 查看卷组信息:\nvgscan 减少卷组:\nvgreduce my_vg /dev/sdc1 删除卷组:\nvgremove my_vg 3. 逻辑卷 (Logical Volume, LV) # 逻辑卷是在卷组上创建的，它类似于传统的磁盘分区，但更灵活。\n创建逻辑卷:\nlvcreate -L 10G -n my_lv my_vg 扩展逻辑卷:\nlvextend -L +5G /dev/my_vg/my_lv 查看逻辑卷:\nlvdisplay 查看逻辑卷信息:\nlvscan 减少逻辑卷（在减小逻辑卷之前，请确保文件系统大小已经减小）:\nlvreduce -L -5G /dev/my_vg/my_lv 删除逻辑卷:\nlvremove /dev/my_vg/my_lv 4. 文件系统 # 在创建逻辑卷后，通常需要在其上创建文件系统。\n创建文件系统:\nmkfs.ext4 /dev/my_vg/my_lv 挂载文件系统:\nmount /dev/my_vg/my_lv /mnt/my_mount_point 查看挂载的文件系统:\ndf -h 5. 其他有用的 LVM 命令 # 显示所有 LVM 信息:\nlvs vgs pvs 显示详细的 LVM 信息:\nlvdisplay vgdisplay pvdisplay 通过这些命令，系统管理员可以更灵活地管理存储资源，动态调整存储容量，并且减少系统停机时间。\n补充 # 在 Ubuntu 中查看磁盘空间可以使用以下命令：\ndf -h：显示磁盘使用情况，以人类可读的格式（例如 GB、MB）。\ndf -h du -sh /path/to/directory：显示指定目录的大小，以人类可读的格式。\ndu -sh /home lsblk：列出所有块设备，包括它们的挂载点和大小。\nlsblk fdisk -l：列出所有分区和详细信息。\nsudo fdisk -l lsblk：列出所有块设备及其大小。\nlsblk fdisk -l：显示所有磁盘及其分区信息。\nsudo fdisk -l df -h：显示各个挂载点的使用情况，包括总空间、已用空间和可用空间。\ndf -h parted -l：显示所有磁盘及其分区信息。\nsudo parted -l 这些命令将帮助你查看硬盘的总大小和分区信息。\n","date":"2024年9月27日","externalUrl":null,"permalink":"/notes/system/%E7%A1%AC%E7%9B%98%E7%9B%B8%E5%85%B3-%E5%8D%B7/","section":"笔记","summary":"LVM（逻辑卷管理器）的概念 # LVM（Logical Volume Manager，逻辑卷管理器）是一种用于管理计算机存储设备上磁盘空间分配的方法。它提供了一种更灵活和动态的磁盘管理方式，与传统的分区方式相比，更加灵活和便于调整。\n","title":"硬盘相关-卷","type":"notes"},{"content":"","date":"2024年9月27日","externalUrl":null,"permalink":"/tags/%E7%A3%81%E7%9B%98%E5%AD%98%E5%82%A8/","section":"Tags","summary":"","title":"磁盘存储","type":"tags"},{"content":"","date":"2024年9月21日","externalUrl":null,"permalink":"/tags/jar%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":"JAR文件","type":"tags"},{"content":"","date":"2024年9月21日","externalUrl":null,"permalink":"/categories/java/","section":"Categories","summary":"","title":"Java","type":"categories"},{"content":"下面是一个详细的教程，涵盖如何将多个 Java 文件组织成包、引入包以及如何打包成 JAR 文件的过程。\n1. 创建 Java 包 # 1.1 目录结构 # 假设我们要创建一个名为 com.example.myapp 的包，目录结构如下：\n笔记 一般包名就是目录结构名。因此，我们运行或者编译包时一般都是在目录根结构下运行命令，比如 com.example.myapp 这个包是根据 myapp/src/com/example/myapp 目录下命名的，我们就要在 src 根目录下运行命令\nmyapp/ │ ├── src/ │ └── com/ │ └── example/ │ └── myapp/ │ ├── HelloWorld.java │ └── Utils.java └── bin/ 1.2 编写 Java 文件 # HelloWorld. java:\npackage com.example.myapp; public class HelloWorld { public static void main(String[] args) { System.out.println(\u0026#34;Hello, World!\u0026#34;); Utils.printMessage(\u0026#34;This is a message from Utils.\u0026#34;); } } Utils. java:\npackage com.example.myapp; public class Utils { public static void printMessage(String message) { System.out.println(message); } } 2. 编译 Java 文件 # 打开终端，导航到 src 目录：\ncd path/to/myapp/src 使用 javac 编译 Java 文件：\njavac com/example/myapp/*.java -d ../../bin 这里 -d ../../bin 表示将编译生成的 .class 文件放到 bin 目录下。\n3. 运行 Java 程序 # 确保你在 bin 目录中：\ncd ../../bin 运行程序：\njava com.example.myapp.HelloWorld 4. 创建 JAR 文件 # 仍然在 bin 目录中，使用 jar 命令创建 JAR 文件：\njar cvf myapp.jar com/example/myapp/*.class 这里 c 表示创建，v 表示详细输出，f 表示指定 JAR 文件名。\n确认 JAR 文件已创建：\nls 你应该能看到 myapp.jar 文件。\n5. 运行 JAR 文件 # 运行 JAR 文件时需要指定主类。在 HelloWorld 类中，我们可以在 MANIFEST.MF 中指定主类，或者直接在命令行中运行： java -cp myapp.jar com.example.myapp.HelloWorld 这种方式可以运行 JAR 文件中任意个类，只要你使用 cp 命令指定就行\n6. 添加主类到 JAR 文件 # 为了使 JAR 文件可直接运行，可以添加主类到 MANIFEST.MF 文件中：\n创建一个名为 MANIFEST.MF 的文件，内容如下：\nManifest-Version: 1.0 Main-Class: com.example.myapp.HelloWorld 创建 JAR 文件时指定 MANIFEST.MF：\njar cvfm myapp.jar MANIFEST.MF com/example/myapp/*.class 7. 直接运行 JAR 文件 # 现在你可以直接运行 JAR 文件，而不需要再指定主类：\njava -jar myapp.jar 8. 用第三方包 # java 文件中要用到的包是别人打包好的，我们编译时要通过添加参数来实现使用别人的包，如下：\n编译：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java 运行：\njava --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX 9. 用自己本地打好的包 # 首先，这是原始目录结构：\n```bash myapp ├── bin │ ├── com │ │ └── example │ │ ├── myapp │ │ ├── HelloWorld.class │ │ └── Utils.class │ │ │ │ │ └── myapp.jar └── src └── com └── example ├── app │ ├── HelloWorld.java │ └── Utils.java └── test └── MainApp.java 然后创建一个新的结构如下：\nmyapp ├── bin │ ├── com │ │ └── example │ │ ├── myapp │ │ │ ├── HelloWorld.class │ │ │ └── Utils.class │ │ └── test │ │ └── MainApp.class │ └── myapp.jar └── src └── com └── example ├── app │ ├── HelloWorld.java │ └── Utils.java └── test └── MainApp.java 这个 test 文件夹的 java 文件如下：\npackage com.example.test; import com.example.myapp.Utils; // 导入 Utils 类 public class MainApp { public static void main(String[] args) { Utils.printMessage(\u0026#34;Hello from MainApp!\u0026#34;); } } 导入了 com. example. myapp 这个包中的 Utils 类，并且将这个设置为com. example. test 包。\n编译这个文件方法 # 将 example 文件夹下的所有 java 文件一起编译，这样就不会出现依赖问题 编译时设置路径（-cp），在 src 文件夹下使用如下命令：javac -cp ../bin com/example/test/MainApp.java -d ../bin 结合（-cp）以及 jar 包也行，在 src 文件夹下使用如下命令：javac -cp ../bin/myapp.jar com/example/test/MainApp.java -d ../bin ==最后，就是因为我设置这个包时有个目录，使用 javac 编译文件输出到具体目录时也是会产生相应目录结构，就是会多出文件夹==\n运行这个文件方法 # 运行指令：\njava com.example.test.MainApp 看情况需不需要链接类文件，好像在同一个包的根目录 com/example 下就不需要使用 -cp。不在同一个根目录下就要使用 -cp。\n总之，如果不能运行就自己手动加上 -cp 就行了\n10. --module-path 和 -cp 区别 # --module-path 和 -cp （或 --class-path） 是 Java 中的两个不同选项，它们用于不同的场景，因为 Java 支持两种不同的代码组织方式：模块系统 和 类路径。\n区别： # 1. -cp（--class-path）： # -cp 是用于指定 类路径 的选项，适用于传统的 Java 项目结构，即所有的 .class 文件和 JAR 文件都是通过类路径来管理和加载的。\n使用场景：用于在传统的 Java 项目中查找 .class 文件和 JAR 包。 适合的项目：适合未使用 Java 9 引入的模块系统的项目，或者依旧通过类路径进行管理的库和依赖。 示例：\njavac -cp /path/to/library.jar YourProgram.java java -cp /path/to/library.jar YourProgram 2. --module-path（-p）： # --module-path 是用于 模块系统 的选项，这是 Java 9 引入的特性。在模块系统中，代码和依赖项（例如 JAR 文件）被打包为模块，每个模块都有一个明确的依赖关系和暴露的 API。\n使用场景：用于指定模块路径（即模块化的 JAR 文件），适合 Java 9 及以上的项目，特别是那些使用了 Java 的模块系统的项目。 适合的项目：适合将代码和依赖项组织为模块的项目。例如，JavaFX 是基于模块系统的库。 示例：\njavac --module-path /path/to/modules --add-modules javafx.controls YourProgram.java java --module-path /path/to/modules --add-modules javafx.controls YourProgram 为什么 JavaFX 需要 --module-path？ # JavaFX 自从 Java 9 起就成为了一个独立的模块（不再包含在 JDK 中）。因此，当你在编译和运行 JavaFX 应用时，你需要告诉 Java 编译器和运行时如何找到 JavaFX 模块，这就是为什么使用 --module-path 来指定 JavaFX 模块的路径。\n示例解释：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java --module-path：指定 JavaFX SDK 的路径，其中包含 JavaFX 的模块化 JAR 文件。 --add-modules javafx.controls：明确告诉编译器或运行时要加载 javafx.controls 模块，这是 JavaFX 提供的核心 UI 控件模块。 什么时候用 -cp 和 --module-path？ # 使用 -cp：当你使用的是非模块化的 Java 项目或依赖时，继续使用类路径（-cp）。 使用 --module-path：当你的项目或依赖是模块化的，特别是使用 Java 9 引入的模块系统时，比如 JavaFX。 小结 # -cp 适用于传统的类路径管理。 --module-path 适用于模块化的项目和库（如 Java 9 引入的模块系统）。 总结 # 你已经完成了以下步骤：\n创建 Java 包。 编写 Java 文件并编译。 运行 Java 程序。 创建 JAR 文件。 添加主类到 JAR 文件并直接运行。 这种方法可以帮助你组织代码、简化项目管理，并且便于分发。\n","date":"2024年9月21日","externalUrl":null,"permalink":"/notes/java/java%E4%B8%AD%E6%9C%89%E5%85%B3%E5%8C%85%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE%E4%BB%A5%E5%8F%8A%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":"下面是一个详细的教程，涵盖如何将多个 Java 文件组织成包、引入包以及如何打包成 JAR 文件的过程。\n1. 创建 Java 包 # 1.1 目录结构 # 假设我们要创建一个名为 com.example.myapp 的包，目录结构如下：\n","title":"Java中有关包的相关配置以及介绍","type":"notes"},{"content":"","date":"2024年9月21日","externalUrl":null,"permalink":"/tags/package/","section":"Tags","summary":"","title":"Package","type":"tags"},{"content":"","date":"2024年9月21日","externalUrl":null,"permalink":"/tags/%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84/","section":"Tags","summary":"","title":"项目结构","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/categories/git/","section":"Categories","summary":"","title":"Git","type":"categories"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/svn%E5%AF%B9%E6%AF%94/","section":"Tags","summary":"","title":"SVN对比","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E5%B7%A5%E4%BD%9C%E6%B5%81/","section":"Tags","summary":"","title":"工作流","type":"tags"},{"content":" 常用 Git 命令 # Git简介 # Git是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。 Git是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git与常用的版本控制工具 CVS, Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。 Git与SVN的区别 # Git不仅仅是个版本控制系统，它也是个内容管理系统(CMS),工作管理系统等。 如果你是一个具有使用SVN背景的人，你需要做一定的思想转换，来适应Git提供的一些概念和特征。 Git 与 SVN 区别点：\nGit是分布式的，SVN不是：这是Git和其它非分布式的版本控制系统，例如SVN，CVS等，最核心的区别。 Git把内容按元数据方式存储，而SVN是按文件：所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。 Git分支和SVN的分支不同：分支在SVN中一点不特别，就是版本库中的另外的一个目录。 Git没有一个全局的版本号，而SVN有：目前为止这是跟SVN相比GIT缺少的最大的一个特征。 Git的内容完整性要优于SVN：Git的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性，确保在遇到磁盘故障和网络问题时降低对版本库的破坏。 经常使用 Git ，但是很多命令还是记不住。一般来说，日常使用只要记住下图7个命令就可以了。但要熟练掌握，恐怕要记住40~60个命令，所以整理了一份常用Git命令清单。\nWorkspace：工作区 Index / Stage：暂存区 Repository：仓库区（或本地仓库） Remote：远程仓库 配置用户名和邮箱 # $ git --version # 查看git的版本信息 $ git config --global user.name # 获取当前登录的用户 $ git config --global user.email # 获取当前登录用户的邮箱 登录git\n# 如果刚没有获取到用户配置，则只能拉取代码，不能修改 要是使用git，你要告诉git是谁在使用 $ git config --global user.name \u0026#39;userName\u0026#39; # 设置git账户，userName为你的git账号， $ git config --global user.email \u0026#39;email\u0026#39; # 获取Git配置信息，执行以下命令： $ git config –list 配置https和ssh推送时保存用户名和密码 # # https提交保存用户名和密码 $ git config --global credential.helper store # 生成公钥私钥，将公钥配置到GitHub，ssh提交就可以免输入用户名密码 # 三次回车即可生成 ssh key $ ssh-keygen -t rsa # 查看已生成的公钥 $ cat ~/.ssh/id_rsa.pub 推送到远程仓库正确流程 # 1. git init # 初始化仓库 2. git add .(文件name) # 添加文件到本地仓库 3. git commit -m \u0026#34;first commit\u0026#34; # 添加文件描述信息 4. git remote add origin 远程仓库地址 # 链接远程仓库，创建主分支 5. git pull origin master --allow-unrelated-histories # 把本地仓库的变化连接到远程仓库主分支 6. git push -u origin master # 把本地仓库的文件推送到远程仓库 一、新建本地仓库 # # 创建一个文件夹 $ mkdir GitRepositories # 创建文件夹GitRepositories $ cd GitRepositories # 切换到GitRepositories目录下 # 在当前目录新建一个Git代码库 $ git init # 新建一个目录，将其初始化为Git代码库 $ git init [project-name] # 下载一个项目和它的整个代码历史 $ git clone [url] 二、配置(全局和项目) # # Git的设置文件为.gitconfig，它可以在用户主目录下（全局配置），也可以在项目目录下（项目配置）。 # 显示当前的Git配置 $ git config --list # 编辑Git配置文件 $ git config -e [--global] # 设置提交代码时的用户信息 $ git config [--global] user.name \u0026#34;[name]\u0026#34; $ git config [--global] user.email \u0026#34;[email address]\u0026#34; 三、增加/删除文件 # # 添加指定文件到暂存区 $ git add [file1][file2] ... # 添加指定目录到暂存区，包括子目录 $ git add [dir] # 添加当前目录的所有文件到暂存区 $ git add . # 添加每个变化前，都会要求确认 # 对于同一个文件的多处变化，可以实现分次提交 $ git add -p # 删除工作区文件，并且将这次删除放入暂存区 $ git rm [file1] [file2] ... # 停止追踪指定文件，但该文件会保留在工作区 $ git rm --cached [file] # 改名文件，并且将这个改名放入暂存区 $ git mv [file-original] [file-renamed] 四、代码提交 # # 提交暂存区到仓库区 $ git commit -m [message] # 提交暂存区的指定文件到仓库区 $ git commit [file1] [file2] ... -m [message] # 提交工作区自上次commit之后的变化，直接到仓库区 $ git commit -a # 提交时显示所有diff信息 $ git commit -v # 使用一次新的commit，替代上一次提交 # 如果代码没有任何新变化，则用来改写上一次commit的提交信息 $ git commit --amend -m [message] # 重做上一次commit，并包括指定文件的新变化 $ git commit --amend [file1] [file2] ... 五、分支 # # 列出所有本地分支 $ git branch # 列出所有远程分支 $ git branch -r # 列出所有本地分支和远程分支 $ git branch -a # 新建一个分支，但依然停留在当前分支 $ git branch [branch-name] # 新建一个分支，并切换到该分支 $ git checkout -b [branch] # 新建一个分支，指向指定commit $ git branch [branch] [commit] # 新建一个分支，与指定的远程分支建立追踪关系 $ git branch --track [branch] [remote-branch] # 切换到指定分支，并更新工作区 $ git checkout [branch-name] # 切换到上一个分支 $ git checkout - # 建立追踪关系，在现有分支与指定的远程分支之间 $ git branch --set-upstream [branch] [remote-branch] # 合并指定分支到当前分支 $ git merge [branch] # 选择一个commit，合并进当前分支 $ git cherry-pick [commit] # 删除分支 $ git branch -d [branch-name] # 删除远程分支 $ git push origin --delete [branch-name] $ git branch -dr [remote/branch] 六、标签 # # 列出所有tag $ git tag # 新建一个tag在当前commit $ git tag [tag] # 新建一个tag在指定commit $ git tag [tag] [commit] # 删除本地tag $ git tag -d [tag] # 删除远程tag $ git push origin :refs/tags/[tagName] # 查看tag信息 $ git show [tag] # 提交指定tag $ git push [remote] [tag] # 提交所有tag $ git push [remote] --tags # 新建一个分支，指向某个tag $ git checkout -b [branch] [tag] 七、查看信息 # # 查看目录 $ ls -al\t或者$ ll # 查看仓库状态，显示有变更的文件 $ git status # 显示当前分支的版本历史 $ git log # 显示commit历史，以及每次commit发生变更的文件 $ git log --stat # 搜索提交历史，根据关键词 $ git log -S [keyword] # 显示某个commit之后的所有变动，每个commit占据一行 $ git log [tag] HEAD --pretty=format:%s # 显示某个commit之后的所有变动，其\u0026#34;提交说明\u0026#34;必须符合搜索条件 $ git log [tag] HEAD --grep feature # 显示某个文件的版本历史，包括文件改名 $ git log --follow [file] $ git whatchanged [file] # 显示指定文件相关的每一次diff $ git log -p [file] # 显示过去5次提交 $ git log -5 --pretty --oneline # 显示所有提交过的用户，按提交次数排序 $ git shortlog -sn # 显示指定文件是什么人在什么时间修改过 $ git blame [file] # 显示暂存区和工作区的差异 $ git diff # 显示暂存区和上一个commit的差异 $ git diff --cached [file] # 显示工作区与当前分支最新commit之间的差异 $ git diff HEAD # 显示两次提交之间的差异 $ git diff [first-branch]...[second-branch] # 显示今天你写了多少行代码 $ git diff --shortstat \u0026#34;@{0 day ago}\u0026#34; # 显示某次提交的元数据和内容变化 $ git show [commit] # 显示某次提交发生变化的文件 $ git show --name-only [commit] # 显示某次提交时，某个文件的内容 $ git show [commit]:[filename] # 显示当前分支的最近几次提交 $ git reflog 八、远程同步 # # 下载远程仓库的所有变动 $ git fetch [remote] # 显示所有远程仓库 $ git remote -v # 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库，并命名 $ git remote add [shortname] [url] # 取回远程仓库的变化，并与本地分支合并 $ git pull [remote] [branch] # 上传本地指定分支到远程仓库 $ git push [remote] [branch] # 强行推送当前分支到远程仓库，即使有冲突 $ git push [remote] --force # 推送所有分支到远程仓库 $ git push [remote] --all 九、撤销 # # 恢复暂存区的指定文件到工作区 $ git checkout [file] # 恢复某个commit的指定文件到暂存区和工作区 $ git checkout [commit] [file] # 恢复暂存区的所有文件到工作区 $ git checkout . # 重置暂存区的指定文件，与上一次commit保持一致，但工作区不变 $ git reset [file] # 重置暂存区与工作区，与上一次commit保持一致 $ git reset --hard # 重置当前分支的指针为指定commit，同时重置暂存区，但工作区不变 $ git reset [commit] # 重置当前分支的HEAD为指定commit，同时重置暂存区和工作区，与指定commit一致 $ git reset --hard [commit] # 重置当前HEAD为指定commit，但保持暂存区和工作区不变 $ git reset --keep [commit] # 新建一个commit，用来撤销指定commit # 后者的所有变化都将被前者抵消，并且应用到当前分支 $ git revert [commit] # 暂时将未提交的变化移除，稍后再移入 $ git stash $ git stash pop 十、其他 # # 从当前目录的所有文件中查找文本内容： $ git grep \u0026#34;Hello\u0026#34; # 在某一版本中搜索文本： $ git grep \u0026#34;Hello\u0026#34; v2.5 # 生成一个可供发布的压缩包 $ git archive 附：Git常用命令速查表 # 附：Git指令速查表 # 附：资料链接 # Git 常用命令总结 Git常用命令，很全很详细讲解的也不错 Git详细使用教程 Git使用详细教程 Git 安装和使用教程 Git 教程\n","date":"2024年9月20日","externalUrl":null,"permalink":"/notes/git/%E5%B8%B8%E7%94%A8git%E5%91%BD%E4%BB%A4/","section":"笔记","summary":"常用 Git 命令 # Git简介 # Git是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。 Git是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git与常用的版本控制工具 CVS, Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。 Git与SVN的区别 # Git不仅仅是个版本控制系统，它也是个内容管理系统(CMS),工作管理系统等。 如果你是一个具有使用SVN背景的人，你需要做一定的思想转换，来适应Git提供的一些概念和特征。 Git 与 SVN 区别点：\n","title":"常用Git命令","type":"notes"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/","section":"Tags","summary":"","title":"版本控制","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E9%9B%86/","section":"Tags","summary":"","title":"命令集","type":"tags"},{"content":" git命令使用感想 # git命令初始用 # git init建一个.git仓库(相当于拍一张照，将本地文件都拍下来)，使得创建本地仓库，能够推送代码到远程 git add .这个是一点还是两点看路径，也可以添加想添加的代码文件,这样就将其推到一个暂存区了 git status就是查看推的状况了，可以查看有哪些文件推上去了，哪些失败了额 git commit -m “描述” 这个就是提交上去了 如果还没建分支就用git branch -M 分支名（一般是master或者是main） git remote add origin url（一般最好用ssh密钥即建一个，后面可以不问密码） git push origin 分支名 ssh密钥创建 # 先cd ~/.ssh用ll看看有无id_rsa,id_rsa_pub，无就创建，用该命令： *** ssh-keygen -t rsa -b 4096 -C \u0026ldquo;nni461904@gmail.com\u0026rdquo; *** 创建好后就cat命令直接获取id_rsa_pub里的密钥或者vim复制也可 然后就进github网页设置那设置ssh密钥就可了 git push报错 # 这个学问就大了，要看报错，这是冲突性问题 git push报错git push冲突\n因为折腾过很久，所以在最后提一下这个想法，这个是之前搞不定后，实现的一个操作： 先在github网页复制clone下来 这样操作之后就可以实现本地和远程的同步了，这样我本地版本就大于远程的了 之后就可以按add那样提交就是了 因为文件夹里包含.git文件导致github网页创建了个子系统模块 # 先删除这个.git文件 rm -rf .git 然后用git rm \u0026ndash;cached 文件名实现在暂存区删除这个文件夹，实现停止跟踪这个文件或文件夹，从而可以实现修复 使用git add命令重新添加到暂存区 最后用git commit命令来提交 最后git push 修复 git命令学习 # git命令学习\n","date":"2024年9月20日","externalUrl":null,"permalink":"/notes/git/git-use/","section":"笔记","summary":" git命令使用感想 # git命令初始用 # git init建一个.git仓库(相当于拍一张照，将本地文件都拍下来)，使得创建本地仓库，能够推送代码到远程 git add .这个是一点还是两点看路径，也可以添加想添加的代码文件,这样就将其推到一个暂存区了 git status就是查看推的状况了，可以查看有哪些文件推上去了，哪些失败了额 git commit -m “描述” 这个就是提交上去了 如果还没建分支就用git branch -M 分支名（一般是master或者是main） git remote add origin url（一般最好用ssh密钥即建一个，后面可以不问密码） git push origin 分支名 ssh密钥创建 # 先cd ~/.ssh用ll看看有无id_rsa,id_rsa_pub，无就创建，用该命令： *** ssh-keygen -t rsa -b 4096 -C “nni461904@gmail.com” *** 创建好后就cat命令直接获取id_rsa_pub里的密钥或者vim复制也可 然后就进github网页设置那设置ssh密钥就可了 git push报错 # 这个学问就大了，要看报错，这是冲突性问题 git push报错git push冲突\n","title":"git_use","type":"notes"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/ssh%E5%AF%86%E9%92%A5/","section":"Tags","summary":"","title":"SSH密钥","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"代码管理","type":"tags"},{"content":"在多人协作时，使用 Git 进行版本管理可以有效地避免和解决冲突问题，但仍然可能会遇到版本冲突。冲突的主要原因是不同开发者对同一个文件的同一部分进行修改，而 Git 无法自动合并这些更改。下面详细解释 Git 版本冲突出现的情形、如何解决冲突以及如何在多人合作中进行版本管理。\n一、Git 版本冲突的出现情形 # 同一文件的同一部分被多个开发者修改： 当多个开发者修改同一个文件的同一部分时，如果尝试合并这些修改，Git 无法自动决定哪一部分修改应该保留。例如：\n开发者 A 修改了文件 file.txt 的第 10 行，提交并推送了。 开发者 B 在开发者 A 推送之前也修改了 file.txt 的第 10 行，并在之后尝试推送。 不同分支的合并： 不同分支上的代码通常会有不同的开发进展，当一个开发者在两个不同的分支上修改了同一文件的同一部分内容时，合并分支时会产生冲突。\n手动解决冲突不当： 当冲突发生时，开发者在手动解决冲突时没有正确地保留需要的代码，导致推送到远程仓库时再次产生冲突。\n二、如何解决 Git 版本冲突 # 当发生冲突时，Git 会标记出冲突的地方，文件内容会出现如下格式：\n\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD # 当前分支的代码 ======= # 其他分支的代码 \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 在这种情况下，需要手动决定保留哪一部分代码，或者将两个版本的代码合并。具体步骤如下：\n1. 检查冲突文件 # 当你尝试合并代码时，冲突会阻止提交并显示冲突文件。你可以通过以下命令查看冲突文件：\ngit status 冲突文件会显示在 \u0026ldquo;Unmerged paths\u0026rdquo; 下方。\n2. 手动解决冲突 # 打开冲突文件，根据冲突标记 \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;, =======, \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 来手动解决冲突。例如，你可以决定保留其中一部分代码，或将不同的部分合并在一起。解决后删除冲突标记并保存文件。\n3. 标记解决冲突 # 解决冲突后，告诉 Git 冲突已经被解决，可以通过 git add 命令将文件标记为已解决：\ngit add \u0026lt;conflict-file\u0026gt; 4. 提交合并结果 # 最后，提交冲突解决后的更改：\ngit commit -m \u0026#34;Resolve merge conflict\u0026#34; 5. 推送到远程仓库 # 解决冲突后，可以将合并后的代码推送到远程仓库：\ngit push origin \u0026lt;branch-name\u0026gt; 三、多人合作中的 Git 版本管理实践 # 为了避免和减少冲突，团队在协作时应该遵循一些最佳实践。以下是详细的版本管理流程和策略：\n1. 使用分支策略 # 主分支（main 或 master）：主分支用于存放稳定的、已发布的代码。 开发分支（develop）：用于开发的主分支，集成了所有开发人员的代码。 功能分支（feature/xxx）：每个功能或修复创建独立的功能分支，避免在开发过程中对其他功能产生影响。 开发者可以在功能分支上工作，完成功能开发后再合并到 develop 分支，最后再由 develop 合并到 main。\n2. 定期拉取代码（git pull） # 开发者在工作之前和提交代码之前，应定期从远程仓库拉取最新的代码，以确保自己的代码基于最新的状态。\ngit pull origin \u0026lt;branch-name\u0026gt; 3. 提交频繁且小规模的更改 # 大规模的提交更容易产生冲突，提交频繁且小的更改可以减少冲突的发生。可以使用以下命令来提交更改：\ngit add . git commit -m \u0026#34;Describe the changes made\u0026#34; git push origin \u0026lt;branch-name\u0026gt; 4. 代码审查（Code Review）和合并请求（Pull Request） # 在多人协作中，开发者应该通过创建 Pull Request 来合并代码。通过 Pull Request，其他团队成员可以审查代码，确保代码质量并减少冲突。 Pull Request 通常会在 feature/xxx 分支与 develop 分支之间进行，审查通过后再合并。 5. 解决冲突的技巧 # 频繁拉取代码：当你在开发功能时，定期将 develop 分支合并到你的功能分支，以确保你的分支总是与其他人的工作同步，尽早发现潜在的冲突。 保持清晰的提交记录：在提交代码时，保持清晰的提交信息，可以让其他开发者清楚地了解每次提交的目的。 6. 回滚操作 # 当合并发生错误时，可以通过以下命令回滚：\n撤销最后一次提交（还未推送）： git reset --soft HEAD^ 回滚到某个特定的提交： git reset --hard \u0026lt;commit-hash\u0026gt; 四、总结 # 在多人合作中，版本冲突不可避免，但通过合理的 Git 分支策略、定期拉取代码、频繁小规模提交、代码审查等方式，可以有效减少冲突并高效解决冲突。每个团队成员应尽可能早地处理冲突，并在冲突发生时耐心、细致地解决，以确保代码的稳定性和协作的顺畅。\n","date":"2024年9月20日","externalUrl":null,"permalink":"/notes/git/git%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/","section":"笔记","summary":"在多人协作时，使用 Git 进行版本管理可以有效地避免和解决冲突问题，但仍然可能会遇到版本冲突。冲突的主要原因是不同开发者对同一个文件的同一部分进行修改，而 Git 无法自动合并这些更改。下面详细解释 Git 版本冲突出现的情形、如何解决冲突以及如何在多人合作中进行版本管理。\n","title":"git版本管理","type":"notes"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E5%8D%8F%E4%BD%9C%E5%BC%80%E5%8F%91/","section":"Tags","summary":"","title":"协作开发","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"分支管理","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3/","section":"Tags","summary":"","title":"冲突解决","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/api%E6%96%87%E6%A1%A3/","section":"Tags","summary":"","title":"API文档","type":"tags"},{"content":" 什么是 Javadoc？ # Javadoc 是 Java 编程语言中用于生成 API 文档的工具。它通过解析源代码中的特殊注释，生成带有结构化文档的 HTML 文件，从而为类、方法、字段等生成说明文档。Javadoc 是 Java 编程中极为重要的文档生成工具，有助于开发者理解和使用代码。\nJavadoc 的作用 # 生成 API 文档：Javadoc 可以通过 Java 源代码中的注释生成详细的 API 文档，包含类、方法、字段的使用说明。 增强代码可读性：Javadoc 提供结构化的注释格式，帮助开发者理解代码的功能和使用方法。 简化维护：良好的 Javadoc 文档不仅便于自己维护，还能够让其他开发者快速上手使用代码，尤其在开源项目或团队协作中尤为重要。 javadoc 生成注释文件情况 # Javadoc 工具主要会提取 Java 源代码中的 Javadoc 注释部分（使用 /** ... */ 格式的注释），并通过解析这些注释生成 API 文档。然而，Javadoc 生成的文档不仅仅依赖于注释部分，它还会从源代码中提取类、接口、方法、构造函数、字段等声明的结构和签名，以生成完整的 API 文档。\nJavadoc 生成的文档包含两部分：\n注释部分：通过 /** ... */ 注释编写的描述、标签内容（如 @param、@return、@throws 等）。这些注释部分会被提取并生成相应的文档，解释代码的用途、参数的意义等。\n代码结构部分：Javadoc 不仅提取注释，还会自动包含类、接口、方法、构造函数、字段等的声明。这些元素即使没有注释，也会显示在生成的 API 文档中。Javadoc 会展示这些元素的名称、类型、修饰符（如 public、private）等，使文档不仅描述了代码的功能，还详细地列出了其结构。\n示例：没有注释的 Java 类 # 即使没有添加 Javadoc 注释，Javadoc 工具也会生成基础的 API 文档，列出类、方法和字段的结构。\npublic class Example { private int value; public Example(int value) { this.value = value; } public int getValue() { return value; } } 生成的 Javadoc 文档将展示 Example 类的结构，包括类名、构造函数和 getValue 方法的签名。\n示例：有 Javadoc 注释的 Java 类 # 如果为类、方法等添加 Javadoc 注释，生成的文档会更详细、包含解释性文字：\n/** * Example 类用于展示 Javadoc 的使用。 */ public class Example { /** 存储数值的字段 */ private int value; /** * 构造函数，初始化数值。 * * @param value 初始化的数值 */ public Example(int value) { this.value = value; } /** * 获取存储的数值。 * * @return 返回存储的数值 */ public int getValue() { return value; } } 在这个例子中，Javadoc 工具不仅会生成类和方法的结构信息，还会提取注释并在生成的 HTML 文档中显示这些详细的解释、参数说明和返回值说明。\n结论 # 总结来说，Javadoc 会生成完整的 API 文档，不仅包含代码中的注释部分，还会提取类和方法的结构信息。即使某些部分没有注释，Javadoc 也会生成基础的结构文档。如果添加了注释，文档会更详细，包含开发者为代码所编写的说明和提示。\nJavadoc 注释格式 # Javadoc 使用特殊的注释格式。通常，Javadoc 注释是写在类、方法、字段等声明之前，使用 /** ... */ 来标记注释内容。与普通注释 /* ... */ 不同，Javadoc 通过解析特定的标签生成结构化文档。\n/** * 这是一个简单的类，演示如何使用 Javadoc。 * 它包含两个字段和一个计算加法的方法。 * * @author 作者姓名 * @version 1.0 */ public class Calculator { /** 第一个数字 */ private int num1; /** 第二个数字 */ private int num2; /** * 构造函数，初始化两个数字。 * * @param num1 第一个数字 * @param num2 第二个数字 */ public Calculator(int num1, int num2) { this.num1 = num1; this.num2 = num2; } /** * 计算两个数字的和。 * * @return 返回 num1 和 num2 的和 */ public int add() { return num1 + num2; } } Javadoc 常用标签 # Javadoc 中包含许多标签（tags）来帮助组织和说明代码的功能，以下是一些常用的标签：\n@author\n用于标明类的作者。\n@author John Doe @version\n用于指定类或方法的版本信息。\n@version 1.0 @param\n描述方法的参数，每个参数都应使用一个 @param 标签，并且需要说明其含义。\n@param parameterName 参数描述 @return\n说明方法返回值的类型和意义。仅对有返回值的方法使用。\n@return 返回类型和含义 @throws / @exception\n描述方法可能抛出的异常类型及其含义。\n@throws IOException 如果读取文件时发生错误 @see\n用于提供参考的类、方法或文档链接。\n@see java.lang.String @since\n指示从哪个版本开始添加该类、方法或字段。\n@since 1.5 @deprecated\n标记该类或方法已过时，并建议不要使用。通常会附带替代的类或方法。\n@deprecated Use newMethod() instead. 生成 Javadoc # 使用命令行生成 Javadoc # 在命令行中，使用 javadoc 命令生成文档。假设源代码文件为 Calculator.java，执行以下命令：\njavadoc Calculator.java 生成的 Javadoc 文档会以 HTML 格式保存，可以通过浏览器查看。\n常见的 javadoc 命令选项：\n-d：指定生成的文档存放目录。 -author：在文档中包含 @author 标签的内容。 -version：在文档中包含 @version 标签的内容。 例如：\njavadoc -d doc -author -version Calculator.java 这将把文档生成到 doc 文件夹中，并包含作者和版本信息。\nJavadoc 文档结构 # 生成的 Javadoc 文档通常包含以下部分：\n类概览：列出所有类、接口和枚举的名称，并提供简要描述。 类详细信息：每个类的详细信息，包括构造函数、字段、方法的说明。 方法详细信息：列出每个方法的详细信息，包括参数说明、返回值说明以及可能抛出的异常。 继承结构：显示类的继承层次结构，便于开发者理解类之间的关系。 Javadoc 的最佳实践 # 为每个类和方法编写 Javadoc：即使是私有方法，也建议编写 Javadoc，因为它有助于后续维护和团队合作。\n使用简洁的语言：Javadoc 应该使用简洁明了的语言，避免复杂术语。重点是帮助其他开发者理解代码的用途和使用方式。\n详细描述参数和返回值：尤其是复杂的方法，需要详细说明每个参数的含义和方法的返回值。对可能抛出的异常也应加以说明。\n保持与代码同步：Javadoc 文档应保持与代码一致，如果代码发生变更，及时更新 Javadoc 注释。\n总结 # Javadoc 是 Java 开发中非常有用的工具，它帮助开发者生成清晰、结构化的 API 文档，增强了代码的可读性和可维护性。在团队合作、开源项目中，Javadoc 能极大地提升代码质量和理解效率。通过掌握 Javadoc 的格式和标签，并遵循最佳实践，可以生成易于理解且完整的文档，从而提高开发效率。\n","date":"2024年9月20日","externalUrl":null,"permalink":"/notes/java/javadoc%E7%94%9F%E6%88%90%E6%B3%A8%E9%87%8A%E6%96%87%E4%BB%B6/","section":"笔记","summary":"什么是 Javadoc？ # Javadoc 是 Java 编程语言中用于生成 API 文档的工具。它通过解析源代码中的特殊注释，生成带有结构化文档的 HTML 文件，从而为类、方法、字段等生成说明文档。Javadoc 是 Java 编程中极为重要的文档生成工具，有助于开发者理解和使用代码。\n","title":"javadoc生成注释文件","type":"notes"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A/","section":"Tags","summary":"","title":"代码注释","type":"tags"},{"content":"","date":"2024年9月20日","externalUrl":null,"permalink":"/tags/%E6%96%87%E6%A1%A3%E7%94%9F%E6%88%90/","section":"Tags","summary":"","title":"文档生成","type":"tags"},{"content":" 安装最新版本 neovim # 要在 Ubuntu 上将 Neovim 更新到 0.8.0 及以上版本，可以通过以下步骤操作：\n1. 卸载当前版本的 Neovim # 首先，卸载你当前版本的 Neovim：\nsudo apt remove neovim 2. 添加 Neovim 官方 PPA # Neovim 提供了一个官方的 PPA，包含最新的 Neovim 版本。你可以通过添加该 PPA 来获取最新版本：\nsudo add-apt-repository ppa:neovim-ppa/stable 之后更新软件包列表：\nsudo apt update 3. 安装最新版本的 Neovim # 现在安装 Neovim：\nsudo apt install neovim 这会安装最新的稳定版本，通常是 0.8.0 或更新版本。\n4. 检查版本 # 安装完成后，你可以检查是否成功更新到所需版本：\nnvim --version 5. 配置 LazyVim # 更新 Neovim 后，确保你的 ~/.config/nvim 目录下有正确的 LazyVim 配置文件。一般你可以通过 GitHub 获取 LazyVim 配置。\n其他提示 # 如果你仍然遇到版本问题，可以尝试通过 Neovim 官方发布页面 下载预编译二进制文件，手动安装 Neovim。\nLazyVim 是一个很受欢迎的 Neovim 配置框架，它提供了一个开箱即用的 Neovim 配置，使得 Neovim 的使用更加高效。下面是一些常用命令和导航键的介绍：\n常用命令 # 启动 Neovim\nnvim 打开文件\n:e 文件名 保存文件\n:w 保存并退出\n:wq 退出不保存\n:q! 查找文件\n:find 文件名 替换文本\n:%s/旧文本/新文本/g 跳转到行号\n:行号 显示帮助文档\n:help 常用导航键 # 基本导航\nh：左移 j：下移 k：上移 l：右移 单词导航\nw：跳到下一个单词的开始 e：跳到当前或下一个单词的结尾 b：跳到当前或上一个单词的开始 行导航\n0：跳到行首 $：跳到行尾 G：跳到文件末尾 gg：跳到文件开头 页面导航\nCtrl + f：向下翻一页 Ctrl + b：向上翻一页 跳转到标签\nCtrl + o：跳回到上一个跳转位置 Ctrl + i：跳到下一个跳转位置 LazyVim 特性 # LazyVim 在启动时会自动加载一系列插件，并且配置了很多便捷的功能。以下是一些特定于 LazyVim 的操作：\n插件管理\n使用 :Lazy 命令可以管理插件，如安装、更新和删除插件。 :Lazy install：安装插件 :Lazy update：更新插件 :Lazy clean：删除不再使用的插件 配置文件\nLazyVim 的配置文件通常位于 ~/.config/nvim/lua/user/ 目录下，你可以在这里找到和修改 LazyVim 的配置文件。 快捷键\nLazyVim 可能会定义一些自定义的快捷键，你可以在 LazyVim 的文档或者配置文件中找到这些快捷键的定义。 快速导航方法 # 在 Neovim 中，有几种高效的方式可以快速导航到当前行中的某个单词附近，而不需要使用方向键。这些方法可以帮助你更快速地定位到你需要编辑的位置：\n1. 使用 / 查找 # 你可以使用 / 命令进行文本查找：\n按 /\u0026lt;字符\u0026gt; 进入查找模式。 输入你要查找的单词或文本，然后按 Enter。 使用 n 跳转到下一个匹配项，N 跳转到上一个匹配项。 例如，要查找当前行中的 foo，你可以按 /foo，然后按 Enter。\n2. 使用 f 和 t # f 和 t 命令可以帮助你快速跳转到当前行中的特定字符位置：\nf\u0026lt;char\u0026gt;：跳转到当前行中第一个 \u0026lt;char\u0026gt; 位置。 t\u0026lt;char\u0026gt;：跳转到当前行中第一个 \u0026lt;char\u0026gt; 之前的位置。 例如：\n按 f 然后是 a 将跳转到当前行第一个 a 位置。 按 t 然后是 a 将跳转到当前行第一个 a 之前的位置。 3. 使用 ; 和 , # ;：重复上一个 f、t、F 或 T 命令。 ,：重复上一个 f、t、F 或 T 命令的反向操作。 4. 使用 g 命令 # g/\u0026lt;字符\u0026gt;：在当前行中查找 \u0026lt;字符\u0026gt;。 5. 使用 * 和 # # *：查找光标下单词的下一个匹配项。 #：查找光标下单词的上一个匹配项。 在插入模式下，你可以使用一些技巧来快速移动到行中的特定位置，无需退出插入模式或使用方向键。以下是一些常用的方法：\n1. 使用 \u0026lt;C-o\u0026gt; # \u0026lt;C-o\u0026gt;（Control + o）：在插入模式下按下 \u0026lt;C-o\u0026gt; 进入一次普通模式命令，然后可以使用普通模式命令进行导航。例如： \u0026lt;C-o\u0026gt;f\u0026lt;char\u0026gt;：跳转到当前行中的第一个 \u0026lt;char\u0026gt; 位置。 \u0026lt;C-o\u0026gt;t\u0026lt;char\u0026gt;：跳转到当前行中的第一个 \u0026lt;char\u0026gt; 之前的位置。 \u0026lt;C-o\u0026gt;/pattern：在当前行中查找 \u0026lt;pattern\u0026gt;。 2. 使用 \u0026lt;C-w\u0026gt; # \u0026lt;C-w\u0026gt;（Control + w）：在插入模式下按下 \u0026lt;C-w\u0026gt; 可以帮助你快速移动到单词边界： \u0026lt;C-w\u0026gt; + h/j/k/l：在插入模式下，按 \u0026lt;C-w\u0026gt; 后接方向键可以移动到当前单词的边界。 3. 使用插入模式快捷键 # \u0026lt;C-a\u0026gt; 和 \u0026lt;C-e\u0026gt;：这些快捷键帮助你在插入模式下进行更精确的光标移动： \u0026lt;C-a\u0026gt;：将光标移动到当前单词的开头。 \u0026lt;C-e\u0026gt;：将光标移动到当前单词的结尾。 4. 使用 \u0026lt;C-f\u0026gt; 和 \u0026lt;C-b\u0026gt; # \u0026lt;C-f\u0026gt; 和 \u0026lt;C-b\u0026gt;：在插入模式下，你可以使用这些快捷键在当前行中进行前向和后向滚动： \u0026lt;C-f\u0026gt;：向前滚动。 \u0026lt;C-b\u0026gt;：向后滚动。 5. 使用 \u0026lt;C-u\u0026gt; 和 \u0026lt;C-d\u0026gt; # \u0026lt;C-u\u0026gt; 和 \u0026lt;C-d\u0026gt;：这些快捷键可以用于向上和向下滚动文本，在插入模式下也有效： \u0026lt;C-u\u0026gt;：向上滚动半屏。 \u0026lt;C-d\u0026gt;：向下滚动半屏。 ","date":"2024年9月15日","externalUrl":null,"permalink":"/notes/vim/nvim-command/","section":"笔记","summary":"安装最新版本 neovim # 要在 Ubuntu 上将 Neovim 更新到 0.8.0 及以上版本，可以通过以下步骤操作：\n1. 卸载当前版本的 Neovim # 首先，卸载你当前版本的 Neovim：\n","title":"nvim_command","type":"notes"},{"content":"","date":"2024年9月15日","externalUrl":null,"permalink":"/tags/ppa/","section":"Tags","summary":"","title":"PPA","type":"tags"},{"content":"","date":"2024年9月15日","externalUrl":null,"permalink":"/tags/ubuntu/","section":"Tags","summary":"","title":"Ubuntu","type":"tags"},{"content":"","date":"2024年9月15日","externalUrl":null,"permalink":"/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"安装教程","type":"tags"},{"content":"在 Vim 中，常用的命令和导航键位对初学者和高级用户都非常有帮助。以下是一些常用命令、移动方向的键位以及如何快速移动到某个行内的位置。\n常用命令模式命令 # 进入命令模式：按 Esc 键进入命令模式。 退出 Vim： :q 退出 :q! 强制退出（不保存） :wq 保存并退出 :x 保存并退出（等同于 :wq） 保存文件： :w 保存文件 :w new_filename 另存为新文件 移动方向的键位 # 基本方向移动：\nh：向左移动一个字符 j：向下移动一行 k：向上移动一行 l：向右移动一个字符 快速移动：\n0：移动到行首 $：移动到行尾 gg：移动到文件开头 G：移动到文件末尾 :n：移动到第 n 行（例如 :5 会移动到第 5 行） 精确跳转到行内的某个位置 # 精确跳转到某个字符：\nf\u0026lt;char\u0026gt;：向右跳转到下一个指定的字符，例如 fa 会跳到当前行的下一个字母 a。 F\u0026lt;char\u0026gt;：向左跳转到上一个指定的字符。 t\u0026lt;char\u0026gt;：向右跳转到指定字符的前一个位置。 T\u0026lt;char\u0026gt;：向左跳转到指定字符的后一个位置。 快速跳转到行内的某个位置：\nnG：跳到第 n 行。 n|：跳到当前行的第 n 列。例如 5| 会跳到当前行的第 5 列。 插入模式 # 进入插入模式：\ni：在当前光标位置前插入 I：在当前行的行首插入 a：在当前光标位置后插入 A：在当前行的行尾插入 o：在当前行下方插入新行 O：在当前行上方插入新行 退出插入模式：\n按 Esc 退出插入模式，返回命令模式。 其他常用命令 # dd：删除整行 x：删除光标所在的字符 u：撤销上一步操作 Ctrl+r：重做上一步操作 yy：复制当前行 p：粘贴复制/剪切的内容 Vim 的操作是高度可定制的，掌握这些基本的命令和导航后，你可以更高效地编辑代码。\n","date":"2024年9月14日","externalUrl":null,"permalink":"/notes/vim/vim-command/","section":"笔记","summary":"在 Vim 中，常用的命令和导航键位对初学者和高级用户都非常有帮助。以下是一些常用命令、移动方向的键位以及如何快速移动到某个行内的位置。\n常用命令模式命令 # 进入命令模式：按 Esc 键进入命令模式。 退出 Vim： :q 退出 :q! 强制退出（不保存） :wq 保存并退出 :x 保存并退出（等同于 :wq） 保存文件： :w 保存文件 :w new_filename 另存为新文件 移动方向的键位 # 基本方向移动：\n","title":"vim_command","type":"notes"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E5%AF%BC%E8%88%AA%E9%94%AE%E4%BD%8D/","section":"Tags","summary":"","title":"导航键位","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F/","section":"Tags","summary":"","title":"命令模式","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E8%BD%AF%E8%BF%9E%E6%8E%A5/","section":"Tags","summary":"","title":"软连接","type":"tags"},{"content":" 两者区别 # 创建桌面图标和创建软连接的方式有不同的用途和应用场景，以下是它们的主要区别：\n1. 软连接（符号链接） # 作用：软连接（ln -s 命令）本质上是文件系统中的快捷方式，它为文件或目录创建一个指向目标文件的链接。通过软连接，用户可以使用不同的路径来访问同一个文件或目录。 应用场景： 用于命令行中简化对某个文件或可执行程序的路径访问。例如，你可以通过软连接将 /opt/myapp/myapp_executable 映射到 /usr/local/bin/myapp，使得你可以通过命令 myapp 来运行程序，而不需要输入完整路径。 主要用于命令行和文件系统级别的操作，无法直接创建带图形界面或图标的可视化快捷方式。 特点： 无图形界面，纯粹是文件系统中的一个链接。 用于命令行或脚本的便捷操作。 只适用于终端或脚本中调用程序，而不涉及桌面图标、应用菜单等图形界面元素。 2. 桌面图标（.desktop 文件） # 作用：.desktop 文件是一种定义应用程序启动方式的文件，通常用于 Linux 桌面环境。它不仅可以指向一个可执行文件，还可以包含应用的图标、名称、描述等信息。通过它，用户可以在桌面或应用菜单中图形化地启动应用程序。 应用场景： 用于在桌面环境下为应用程序创建图标或快捷方式，例如将应用程序显示在 Ubuntu 的应用菜单、启动器、桌面等地方。 允许通过图形界面启动程序，而不是通过命令行。 .desktop 文件可以定义更多的行为，如是否在终端运行、传递特定参数、设置图标等。 特点： 主要用于图形化的桌面环境，允许用户通过鼠标点击图标来启动应用程序。 可以设置图标、名称、描述、类别等信息，用户体验更友好。 可以配置应用程序是否运行在终端、是否使用特定的图标等。 主要区别总结： # 特性 软连接 桌面图标（.desktop 文件） 作用 创建文件或目录的快捷方式，用于简化命令行操作 为应用程序创建桌面快捷方式，允许在图形界面中启动应用 应用场景 文件系统、命令行中的快捷方式 桌面环境、应用菜单中的图标，图形化启动应用程序 使用方式 通过终端命令执行 通过点击桌面图标或应用菜单启动 图标支持 无图标支持，只是文件路径的指向 支持图标显示，可以自定义图标、描述、应用类别等 是否需要图形界面 不需要，完全依赖于命令行或文件系统的路径操作 需要图形界面环境，用于桌面或应用菜单中的应用程序快捷方式 可执行文件的配置选项 只能指向文件或目录，没有其他配置选项 可以配置是否使用终端、参数传递、应用程序分类、图标等 何时使用软连接？ # 当你主要通过命令行使用应用程序时，软连接是一个方便的方式。它可以让你在终端中使用简短命令，而无需输入完整路径。 何时使用桌面图标（.desktop 文件）？ # 当你希望在桌面、应用菜单或启动器中图形化启动应用程序时，应该使用 .desktop 文件。它适用于日常用户需要通过点击图标的方式启动应用程序的场景。 两者可以结合使用，比如为命令行用户创建软链接，同时为图形化用户提供 .desktop 文件，这样既方便终端操作，也方便图形界面使用。\n示例 # 当然，以下是分别使用软连接和 .desktop 文件的示例，以帮助你更清楚地理解两者的区别和用途。\n1. 软连接的示例 # 场景：假设你安装了一个应用程序 myapp，它的可执行文件位于 /opt/myapp/myapp_executable。你希望在终端中直接使用 myapp 命令来启动这个程序，而不需要输入完整路径。\n步骤： # 查找可执行文件的路径（假设它在 /opt/myapp/myapp_executable）。 创建软连接，将其链接到 /usr/local/bin/，方便在终端中使用： sudo ln -s /opt/myapp/myapp_executable /usr/local/bin/myapp 结果： # 你现在可以直接在终端中通过输入 myapp 来启动这个应用程序，而不需要输入完整路径 /opt/myapp/myapp_executable。 软连接就是文件系统中的一个快捷方式，可以通过命令行快速访问应用程序。 验证： # 可以运行以下命令查看软链接是否创建成功：\nls -l /usr/local/bin/myapp 你会看到输出类似于：\nlrwxrwxrwx 1 root root 24 Sep 14 10:00 /usr/local/bin/myapp -\u0026gt; /opt/myapp/myapp_executable 这意味着 /usr/local/bin/myapp 现在指向 /opt/myapp/myapp_executable。\n2. 创建桌面图标的示例 # 场景：你希望为 myapp 创建一个图标，显示在桌面或应用菜单中，方便通过点击启动程序。同时，你希望这个图标有一个合适的名称和图标文件。\n步骤： # 首先，找到应用程序的可执行文件（例如 /opt/myapp/myapp_executable）和图标文件（例如 /opt/myapp/myapp_icon.png）。 创建 .desktop 文件。你可以使用文本编辑器创建一个新的 .desktop 文件： gedit ~/.local/share/applications/myapp.desktop 在 .desktop 文件中添加以下内容： [Desktop Entry] Version=1.0 Name=MyApp Comment=This is my custom application Exec=/opt/myapp/myapp_executable # 应用程序的实际路径 Icon=/opt/myapp/myapp_icon.png # 图标文件路径 Terminal=false # 不需要在终端中运行 Type=Application Categories=Utility; # 类别，可根据需要选择 保存并关闭文件。\n赋予 .desktop 文件执行权限：\nchmod +x ~/.local/share/applications/myapp.desktop 结果： # 你现在可以在 Ubuntu 的应用菜单中搜索 MyApp，并通过图标启动它。 如果你希望将图标显示在桌面上，还可以将 .desktop 文件复制到桌面： cp ~/.local/share/applications/myapp.desktop ~/Desktop/ 验证： # 在应用菜单中搜索 MyApp。 双击桌面上的图标，应用程序将启动。 对比总结： # 特性 软连接示例 桌面图标示例 目的 在命令行中方便运行应用程序 在桌面或应用菜单中方便启动应用程序 操作系统级别 文件系统（终端和路径操作） 桌面环境（图形用户界面） 创建命令 sudo ln -s /opt/myapp/myapp_executable /usr/local/bin/myapp 编辑并创建 .desktop 文件，赋予执行权限 启动方式 在终端中输入 myapp 点击桌面或应用菜单中的图标 是否支持图标 不支持图标，仅支持命令行使用 支持图标，可以在桌面、应用菜单中显示自定义图标 适合的用户 终端用户、开发者、需要频繁通过命令行启动程序的用户 图形界面用户、希望通过点击图标方便启动应用的普通用户 这两个方法可以结合使用，例如：\n为终端用户创建一个软链接，方便通过命令行快速启动程序。 为桌面用户创建一个 .desktop 文件，提供友好的图形化启动方式。 说明 # 在 Ubuntu 及其他类 Unix 系统中，通常将软链接（符号链接）放到 /usr/local/bin 目录有几个重要原因。这个做法涉及到文件系统的组织、环境变量和可执行文件的查找机制。\n1. /usr/local/bin 是用户本地安装软件的标准目录 # /usr/local/bin 是 Linux 系统中一个专门用来存放用户自己安装的软件或脚本的目录，而不是操作系统包管理器（如 apt）管理的软件。其主要目的包括：\n分离系统软件和用户自定义软件：系统自带的核心程序通常位于 /bin、/usr/bin 等目录，而 /usr/local/bin 是为本地用户安装的软件或脚本准备的，不会被系统包管理器覆盖或删除。 避免系统升级影响：如果用户将自己的程序放在 /usr/bin 或 /bin，在系统升级时可能会被系统文件覆盖。而 /usr/local/bin 被保留用于本地用户操作，系统升级不会动这个目录里的文件。 2. /usr/local/bin 在系统的 PATH 环境变量中 # PATH 是 Linux 系统中的一个环境变量，存放着系统在终端中查找可执行文件的目录。当你在终端中输入命令时，系统会从 PATH 列出的目录中按顺序查找该命令对应的可执行文件。如果你将软链接放到 PATH 中的某个目录（如 /usr/local/bin），用户可以直接在终端中运行该程序，而无需输入完整路径。\n可以通过以下命令查看 PATH 变量的内容：\necho $PATH 通常 PATH 中包含 /usr/local/bin，所以将应用程序或其软链接放到这个目录中后，系统能够自动找到它。\n例如，$PATH 通常可能包含类似以下路径：\n/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin 如果将软链接放在 /usr/local/bin，就无需输入绝对路径，可以直接通过命令名运行程序。\n3. 方便命令行调用 # 将软链接放在 /usr/local/bin 中后，用户可以直接在终端中输入程序名称，而不必输入程序的完整路径。例如，如果你的程序可执行文件位于 /opt/myapp/myapp_executable，而你创建了一个软链接到 /usr/local/bin/myapp，则只需输入 myapp 即可运行该程序，而不需要每次都输入 /opt/myapp/myapp_executable。\n4. 遵循文件系统层次标准（FHS） # Linux 系统通常遵循 文件系统层次标准（FHS, Filesystem Hierarchy Standard）。根据 FHS 规范：\n/bin 和 /usr/bin：用于存放操作系统自带的程序和包管理器安装的软件。系统的核心工具和程序都放在这些目录中。 /usr/local/bin：这是专门为用户安装的程序、脚本或工具准备的目录，通常用于非操作系统提供的、用户自定义安装的软件。它可以看作是 \u0026ldquo;本地\u0026rdquo;（local）空间，供管理员或用户自行管理。 因此，遵循这种标准，可以帮助管理员和开发者更好地组织系统中的应用程序，确保系统升级和维护的安全性与一致性。\n5. 权限管理 # 通常，系统目录（如 /bin、/usr/bin）是只读的，只有超级用户（root）可以对其进行写操作。相反，/usr/local/bin 通常为系统管理员和某些用户提供写权限，便于用户安装自定义程序和脚本，而不会影响系统的核心文件。\n总结： # 将软链接放在 /usr/local/bin 是一种惯例和最佳实践，因为该目录是专门为用户安装的本地程序保留的，它在 PATH 变量中，确保了你可以在终端中方便地访问和运行程序。 这样做可以保持系统的整洁和安全，避免本地安装的软件与操作系统自带的核心软件发生冲突。 通过这种方式，用户可以在不破坏系统核心文件的情况下，自由安装和使用自己的软件。\n","date":"2024年9月14日","externalUrl":null,"permalink":"/notes/linux/%E8%BD%AF%E8%BF%9E%E6%8E%A5%E5%92%8C-desktop%E5%9B%BE%E6%A0%87/","section":"笔记","summary":"两者区别 # 创建桌面图标和创建软连接的方式有不同的用途和应用场景，以下是它们的主要区别：\n","title":"软连接和/.desktop图标","type":"notes"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F/","section":"Tags","summary":"","title":"快捷方式","type":"tags"},{"content":"在Java中，编译和运行程序涉及几个主要命令，包括 javac（编译器）、java（运行器）以及其他辅助命令。以下是最常用的Java编译和运行命令：\n1. 编写Java源代码 # 首先你需要一个Java源文件，例如，创建一个名为HelloWorld.java的文件，内容如下：\npublic class HelloWorld { public static void main(String[] args) { System.out.println(\u0026#34;Hello, World!\u0026#34;); } } 2. 编译Java程序 # 使用javac命令编译Java源文件。该命令会将.java文件编译为Java字节码，并生成相应的.class文件。\njavac HelloWorld.java 执行后会生成HelloWorld.class文件，这就是可以由Java虚拟机（JVM）执行的字节码文件。\n编译多个文件： # 如果你有多个Java文件，也可以一起编译，例如：\njavac File1.java File2.java 或者编译当前目录下的所有Java文件：\njavac *.java 3. 运行Java程序 # 使用java命令运行编译好的Java类文件：\njava HelloWorld 注意：运行Java程序时不需要加.class扩展名，只需提供类名。\n4. 编译并指定输出目录 # 你可以通过-d选项指定编译后的.class文件的输出目录：\njavac -d bin HelloWorld.java 这会将编译后的.class文件输出到bin目录下。\n5. 设置类路径（CLASSPATH） # 如果你的Java程序依赖其他库或者class文件，你需要通过-cp（或-classpath）选项指定类路径。例如：\njava -cp .:lib/somelibrary.jar HelloWorld 在这个例子中，当前目录（.）和lib/somelibrary.jar都被加入到类路径中（注意Linux使用冒号:分隔，Windows使用分号;分隔）。\n6. 打包成JAR文件 # 你可以将Java程序打包成一个JAR文件（Java Archive），然后可以通过JVM直接运行这个JAR包。步骤如下：\n创建JAR文件： # jar cvf HelloWorld.jar HelloWorld.class 创建可执行的JAR文件： # 如果你的Java程序有main方法，可以创建一个可执行的JAR文件：\n首先，创建一个manifest.txt文件，指定入口点： Main-Class: HelloWorld 然后执行以下命令打包JAR文件： jar cfm HelloWorld.jar manifest.txt HelloWorld.class 运行JAR文件： java -jar HelloWorld.jar 打包成 JAR 文件另法 # 利用 jar 命令来对生成的字节码文件进行打包。\njar -cvf hello.jar HelloWorld.class 其中 c 表示创建一个新 jar 包，v 表示创建过程中打印创建过程中的信息，f 则表示对新生成的 jar 命名。\n最后，利用以下命令来运行 jar 包。\njava -jar hello.jar 不过并不会顺利出现我们想要的结果，此时会报错 hello.jar 中没有主清单属性。这是因为我们还没有在 MENIFEST.MF 文件中添加 Main-Class 属性。 用压缩软件打开刚创建的 hello.jar，里边除了 HelloWorld.class 文件之外，还会多一个 META-INF 文件夹，里边还有一个 MENIFEST.MF 文件，此时我们只需要用编辑器打开该文件，然后在文件中加入以下代码。（记得添加之后要保证整个文件最后保留一行空行）\nMain-Class: HelloWorld 添加完成之后，再次运行 java -jar hello.jar ，就可以成功在控制台打印 Hello World! 了。\n7. 常见的JDK命令总结 # javac：Java编译器，将Java源文件（.java）编译为字节码文件（.class）。\njavac HelloWorld.java java：Java解释器，运行编译后的字节码（.class）文件。\njava HelloWorld jar：用于创建和管理JAR文件（Java归档文件），可以打包多个.class文件和其他资源。\njar cvf HelloWorld.jar HelloWorld.class javadoc：用于生成Java源代码的API文档。\njavadoc HelloWorld.java javap：反编译.class文件，查看字节码或方法签名。\njavap HelloWorld 8. 调试（jdb） # 如果需要调试Java程序，Java开发工具包还提供了jdb命令（Java Debugger），用于设置断点、检查变量等：\njdb HelloWorld 总结 # Java程序的编译和运行过程主要使用到两个核心命令：javac（编译）和java（运行）。通过这些命令，可以编写、编译、运行Java代码。此外，jar命令允许将Java程序打包成JAR文件，javap可以反编译字节码，jdb可以调试Java程序。这些命令组合起来使得Java开发非常灵活且强大。\n","date":"2024年9月14日","externalUrl":null,"permalink":"/notes/java/java-command/","section":"笔记","summary":"在Java中，编译和运行程序涉及几个主要命令，包括 javac（编译器）、java（运行器）以及其他辅助命令。以下是最常用的Java编译和运行命令：\n","title":"java_command","type":"notes"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/jdk/","section":"Tags","summary":"","title":"JDK","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91/","section":"Tags","summary":"","title":"编译","type":"tags"},{"content":" 笔记 在 javac 编译和使用 java 运行时自动添加库，使用\u0026ndash;module-path 添加库路径，使用\u0026ndash;module-add 添加库\n在较新的 Ubuntu 版本（如 22.04）中，openjdk-8-jdk 包仅包含 OpenJDK 8 的核心功能，不再包括 JavaFX 库。JavaFX 自 Java 11 起被移除出了 JDK，所以在 OpenJDK 8 也没有自动包含 JavaFX。\n原因： # JavaFX 的分离：\n自 Java 11 起，JavaFX 被从标准 JDK 中移除，成为一个独立的模块。OpenJDK 8 并不包含 JavaFX，因为它的设计理念和未来版本的 JavaFX 模块化策略有所不同。 JavaFX 与 OpenJDK 8：\n在 OpenJDK 8 时，JavaFX 是作为 JDK 的一部分提供的，但在更高版本中（如 Java 11 和之后），它被移除到一个独立的库中。因此，OpenJDK 8 的包中不再包含 JavaFX。 解决方法： # 要在使用 OpenJDK 8 的系统上添加 JavaFX，你需要手动安装 JavaFX SDK。请按照以下步骤操作：\n下载 JavaFX SDK：\n访问 OpenJFX官网 下载与你的操作系统匹配的 JavaFX SDK。 解压并配置 JavaFX：\n解压下载的 SDK 文件。你会得到一个包含 JavaFX JAR 文件的 lib 文件夹。 编译和运行 JavaFX 应用程序：\n编译 JavaFX 程序时，使用 --module-path 参数来指定 JavaFX JAR 文件的位置。 运行 JavaFX 程序时，使用 --module-path 和 --add-modules 参数来确保 JavaFX 模块可用。 示例： # 假设你下载并解压的 JavaFX SDK 位于 /path/to/javafx-sdk，然后你可以使用以下命令编译和运行 JavaFX 程序：\n编译：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java 运行：\njava --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX 这样可以确保 JavaFX 库正确地与 OpenJDK 8 一起使用。\n","date":"2024年9月14日","externalUrl":null,"permalink":"/notes/java/java-lib%E5%B8%B8%E8%A7%81error/","section":"笔记","summary":" 笔记 在 javac 编译和使用 java 运行时自动添加库，使用–module-path 添加库路径，使用–module-add 添加库\n","title":"java_lib常见error","type":"notes"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/openjdk/","section":"Tags","summary":"","title":"OpenJDK","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E5%BA%93%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"库管理","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/classpath/","section":"Tags","summary":"","title":"CLASSPATH","type":"tags"},{"content":"在Windows上配置Java环境以便能够编译和运行Java源程序涉及三个主要步骤：安装JDK、配置环境变量（包括JAVA_HOME、PATH和CLASSPATH），以及设置CLASSPATH以便程序能够找到所需的库文件。以下是详细步骤：\n1. 安装JDK # 访问 Oracle官网 或 OpenJDK 下载适合你操作系统的Java Development Kit (JDK) 版本。 安装下载的JDK，按照安装向导进行操作，默认安装路径通常是C:\\Program Files\\Java\\jdk-version。 2. 配置环境变量 # 为了使系统能够识别和使用Java编译器和运行时环境，需要将JAVA_HOME和PATH环境变量配置到系统中。\n设置JAVA_HOME环境变量 # 右键点击“此电脑”或“我的电脑”，选择**“属性”**。 点击**“高级系统设置”，然后点击“环境变量”**。 在**“系统变量”部分，点击“新建”**，创建一个新的环境变量： 变量名：JAVA_HOME 变量值：JDK的安装路径，例如C:\\Program Files\\Java\\jdk-17（根据你安装的版本路径来设置）。 点击确定保存。 配置 PATH 环境变量 # 在同一个“环境变量”窗口的系统变量部分，找到名为Path的变量，选中它后点击编辑。\n点击**“新建”**，然后将以下路径添加到Path变量中：\n%JAVA_HOME%\\bin\n点击确定保存。\n3. 设置CLASSPATH环境变量（可选） # CLASSPATH用于告诉Java编译器和运行时环境在哪里查找用户定义的类和包。如果你使用的是第三方库或你自己的Java类存放在自定义目录下，可以通过CLASSPATH来配置路径。对于普通Java开发，通常可以忽略这个步骤，因为JDK会自动查找标准库。\n如果需要配置CLASSPATH： # 在**“系统变量”部分，点击“新建”**，创建一个新的环境变量：\n变量名：CLASSPATH 变量值：包含库文件路径，添加.表示当前目录： .;%JAVA_HOME%\\lib;%JAVA_HOME%\\jre\\lib\n点击确定保存。\nCLASSPATH的每个路径之间用分号（;）隔开，.表示当前目录，JVM会从这里开始查找类文件。\n4. 验证配置 # 打开命令提示符，输入以下命令以验证JAVA_HOME是否配置正确：\necho %JAVA_HOME%\n它应该输出你安装的JDK的路径。\n验证java和javac命令是否能正常使用：\njava -version javac -version\n你应该能看到Java版本信息。如果没有看到正确的版本，检查环境变量的配置。\n4. Linux 下修改 java 版本 # 修改 java 版本 # 从你的输出中可以看到，OpenJDK 8（openjdk-8-jdk）已经安装在你的系统中，版本为 8u422-b05-1~22.04。因此，你现在应该已经拥有OpenJDK 8。\n如果你运行java -version后仍然看到OpenJDK 11，可能是因为系统仍在使用OpenJDK 11作为默认Java版本。\n你可以通过以下命令切换默认的Java版本：\nsudo update-alternatives --config java 然后在提示的列表中选择OpenJDK 8的路径（通常带有java-8-openjdk）。\n执行这个命令后，再次运行 java -version，应该会显示你刚设置的Java版本。\n修改 javac 版本 # 要将 javac 的版本切换为 OpenJDK 8，可以使用 update-alternatives 工具来设置默认的 javac 版本，和你之前设置 java 版本的方法相同：\n查看可用的 javac 版本： sudo update-alternatives --config javac 在出现的列表中选择与 OpenJDK 8 相关的路径，通常路径中会包含 java-8-openjdk。\n选择完成后，再次检查 javac 的版本：\njavac -version 这样你就可以将 javac 的版本与 java 设置为一致的 OpenJDK 8。\n","date":"2024年9月14日","externalUrl":null,"permalink":"/notes/java/java-set/","section":"笔记","summary":"在Windows上配置Java环境以便能够编译和运行Java源程序涉及三个主要步骤：安装JDK、配置环境变量（包括JAVA_HOME、PATH和CLASSPATH），以及设置CLASSPATH以便程序能够找到所需的库文件。以下是详细步骤：\n","title":"java_set","type":"notes"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/windows%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"Windows配置","type":"tags"},{"content":"","date":"2024年9月14日","externalUrl":null,"permalink":"/tags/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F/","section":"Tags","summary":"","title":"环境变量","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/github-pages/","section":"Tags","summary":"","title":"GitHub Pages","type":"tags"},{"content":" 笔记 三条常用命令：\nhexo clean hexo generate hexo deploy Tips 上面的这些命令以及配置文件（. yml）这些全部都是基于 source 目录下去搜索执行的。因此，如果是配置路径是就要注意这个是要基于 source 目录的，配置路径直接/img/test. jpg 这种是以 source 目录作为根目录的。因此要你就写完整相对目录，不然就在 source 文夹下创建文件夹。\nGithub + Hexo 实现个人博客及主题配置（超详细） # 前言 # 现在市面上的博客很多，如 CSDN，博客园，简书等平台，可以直接在上面发表，用户交互做得好，写的文章百度也能搜索到。缺点是比较不自由，会受到平台的各种限制和恶心的广告。\n而自己购买域名和服务器，搭建博客的成本实在是太高了，不光是说这些购买成本，单单是花力气去自己搭这么一个网站，还要定期的维护它，对于我们大多数人来说，实在是没有这样的精力和时间。\n那么就有第三种选择，直接在 GitHub Pages 平台上托管我们的博客。这样就可以安心的来写作，又不需要定期维护，而且 Hexo 作为一个快速简洁的博客框架，用它来搭建博客真的非常容易。\nHexo 简介 # Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他渲染引擎）解析文章，在几秒内，即可利用靓丽的主题生成静态网页。\n大家可以进入 Hexo 官网进行详细查看，因为 Hexo 的创建者是台湾人，对中文的支持很友好，可以选择中文进行查看。\nHexo 是基于 Node.js 的静态博客框架，依赖少易于安装使用，可以方便地生成静态网页托管在 GitHub 和 Coding 上，是搭建博客的首选框架。\nHexo 搭建教程 # 本次教程主要分为 8 个部分，看完便可搭建自己喜欢的个人博客。\n安装 Git 安装 Node.js 安装 Hexo GitHub 创建个人仓库 将 Hexo 部署到 GitHub 设置个人域名 更改主题（以 Fluid 为例） 发布文章 1. 安装 Git # Windows: 到 Git 官网 下载并安装即可，下载后会有一个 Git Bash 的命令行工具，以后就用这个工具来使用 Git。 Mac: 可通过 MacPorts 或安装程序安装。 Linux (Ubuntu, Debian): sudo apt-get install git-core Linux (Fedora, Red Hat, CentOS): sudo yum install git-core 2. 安装 Node.js # Hexo 是基于 Node.js 编写的，所以需要安装 Node.js 和 npm 工具。\nWindows: 官方安装，选择 LTS 版本即可。 Linux: sudo apt-get install nodejs sudo apt-get install npm 安装完成后，打开命令行，输入以下两行检查安装是否成功： node -v npm -v 3. 安装 Hexo # 安装好 Git 之后，选择你想要安装 Hexo 的地方（例如新建文件夹）。进入文件夹后，我们可以右击选择 \u0026ldquo;Git Bash Here\u0026rdquo; 来打开命令行窗口，输入：\n$ npm install -g hexo-cli 输入 hexo -v 查看一下版本，至此环境准备完成，开始使用 Hexo 搭建个人博客。\n4. 初始化 Hexo # 在你选择的文件夹中，输入以下指令：\n$ hexo init 文件夹名 $ cd 文件夹名 $ npm install 这会生成许多文件夹，不用担心，了解配置即可。\nnode_modules: 依赖包 public: 存放生成的页面 scaffolds: 生成文章的一些模板 source: 存放你的文章 themes: 主题 _config.yml: 博客的配置文件 接着，输入以下指令进行本地启动查看：\n$ hexo clean $ hexo g $ hexo s 在浏览器中输入 localhost:4000 就可以看到生成的博客页面了。使用 ctrl + c 可以关闭服务。\n5. 创建 GitHub 仓库 # 首先，你需要有一个 GitHub 账户。如果还没有，请去注册一个。\n注册完后，登录 GitHub，在 GitHub. com 中点击 \u0026ldquo;New repository\u0026rdquo; 创建一个新的仓库。创建一个与用户名相同的仓库，后面加 .github.io。例如，如果你的 GitHub 用户名是 zjc2782171149，则仓库名称应为 zjc2782171149.github.io。\n6. 生成 SSH 密钥并添加到 GitHub 仓库 # 如果之前已绑定 GitHub 仓库的 SSH 密钥，可跳过此步骤。在 Git Bash 中输入：\ngit config --global user.name \u0026#34;yourname\u0026#34; git config --global user.email \u0026#34;youremail\u0026#34; 替换 yourname 和 youremail 为你的 GitHub 用户名和邮箱。检查配置：\ngit config user.name git config user.email 生成 SSH 密钥：\nssh-keygen -t rsa -C \u0026#34;youremail\u0026#34; 在 GitHub 的设置中找到 SSH keys 选项，点击 \u0026ldquo;New SSH key\u0026rdquo; 并将 id_rsa.pub 的内容复制进去。\n7. 将 Hexo 部署到 GitHub # 打开 Hexo 的站点配置文件 _config.yml，修改以下内容：\ndeploy: type: git repo: git@github.com:zjc2782171149/zjc2782171149.github.io.git branch: master 安装 hexo-deployer-git 插件：\nnpm install hexo-deployer-git --save 进行部署：\nhexo clean hexo generate hexo deploy 访问 http://你的用户名.github.io 查看部署结果。\n8. 个人域名配置 # 购买并注册一个域名，例如通过阿里云。配置域名解析，将域名解析到 你的用户名.github.io 上。在 GitHub 仓库的 \u0026ldquo;Settings\u0026rdquo; 中，设置 Custom domain，输入你的域名。\n在 source 文件夹中创建一个名为 CNAME 的文件，文件内容为你的域名。例如：\nblog.zhangjiancong.top 更改默认分支为 master，并重新部署：\nhexo clean hexo g hexo d 更改主题 # 以 Fluid 主题为例：\n下载 Fluid 主题的压缩包，将其解压到 themes 文件夹下，并更名为 fluid。 修改 _config.yml 文件，将 theme 属性更改为 fluid，并将 language 属性修改为 zh-CN。 输入以下指令重新部署： hexo clean hexo g hexo d 发布文章 # 发布新文章：\nhexo new newpapername 编辑 source/_posts 中的 Markdown 文件，完成后进行重新部署：\nhexo clean hexo g hexo d 每次更新文章或配置文件时都需要重新部署。\n如有小伙伴觉得每次重新部署都麻烦，可以研究 GitHub Pages 的自动部署功能。\nhexo 网站的完善 # 在 Hexo 网站上，标签页面和分类页面是两种不同的内容组织方式，但它们都可以帮助你对文章进行分类和组织。让我们先看看如何分别实现 标签页面 和 分类页面，以及如何使用 tags 和 categories 来实现文章的标签和分类。\n1. 标签页面（Tags Page） # 标签页面展示所有文章的标签及其对应的文章。它通常和文章的 tags 属性相结合，让读者可以通过点击某个标签来浏览该标签下的所有文章。\n如何实现标签页面 # 创建标签页面： 在 source 目录下创建一个 tags 文件夹，并在其中创建一个 index.md 文件。\nmkdir -p source/tags touch source/tags/index.md 编辑 index.md： 在 index.md 文件中，添加以下内容来指定它是一个标签页面：\n--- title: Tags date: 2024-09-06 00:00:00 type: \u0026#34;tags\u0026#34; --- 这里 type: \u0026quot;tags\u0026quot; 告诉 Hexo 这是一个标签页面，Hexo 会自动生成所有标签的列表。\n使用文章中的 Tags： 在你的文章（.md 文件）中使用 tags 字段来为文章指定标签。例如：\n--- title: 我的文章 date: 2024-09-06 tags: - 技术 - Hexo --- 在这个例子中，文章被打上了 技术 和 Hexo 两个标签。生成网站后，在标签页面 (/tags/) 中，点击这些标签会列出相关的文章。\n2. 分类页面（Categories Page） # 分类页面展示所有文章的分类及其对应的文章。与 tags 不同，分类是更高层次的组织方式，通常用于将文章分组为不同的主题或类型。\n如何实现分类页面 # 创建分类页面： 和创建标签页面类似，在 source 目录下创建一个 categories 文件夹，并在其中创建一个 index.md 文件。\nmkdir -p source/categories touch source/categories/index.md 编辑 index.md： 在 index.md 文件中，添加以下内容来指定它是一个分类页面：\n--- title: Categories date: 2024-09-06 00:00:00 type: \u0026#34;categories\u0026#34; --- 这里 type: \u0026quot;categories\u0026quot; 告诉 Hexo 这是一个分类页面，Hexo 会自动生成所有分类的列表。\n使用文章中的 Categories： 在你的文章（.md 文件）中使用 categories 字段来为文章指定分类。例如：\n--- title: 我的文章 date: 2024-09-06 categories: - 编程 - 前端开发 --- 在这个例子中，文章被归类为 编程 和 前端开发。生成网站后，在分类页面 (/categories/) 中，点击这些分类会列出相关的文章。\n3. 标签（Tags）和分类（Categories）的区别与组合使用 # 标签 (Tags)：标签通常用于给文章添加多个关键字，便于读者快速找到具有相似标签的文章。文章可以有多个标签，没有层级结构，通常适合更细粒度的内容分类。\n分类 (Categories)：分类则是将文章组织成更大的类别，具有层级结构。例如，一个分类可以有子分类。每篇文章通常只有一个主分类，适合更广泛的内容分组。\n示例文章使用标签和分类 # --- title: 我的博客文章 date: 2024-09-06 tags: - Hexo - 博客搭建 categories: - 技术 - Web开发 --- Tags (标签)：文章有两个标签 Hexo 和 博客搭建，点击这些标签可以找到所有有这些标签的文章。 Categories (分类)：文章被归类为 技术 -\u0026gt; Web开发，读者可以通过分类页面找到属于该分类的所有文章。 4. 关于我（about）页面 # 在 Hexo 中，你可以通过创建“关于页面”（About Page）来展示个人信息，比如 GitHub 链接、博客、QQ 等。可以通过创建一个单独的 Markdown 文件，并在其中定义这些信息。接下来，我会详细介绍如何设置“关于页面”以及如何在其中展示和跳转到你的个人信息。\n1. 创建“关于页面” # 首先，你需要在 Hexo 的 source 目录下创建一个新的文件夹，并在其中添加一个 index.md 文件：\nmkdir -p source/about touch source/about/index.md 2. 编辑 index.md 文件 # 在 index.md 文件中，你可以使用 Markdown 来书写内容并添加个人信息。Hexo 会将这个文件渲染成 HTML 页面。你可以使用 HTML 或 Markdown 链接来跳转到 GitHub、博客等。\n以下是一个“关于页面”的示例：\n--- title: 关于我 date: 2024-09-06 type: \u0026#34;about\u0026#34; --- # 关于我 你好，我是 [你的名字]，这是一段关于我的介绍。 ## 联系方式 - **GitHub**: [我的 GitHub](https://github.com/yourusername) - **博客**: [我的博客](https://yourblog.com) - **QQ**: 123456789 欢迎通过以上方式联系我！ 3. 显示个人信息并添加链接 # 你可以用 Markdown 格式轻松展示个人信息并添加超链接，比如：\nGitHub 链接可以写成我的 GitHub。 博客链接可以写成我的博客。 QQ 号可以直接展示为文本或通过一些 QQ API 实现联系跳转。 4. 在导航栏中添加“关于”页面 # 为了让用户可以轻松访问“关于页面”，你可以在 Hexo 的主题配置文件（通常是 themes/your-theme/_config.yml）中，将“关于”页面的链接添加到导航栏中。\n找到 menu 配置项，并添加 about 页面：\nmenu: Home: / Archives: /archives About: /about 这样，导航栏中就会出现“关于”链接，点击后会跳转到 /about 页面。\n5. 自定义样式 # 如果你想要让“关于页面”更加个性化，可以在主题的 CSS 文件中添加自定义样式。例如，可以为页面的个人信息部分定义一些自定义的样式，以美化显示效果。\n在 themes/your-theme/source/css/ 中的样式文件（例如 style.css 或 custom.css）中，添加以下内容：\n.about-info { font-size: 18px; color: #333; } .about-info a { color: #007bff; text-decoration: none; } .about-info a:hover { text-decoration: underline; } 然后，在 index.md 文件中将内容包裹在一个 div 里：\n\u0026lt;div class=\u0026#34;about-info\u0026#34;\u0026gt; ## 联系方式 - **GitHub**: [我的 GitHub](https://github.com/yourusername) - **博客**: [我的博客](https://yourblog.com) - **QQ**: 123456789 \u0026lt;/div\u0026gt; 这样，你就可以通过 CSS 自定义“关于页面”的样式。\n","date":"2024年9月7日","externalUrl":null,"permalink":"/misc/hexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E5%88%9B%E5%BB%BA/","section":"杂项","summary":" 笔记 三条常用命令：\n","title":"hexo个人博客创建","type":"misc"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E4%B8%BB%E9%A2%98%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"主题配置","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/","section":"Tags","summary":"","title":"博客搭建","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E6%AE%B5/","section":"Tags","summary":"","title":"数据段","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E5%A0%86%E5%92%8C%E6%A0%88/","section":"Tags","summary":"","title":"堆和栈","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E6%AE%B5/","section":"Tags","summary":"","title":"代码段","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"内存管理","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/categories/%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F/","section":"Categories","summary":"","title":"内存区域","type":"categories"},{"content":" 笔记 The program code, global variables, and constant global variables are all stored in static segments, as these all have static lifetimes and known sizes at compile time.\nIf we look at the addresses printed by mexplore, we see that the global variables stored in are at relatively low memory addresses (around 0x60'0000 hexadecimal and 0x40'0000 hexadecimal), while the local variable is stored at a high address, close to 0x7fff'ffff'ffff (about 247), and the dynamically allocated character is stored in between (albeit closer to the static segments).\nThere is a reason for this placement: it allows both types of segments to grow without risk of getting in each other\u0026rsquo;s way. In particular, the automatic-lifetime segment grows downwards as more local variables come into scope, while the dynamic-lifetime segment grows upwards. If they ever meet, your computer runs out of memory.\n内存区域划分 # 在计算机中，程序运行时的内存布局通常分为多个区域，每个区域负责存储特定类型的数据。这种内存布局通常包括以下主要部分：\n代码段（Text Segment）:\n存储内容: 存放程序的可执行代码，也就是编译后的机器指令。该区域通常是只读的，以防止程序的代码被意外或恶意修改。 特性: 常见操作系统将其标记为只读并可执行。 数据段（Data Segment）:\n存储内容: 存放已初始化的全局变量和静态变量。这个段可以进一步分为两部分： 已初始化数据段（Initialized Data Segment）: 存储在编译时已初始化的全局和静态变量。 未初始化数据段（BSS Segment，Block Started by Symbol）: 存放未初始化的全局变量和静态变量。在程序开始执行时，BSS 段的内容会被初始化为零。 特性: 数据段是可读写的。 堆（Heap dynamic-lifetime segment is called the heap）:\n存储内容: 用于动态分配的内存块。malloc、calloc、realloc 等函数在运行时从堆中分配内存。堆的大小可以在运行时增长和缩小。 特性: 堆区域通常从低地址向高地址扩展。 栈（Stack automatic-lifetime segment is called the stack.）:\n存储内容: 用于存放函数的局部变量、函数参数和返回地址。当函数被调用时，会在栈上分配一个新的栈帧，用于存储该函数的局部数据。函数调用结束后，栈帧会被释放。 特性: 栈区域通常从高地址向低地址扩展。栈的大小是有限的，过度使用栈空间（如过深的递归调用或大量局部变量）可能导致栈溢出。 常量段（Rodata Segment）:\n存储内容: 存放只读数据，比如字符串常量和其他不变数据。这些数据通常在编译时确定并在运行时不会改变。 特性: 常量段通常是只读的。 执行堆栈（Call Stack）:\n存储内容: 特指用于存储每个线程的调用信息，包括函数调用的返回地址、局部变量和函数参数。执行堆栈与栈区域有些重叠的概念。 特性: 递归调用或大量局部变量可能导致栈溢出。 内存布局示意图 # 典型的内存布局==从高地址到低地址==可以如下表示：\n+--------------------+ | 栈 (Stack) | +--------------------+ | 堆 (Heap) | | (动态分配区) | +--------------------+ | 未初始化数据段 (BSS) | +--------------------+ | 已初始化数据段 (Data) | +--------------------+ | 只读数据段 (Rodata) | +--------------------+ | 代码段 (Text) | +--------------------+ | 其他系统区域 (Other) | +--------------------+ 在 C 语言中，变量根据其类型和作用域分布在程序的不同内存区域。以下是不同类型的变量及其存储位置的详细说明：\n全局变量（Global Variables）:\n定义: 在函数外部定义的变量，具有全局作用域，整个程序运行期间都可以访问。 存储位置: 已初始化的全局变量存储在**数据段（Data Segment）**的已初始化数据区域。 未初始化的全局变量存储在BSS 段（Block Started by Symbol Segment），程序开始时 BSS 段的内容被初始化为零。 静态变量（Static Variables）:\n定义: 使用 static 关键字修饰的变量。可以是全局或局部的，但在程序的生命周期内都只分配一次，并保留其值。 存储位置: 静态全局变量：与全局变量类似，已初始化的存储在数据段，未初始化的存储在 BSS 段。 静态局部变量：尽管其作用域仅限于函数内，但其生命周期与程序相同，也存储在数据段或 BSS 段中。 常量变量（Constant Variables）:\n定义: 使用 const 关键字修饰的变量，声明后不能更改其值。 存储位置: 常量全局变量：通常存储在只读数据段（Rodata Segment）。但如果常量是局部的，它可能存储在栈或寄存器中，具体取决于编译器优化。 常量局部变量：通常在栈上分配，或者在某些情况下编译器可能将它们优化到寄存器中。 局部变量（Local Variables）:\n定义: 在函数或块中定义的变量，只在其定义的作用域内有效。 存储位置: 通常存储在**栈（Stack）**中。函数调用时，局部变量在栈上分配空间，函数返回时这些空间被释放。 动态分配的内存:\n定义: 使用 malloc、calloc、realloc 等函数动态分配的内存。大小和数量在运行时决定。 存储位置: 存储在**堆（Heap）**中。堆内存的生命周期由程序员控制，需要手动释放（使用 free 函数），否则会导致内存泄漏。 总结 # 代码段（Text Segment）: 存放程序的可执行代码。 数据段（Data Segment）: 存放已初始化的全局变量和静态变量。 BSS 段（Block Started by Symbol Segment）: 存放未初始化的全局变量和静态变量，程序开始时初始化为零。 只读数据段（Rodata Segment）: 存放常量数据和字符串常量。 栈（Stack）: 存放局部变量、函数参数和返回地址。 堆（Heap）: 存放动态分配的内存。 理解这些内存区域及其作用对于高效和安全的编程至关重要，特别是在涉及到动态内存管理、全局状态和并发问题时。\n","date":"2024年9月7日","externalUrl":null,"permalink":"/notes/system/%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":" 笔记 The program code, global variables, and constant global variables are all stored in static segments, as these all have static lifetimes and known sizes at compile time.\n","title":"内存区域介绍","type":"notes"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/futamura%E6%8A%95%E5%BD%B1/","section":"Tags","summary":"","title":"Futamura投影","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91%E6%8A%80%E6%9C%AF/","section":"Tags","summary":"","title":"编译技术","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/categories/%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E5%92%8C%E8%AF%AD%E8%A8%80/","section":"Categories","summary":"","title":"编程思想和语言","type":"categories"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E8%A7%A3%E9%87%8A%E5%99%A8/","section":"Tags","summary":"","title":"解释器","type":"tags"},{"content":"Futamura 投影（Futamura Projection）是编程语言和编译技术中的一个重要概念，由日本计算机科学家 Futamura Yoshihiko 在 1971 年提出。Futamura 投影解释了如何通过对解释器进行部分求值（Partial Evaluation），将解释器转换为编译器，以及进一步的可能转变。\n背景与基本概念 # 在理解 Futamura 投影之前，需要了解以下两个概念：\n解释器（Interpreter）：一种程序，它读取并执行另一个程序的代码。解释器按代码的一行或一段来处理，逐行或逐段地将源代码翻译成目标代码并执行。\n部分求值（Partial Evaluation）：一种编译技术，旨在通过提前计算程序中能够在编译时确定的部分，从而生成更高效的代码。部分求值会保留在运行时才知道的部分，从而使得程序既高效又灵活。\n三次 Futamura 投影 # Futamura 投影可以分为三个阶段，称为“第一次投影”、“第二次投影”和“第三次投影”：\n第一次 Futamura 投影：\n将解释器和一个特定的源程序作为部分求值器的输入，生成一个针对该特定源程序的专用编译器。这个编译器可以直接将源程序翻译成目标代码。 形式化描述：如果有解释器 I 和源程序 P，通过部分求值（PE），我们可以生成一个等价的目标程序 P'。 表示为：PE(I, P) = P'，其中 P' 是源程序 P 对应的目标代码。 第二次 Futamura 投影：\n将解释器本身作为部分求值器的输入，生成一个针对任何源程序的编译器。这意味着将解释器的行为部分求值，得到一个编译器。 形式化描述：通过部分求值器 PE 对解释器 I 进行部分求值，可以生成一个编译器 C，这个编译器可以将任何源程序 P 翻译成目标程序 P'。 表示为：PE(PE, I) = C，其中 C 是一个可以将源程序 P 编译成目标代码 P' 的编译器。 第三次 Futamura 投影：\n将部分求值器本身作为输入，对其进行部分求值，生成一个可以生成编译器的编译器生成器（也称为编译器生成器、编译器编译器）。 形式化描述：通过部分求值器 PE 对 PE 进行部分求值，可以生成一个编译器生成器 G，该生成器能够生成编译器。 表示为：PE(PE, PE) = G，其中 G 是一个可以生成编译器的编译器生成器。 意义与应用 # Futamura 投影展示了如何通过部分求值将解释器转化为编译器，并进一步展示了编译器生成器的潜力。这一思想在编程语言的实现和优化领域具有重要意义，特别是在自动化生成编译器和提高编译效率方面。\n第一次投影提供了从解释器生成专用编译器的方法，使得特定源程序可以直接被编译为目标代码。 第二次投影展示了如何生成通用编译器，从而无需解释器就可以直接编译任何源程序。 第三次投影则展示了生成编译器生成器的可能性，即自动化工具可以生成编译器，这对编程语言设计与实现具有深远影响。 Futamura 投影是一种理论工具，展示了编译和解释之间的深层联系，并引导了编译器自动生成技术的发展。\n摘自论坛 # 相比上一节强调物理或逻辑上客观存在的 “界限”，本节主要强调一种心智活动。这样的心智活动具有一个明显的特征，那就是它具有一种 “递归” 的结构。我们首先从一个概念中抽象出它的形成过程，再将这样的思维过程应用于这个概念自身。如此，我们得到的新概念常常具有这样的结构： A 的 A。\n个人认为，本节讨论的思想或许是本文的所有思想中最为重要的那一个。这是因为高阶这一思想具有很好的方法论层面上的指导意义，并且在实践中操作起来又有趣又直截了当——同时，个人的看法是通过这一思想生成的很多概念对启迪编程初学者的思维具有非常重要的意义（事实上，如果要从整个技术界中选一个本科教育一般不大重视、但又极度有价值的术语，我会选闭包和高阶函数）。\n通过这样一种 “递归式” 的抽象过程得到的 A 的 A 这一形式的概念，我们通常将其称作 “高阶 A”。比如，将函数代表的过程 (A -\u0026gt; B) 抽象出来，再应用于函数自身，我们就得到了 函数的函数，即 高阶函数（higher-order function）。个人认为高阶函数和闭包是计算机领域最为重要的抽象之一。\n例如，将类型的形成过程（类型描述一个语言中最小单位的类别）抽象出来，应用于类型自身，我们就得到了 类型的类型，即 型别（kind）。\n二村映射 (Futamura Projections) # 最后，作为压轴，讲一个我昨晚在一篇屯了半年一年的文章中看到的一个概念。我想这个概念应该是超出大部分读者的经验的，而我觉得它还蛮有意思，所以放到最后来大概说说。我们需要从故事的最开始讲起——可能要过很久才能最终看到这个词。\n编译技术的分类 # 我们知道大体上编译技术可以分为两种，分别是 编译器 (compiler) 和 解释器 (interpreter)。编译器执行的是一个两阶段的过程：首先，自源代码编译出目标程序 (target program)，再向目标程序提供输入，然后得到程序的输出；解释器执行的则是一个一阶段的过程：解释器接收源代码和输入，并直接得到程序的输出。在本节中，我们主要关注解释器。\n绝大部分的编译原理教科书和课程都只涵盖这两者。然而，事实上还存在着第三种编译技术：某种程度上，它介于解释器和编译器之间，但它既不是解释器又不是编译器——请特别注意这一点。为了方便理解，我先岔开点讲。\n函数式编程中的概念 # 稍有函数式经验的读者应该都听过 柯里化 (Currying)。如果有一个接收 n 个输入的函数，我们可以把它转化成一叠嵌套的高阶函数，这叠高阶函数中的第 i 个接收原函数中的第 i 个输入（仅一个），然后返回接收剩下 n-i 个输入的函数（故为 “高阶函数”）：比如，一个接收 3 个输入的函数 ((A, B, C) -\u0026gt; Output) 经柯里化后会成为一叠高阶函数，这个高阶函数中的第一个接收一个输入 A，然后返回一个接收输入 B 的高阶函数；再向其提供输入 C，才能得到最终结果，即柯里化后的结果是 (A -\u0026gt; (B -\u0026gt; (C -\u0026gt; Output)))。\n尽管在支持闭包和高阶函数的语言中柯里化和 反柯里化 (Uncurrying) 都能以非常浅显 (trivial) 的方式实现，但仅有少数的编程语言才一等地支持了柯里化，比如 ML 系语言（Haskell, SML, OCaml, F#）和 Scala。\n对于一个经柯里化后的函数，如果我们只向它提供前 i (i\u0026lt;n) 个输入，那么我们必然得不到最终的输出，而只能得到一个高阶函数，它会再处理余下的输入：这称作 部分应用 (Partial Application)。\n讲了这么一大堆，其实主要就是为了部分应用这个概念。你可能会想知道为什么我在上文不用参数这个惯用说法而是用输入，你可能也已经猜到了，这是因为我希望在此强调将解释器理解成一个函数的思想：它的输入是源代码和这段代码的输入，它的输出是这段代码的输出（事实上，很多语言都提供一些十分类似解释器的机制，它们确实是函数（比如 eval））。\n部分求值 (Partial Evaluation) # 是的，之所以要将解释器理解成一个函数，是因为我正是要借用部分应用的思想：既然解释器的输入是源代码和输入，那么我能不能部分应用这个 “函数”（即解释器），首先给定源代码，得到一个 “处理剩下参数的高阶函数”（即一个中间程序），然后对于这个中间程序，再提供它的输入，然后得到结果？\n答案是，这是可行的。事实上，这一过程称作 部分求值 (Partial Evaluation)，而执行这一过程的程序我们称作 部分求值器 (Partial Evaluator)。部分求值器作用于一个程序和它的一些参数，输出一个该程序对于这组参数 “特化” 后的新的程序。这里的 “一个程序” 我们称作 源程序 (Source Program)，“新的程序” 我们称作 残差程序 (Residual Program)。\n回到上边有关解释器举例上来。我们将一个解释器和一个源程序传入部分求值器中，得到的残差程序就是一个接收 输入 返回 输出 的中间程序。\n是不是觉得非常的熟悉……？第一阶段我们提供源代码，得到中间程序；第二阶段我们提供输入，随后得到输出。这是在做什么…？\n是的，这就是编译。神奇之处在于，我们并没有手工编写一个编译器，我们只是将一个解释器和需要被求值的源代码扔给了部分求值器而已——某种意义上来说，我们是 “免费” 得到了一个编译器。\n部分求值器与语言虚拟机 # 你可能在思考这有什么用？你或许已经将这一思路和语言虚拟机联系了起来：众所周知，实现编译器通常比实现解释器难得多，那我们能不能弄一个本质其实是一个部分求值器的语言虚拟机，在这个虚拟机上实现一门新的语言只需要编写一个解释器就行了，而语言虚拟机会调用它的部分求值器自动帮我生成对应的、更加高效的 “编译器” 呢？\n显然，这是完全可行的——事实上这是有产业实践的。PyPy 是一个 Python 语言的解释器实现，它正是基于以上语言虚拟机的思路实现的：在 PyPy VM 上工作的语言有 Python, Ruby 和逻辑编程语言 Prolog 等；除此以外，泛语言高级语言虚拟机（HLLVM）GraalVM 的一个重要组件同样如此，工作在 GraalVM 上的语言包括 JavaScript, Ruby, Python, Java, R 等。\n这其实就是 第一类二村映射 (First Futamura Projection)。\n二村映射的递归性 # 既然是放在 “高阶” 这一主题下的，我们当然要用到这一思想：正如前几段的粗体所提示的，部分求值器其实也是一个程序，它自身也能够被部分求值。\n回想一开始，解释器 的输入是 源代码 和 输入，我们将 解释器（主体） 和 源代码（主体的其中一个输入） 单独拎出来，扔进了部分求值器中，这是第一类二村映射；即是说，在第一类二村映射中，部分求值器 的输入是 解释器 和 源代码。\n我们将这一过程——即 “将 主体 和 主体的其中一个输入 单独拎出来扔进部分求值器中”—— “高阶化”，作用于自身，简单的代换就能得到，我们需要将 部分求值器（主体） 和 解释器 （主体的其中一个输入） 扔进一个部分求值器中。现在我们需要弄清楚，这个 “高阶过程” 得到的残差程序是什么。\n注意到，在第一类二村映射中输入只有 解释器 和 源代码。既然我们已经部分求值了前者，那么下一步自然只剩下后者了：向上一段提到的 残差程序 提供 源代码，我们就能得到 目标程序。\n向一个程序提供源代码得到另一个程序…… 听起来有点像是某个我们已经知道的东西会做的事情。是的，这就是编译。我们的这个 “高阶过程” 得到的残差程序其实是一个编译器，而我们的这个 “高阶过程” 所做的，正是在生成编译器，即，这一过程其实是编译器的编译器——如此，我们终于见到了本节概述中提到的 A 的 A 这样的结构，如果你愿意的话，或许你还可以将这称作高阶编译器。\n这其实就是 第二类二村映射 second Futamura projection。\n那么，这个 “高阶化” 的过程还能继续下去吗？显然如此。再继续下去，我们就能得到_编译器的编译器的编译器_，也即第三类二村映射 third Futamura projection。再往后呢？这点就留给读者自行阅读和思考啦。\n","date":"2024年9月7日","externalUrl":null,"permalink":"/notes/system/%E4%BA%8C%E6%9D%91%E6%98%A0%E5%B0%84/","section":"笔记","summary":"Futamura 投影（Futamura Projection）是编程语言和编译技术中的一个重要概念，由日本计算机科学家 Futamura Yoshihiko 在 1971 年提出。Futamura 投影解释了如何通过对解释器进行部分求值（Partial Evaluation），将解释器转换为编译器，以及进一步的可能转变。\n","title":"二村映射","type":"notes"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E9%83%A8%E5%88%86%E6%B1%82%E5%80%BC/","section":"Tags","summary":"","title":"部分求值","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/c%E8%AF%AD%E8%A8%80/","section":"Tags","summary":"","title":"C语言","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/gcc/","section":"Tags","summary":"","title":"GCC","type":"tags"},{"content":" 编译系统（过程） # 编译过程是将源代码转换成可执行文件的几个步骤的集合。每一步生成不同类型的文件，这些文件在最终的可执行程序中扮演不同的角色。以下是详细的步骤以及每个阶段产生的文件的作用：\n1. 预处理（Preprocessing） # 命令: gcc -E test.c -o test.i\n文件: test.i\n作用:\n预处理器处理 #include、#define 和其他预处理指令。\n结果是一个展开的源文件，其中所有的宏已被展开，包含文件已被插入。\n2. 编译（Compilation） # 命令: gcc -S test.i -o test.s\n文件: test.s\n作用:\n编译器将预处理过的源文件（通常是 .i 文件，但可以直接使用 .c 文件）转换成汇编语言代码。 生成的汇编文件包含目标平台的汇编指令，但尚未转换为机器代码。 3. 汇编（Assembling） # 命令: gcc -c test.s -o test.o\n文件: test.o\n作用:\n汇编器将汇编语言代码（.s 文件）转换为机器代码。 生成的目标文件（.o 文件）包含机器代码以及与程序相关的其他信息，如符号表和重定位信息。 目标文件尚未完全链接为可执行文件。它是可以被其他目标文件链接的中间文件。 4. 链接（Linking） # 命令: gcc test.o -o test\n文件: test\n作用:\n链接器将一个或多个目标文件（.o 文件）和可能的库文件链接成一个最终的可执行文件。 处理符号解析和重定位。符号解析涉及到解决目标文件中引用的外部符号（例如库函数）的地址。 生成的可执行文件可以直接在操作系统上运行。 中间文件总结 # .i 文件（预处理输出）:\n包含展开的源代码，所有宏和包含文件都被处理。 用于检查预处理后的代码是否正确。 .s 文件（汇编代码）:\n包含汇编语言的源代码，描述了机器指令。 用于查看编译器生成的汇编代码。 .o 文件（目标文件）:\n包含机器代码、符号表、重定位信息等。 用于最终的链接步骤，是编译的中间产物。 可执行文件（通常没有文件扩展名）:\n包含最终的机器代码，可以直接运行。 经过链接器处理，所有符号都被解析，重定位信息已处理。 例子总结 # 假设 test.c 内容如下：\nvoid f() { // 空实现 } int main() { return 0; } 预处理: gcc -E test.c -o test.i 编译: gcc -S test.c -o test.s 汇编: gcc -c test.c -o test.o 链接: gcc test.c -o test 解释:\ntest.i 是预处理后的源代码。 test.s 是编译生成的汇编代码。 test.o 是编译生成的目标文件。 test 是最终生成的可执行文件，可以运行。 gcc 相关命令用法 # GCC（GNU Compiler Collection）是一个强大的编译器，支持多种编程语言。以下是一些常用的 GCC 命令和选项，特别是用于编译 C 程序以及生成汇编文件的相关选项：\n编译 C 程序 # 编译并链接\ngcc -o output_executable source.c -o output_executable：指定输出可执行文件的名称。 source.c：源文件。 仅编译，不链接\ngcc -c source.c -c：只编译源代码，==不链接==生成可执行文件。这会生成一个目标文件（source.o）。 生成汇编文件 # 生成汇编代码 gcc -S source.c -S：将源代码编译为汇编代码，而不是目标文件。生成的汇编文件扩展名通常为 .s。 预处理 # 仅进行预处理 gcc -E source.c -o source.i -E：仅执行预处理，并将结果输出到指定文件。 显示编译信息 # 显示编译过程中调用的命令\ngcc -v source.c -v：显示编译过程中的详细信息。 显示预定义宏\ngcc -dM -E - \u0026lt; /dev/null -dM：显示所有预定义的宏。 -E：执行预处理。 优化选项 # 优化代码 gcc -O1 source.c -o output_executable gcc -O2 source.c -o output_executable gcc -O3 source.c -o output_executable -O1, -O2, -O3：分别表示不同级别的优化。 调试信息 # 生成调试信息 gcc -g source.c -o output_executable -g：生成调试信息，用于调试器（如 gdb）。 其他常用选项 # 定义宏\ngcc -DNAME=value source.c -o output_executable -DNAME=value：定义一个预处理宏。 包含目录\ngcc -I/path/to/include source.c -o output_executable -I/path/to/include：指定额外的头文件搜索路径。 链接库\ngcc -L/path/to/lib -lname source.c -o output_executable -L/path/to/lib：指定库文件搜索路径。 -lname：链接名为 libname.so 或 libname.a 的库。 实例 # 假设有一个简单的 C 程序文件 main.c，我们可以使用以下命令：\n编译并链接生成可执行文件\ngcc -o my_program main.c 生成汇编文件\ngcc -S main.c 仅编译为目标文件\ngcc -c main.c 进行优化编译\ngcc -O2 -o my_program main.c 生成带有调试信息的可执行文件\ngcc -g -o my_program main.c 这些命令和选项可以帮助你在不同的场景下灵活使用 GCC 编译器。\nobjdump 和 hexdump 是两个在软件开发和分析中非常有用的命令行工具。它们用于处理和查看二进制文件的内容，但用途和输出方式不同。下面是这两个命令的详细介绍及示例说明。\n相关常用的代码分析工具 # objdump # objdump 是一个用于显示二进制文件的内容的工具，尤其是可执行文件、目标文件和库文件。它可以显示反汇编代码、符号表、段信息等。objdump 常用于调试和逆向工程。\n常用选项 # -d：反汇编可执行文件或目标文件的代码。 -S：在反汇编代码中插入源代码（如果可用）。 -t：显示符号表。 -h：显示段头信息。 -x：显示所有头信息。 示例 # 假设有一个简单的 C 文件 hello.c：\n#include \u0026lt;stdio.h\u0026gt; void hello() { printf(\u0026#34;Hello, World!\\n\u0026#34;); } int main() { hello(); return 0; } 编译成可执行文件 hello：\ngcc -o hello hello.c 使用 objdump 查看反汇编代码：\nobjdump -d hello 输出可能如下所示（部分）：\nhello: file format elf64-x86-64 Disassembly of section .text: 0000000000001139 \u0026lt;hello\u0026gt;: 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: bf 00 00 00 00 mov $0x0,%edi 1142: e8 00 00 00 00 callq 0 \u0026lt;puts@plt\u0026gt; 1147: 90 nop 1148: 5d pop %rbp 1149: c3 retq 000000000000114a \u0026lt;main\u0026gt;: 114a: 55 push %rbp 114b: 48 89 e5 mov %rsp,%rbp 114e: e8 f0 ff ff ff callq 1139 \u0026lt;hello\u0026gt; 1153: b8 00 00 00 00 mov $0x0,%eax 1158: 5d pop %rbp 1159: c3 retq hexdump # hexdump 是一个用于查看文件的十六进制表示的工具。它可以显示文件内容的十六进制值及其对应的 ASCII 字符。hexdump 常用于查看和分析二进制文件或数据文件的内容。\n常用选项 # -C：以十六进制和 ASCII 字符显示文件内容。 -n：指定读取的字节数。 -v：显示所有行（默认情况下，连续相同的行会被压缩）。 示例 # 假设有一个文本文件 example.txt，内容如下：\nHello, World! 使用 hexdump 查看其十六进制表示：\nhexdump -C example.txt 输出可能如下所示：\n00000000 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0a |Hello, World!.| 0000000e 其中，左侧是文件的偏移地址，中间是文件内容的十六进制表示，右侧是对应的 ASCII 字符。\n总结 # objdump：用于分析和反汇编二进制文件，适合调试和逆向工程。 hexdump：用于查看文件的十六进制表示，适合查看和分析文件的低级内容。 这两个工具在不同的情况下都有很大的用处，可以帮助开发者和分析人员更好地理解和处理二进制文件。\nc 程序中内联汇编代码 # 在 C 程序中插入汇编代码有两种主要的方法：内联汇编（Inline Assembly） 和 汇编语言文件（Assembly Language Files）。这两种方法允许在 C 程序中嵌入汇编代码，但它们的用法和应用场景有所不同。下面是对这两种方法的详细说明：\n1. 内联汇编（Inline Assembly） # 内联汇编允许在 C 代码中直接嵌入汇编指令。这种方法适用于在 C 程序中需要插入少量汇编代码的情况。它使得在一个 C 函数中直接嵌入汇编代码成为可能。\n语法 # 在 GCC 中，内联汇编使用 __asm__ 或 asm 关键字。可以使用以下语法：\nasm(\u0026#34;assembly code\u0026#34; : output operands : input operands : clobbered registers); \u0026quot;assembly code\u0026quot;: 要插入的汇编代码。 output operands: 说明汇编代码将写入哪些 C 变量（如果有的话）。 input operands: 说明汇编代码将读取哪些 C 变量（如果有的话）。 clobbered registers: 说明汇编代码将修改哪些寄存器（如果有的话）。 示例 # 下面是一个在 C 代码中使用内联汇编的示例：\n#include \u0026lt;stdio.h\u0026gt; int main() { int a = 10; int b; // 内联汇编代码：将 a 的值加到 b 中 asm(\u0026#34;addl %1, %0\u0026#34; : \u0026#34;=r\u0026#34; (b) : \u0026#34;r\u0026#34; (a), \u0026#34;0\u0026#34; (b)); printf(\u0026#34;The result is %d\\n\u0026#34;, b); return 0; } 解释:\naddl %1, %0 是要执行的汇编指令，将 a 的值加到 b 中。 \u0026quot;=r\u0026quot; (b) 表示 b 是一个输出操作数，使用一个通用寄存器。 \u0026quot;r\u0026quot; (a) 表示 a 是一个输入操作数，也使用一个通用寄存器。 \u0026quot;0\u0026quot; (b) 表示 b 在汇编代码中也是一个输入操作数，但也用作输出操作数，0 表示它是输出操作数的寄存器的相同位置。 2. 汇编语言文件（Assembly Language Files） # 汇编语言文件是使用汇编语言编写的独立文件，通常具有 .s 或 .asm 扩展名。这种方法适用于需要大量汇编代码或需要将汇编代码与 C 代码分开的情况。\n使用方法 # 创建汇编语言文件:\n创建一个名为 example.s 的汇编文件，内容如下：\n.global my_function .text my_function: movl $42, %eax ret 解释:\n.global my_function 将 my_function 声明为全局符号，使其可以被其他文件引用。 movl $42, %eax 将值 42 加载到寄存器 %eax。 ret 返回到调用函数的地方。 在 C 程序中声明和调用汇编函数:\n#include \u0026lt;stdio.h\u0026gt; extern int my_function(); int main() { int result = my_function(); printf(\u0026#34;The result is %d\\n\u0026#34;, result); return 0; } 编译和链接:\n编译和链接汇编语言文件和 C 文件：\ngcc -c example.s -o example.o gcc -o main main.c example.o 这将生成可执行文件 main，其中包含汇编和 C 代码的混合。\n总结 # 内联汇编：直接在 C 代码中插入汇编指令，适用于少量汇编代码和需要直接与 C 变量交互的场景。 汇编语言文件：将汇编代码放在独立文件中，适用于较复杂的汇编代码或需要与多个 C 文件分开的情况。 这两种方法各有其优点和适用场景，可以根据具体需求选择使用。\n","date":"2024年9月7日","externalUrl":null,"permalink":"/notes/tools/gcc%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":"编译系统（过程） # 编译过程是将源代码转换成可执行文件的几个步骤的集合。每一步生成不同类型的文件，这些文件在最终的可执行程序中扮演不同的角色。以下是详细的步骤以及每个阶段产生的文件的作用：\n","title":"gcc介绍","type":"notes"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B/","section":"Tags","summary":"","title":"编译过程","type":"tags"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/categories/%E7%BC%96%E8%AF%91%E5%99%A8/","section":"Categories","summary":"","title":"编译器","type":"categories"},{"content":"","date":"2024年9月7日","externalUrl":null,"permalink":"/tags/%E9%A2%84%E5%A4%84%E7%90%86/","section":"Tags","summary":"","title":"预处理","type":"tags"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/tags/hamming-number/","section":"Tags","summary":"","title":"Hamming Number","type":"tags"},{"content":" hamming number 题解思路 # 题目：codewars网站 # solutions # ***个人第一题目思路：***就一个数的质因数分解，我只要检查这个数质因数是不是只有2 3 5就行了。 # def is_hamming_number(num): \u0026#34;\u0026#34;\u0026#34;判断一个数是否是 Hamming 数\u0026#34;\u0026#34;\u0026#34; while num % 2 == 0: num //= 2 while num % 3 == 0: num //= 3 while num % 5 == 0: num //= 5 return num == 1 def generate_hamming_numbers(n): \u0026#34;\u0026#34;\u0026#34;生成前 n 个 Hamming 数\u0026#34;\u0026#34;\u0026#34; hamming_numbers = [] num = 1 while len(hamming_numbers) \u0026lt; n: if is_hamming_number(num): hamming_numbers.append(num) num += 1 return hamming_numbers def nth_hamming_number(n): \u0026#34;\u0026#34;\u0026#34;查询第 n 个 Hamming 数\u0026#34;\u0026#34;\u0026#34; hamming_numbers = generate_hamming_numbers(n) return hamming_numbers[-1] if n \u0026lt;= len(hamming_numbers) else None 简单明了的思路，就是不断遍历，直到找到质因数只有2，3，5的数，然后放进列表内。\n然后，对比上面的solutions和我的思路可以发现很明显的优化，就是solutions不做多余的操作，直接找到下一个hamming number。他能做到这样，个人认为是简单分析了数字的规律，用到了一点数学分析和数论知识。以下是我的简单分析证明：\n一个自己关于算法正确性的证明 这个最后我认为不可能的，是因为之前没想到，只要这个数是2 3 5的倍数就行了的。\n","date":"2024年6月6日","externalUrl":null,"permalink":"/notes/math/hamming-number-solutions/","section":"笔记","summary":"hamming number 题解思路 # 题目：codewars网站 # solutions # ***个人第一题目思路：***就一个数的质因数分解，我只要检查这个数质因数是不是只有2 3 5就行了。 # def is_hamming_number(num): \"\"\"判断一个数是否是 Hamming 数\"\"\" while num % 2 == 0: num //= 2 while num % 3 == 0: num //= 3 while num % 5 == 0: num //= 5 return num == 1 def generate_hamming_numbers(n): \"\"\"生成前 n 个 Hamming 数\"\"\" hamming_numbers = [] num = 1 while len(hamming_numbers) \u003c n: if is_hamming_number(num): hamming_numbers.append(num) num += 1 return hamming_numbers def nth_hamming_number(n): \"\"\"查询第 n 个 Hamming 数\"\"\" hamming_numbers = generate_hamming_numbers(n) return hamming_numbers[-1] if n \u003c= len(hamming_numbers) else None 简单明了的思路，就是不断遍历，直到找到质因数只有2，3，5的数，然后放进列表内。\n","title":"hamming_number_solutions","type":"notes"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/categories/math/","section":"Categories","summary":"","title":"Math","type":"categories"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/tags/%E7%BC%96%E7%A8%8B%E7%BB%83%E4%B9%A0/","section":"Tags","summary":"","title":"编程练习","type":"tags"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/tags/%E9%A2%98%E8%A7%A3/","section":"Tags","summary":"","title":"题解","type":"tags"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/tags/%E7%AE%97%E6%B3%95/","section":"Tags","summary":"","title":"算法","type":"tags"},{"content":"","date":"2024年6月6日","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6/","section":"Tags","summary":"","title":"数学","type":"tags"},{"content":"","date":"2024年5月15日","externalUrl":null,"permalink":"/tags/%E6%89%A9%E5%B1%95%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97/","section":"Tags","summary":"","title":"扩展欧几里得","type":"tags"},{"content":"","date":"2024年5月15日","externalUrl":null,"permalink":"/tags/%E8%B4%9D%E7%A5%96%E5%AE%9A%E7%90%86/","section":"Tags","summary":"","title":"贝祖定理","type":"tags"},{"content":"","date":"2024年5月15日","externalUrl":null,"permalink":"/tags/%E6%95%B0%E8%AE%BA/","section":"Tags","summary":"","title":"数论","type":"tags"},{"content":" #数论知识 # :raised_hands:一些个人理解和思考（素数的一些建议看书，书本讲得不错，虽然有些点不加以细证），尤其Extened Chinese Remider Theroy那里，是个人思考总结的，仅供参考。:raised_hands:\n数论pdf #数论知识 #贝祖定理（Bézout\u0026rsquo;s identity） #扩展欧几里得思路解析： #扩展欧几里得c++代码 #中国剩余定理（Chinese Remainder Theorem） #扩展中国剩余定理（Extened Chinese Remider Theroy） #扩展中国剩余定理代码实现： #贝祖定理（Bézout\u0026rsquo;s identity） # 用贝祖定理求出一个方程：ax + by = c 的解\nx = x0 + b/gcd(a, b) * t y = y0 - a/gcd(a, b) * t 贝祖定理的右边c改为1，就是求解模逆元的公式。所以，我才说，这个扩展欧几里得，本质上就是用贝祖定理。\n贝祖定理的证明\n#扩展欧几里得思路解析： # 其实这个扩展欧几里得，我觉得本质上就是用贝祖定理\n扩展点就是：能够实现自验证；最有用的点就是在运算过程中，找到贝租系数，从而就求解模逆元\n看一下wiki就可以了\nextgcd wiki 手稿\n#扩展欧几里得c++代码 # #include \u0026lt;iostream\u0026gt; using namespace std; int extgcd(int a, int b, int \u0026amp;x, int \u0026amp;y) { if (b == 0) { x = 1; y = 0; return a; } int d = extgcd(b, a % b, x, y); int t = x; x = y; y = t - a / b * y; return d; } /* 另一种写法，更接近数学表达 struct EuclidResult { int d, x, y; }; int extgcd(int a, int b) { int x_0 = 1, y_0 = 0, x_1 = 0, y_1 = 1; while (b) { int temp; int q = a / b; temp = a- q * b; a = b; b = temp; temp = x_0 - q * x_1; x_0 = x_1; x_1 = temp; temp = y_0 - q * y_1; y_0 = y_1; y_1 = temp; } return EuclidResult{a, x_0, y_0}; } */ 这个x和y就是贝祖系数，一组解。\n#中国剩余定理（Chinese Remainder Theorem） # 大致阐述：一个数x对于模数n1,n2,n3...nr的余数分别为r1,r2,r3...rn，且n1,n2,n3...nr两两互质，则x对于模数n1*n2*n3...*nr的余数为r1,r2,r3...rn的解。\n这个思路还是要单独写一下，因为这个思路是很重要的，在后面的扩展中国剩余定理中会用到。其次要关注一下贝祖定理的思路分析\n$$ S : \\left\\{ \\begin{array}{ll} x \\equiv r_1 \\pmod{a_1} \\\\ x \\equiv r_2 \\pmod{a_2} \\\\ % \\cdots \\\\ \\vdots \\\\ x \\equiv r_n \\pmod{a_n} \\\\ \\end{array} \\right. $$模逆元求解使用到了扩展欧几里得算法，看下方tips\n$$ N = n_1 \\cdot n_2 \\cdot n_3 \\cdots n_i \\cdots n_n \\\\ 记N_i = \\frac{N}{n_i} = n_1 \\cdot n_2 \\cdots n_{i-1} \\cdot n_{i+1} \\cdots n_n \\\\ 可得 N_i 和 n_i 互质 即 gcd(N_i, n_i) = 1 \\\\ 所以 模逆元 N_i \\cdot t_i \\equiv 1 \\pmod{n_i} \\\\ 即 a_i \\cdot N_i \\cdot t_i \\equiv a_i \\pmod{n_i} \\\\ 可得：当j \\neq i时，a_j \\cdot N_j \\cdot t_j \\equiv 0 \\pmod{n_i} \\text{因为}N_j包含了n_i \\\\ $$ $$ \\begin{align} x\u0026=a_1 \\cdot N_1 \\cdot t_1 + a_2 \\cdot N_2 \\cdot t_2 + \\cdots + a_n \\cdot N_n \\cdot t_n \\\\ \u0026= a_i \\cdot N_i \\cdot t_i + \\sum_{j \\neq i} a_j \\cdot N_j \\cdot t_j \\\\ \u0026\\equiv a_i \\pmod{n_i} \\end{align} $$ $$ 所以可以得到：x的一个解为：a_1 \\cdot t_1 \\cdot N_1 + a_2 \\cdot t_2 \\cdot N_2 + \\cdots + a_n \\cdot t_n \\cdot N_n \\\\ 记x_1 and x_2 为两个解，可得：\\\\ x_1 \\equiv a_i \\pmod{n_i} \\\\ x_2 \\equiv a_i \\pmod{n_i} \\\\ x_1 - x_2 \\equiv 0 \\pmod{n_i} \\\\ 所以可得：n_i | x_1 - x_2 \\\\ 因为任何一个i都成立，所以可得：n_1 \\cdot n_2 \\cdots n_i \\cdots n_n | x_1 - x_2 \\\\ 即：N | x_1 - x_2 \\\\ =\u003e x_2 = x_1 + kN \\\\ 则x_2 和 x_1 差kN 所以最终的解为：x=a_1 \\cdot t_1 \\cdot N_1 + a_2 \\cdot t_2 \\cdot N_2 + \\cdots + a_n \\cdot t_n \\cdot N_n + kN $$中国剩余定理\n#扩展中国剩余定理（Extened Chinese Remider Theroy） # 一些分析：我们可以看到，中国剩余定理的前提是模数两两互质，那么如果模数不互质，有一些思路提供参考：一：我们直接将这个同余方程组硬求解，利用到贝祖定理和中国剩余定理的思路；二：我们将其转化为中国剩余定理，即将模数变为两两互质，从而转换解的求解\n扩展中国剩余定理的思路思考\n扩展中国剩余定理转为中国剩余定理的思路\n#扩展中国剩余定理代码实现： # int exCRT(int n, int *a, int *m) { int M = 1; for (int i = 0; i \u0026lt; n; i++) M *= m[i]; int ans = 0; for (int i = 0; i \u0026lt; n; i++) { int Mi = M / m[i]; ans = (ans + a[i] * Mi % M * mod_inv(Mi, m[i])) % M; } return (M + ans % M) % M; } ","date":"2024年5月15日","externalUrl":null,"permalink":"/notes/math/%E6%95%B0%E8%AE%BA%E7%9F%A5%E8%AF%86/","section":"笔记","summary":"#数论知识 # :raised_hands:一些个人理解和思考（素数的一些建议看书，书本讲得不错，虽然有些点不加以细证），尤其Extened Chinese Remider Theroy那里，是个人思考总结的，仅供参考。:raised_hands:\n","title":"数论知识","type":"notes"},{"content":"","date":"2024年5月15日","externalUrl":null,"permalink":"/tags/%E4%B8%AD%E5%9B%BD%E5%89%A9%E4%BD%99%E5%AE%9A%E7%90%86/","section":"Tags","summary":"","title":"中国剩余定理","type":"tags"},{"content":" Divide-and-Conquer Method Share # \\section{Substitution Method}\n1. 阐释 # 代换法的关键在于利用数学归纳法。首先，要猜测一个大致的上界，并结合数学归纳法进行证明。然后，验证基本条件，确保递归满足这个上界。若无法准确猜测界限，可能需要一个一个试着证明，直到达到比较准确的界限为止。\n2. 例子 # $$ T(n) = 2T\\left(\\frac{n}{2}\\right) + \\Theta(n) $$ 3. 解释 # 这个递归表达式明显属于归并排序，时间复杂度为 $\\mathcal{O}(n \\log n)$。可以尝试用 $c n \\log n$ 进行证明。首先，假设小于 n 的情况符合这个式子，然后证明当 n 时也符合。\n$$ \\begin{align} T(n) \u0026= 2T\\left(\\frac{n}{2}\\right) + \\Theta(n) \\\\ \u0026= 2 \\cdot c \\cdot \\left(\\frac{n}{2}\\right) \\cdot \\log\\left(\\frac{n}{2}\\right) + \\Theta(n) \u0026\u0026 \\text{代入猜想} \\\\ \u0026= c n \\cdot (\\log n - \\log 2) + \\Theta(n) \u0026\u0026 \\text{展开} \\\\ \u0026= c n \\cdot \\log n - c n \\cdot \\log 2 + \\Theta(n) \\\\ \u0026\\leq c n \\cdot \\log n \u0026\u0026 \\text{得到不等式} \\end{align} $$一般 n 和常数 c 都要很大，通常都趋近于无穷大。这个分析表明 n 必须大于 1，因为有对数运算。因此，我们需要验证 n=2 或 n=3 之类的基本情况。具体分析过程可以参考《算法导论》。\n4. 注意 # 在代换法中，连续使用渐进符号 $\\mathcal{O}$ 会导致忽略常数的影响。在数学递归论证中，应避免使用渐进符号，因为它们可能会导致论证不严谨。此外，代换法中必须有余项，尤其是负的余项。这是因为渐进符号可能会忽略常数的影响。\n示例：若 n 为 $\\Theta(1)$，首先 1=$\\Theta(1)$。假设小于 n 的情况都符合这个式子，则 n-1=$\\Theta(1)$。因此，n = (n-1) + 1 = $\\Theta(1)$。这种情况是不行的，因为它忽略了常数的影响。\n\\section{Recursion-Tree Method}\n递归树方法是分析其他递归问题的有力工具。您可以参考此递归树的图片了解更多。\n\\section{Master Method}\n根据 PDF 所讲内容： Case 1: 如果存在常数 $\\epsilon \u003e 0$，使得 $f(n)=O(n^{log_ba-\\epsilon})$，则 $T(n)=\\Theta(n^{log_ba})$。 Case 2: 如果存在常数 k $\\geq 0$，使得 $f(n)=\\Theta(n^{log_ba} \\cdot lg^k n)$，则 $T(n)=\\Theta(n^{log_ba} \\cdot lg^{k+1}n)$。 Case 3: 如果存在常数 $\\epsilon \u003e 0$，使得 $f(n)=\\Omega(n^{log_ba+\\epsilon})$，并且 f(n) 满足正则性条件 $ af(n/b) \\leq cf(n) $ 对于某个常数 $c \u003c 1$ 且足够大的 n，则 $T(n) = \\Theta(f(n))$。 主要思路：比较 f(n) 和 $n^{log_b(a)}$ 即驱动函数和分水岭函数，即 $T(n)=aT(n/b)+f(n)$ 表达式，就是要比较 f(n) 和 $n^{log_b(a)}$。 # 为什么会有这个 $n^{log_b(a)}$? # 因为前面的 $T(n)$ 由递归树分析得到，因此直接比较最高项。\n1. case1: 对应数学符号是 \u0026gt;，即分水岭函数 $n^{log_b(a)}$ \u0026gt; 驱动函数 f(n)。 # $f(n)=O(n^{log_b(a)-\\epsilon})$，存在常数 $\\epsilon \u003e 0$。 那么 $T(n)=\\Theta(n^{log_b(a)})$。 2. case2: 对应数学符号是 =，即分水岭函数 $n^{log_b(a)}$ = 驱动函数 f(n)。 # $f(n)=\\Theta(n^{log_b(a)}*lg^k(n))$，k 是非负数，即 k $\\geq$ 0。 $T(n)=\\Theta(n^{log_b(a)}*lg^{k+1}(n))$。 3. case3: 对应数学符号是 \u0026lt;，即分水岭函数 $n^{log_b(a)}$ \u0026lt; 驱动函数 f(n)。 # $f(n)=\\Omega(n^{log_b(a)+\\epsilon})$，存在常数 $\\epsilon \u003e 0$。 若 $af(n/b)\\leq(1-\\epsilon')*f(n)$，其中 $\\epsilon' \u003e 0$，可判断 f(n) 是递减的，并且下一层是上一层的常数倍。因此，顶层是最大的，可以简化得出 $\\Theta(f(n))$。 $T(n)=\\Theta(f(n))$。 \\section{Master Method Prove}\nLemma Definition # 令 a \u0026gt; 0 和 b \u0026gt; 1 为常数，且 f(n/b) 为在实数范围内定义的函数 $n\\geq 1$。那么递归：\n\\[ T(n) = \\begin{cases} \\Theta(1), \u0026 \\text{if } 0\\leq n\u003c1 \\\\ aT(n/b)+f(n), \u0026 \\text{if } n \\geq 1 \\end{cases} \\]其解为 $T(n) =\\Theta(n^{\\log_b a}) + \\sum\\limits_{j=0}^{\\lfloor{log_bn}\\rfloor}a^j f(n/b^j)$。\n1. case 1 prove # f(n)=O($n^{log_b(a)}$)\n$$ \\begin{align} g(n) \u0026= \\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}a^j*f\\left(\\frac{n}{b^j}\\right) \\\\ \u0026= \\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}a^j*O\\left(\\left(\\frac{n}{b^j}\\right)^{log_b(a)-\\epsilon}\\right) \\\\ \u0026= O\\left(n^{log_b(a)-\\epsilon}\\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}\\frac{a^j}{(b^j)^{log_b(a)-\\epsilon}}\\right) \\\\ \u0026= O\\left(n^{log_b(a)-\\epsilon}\\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}(b^\\epsilon)^j\\right) \\\\ \u0026 \\leq O\\left(n^{log_b(a)-\\epsilon}*\\frac{n^\\epsilon*b^\\epsilon-1}{b^\\epsilon-1}\\right) \\\\ \u0026= O\\left(n^{log_b(a)}\\right) \\end{align} $$ 2. case 2 prove # f(n)=$\\Theta(n^{log_b(a)}*lg^k(n))$\n$$ \\begin{align} \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\lg^k n - {j \\cdot \\log_b b \\cdot \\lg^k n}\\right) \\tag{eq:1} \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\lg^k n\\right) \\quad (\\text{因为 } b \u003e 1) \\end{align} $$将上式代入公式(\\ref{eq:1})并反复应用习题3-5(c)，得\n$$ \\begin{align} g(n)\u0026= \\Theta\\left(\\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} a^j \\left(\\frac{n}{b^j}\\right)^{\\log_b a} \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\frac{a^j}{(b^j)^{\\log_b a}} \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j \\cdot \\lg^k n\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{\\left(\\frac{a}{b^{\\log_b a}}\\right)^{\\lfloor \\log_b n\\rfloor + 1} - 1}{\\frac{a}{b^{\\log_b a}} - 1}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{\\left(\\frac{a}{b^{\\log_b a}}\\right)^{\\log_b n} \\cdot \\frac{a}{b^{\\log_b a}} - 1}{\\frac{a}{b^{\\log_b a}} - 1}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{a^{\\log_b n} - b^{\\log_b a}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{a^{\\log_b n}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{n^{\\log_b a}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a + 1}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n}{a - b} \\cdot \\lg^k n\\right) \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n \\cdot \\lg^k n}{a - b}\\right) \\end{align} $$","date":"2024年4月30日","externalUrl":null,"permalink":"/notes/tools/latex/","section":"笔记","summary":"Divide-and-Conquer Method Share # \\section{Substitution Method}\n1. 阐释 # 代换法的关键在于利用数学归纳法。首先，要猜测一个大致的上界，并结合数学归纳法进行证明。然后，验证基本条件，确保递归满足这个上界。若无法准确猜测界限，可能需要一个一个试着证明，直到达到比较准确的界限为止。\n","title":"latex","type":"notes"},{"content":"","date":"2024年4月30日","externalUrl":null,"permalink":"/tags/latex/","section":"Tags","summary":"","title":"LaTeX","type":"tags"},{"content":"","date":"2024年4月30日","externalUrl":null,"permalink":"/categories/technology/","section":"Categories","summary":"","title":"Technology","type":"categories"},{"content":"","date":"2024年4月30日","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6%E5%BD%92%E7%BA%B3%E6%B3%95/","section":"Tags","summary":"","title":"数学归纳法","type":"tags"},{"content":"","date":"2024年4月30日","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E6%8D%A2%E6%B3%95/","section":"Tags","summary":"","title":"代换法","type":"tags"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/devops/","section":"Tags","summary":"","title":"DevOps","type":"tags"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":" 注意这些全都要根据自己所用系统是什么，ubuntu和debian这些可能都有些许出入，架构问题等 # 更新，安装依赖项 # sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common\n添加docker官方gpg密钥 # curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -\n根据自己来设定，因为这个可能debian已经不用apt-key了，已经改用gpg这些了 # 添加docker仓库 # sudo add-apt-repository \u0026ldquo;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\u0026rdquo;\n安装docker-ce # sudo apt update sudo apt install docker-ce\n验证是否安装 # sudo systemctl status docker\n创建gitlab目录来存放数据这些 # sudo mkdir -p /srv/gitlab/config /srv/gitlab/data /srv/gitlab/logs\n运行容器 # sudo docker run \u0026ndash;detach \u0026ndash;hostname ubuntu \u0026ndash;publish 443:443 \u0026ndash;publish 80:80 \u0026ndash;publish 22:22 \u0026ndash;name gitlab \u0026ndash;restart always \u0026ndash;volume /srv/gitlab/config:/etc/gitlab \u0026ndash;volume /srv/gitlab/logs:/var/log/gitlab \u0026ndash;volume /srv/gitlab/data:/var/opt/gitlab \u0026ndash;env GITLAB_ROOT_PASSWORD=LJB626901 yrzr/gitlab-ce-arm64v8:latest\n最后一行就是要根据自己架构来获取镜像了，arm架构和amd都不一样的，Your hostname要改 # 有时要注意这些端口是否被占用 # netstat -tuln | grep 80\n查询所有的镜像 # docker ps -a\n此时会看到一个 gitlab-ce 的容器是没有启动的状态 # 删除 gitlab-ce 容器 # docker rm gitlab-ce\n再次运行时，删除开放的 22 端口 # docker run \u0026ndash;detach \u0026ndash;restart always \u0026ndash;name gitlab \u0026ndash;privileged \u0026ndash;memory 4096M \u0026ndash;publish 80:80 \u0026ndash;publish 443:443 \u0026ndash;hostname ubuntu \u0026ndash;env GITLAB_OMNIBUS_CONFIG=\u0026quot; nginx[\u0026lsquo;redirect_http_to_https\u0026rsquo;] = true; \u0026ldquo;\n\u0026ndash;volume /srv/gitlab/config:/etc/gitlab \u0026ndash;volume /srv/gitlab/logs:/var/log/gitlab \u0026ndash;volume /srv/gitlab/data:/var/opt/gitlab \u0026ndash;env GITLAB_ROOT_PASSWORD=LJB626901 yrzr/gitlab-ce-arm64v8:latest\n","date":"2024年4月28日","externalUrl":null,"permalink":"/notes/tools/docker-gitlab-md/","section":"笔记","summary":"注意这些全都要根据自己所用系统是什么，ubuntu和debian这些可能都有些许出入，架构问题等 # 更新，安装依赖项 # sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common\n","title":"docker_gitlab.md","type":"notes"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/gitlab/","section":"Tags","summary":"","title":"GitLab","type":"tags"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/%E9%83%A8%E7%BD%B2/","section":"Tags","summary":"","title":"部署","type":"tags"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/%E5%AE%B9%E5%99%A8/","section":"Tags","summary":"","title":"容器","type":"tags"},{"content":" docker learn # table of contents # docker learn table of contents 虚拟机和docker docker和容器 docker讲解 docker_desktop介绍 虚拟机和docker # 虚拟机和容器都是一种虚拟化技术，都是能实现一个操作系统上运行一些别的系统，运行一些安装包什么的\n虚拟机和docker的区别：\n虚拟机是在主机操作系统上借助vwmare这些实现利用本地的硬件设施完成将本地服务器拆分为逻辑服务器，以此来实现可以运行多个系统。但是，这个就是会占用到很多本地硬件设施，会导致一些资源利用问题，比如：本来，我就只是想用一些软件包就行的，但非要安装os系统才行等。而且，虚拟机得启动缓慢，性能不是很好。\ndocker就是一个容器化平台，可以实现通过“鲸鱼”运载“集装箱”，以此我只是需要安装镜像就可以利用到我需要的容器即软件包等东西。占用的资源少，启动快，并且相比虚拟机可以安装很多个，只要看你想安装什么镜像，用到什么容器即可。\n图片连接解释\ndocker和容器 # 容器也是一种虚拟化技术，从上面的描述和比较就可以知道。而docker就是一种使用到容器虚拟化技术的一个平台，所以docker就是容器化平台。\n这个容器化平台就是有了很多功能和设置，使得我们更好利用到这个虚拟化技术，而且其实这个就是图形化界面，你直接用命令行安装docker也行的。\ndocker讲解 # docker包含镜像，容器等概念。\n镜像就是从dockerhub上下载的，你也可以自己编译吧，应该。这个dockerhub就是类似github的东西，这里有一些大佬弄出来的镜像。他相当于c++的类，这个镜像是自定义了一些东西。\n容器就是我们使用docker命令弄了出来的，然后是通过镜像这个类弄出来的一个实例，他是一个独立的，轻量级的可执行软件包，包含：代码，环境，系统工具等\n图片链接解释\ndocker_desktop介绍 # docker desktop里的container是指容器，就是镜像的一个实例就是你自己的一个东西了\nimage就是镜像，就是你自己从docker hub上下载下来，用于你自己搞东西的一个模板\nbuilds就是通过使用Docker Desktop中的Build功能，你可以方便地在本地开发环境中构建和测试Docker镜像，然后可以将这些镜像推送到Docker Hub或者其他Docker仓库中，以便在其他地方部署和使用。\nvolumes就是你本来用docker搞东西，这些文件什么的，就是一个类似虚拟化的东西，保存时间不久的，所以当你想保存的时候，你要将其放到本地主机的硬盘上，所以你就要用到volume了，将其弄成卷放到本地上。\n很好的视频讲解\n","date":"2024年4月28日","externalUrl":null,"permalink":"/notes/tools/docker/","section":"笔记","summary":"docker learn # table of contents # docker learn table of contents 虚拟机和docker docker和容器 docker讲解 docker_desktop介绍 虚拟机和docker # 虚拟机和容器都是一种虚拟化技术，都是能实现一个操作系统上运行一些别的系统，运行一些安装包什么的\n","title":"docker","type":"notes"},{"content":"","date":"2024年4月28日","externalUrl":null,"permalink":"/tags/%E8%99%9A%E6%8B%9F%E5%8C%96/","section":"Tags","summary":"","title":"虚拟化","type":"tags"},{"content":"","externalUrl":null,"permalink":"/ja/notes/git/","section":"ノート","summary":"","title":"Git","type":"notes"},{"content":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.\nQuick Start # Create a new post # $ hexo new \u0026#34;My New Post\u0026#34; More info: Writing\nRun server # $ hexo server More info: Server\nGenerate static files # $ hexo generate More info: Generating\nDeploy to remote sites # $ hexo deploy More info: Deployment\n","externalUrl":null,"permalink":"/misc/hello-world/","section":"杂项","summary":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.\nQuick Start # Create a new post # $ hexo new \"My New Post\" More info: Writing\n","title":"Hello World","type":"misc"},{"content":"","externalUrl":null,"permalink":"/ja/notes/java/","section":"ノート","summary":"","title":"Java","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/linux/","section":"ノート","summary":"","title":"Linux","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/vim/","section":"ノート","summary":"","title":"Vim / Neovim","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/tools/","section":"ノート","summary":"","title":"ツールとデプロイ","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/","section":"ノート","summary":"","title":"ノート","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/system/","section":"ノート","summary":"","title":"システム底层","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/security/","section":"ノート","summary":"","title":"セキュリティと暗号","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/misc/","section":"その他","summary":"","title":"その他","type":"misc"},{"content":"","externalUrl":null,"permalink":"/tags/%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"教程","type":"tags"},{"content":"","externalUrl":null,"permalink":"/ja/notes/math/","section":"ノート","summary":"","title":"数学","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/notes/regex/","section":"ノート","summary":"","title":"正規表現","type":"notes"},{"content":"","externalUrl":null,"permalink":"/ja/life/diary/","section":"生活","summary":"","title":"日常","type":"life"},{"content":"","externalUrl":null,"permalink":"/tags/%E5%85%A5%E9%97%A8/","section":"Tags","summary":"","title":"入门","type":"tags"}]