コンピュータのハードウェアコンポーネント
最新のコンピュータの基本コンポーネントは、CPU、メモリ、マザーボードの3つの主要部品で構成されています。
あなたが書いたプログラム、あなたが開いたPCアプリケーション。メモリに保存されたプログラムとそのデータはCPUが読み込む必要があり、CPUは計算後に対応するデータをメモリに書き戻す必要があります。マザーボードの役割は、その両方をホストすることです。
マザーボード上のチップセットとバスが、CPUとメモリの通信方法の問題を解決します。チップセットはデータ転送の流れ、つまりデータがどこからどこへ行くのかを制御します。一方、バスはデータ転送のための実際の高速道路です。
具体的なプロセスとしては、CPUがメモリ上のバイナリ命令を読み取り、それをデコードし、制御信号を通じて対応する演算プリミティブや記憶装置を操作して演算を実行します。
フォン・ノイマン・アーキテクチャ
今日使用されているマシンは、チューリングマシンともフォン・ノイマン・マシンとも呼ばれています。フォン・ノイマンはハードウェアの抽象化に重点を置き、チューリング・マシンは計算の抽象化に重点を置いています。
ストアード・プロシージャ・コンピュータとしても知られるフォン・ノイマン・アーキテクチャは、フォン・ノイマンが発表した論文「第一草稿」に由来します。つまり、「格納可能」で「プログラム可能」なコンピュータであり、また、コンピュータのどのような部分から構成されるべきか、つまり、演算器、コントローラ、メモリ、入力装置、出力装置、5つの部分についても述べられています。
彼は、現代のコンピュータは「プログラマブル」コンピュータであるべきだと考えています。つまり、また、ストアドプログラムコンピュータとして知られています。過去のコンピュータの回路は、現在の電卓のように、回路基板に溶接されているため、他の操作を行いたい場合は、その後、回路を再溶接する必要があり、それはプログラマブルコンピュータではありません。後のコンピュータは「プラグ・アンド・プレイ」コンピュータで、使用するプログラムのコンポーネントを差し込む必要がありました。この方法では、プログラムをコンピュータに保存することはできず、使用するたびにプラグを抜き差しする必要があります。保存できないコンピューターなのです。
図は、フォン・ノイマンのアーキテクチャを示しています:
チューリング機械
チューリングは、コンピュータのハードウェア的な基礎は考えず、計算の数学的モデルから見たコンピュータの実現可能性だけを考えたコンピュータを構想した数学者です。つまり、彼は「コンピュータ」として何をすべきか、どう動くべきかだけを考えていたのです。それもそのはず、当時のコンピュータの本来の役割は、数学者が道具製品として計算を行うのを助けることだったからです。チューリングが考えたのは、コンピュータの計算モデルだけであり、コンピュータの理論的な論理、いわゆる「計算」の実装方法についてだけです。
チューリング・マシンは、読み書きヘッドと読み書きヘッドの作業を制御するコマンドのセットを持つ無限に拡張可能なテープとして見ることができ、計算の抽象的なモデルです。
これは、汎用コンピューティングの理論を証明し、コンピュータ実現の可能性を確証するものです。同時に、コンピュータのあるべき姿の主要な構造を示し、読み書き、アルゴリズム、プログラミング言語の概念を紹介。かつてのコンピュータの設計に大きなブレークスルーをもたらしたのです。
実際、チューリング機械は本質的に状態機械であり、コンピュータの理論的モデルです。フォン・ノイマン・システムは、チューリング・マシンの具体的な物理的実装のようなもので、操作、制御、記憶、入力、出力の5つの部分を含みます。コンピュータに対するフォン・ノイマン・システムの最大の革新は、プログラムとデータの保存です。そこから機械は内部的にプログラムされます。チューリング・マシンの紙テープはフォン・ノイマン・システムにおけるストレージに相当し、読み書きヘッドは入出力の規則と操作に相当し、紙テープの動き方は制御に相当します。理論的には、チューリング・マシンは、複雑であろうとなかろうと、すべての人間の計算プロセスをシミュレートすることができます。
私の考えでは、チューリング・マシンは抽象的な「思考実験」であり、フォン・ノイマン・マシンはこの「思考実験」の「物理的実現」です。チューリング・マシンをコンピュータの最も抽象的な性質を表す「魂」とするならば、フォン・ノイマン・マシンはコンピュータの最も抽象的な性質を表す「肉体」です。フォン・ノイマン機械」は、コンピュータの最も具体的な性質を表す「身体」です。この2つの関係は、むしろ理論物理学者と実験物理学者のパートナーシップのようなもので、同じコインの表と裏のようなものです。
チップセット、バス
チップセット
チップセットはマザーボードの核となるコンポーネントで、CPUとその他の周辺デバイスの動作をリンクします。かつてマザーボードで最も重要なチップセットは、ノースブリッジとサウスブリッジのチップセットでした。
ノースブリッジとサウスブリッジのチップセット→マザーボード上の2つの主要なチップセットで、上にあるものをノースブリッジ、下にあるものをサウスブリッジと呼びます。
- ノースブリッジはCPUとの通信を担当し、高速デバイスをリンクし、サウスブリッジと通信します。
- サウスブリッジは低速デバイスとの通信を担当し、ノースブリッジと通信します。
バス
マザーボードのチップセットとバスは、CPUとメモリの通信問題を解決します。 チップセットはデータ転送の流れを制御し、バスは実際のデータ転送の高速道路です。
CPU
命令セット、マイクロアーキテクチャ
命令セット・アーキテクチャとも呼ばれ、基本データ型、命令セット、レジスタ、アドレッシング・モード、メモリ・システム、割り込み、例外処理、外部I/Oを含む、コンピュータのアーキテクチャのプログラミング関連部分を指します。命令セット・アーキテクチャは、一連のオペコード(オペレーティング・コード)と、特定のプロセッサが実行する基本コマンドで構成されます。
インテルIA-32やx86-64、IBM/Freescale Power、ARMプロセッサ・ファミリーなど、異なるプロセッサ「ファミリー」は、異なる命令セット・アーキテクチャを持っています。-異なる命令セットアーキテクチャ
命令セットアーキテクチャはマイクロアーキテクチャとは異なります。異なるマイクロアーキテクチャを使用するコンピュータは、命令セットを共有することができます。 例えば、インテルのPentiumとAMDのAMD Athlonは、ほぼ同じバージョンのx86命令セットシステムを使用していますが、内部のハードウェア設計は根本的に異なります。
仮想マシンの中には、Smalltalk、Java仮想マシン、MicrosoftのPublic Language Runtime仮想マシンで生成されたバイトコードをベースにしたバイトコードをサポートするものがあり、それらの命令セットシステムは、バイトコードを一般的な手段として使用されるコードパスからローカルな機械語に変換し、一般的に使用されないコードパスを解読して実行します。同様に、オールマートはx86命令システムベースのVLIWプロセッサを開発しました。
命令セットとはすべてのマシン命令の集合であり、アセンブリは開発者の便宜のために命令セットを抽象化したものと考えることができます。
命令セット設計の利点と欠点:マイクロプロセッサの命令セットには2種類あります。1つは複雑な命令セットで、多くの異なる命令を持ちます。1970年代、IBMのような多くの組織が、不要な命令が多いことに気づきました。その結果、より少ない命令で構成されるライト命令セットが作られました。ライト命令セットは、高速化、プロセッサの小型化、電力損失の低減を実現します。しかし、より複雑な命令セットの方が、より効率的なメモリとキャッシュ、より単純なコードで、より良く動作させることが容易です。
命令セットの中には、システムコールやソフトウェア割り込みを実行するために1つ以上のオペコードを確保するものもあります。
命令セットの分類
複雑な命令セットコンピュータには、アプリケーションではめったに使用されない特定の命令が多数含まれており、その結果、命令長が可変であるという欠点があります。一方、特殊な操作はサブルーチンとして実装され、その特殊な使用はプロセッサの余分な実行時間によって補われます。理論的に重要なタイプには、最小命令セットコンピュータや単一命令セットコンピュータもありますが、どちらも商用プロセッサとしては使用されていません。もう1つの派生型は、プロセッサが多数の符号化命令を受け入れ、検索によって命令語を抽出して実行する、非常に長い命令語です。
X86命令セットとARM命令セットの違いの例:
Aは車でBに行きます。
- ARM:4つのライブラリと座席を用意>Aを座席に乗せる>4つのライブラリの座席に座る>Bへ行く
- x86: 倉庫探し車 > >
結果は目的地にAを送信するために同じですが、同じことが、腕は倉庫を持っていない、すべてが構築されなければならない、x86は倉庫の束である、ランダムな使用は、違いは、腕のプロセスが少ない消費することです、ちょうど車のシェル ライブラリが、車のライト、エアコン、および空席の残りの部分は、物事のこの実行で必要とされていないだけでなく、一緒に入るのでx86は何を持っているので、大きな腕よりもリソースを占有するが、複雑なことを行うには、腕の開発よりも簡単なので、統一された標準ライブラリも、より効率的にすることができます。
命令セットは、バイナリコマンド文字列に対応するこれらのアセンブリコマンドを移動する追加され、マイクロアーキテクチャは、ハードウェアレベルで、これらのバイナリコマンド文字列は、機能を実現する方法であり、実際には、ICです
- zh.wikipedia.org/wiki/instruction-set-architecture
- zh.wikipedia.org/wiki/central-processor
- zh.wikipedia.org/wiki/マイクロプロセッサ
- zh.wikipedia.org/wiki/
- zh.wikipedia.org/wiki/assembly-language
- zh.wikipedia.org/wiki/ARM-architecture
Intel,AMD
- zh.wikipedia.org/wiki/ARM
- ARMはRISCアーキテクチャのCPUです。
CISCは複雑な命令セットを持つCPUで、命令が長く、実行する命令がいくつかのマイクロ命令に分かれているため、プログラムの開発が容易です。RISCはコンパクトな命令セットCPUで、命令が短く、内部のパイプラインがうまくできていれば、命令のデコードやデータの処理が速くなり、実行効率が高くなります。RISCはコンパクトな命令セットCPU。
近年、インテルのPentium-ProやAMDのK5、K6などのCISC CPUは、実はCISCを改良したもので、1マシンサイクルで1命令を実行することもできます。ARM自体はCPUを製造しておらず、CPUアーキテクチャを設計し、クアルコムやメディアテックなど、ARMアーキテクチャのCPUを製造する他のベンダーにライセンスしているだけです。おなじみのCPUメーカーはすべてARMアーキテクチャを採用しています。
ARMアーキテクチャは最初から電力効率を重視して設計されているため、一般的に言って実行速度はIntelやAMDのCPUほど速くありません。しかし、ハンドヘルドデバイスの世界では、ARMの省電力と、自作ではなくライセンスという共同戦力のアプローチが、他を圧倒して王者となり、現在では他の分野にも徐々に進出しています。
- ARM これがRISC CPUです。
- news.mydrivers.com/1/472/47231...
CPUGPU
CPUは中央演算処理装置で、GPUはグラフィックプロセッサで、今のコンピュータは、GPUのほとんどはCPUに統合され、統合グラフィックとも呼ばれ、その後、元のGPUは、CPU基板上にパッケージ化された独立したチップとしてノースブリッジメモリコントローラに属しています。だから、後で、そのマザーボードは、サウスブリッジであったPCHチップのみを残して、ノースブリッジとサウスブリッジに分割されていません。もちろん、あなたのPCがいくつかの大規模なゲームを実行する場合、またはGPUの仕事の高い要件のいくつかは、また、マザーボードに別のGPUカードを構成することができます。
過去
- CPU - ノースブリッジ - メモリ
- CPU - ノースブリッジ - グラフィックス
- CPU - ノースブリッジ - サウスブリッジ - ハードディスクドライブ
- CPU - ノースブリッジ - サウスブリッジ - ネットワークカード
- CPU - ノースブリッジ - メモリ
CPUは通常 マザーボードのCPUソケットに取り付けられています。
データパス:実際にプログラムの実行と計算を容易にするために、全体の演算子とコントローラを接続し、最終的にCPUを構成します。
CPUは一般的に超大規模集積回路と呼ばれ、トランジスタの組み合わせによって、CPUの計算プロセスは、実際には "スイッチ "信号のトランジスタが常に "オープン "と "クローズ "させることです。CPUの演算処理は、トランジスタの「スイッチ」信号を連続的に「開」「閉」させて様々な演算や機能を完成させることであり、ここでの「開」「閉」動作の速度はCPUの主周波数に影響されます。
コントローラ:制御プロセスを実行するためのコマンドは、制御するコンピュータコントローラの5つのコンポーネントの一つです。
CPU 関数呼び出しの最後に実行されるコア数とスレッド数は予約されています。
PCのCPUコアとスレッドの正確な数を確認するには、タスクマネージャーインターフェイスで確認するか、コンピュータの右クリックプロパティのデバイスマネージャーで確認できます。または、次のコマンドで確認できます。
wmic
cpu get *
-----------対応する属性
NumberOfCores
NumberLogicProcessors
- news.mydrivers.com/1/472/47231 - Haskely Gabriel が回答しました
CPU
CPUの主な周波数は、カーネルが動作するクロック周波数であり、通常、CPUが何メガヘルツであると呼ばれ、ここでいわゆるメガヘルツは、CPUの主な周波数の記述であり、CPUのモデル番号の後にデジタル記述の主な周波数である2.4 GHZが続きます。
メイン周波数はCPUの演算速度を直接表すものではないので、CPUのメイン周波数が高くてもCPUの演算速度は遅く、メイン周波数はCPUの性能の一面に過ぎません。
CPUスレッドとJavaスレッドの関係
JavaのすべてのスレッドはJVMプロセス内にあり、CPUはプロセス内のスレッドをスケジュールします。
CPUのスレッド数とJavaのスレッド数は、直接の関係を持っていない、CPUはスレッドを実行するスライス機構を使用して、各スレッドは、実行する小さな時間粒子に分割されますが、実際のプロジェクトは、プログラムが多くの操作を行うには、読み取りとディスクへの書き込み、データロジックの処理、必要なハイバネーションなどのビジネスニーズのうち、プログラムがI / O操作にスレッドを実行している場合、スレッドは、その後です。I/O操作が完了すると、CPUはハードディスクからの割り込み信号を受信し、割り込み処理ルーチンを入力し、手元のスレッドが中断される可能性があります、戻ってレディキューに、この時点で、CPUは、他のスレッドのタスクに対処するために、コンテキストスイッチングを行いますブロッキング状態を入力します。I/O操作が完了すると、CPUはハードディスクから割り込み信号を受信し、割り込み処理ルーチンに入ると、現在実行中のスレッドは割り込まれる可能性があり、レディキューに戻ります。
プロセスとスレッド
スレッドはオペレーティング・システムの最小のスケジューリング単位であり、プロセスはオペレーティング・システムのリソース割り当ての小さい単位です。
プロセス:プロセスはオペレーティングシステムのリソース割り当ての基本単位で、各プロセスは仮想的に独立したメモリ空間、ストレージ空間、CPUリソースを持っています。各プロセスは、仮想化された後、独自のメモリ空間、ストレージ空間、およびCPUリソースを持っています。 すべての種類のPCアプリケーションは、独立したプロセスです。
ハイパースレッディング
インテルのハイパースレッディング・テクノロジーは、シングルコアCPUのリソースをフル活用するために設計されています。シングルコアCPUでは、機械命令を実行する際にCPUのリソースをすべてフル活用することはできず、実際には大量のリソースが未使用のまま残ります。ハイパースレッディング技術は、2つのスレッドが競合することなくCPUのリソースを同時に使用することを可能にします。例えば、ある整数命令が整数ユニットだけを使用し、浮動小数点ユニットが空いていて、ハイパースレッディングが使用されている場合、たまたまその時に別のスレッドが浮動小数点命令を実行していれば、CPUは2つの異なるスレッドに属する整数命令と浮動小数点命令を同時に実行することを許可し、これが真の並列処理です。他のハードウェアのマルチスレッド技術については知りませんが、ハイパースレッディングだけでも真の並列処理が可能です。しかし、だからといって、同じCPU上の2つのスレッドが常に並列に実行できるわけではなく、たまたま現在実行中の命令が同じCPUリソースを使用しない状況に遭遇したときにのみ実行できるのです。
基本的に、物理コアは1つのスレッドを実行しながら、アイドル状態の演算ユニットを使って他の命令を実行し、パフォーマンスを向上させます。
性能と消費電力
コンピュータにとって性能とは、応答時間の逆数です。これを一定にするために使用される2つの指標があります。
- 応答時間:すなわちタスクの実行時間。
- スループット:すなわち帯域幅。
スループット率を向上させる方法はたくさんありますが、マルチコアプロセッサもその1つです。つまり、プロセッサは現在6コア8コアなどであり、同時にCPUが複数のタスクを実行するようにすることができ、対応する応答時間は短くなり、また、コンピュータのスループット率を向上させることができますが、現在、ボトルネックを改善するために応答時間のCPUは、"ムーアの法則 "はもはや適用されません。
パフォーマンス= 1 /応答時間。短い応答時間、より大きなパフォーマンスの値!
理論的には、プログラムの実行時間= ユーザーランドの命令を実行するのにかかる時間+カーネルランドの命令を実行するのにかかる時間。
しかし、スレッドスケジューリングの影響で、CPUは特定のプログラムの命令を実行するだけでなく、多くのTaskを同時に実行することになり、同じコンピュータでもCPUがフルロードの場合もあれば、CPUをダウンサンプリングして実行する場合もあります。また、プログラムの実行時間は、対応するマザーボードやメモリの影響を受けます。下図のように
プログラムのCPU実行時間=CPUクロックサイクル数×単位クロックサイクル時間。
例えば、Intel Core-i7-7700HQ 2.8GHZの場合、ここでいう2.8GHZとは、CPUが1秒間に実行できる単純な命令数が2.8Gであるという大まかな理解です。正確には、CPUの「クロック」が認識できる最小の時間間隔です。
コンピュータの計時装置:CPUクロック。
クロック・サイクル・タイム: CPUの内部には、電子クオーツ時計と同じように、「クリスタル」と略される水晶発振器があり、水晶の「ティック」1回1回が電子クオーツ時計のクロック・サイクル・タイムとなります。メイン周波数2.8GHZのCPUでは、このクロック・サイクル・タイムは1/2.8GHZです。CPUはこの「クロック」に従い、時間によって促され、自身のオペレーションを実行します。メイン周波数が高いほど、表が速くなることを意味し、CPUも同様に速くなることを「強制」されます。CPUはまた、より速く行くために "強制 "され、より速いCPUは、もちろん、より大きな放熱圧力。
CPUのクロックサイクル= 命令数*命令の平均クロックサイクル数。
ここでは、命令のクロックサイクルの平均数を言ったので、我々は、異なる命令の実行時間が異なっていることを知っている、つまり、クロックサイクルの数は、コンピュータに固有の、クロックサイクル数の乗算は、加算よりも多くなるように費やされる異なっています。しかし、最近のCPUは、個々のコマンドの実行にかかるCPUクロックサイクルを少なくするためにパイプライン技術を使用しています。
プログラムは複数のステートメントを含み、ステートメントは複数の命令に対応し、CPU命令は完了するために複数のCPUクロックサイクルを必要とするかもしれません。
プログラムのCPU実行時間: 命令数 * 命令の平均クロックサイクル数 * クロックサイクル時間
上記の式から、プログラムのCPU実行時間を短縮したい場合は、上記の3点から始めなければなりません。ただし、命令数はコンパイラによって決まり、クロックサイクル時間はCPUの周波数によって決まり、1命令あたりの平均クロックサイクル数はパイプライン技術によって最適化することができます。
ムーアの法則:ムーアの法則は常にコンピュータの主要周波数を高めることを目的としており、インテル創業者の一人であるゴードン・ムーアはかつて、「価格が変わらない場合、集積回路に収容できる部品の数は18~24カ月ごとに倍増し、性能も同様に倍増する」と述べています。
以上の分析から、CPUの周波数の向上が効果的にコンピュータの性能を向上させることができますが、マシンの消費電力の影響により、 コンピュータのメイン周波数が高ければ高いほど、コンピュータの性能が強くなるのではなく、メイン周波数の向上は、CPUの電力放散も増加することを意味しますが、CPU自身の熱の程度は、CPUのメイン周波数を制限し、放熱のこのような大幅な増加に追いつくことができない可能性があることに注意する必要があります !上げることができない、高すぎる周波数は、彼の本来の性能を再生することはできません、あるいは低い。
CPUの消費電力 ~= 1/2 *負荷容量 *電圧の二乗 *スイッチング平坦度 *トランジスタ数
CPUの性能を効果的に高めることができる方法:
プロセス:
ナノメートルプロセスは、例として14nmに、そのプロセスは、チップの中で、線が14nmの最小サイズにすることができることを意味し、トランジスタを縮小すると、消費電力を削減することができ、同時に、回路間の信号量の伝送速度を向上させることができ、プロセスを縮小し、トランジスタ間の容量も低くなるので、それらの間のスイッチング周波数を向上させることができます。消費電力は静電容量に正比例するため、伝送速度が速くなり、エネルギー効率も向上することがわかります。
コンピュータの性能を向上させるために、応答時間から脱却することは困難であったため、後の開発者はコンピュータのスループット率に取り組みました。つまり、マルチコア手段のハイパースレッディングによってコンピュータの性能を向上させるだけでなく、並列処理によって手段の性能を向上させるのです。
アムダールの法則:
並列最適化、すべての問題が並列化によって最適化できるわけではありません。
- 条件1:実行する必要がある計算自体が複数の並列タスクに分解可能であること(例えば、乗算は複数の加算に分解可能)。
- 条件2:計算が分解可能で、最終的にマージできること。
- 条件3:「集約」フェーズはもはや並列に最適化することはできません。
最適化された実行時間 = 最適化/加速の影響を受ける実行時間 + 影響を受けない実行時間。
ムーアの法則と並列コンピューティングに加えて、確率的事象の高速化、パイプライン化、予測によってコンピュータの性能を向上させる方法があります。
コンパイラ
アセンブラは、アセンブリ言語のソース・プログラムを機械語に変換するユーティリティ・プログラムです。機械語は、コンピュータ・プロセッサが理解できるように特別に設計されたデジタル言語です。すべてのx86プロセッサは共通の機械語を理解します。
アセンブリ言語は、ADD、MOV、SUB、CALL などの短い補助文字で記述されたステートメントで構成されています。アセンブリ言語は機械語と1対1の関係にあり、各アセンブリ言語命令は1つの機械語命令に対応します。つまり、使用する機械言語が異なれば、異なるプロセッサ・モデルでもアセンブリ言語が同じになることはありません。
Python、C++、Javaなどの高級言語は、アセンブリ言語や機械語と1対多の関係にあります。例えば、C++では1つのステートメントが複数のアセンブリ命令やマシン命令に展開されます。ソース・プログラムをコンパイルして、さまざまなコンピュータ・システムで実行できる場合、その言語は移植性があると言われます。
アセンブリ言語は、特定のプロセッサ・ファミリー向けに設計されているため、移植性がありません。現在、広く使われているアセンブリ言語には、プロセッサ・ファミリに基づいたさまざまなものがあります。 モトローラ68×00、x86、SUN Sparc、Vax、IBM-370など、広く知られているプロセッサー・ファミリーの場合、アセンブリ言語の命令は、そのコンピューター・アーキテクチャに直接対応させるか、マイクロコード・インタープリターとして知られるプロセッサー内蔵プログラムで実行時に変換します。
一般的なインテルCPUには約2000のCPU命令があります。一般的な命令は、主に5つのカテゴリーに分けられます:
- 最初のカテゴリーは算術命令です。CPUレベルでは、加算、減算、乗算、除算はすべて1つの演算命令になります。
- 2つ目はデータ転送命令。変数に値を代入したり、メモリのデータを読み書きしたりするのは、すべてデータ転送命令です。
- 3つ目は論理命令です。論理あり、論理なしはすべてこのクラスの命令です。
- 4つ目は条件分岐です。毎日書いている「if/else」も、実は条件分岐命令です。
- 最後のカテゴリは、無条件ジャンプ命令です。ある程度大きなプログラムを書くと、関数やメソッドを書く必要が出てきます。関数を呼び出すとき、実際には無条件ジャンプ命令を起動しています。
アセンブリコードを見てみましょう:
#include <time.h>
#include <stdlib.h>
int main()
{
srand(time(NULL));
int r = rand() % 2;
int a = 10;
if (r == 0)
{
a = 1;
} else {
a = 2;
}
==================
if (r == 0)
3b: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0
3f: 75 09 jne 4a <main+0x4a>
{
a = 1;
41: c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1
48: eb 07 jmp 51 <main+0x51>
}
else
{
a = 2;
4a: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2
51: b8 00 00 00 00 mov eax,0x0
}
機械語、アセンブリ言語、コンパイラ:
過去には、プログラムの書き込みは、紙テープパンチの方法を通じて、その後唯一のプログラムの準備のための "0,1 "マシンコードを介しており、後でアセンブリ言語を開発し、アセンブリ言語は、人間の言語に近い言語であり、アセンブラは、機械語にアセンブリ言語することができます。アセンブラは、コンピュータに特定のアセンブリ命令によると、トランスレータの存在に相当するバイナリコードを識別することができます、つまり、CPUの開発者がマシンコードを提供するために、アセンブリコードとCPUがマシンコードを識別することができますので、1対1の対応です。
つまり、元のアセンブリ言語の設計者は、CPUの開発者がアセンブリ言語の定義の開発に対応するために提供される命令セットマニュアルを介してアセンブリ言語を書いて、異なるCPUの様々なモデルに固有の命令セットは、CPUに焼かれている間、一般的なアセンブリ言語は、プロセッサと一対一であることが知られています。
- C:C言語→コンパイラ→アセンブリ言語→アセンブラ→マシンコード||C言語→コンパイラ→マシンコード
- Java:Java言語→コンパイラによるコンパイル→バイトコード→JVM→マシンコード
レジスタ
最初に読むことができます:
メモリ、レジスタ、ストレージの違い
基本概念
- RAM(ランダム・アクセス・メモリー)は、停電が起きると記憶内容が失われるタイプのメモリーで、主に短時間使用するプログラムの保存に使われます。
- ROMとはリード・オンリー・メモリのことで、あらかじめ記憶されたデータのみを読み出すことができる固体半導体メモリです。
レジスタ
レジスタは中央処理装置内の部品。レジスタは、命令やデータ、アドレスなどを一時的に記憶するための、記憶容量に制限のある高速記憶部品です。CPUの制御部に含まれるレジスタは、命令レジスタとプログラムカウンタです。
- レジスタはCPUに付属しており、少量の情報しか格納できませんが、アクセスが特に高速です;
- メモリは、ハードディスク、フラッシュドライブ、フロッピー、CD-ROMなどの外部記憶ツールを指し、最も低速です;
- メモリは、メモリスティックを指し、ハードドライブの読み取り速度の半分が非常に遅いので、物事は内部のメモリスティックに読み取る内部の最初のハードドライブで、その後、CPUで処理するために、これは、システムの動作を高速化することです;
CPUの近くから遠い配置までの距離に応じてメモリの様々なタイプは、レジスタ、キャッシュ、メインメモリ、補助メモリです。
他の質の高い回答
レジスタの種類
論理的には、CPUは実際にはレジスタの束で構成されていると考えることができます。レジスタとは、複数のフリップフロップやラッチで構成されるCPU内部の簡単な回路のことです。
N個のフリップフロップやラッチでNビットのレジスタを構成し、Nビットのデータを格納することができます。例えば、64ビットのIntelサーバーを使用する場合、レジスタは64ビットです。
CPUには、機能の異なるさまざまな種類のレジスタがあります。特別なものは次の3つです。
- 命令アドレス・レジスタとも呼ばれるPCレジスタ。これは、次に実行する必要のあるコンピュータ命令のメモリアドレスを保持するために使用されます。
- 命令レジスタ:現在実行中の命令を保持します。
- 条件コード・レジスタ(Condition Code Register)。CPUが実行した算術計算や論理計算の結果を、内部の1対1のマーカー・ビットを使って保持します。
CPU内部には、この他にもデータやメモリアドレスを格納するレジスタが多数あります。通常、これらのレジスタは1つのクラスに複数あります。整数レジスタ、浮動小数点レジスタ、ベクタレジスタ、アドレスレジスタなど、保持するデータに応じて名前が付けられることがよくあります。データとアドレスの両方を保持できるレジスタもあり、汎用レジスタと呼ばれます。
プログラムが実行されると、CPUはPCレジスタのアドレスに従って実行する命令をメモリから命令レジスタに読み出し、命令長のインクリメントに従って次の命令を順に読み始めます。プログラムの1命令はメモリに連続的に格納され、1つずつ順次ロードされます。
ジャンプ命令として知られるJタイプ命令などの一部の特殊命令は、PCレジスタ内のアドレス値を変更します。そのため、次に実行される命令はメモリから順次ロードされません。実は、このジャンプ命令があるからこそ、プログラムを書くときにif...else条件文やwhile/forループを使うことができるのです。
CPUのレジスタ数はそれほど多くなく、一般的なIntel i7のCPUでは64ビットレジスタが16本、つまり8バイトレジスタが16本しかありません。
ビット演算
ビット演算はメモリ上の整数のバイナリビットを直接操作するため、実行効率が非常に高く、プログラムのパフォーマンスを大幅に向上させることができます。もちろん、読みやすさが第一の目標です。
ビット演算子
- & & & 両方のビットが 1 なら結果は 1、そうでなければ 0。
1 0 0 1 1
&
1 1 0 0 1
------------------------------
1 0 0 0 1
- Or 両方のビットが 0 なら結果は 0、そうでなければ 1。
1 0 0 1 1
|
1 1 0 0 1
------------------------------
1 1 0 1 1
- 両方のビットが同じ場合は 0、異なる場合は 1。
1 0 0 1 1
^
1 1 0 0 1
-----------------------------
0 1 0 1 0
- ~ ^ 逆演算、0 が 1 になり、1 が 0 になります。
~ 1 0 0 1 1
-----------------------------
0 1 1 0 0
- << 左シフト演算、左にシフトし、上位ビットは破棄され、下位ビットは0になります。
int a = 8;
a << 3;
シフト前:0000 0000 0000 0000 0000 0000 1000
シフト後:0000 0000 0000 0000 0000 0000 0000 0100 0000
- >> 符号なし数値の場合、上位バイトは 0、符号付き数値の場合、上位バイトは符号バイト。
unsigned int a = 8;
a >> 3;
シフト前:0000 0000 0000 0000 0000 0000 1000
シフト後:0000 0000 0000 0000 0000 0000 0001
int a = -8;
a >> 3;
シフト前:1111 1111 1111 1111 1111 1111 1111 1111 1000
シフト後:1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
スタック
実際のプログラムでは、スタックは関数呼び出しのリターンアドレスだけではありません。例えば、関数 A が関数 B を呼び出すとき、いくつかのパラメータデータを転送する必要があります。関数 A 全体によって占有されるすべてのメモリ空間が、関数 A のスタック・フレームです。
実際のプログラムのスタックレイアウトは、ピンポンバケツと比べて上下が逆になっています。スタックの一番下のメモリアドレスが最初に固定されているため、一番下が上、一番上が下になっています。スタックが1層ずつ押された後、スタックの一番上の要素のメモリアドレスは、大きくなるどころか小さくなっていきます。
一般的な StackOverFlow のトリガー:スタック上に非常にメモリ集約的な変数を宣言する関数への再帰的な呼び出し。
プログラム実行のための一般的な最適化:
実際に呼び出された関数によって生成された命令を取り出し、その関数が呼び出された場所に直接挿入することで、対応する関数呼び出し命令を置き換えます。この方式は、呼び出される関数内で他の関数が呼び出されない場合に有効です。これは、関数インライニングと呼ばれるコンパイラによる自動最適化の一般的なシナリオです。
コンパイラによる最適化で特に問題になるのは、単純に実行される命令の数を減らすことではなく、関数が頻繁にスタックを出し入れすることによるオーバーヘッドです。つまり、CPUが繰り返しメモリを操作することによるオーバーヘッドは、依然として大きいのです。つまり、上記の文章では、呼び出された関数が他の関数を呼び出さない場合を強調していますが、呼び出された場合、レジスタの容量に十分なオーバーヘッドがない可能性があり、メインメモリを操作するというボトルネックが残るからです。
コードデモ:
#include <stdio.h>
int static add(int a, int b)
{
return a+b;
}
int main()
{
int x = 5;
int y = 10;
int u = add(x, y);
}
上記のadd関数のアセンブリコードに対応し、メイン関数は、12〜13行目のadd関数の終了後、0〜1行目のadd関数のエントリ、add関数を呼び出します。
34行目のcall命令を呼び出す際には、関数呼び出し後に実行される命令のアドレスを保持するために、現在のPCレジスタの次の命令のアドレスがスタックされます。add関数の0行目のpush rbp命令はスタックプレスです。rbp はスタックフレームポインタとも呼ばれ、現在のスタックフレームの位置を保持するレジスタです。push rbp は、呼び出し元の関数のスタックの一番下、つまりメイン関数のスタックフレームのアドレスをスタックに押します。
そして、1 行目の mov rbp, rsp コマンドは、スタック・フレーム・ポインタである rsp の値を rbp にコピーします。rbp は常にスタックの先頭(add 関数のスタック・フレームのエントリ・ポイント)を指しています。これは、rbp が指すアドレス(実際の変更命令)が現在のスタックの先頭、つまり add 関数のスタックフレームの一番下のアドレスになることを意味します。
add 関数の実行が終わると、12 行目の pop rbp を呼び出して現在のスタックの先頭をスタックから取り出し、スタックフレーム全体を維持します。その後、13行目のret命令を呼び出すと、呼び出し呼び出し中にPCレジスタに押された次の命令もPCレジスタから取り出し、PCレジスタに更新し、呼び出し呼び出し後のプログラムの制御をスタックの先頭に戻します。
コンパイル、リンク、ロード:プログラム実行の逆アセンブル
C言語ファイルはコンパイルされ、接尾辞が.oのアセンブリ言語ファイルを生成します。例えば、add_lib.oやlink_example.oは実行可能ファイルではなく、オブジェクト・ファイルです。実行可能ファイルを得る唯一の方法は、複数のターゲット・ファイルとリンカによって呼び出される様々なライブラリをリンクすることです。
Cコード - アセンブリコード - マシンコード このプロセスは、コンピュータ上で実行される場合、2つの部分から構成されます。
- 最初の部分は、コンパイル、アセンブリ、リンクの3つの段階から成ります。これらの3つの段階が完了すると、実行可能ファイルが生成されます。
- 第2段階では、実行可能ファイルがローダーによってメモリにロードされ、CPUがメモリから命令とデータを読み込んでプログラムの実際の実行を開始します。
以上のことから、プログラムは最終的にローダーによってメモリに読み込まれ、命令やデータに変換されるため、生成される実行コードは単なる1命令ではないことがわかります。
ELF
Linuxでは、実行ファイルとターゲットファイルはELFと呼ばれるファイル形式を使用しており、コンパイルされたアセンブリ命令を保持するだけでなく、他の多くのデータも保持しています。
ELFフォーマットには、addやmainなどの関数名やグローバルにアクセス可能な変数名が格納されています。これらの名前と対応するアドレスは、ELFファイル内のシンボルテーブルと呼ばれる場所に格納されます。シンボル・テーブルはアドレス帳に相当し、名前とアドレスを関連付けます。
- リロケーションテーブル:リンク前に発生し、そのファイルで参照される複数の関数のアドレスがまだ明確ではありません。 関数ファイルはそれぞれ別のファイルに書かれ、ターゲットファイル用に別々にコンパイルされ、完全な実行可能ファイルを形成するためにコネクタによってリンクされない限り、不確実な関数のアドレスは、最初の段階でここに記録され、リンク後に変更されます。
実行プロセス: リンカはすべての入力ターゲットファイルをスキャンし、シンボルテーブルの情報をすべて集めてグローバルシンボルテーブルを形成します。次に、再配置テーブルに従って、ジャンプ先のアドレスが不明なすべてのコードを、シンボルテーブル内に格納されているアドレスに従って一旦修正します。最後に、ターゲットファイルの対応するすべてのセグメントを一度マージし、最終的な実行可能コードにします。
そのため、同じコンピュータで同じCPUを使っていても、OSが異なると実行可能なファイルと実行不可能なファイルが混在することがあります。根本的な理由は、OSローダーによって解析できるファイル形式が異なるからです。LinuxローダーはEFLファイル形式しかロードできませんが、WindowsはPEファイルをロードします。
追記:コンパイルされた高級言語であれば、そのほとんどがアセンブリ言語にコンパイルされ、その後実行用のマシンコードにコンパイルされます。また、インタープリタ(仮想マシン)を通して、実際のマシンコード命令に変換されて実行されます。
。ターゲットファイルに対応するコネクタリンクを介して各プラットフォームのJava仮想マシンは、実行可能ファイルの対応する形式を生成するために、ホストOSに応じてすることができます。これは、ローダーが正常にロードすることができます。
ローダーは、2つの要件を満たす必要があります。
上記から、コネクタの役割により、ローダーはCPUが実行するために、対応する命令とデータをメモリにロードするだけでよいことがわかります。しかし、ローダーはさらに2つの要件を満たす必要があります:
- まず、ロードされた実行プログラムが占有するメモリ空間は連続している必要があります。なぜなら、プロセッサが命令を実行するとき、プログラム・カウンタは命令を1つずつ順番に実行するので、命令が連続して格納されている必要があるからです。
- 第二に、多くのプログラムを同時にロードする必要があり、プログラムがメモリのどこにロードすべきかを指定することはできません。 コンパイルされた命令は、すでに対応するさまざまなメモリアドレスを持っていますが、実際にロードされるとき、プログラムがメモリアドレスのどのセクションにロードされなければならないかを確実にする方法はありません。現在のコンピュータは多くのプログラムを同時に実行することが多いため、欲しいメモリ・アドレスがすでにロードされた別のプログラムによって占有されている可能性もあります。
解決策
セグメンテーション: メモリ空間の連続したセクションをメモリ内で分割し、それをロードされたプログラムに割り当て、連続したメモリ空間を命令が指すメモリアドレスにマッピングします。
命令で使用されるメモリ・アドレスを仮想メモリ・アドレス、実際のメモリ・ハードウェア内部の物理空間を物理メモリ・アドレスと呼びます。プログラマーは仮想メモリ・アドレスだけを気にすればよいのです。つまり、仮想メモリと物理メモリのマッピング関係の開始アドレスと、対応する空間サイズだけを管理すればよいのです。
問題:メモリの断片化
解決策
メモリスワップ。つまり、メモリ内の特定のアプリケーションが占有するメモリは、最初にハードドライブに書き込まれ、次にハードドライブからメモリに読み戻されます。ただし、読み戻されたメモリは、前のアプリケーションが占有したメモリ領域のすぐ後ろにあるため、連続したメモリフットプリントが作成されます。
問題点:パフォーマンスのボトルネック、メモリの断片化とスワッピングの多さ、ハードドライブの読み書き速度の遅さ
解決策
メモリページング。原理は、メモリの断片化を少なくすることです。また、メモリスワップが必要なときに、ディスクから書き込んだり読み込んだりするためにスワップする必要があるデータを少なくします。セグメンテーションが、連続した領域全体をプログラムに割り当てるのとは対照的に、ページングは物理メモリ空間全体を固定サイズのセグメントに分割します。アプリケーションが占有する対応する仮想メモリ空間も、固定サイズのセグメントに分割されます。 このような連続した固定サイズのメモリ空間がページです。一般的に、ページは数キロバイトというプログラムサイズよりもはるかに小さいものです。
メモリページングの場合には、十分なメモリ空間がない場合でも、あなただけのいくつかのメモリページを解放するために、メモリスワップを通じて、他のプログラムを実行して、既存のようにする必要がある、ディスクへの1回の書き込みは、ページまたはいくつかのページの数が少ないです、それはあまりにも多くの時間がかかることはありませんので、ジャムにメモリスワップのプロセスによってマシン全体。
ページングアプローチは、それらをロードするときに一度に物理メモリにプログラムをロードする必要がなくなります。仮想メモリと物理メモリの間でページをマッピングし、実際には物理メモリにページをロードせず、プログラムが対応する仮想メモリページの命令とデータを使用する必要がある場合にのみ物理メモリにロードすることが可能です。
仮想メモリはアドレスですが、物理メモリにロードされない場合、実際にはハードディスク上に置かれます。
仮想メモリ、メモリスワップ、メモリページングの組み合わせにより、アプリケーションの実行に必要なメモリ量は実際には非常に小さくなります。
JVMも実行可能ファイルであり、他のアプリケーションと同じようにオペレーティング・システムのメモリ管理とロード手順に依存します。物理メモリにどのようにマッピングされるかを考えることなく、独自の方法でJavaアプリケーションのためのメモリ空間を計画することができます。これは、それをホストするオペレーティング・システムが行う必要があることであり、各アプリケーションは、使用できるメモリ空間の量に一定の制限があります。
リンクは動的なものと静的なものに分けられ、動作のためにメモリを共有します。
リンカを使用してコードがマージされる場合、それは静的リンクであり、それに応じて動的リンクも存在します。プログラムが同じコードでロードされるとき、複数のプログラムが静的リンクされていれば、同じコードの複数の共有がメモリを占有することになり、メモリの消費という点でも非常にコストがかかります。ダイナミック・リンクのプロセスで「リンク」したいのは、ハードディスク上のターゲット・ファイルに格納されたコードではなく、メモリ上にロードされた共有ライブラリです。
共有コードなので、メモリにロードされるのは1つのコピーだけです。プログラムが共有ライブラリのメモリアドレスにリンクすると、共有ライブラリにリンクすることができます。異なるシステムでは、共有ライブラリのファイル末尾が異なります:Windowsは.dll、Linuxは.soです。
共有ライブラリファイルのコード要件:
コンパイルされた共有ライブラリファイルの命令コードはアドレス非依存です。 異なるプログラムが同じ共有コード・ベースを使用する場合、そのコードの仮想アドレスはプログラムによって異なり、物理アドレス上は同じですが、その共有コード・ベースの仮想アドレスと物理アドレスのマッピングは維持できないからです。
再配置テーブルを利用するコードは、アドレスに関連するコードです。リロケーションテーブルを利用するコードは、プログラムリンク時に関数呼び出し後のジャンプでアクセスするアドレスを特定するため、この関数が異なるメモリアドレスにロードされた場合、ジャンプは失敗します。
すべての動的にリンクされた共有ライブラリでは、共有ライブラリは同じ物理メモリ・アドレスを使用しますが、アプリケーションによって異なる仮想メモリ・アドレスに配置されます。
相対アドレス: ダイナミック・コード・ベース内のデータや命令の仮想アドレスは、互いに相対的にアクセスされます。さまざまな命令で使用されるメモリ・アドレスは、絶対アドレス空間としてではなく、現在の命令オフセットからの相対メモリ・アドレスとして指定されます。共有ライブラリ全体が仮想メモリ・アドレスの連続したセクションに配置されるため、異なる命令間の相対アドレスは、どのアドレスがロードされても同じままです。
共有ライブラリのコード部分の物理メモリは共有されますが、データ部分は動的にリンクする各アプリケーションによってロードされることに注意してください。
グローバル・オフセット・テーブル: GOTテーブルは共有ライブラリのデータ・セクションにあります。そのため、ダイナミック・リンクを使用する各アプリケーションは、共有ライブラリ内に独自のGOTを生成し、各アプリケーションのGOTは異なります。 GOTテーブルのデータは、各共有ライブラリがロードされるときに書き込まれます。
そのため、現在実行中のアプリケーションの共有ライブラリ命令が外部変数や関数のアドレスを使用する必要がある場合、GOTに照会、現在実行中のアプリケーションの仮想メモリアドレスを探します。
同じ共有ライブラリを呼び出す異なるプロセスは、最終的にロードされた DLL の仮想メモリ・アドレスを指す GOT アドレスが異なります。
異なるプログラムは、同じダイナミックライブラリを呼び出すと、メモリアドレスのそれぞれのデータ部分は独立していますが、同じダイナミックライブラリを呼び出すが、使用されるアドレス内部のダイナミックライブラリのコードを変更する必要はありませんが、各プログラムは、対応するダイナミックライブラリの仮想アドレスを見つけることができるように、独自のGOTを維持することができます。
間接的にコードの先頭を呼び出すためにジャンプする "アドレスデータ "を変更することにより、このような動的リンクは、Javaでは、ポリモーフィズムの実装に似て、アイデアの場所を決定することはできません。
次のコード:
public class DynamicCode {// ダイナミック・コード・ライブラリ
private HashMap<String, Object> data; // プライベート・データ・セクション-そのうちの1つはGOTである。
public static void main(strs[] args) {// 公開コードセクション
}
}
バイナリ符号化
2進数→10進数: 右からN桁目に2のN乗をかけ、0から順に加算。
例
0011 ===== 0×2^3 + 0×2^2 + 1×2^1 + 1×2^010進数→2進数: 短い割り算。つまり、10進数を2で割り、余りを右端の桁とします。次に商を2で割り、対応する余りを余りのすぐ右に置き、商が0になるまで再帰的に反復すれば十分です。下から上への余りのシーケンスは、整数の2進表現です。
一次、逆、補数
まず最初に、理解する必要があります:コンピュータでは、数は、補数コードで格納されており、数のバイトの表現の補数コードについては、1000 0000の規定は、-128であり、正の数については、逆コード、補数は、元のコード自体であり、コンピュータ内の補数コードを介して負の数を格納します。
負の2進数の補数は、その負の数の逆数プラス1に等しく、また、ビットプラス1でその正の数の逆数に等しいです。
元のコード: 0001は元のコードでは+1として表され、1001は左の最初の桁が1であるため-1として表されます。これは実際に整数の元のコード表現です。
原始表現の問題点
- 0を表現するには、-0と+0(0000)の2つの方法があります。
- +1(0001)と-1(1001)は0に加算されません。 - 逆符号で解決
逆符号: 「正負の和が0にならない」という問題を解決するために、「原符号」をもとに「逆符号」を考案しました。逆符号」は負の数に対して使われる表現で、符号の位置は変えずに残りの位置を逆にします。
これにより、正の数と負の数の足し算がゼロにならないという問題が解決されます。
逆符号表現の問題が解決されました:
- しかし、現在、0を表す他の2つの表現が存在します。
1の補数で、最上位ビットを失います。
これは、"正と負の数は0に加算する "問題に加えて、同時に+ 0と- 0の問題を解決し、また満足されること。同時にもう一桁-8があります。
生コードの1バイトの範囲は-127~127、補数コードの1バイトの範囲は-128~721です。
負の2進数の補数は、負の数の逆数に1を足したもの、または正の数の逆数に1を足したものに等しくなります。
正の数の逆数は数そのものであり、負の数の逆数は元の数に基づいて符号ビットを変更せず、残りのビットを反転したものです。
キーポイント
以上、3つの違いとその由来を説明しました。コンピュータではデータは補数で保存されると最初から言われているので、2進数に相当する10進数をコンピュータで素早く把握する方法を考えればよいのです。
数の正負は、やはり左端の最初のビットの0と1で決まります。しかし、このビットを別の符号ビットとして扱い、残りのビットについて計算された10進数の前にプラスまたはマイナスの符号を付ける代わりに、2進数全体の値を計算するときに、左端の最上位ビットの前にマイナス符号を付けます。
例えば、4ビットの2進補数値1011を10進数に変換すると、-1×2^3 + 0×2^2 + 1×2^1 + 1×2^0 = -5となります。最上位ビットが1であれば、その数値はマイナスでなければならず、最上位ビットが0であればプラスでなければなりません。そして、この場合、0000だけが0を意味し、1000は-8を意味します。4ビットの2進数は、1ビットも無駄にすることなく、-8から7までの16個の数を表すことができます。
Javaでは、~はビットによる逆数を意味しますが、ビットによる逆数は逆数コードではないことに注意してください。
文字列のエンコード
初期のコンピュータは、英字と数字といくつかの特殊記号を使うだけで、日常生活で必要なすべての文字を8ビットの2進数で表すことができました。
ASCIIコードは辞書のようなもので、8ビット2進数の128種類の数字が128種類の文字にマッピングされています。例えば、ASCIIの小文字aは97番目、2進数では0110 0001で、16進数では61に相当し、大文字のAは65番目、2進数では0100 0001で、16進数では41に相当します。
注意してください:
ASCIIでは、数字9はASCII文字セットの後ろにあるため、整数表現のように0000 1001では表されなくなりました。代わりに、57桁目の0011 1001で表されます。文字列 "15 " も8ビットの0000 1111では表現されず、0011 0001と0011 0101の2つの文字1と5が連続して配置され、2つの8ビット表現が必要になります。 8ビット表現が2つ必要な理由は、4ビット表現では最大で
おわかりのように、最大の32ビット整数は2147483647であり、整数表現を使えば32ビットで表現できます。しかし、文字列を使って表現すると、全部で10文字あり、1文字を8ビットで表現すると、全部で80ビット必要です。これは整数で表現するよりもはるかに大きなスペースです。このため、データはバイナリー・シリアライゼーションで保存されます。
Unicode(ユニコード): ASCIIのような文字セットで、150の言語の14万種類の文字が含まれています。
ユニコードは、これらの文字をバイナリで表現する方法の辞書です。Unicodeは、前述のように、UTF-8、UTF-16、あるいはUTF-32でエンコードし、バイナリとして保存することができます。ですから、Unicodeを使えば、実はUTF-8以上のエンコーディングでも、他の人がエンコーディングのルールさえ知っていれば、普通に送信したり表示したりすることができるのです。
同じテキストが異なるエンコーディングで保存されます。異なるエンコーディングの別のプログラムがデコードして表示すると、文字化けします。
プログラムが一般的に使用されていない古い文字を使用または保存している場合、それらはUnicode文字セットに存在しない可能性があるため、UnicodeはそれらをU+FFFDとして記録することに注意する必要があります。UTF-8 フォーマットで保存されている場合は、 \xefùxbfùxbf となります。
回路
リレー、ゲート
コンピュータが10進数を使わず2進数を使うのは、次のような理由からです:
電気信号を伝送する場合、電線が長すぎると抵抗が大きくなり、必要な電圧が高くなったり、電化製品が反応しなくなったりします。そのため、長距離で情報を伝送する際に長い回路を避けるために、リレーが発明されました。リレーは、電気信号を伝導したり、必要に応じて「and」、「or」、「not」などの所望の論理回路を形成するために使用することができます。
基本的なゲート回路の表現:
- or "回路は、回路上に2つのスイッチを直列に接続することに相当し、両方のスイッチが開いているときに回路がオンになります。
- "or "回路は、入力の2つの回路を出力に接続することに相当し、どちらかの回路が開いていれば出力の回路が接続されます。
- "Not "は、デフォルトでスイッチがオフになっており、磁界が通電しているときのみスイッチがオンになるスイッチから、デフォルトでスイッチがオンになっており、磁界が通電しているときのみスイッチがオフになるスイッチに切り替えることに相当します。
これらの3つの基本的な論理回路は、比較的簡単に実装することができます。複雑な作業を行いたい場合は、実現する方法を重ねたり組み合わせたりして、より多くの論理回路が必要になります。
結論:回路は「オン」と「オフ」にして「1」と「0」を表します。トランジスタのように、異なる状況では、導電性の「1」と絶縁性の「0」として動作します。
これらのゲートは、CPUとメモリを作る基本的な論理ユニットです。コンピュータの2進数の「0」と「1」に対するさまざまな演算は、実際には組み合わせ論理回路と呼ばれるゲート回路から生まれます。
デジタル回路では、いわゆる「ゲート」は基本的な論理関係しか実現できない回路、つまり基本構成単位です。最も基本的な論理関係はwith、or、notであり、最も基本的な論理ゲートはwith、or、notです。以下は最も基本的なゲート回路で、これらのゲートを組み合わせて他の複雑なゲート回路を構成します。これらは、現代のコンピュータ・ハードウェアの「ビルディング・ブロック」です。
加算器
半加算器
図に示すように、1桁の数字の加算は、イソゲートまたはゲートを通して桁を計算し、アンドゲートを通してそれがある場所に入るか入らないかを計算することによって、回路を通して計算することができます。そこで、2つのゲートをパッケージ化して半加算器と呼びます。
全加算器
半加算器は、入力状況の2、4、8ビットは、最初の桁の操作を解決することができます。これは、加算数と加算数に加えて、最初の桁からの丸め信号もあるため、結果を得るために合計3つの数を加算する必要があるためです。つまり、2つの半加算器とorゲートを組み合わせて全加算器を構成することができます。
W:2ビットの値。全加算器では、2つの8ビット数の加算が理論的に可能です。
各桁の全加算器では、より多くの全加算器を直列に接続することで、より多くの桁の加算が可能です。図に示すように、8ビット加算器は8個の全加算器を直列に接続することで構成できます。
ご覧のように、1桁目は他の上位桁と異なり、半加算器が1つしか必要ありません。最上位ビット、つまり左端のビットは、加算がオーバーフローしたかどうかを示します。回路全体は、行われた加算演算がオーバーフローしたかどうかを示す信号を持っています。この信号は、コンピュータのハードウェア・レベルでサポートできるように、オーバーフローしたことをコンピュータに知らせるために、ハードウェア・レベルで他のフラグに与えることができます。
算術論理演算ユニット:中央演算プロセッサの実行ユニットで、すべての中央演算プロセッサのコア・コンポーネントです。 とゲート、またはゲートで構成される算術論理演算ユニットの主な機能は、加算、減算、乗算などの2進算術演算を実行することです。
乗算器
13*9=711の2進縦列表:
人間の計算の観点から示したように、実際のコンピュータレベルでの乗算は、置換と加算の組み合わせです。2進数の乗算であるため、乗算器の各ビットと被乗数の積はすべてゼロか、被乗数のコピーになります。乗算器の各ビットが1回乗算された後、次の演算の結果を1ビット上にシフトする必要があることに注意することが重要です。
乗算器の簡単な実装:決定するために、各シフトの出力信号を制御するゲート回路を介してハイビットへのすべての方法最初のビットから乗算器によると、乗算された数値の結果は、すべての0出力または出力のコピーのコピーの乗算された数であり、その結果は、特定のレジスタのユニティに追加されますすることができます。図に示すように:
具体的には:まず、乗算器の右端の桁を被乗数を乗算し、その結果をレジスタに預け、次に、被乗数を1つ左に移動し、乗算器を1つ右に移動し、まだ被乗数を乗算器の桁を乗算し、その結果を先ほどのレジスタに加算します。このステップを、両者がそれぞれ左と右にシフトできなくなるまで何度も繰り返します。このように、乗算器と被乗数には、単純な加算器と、その左1位シフトと右1位シフトに対応できる回路、そして乗算全体を実行するスイッチがあればよいのです。 図に示すように
ここでの制御テストは、実際には、左シフト、右シフト、および乗算と加算の再計算のタイミングを制御するクロック信号です。
しかし、加算器で学習することができ、乗算器は、いわゆる変位+加算、演算の各ビットは独立していない、乗算演算の上位ビットの乗算器はまだできるようにするために下位ビットの演算結果が必要です。たとえば、乗算器は4ビット乗算器の場合、我々は "シフト+加算 "操作の4セットを待つ必要があります。
乗算器全体の演算を最適化しようとすると、次のような理由で実行速度に影響が出ることがわかります:
- 各シフト+加算演算は相関性が強く、逐次的です。
- 制御テ ス ト は、 置 き 換え と 加算が実行 さ れ る たびに、 待機す る ク ロ ッ ク の周波数を決定 し ます。
- 各乗算結果が加算器を経由してレジスタに累積される際にゲート遅延の影響を受けます。
解答
展開する回路:まず1点目ですが、上で見た縦列図で、各グループの変位+加算と逐次関係のいわゆる強い相関関係を分析しているのは、人間の分析によるものですが、実はコンピュータの回路にとっては、2つの数値の加算が決まると、その上位ビットが実際にビットに入るかどうかも決まります。つまり、コンピュータの回路にとって、高い位置と位置は結果の同じ時間になることができ、回路は当然並列であり、いわゆる強い相関関係はありません。ゲート遅延の3番目のポイントに対応すると同時に、ゲート遅延の動作のための加算のセットだけが存在する、つまり、ゲート遅延の3Tです。そして、シフトが待つ必要があるクロック周波数はもう必要ありません。
実際、乗算器の実装には2つの方法があることがわかります:
- RISC:すなわち、無駄のない回路で、ゲートとレジスタの数は少ないが、命令を計算するために比較的長いゲート遅延とクロック・サイクルが必要。
- CISC:回路がより複雑で、複雑な命令を計算するためのゲート遅延とクロック・サイクルが短い。
このトレードオフは、実際、コンピュータ・アーキテクチャにおけるRISCとCISCの古典的な戦いです!
固定小数点、浮動小数点
固定小数点
固定小数点数は0から9までの整数を4ビットで表現するので、32ビットで8個の整数を表現できます。そして、0から9までの整数のうち、右端の2つの整数を小数部として扱い、0から9までの整数のうち、左端の6つの整数を整数部として扱います。このようにして、0から999999.99までの1億個の実数を32ビットで表現することができます。
例えば0001 1001は25ではなく19です。
10進数を2進数でエンコードすることをBCDエンコードといいます。
欠点:表現できる値が小さすぎる、元の32ビット数値表現では、表現できる最大値は42億。BCDコードで表現できる最大値は42億、BCDコードで表現できる最大値はスーパーマーケットでよく使われている100wです。
浮動小数点
固定小数点数で表現すると、間違いなく表現できない大きな実数が存在することがわかります。浮動小数点数の 科学的記数法はIEEE 457規格で、2つの基本形式が定義されています。 1つは32ビットの単精度浮動小数点数で、しばしばfloatまたはfloat32と呼ばれます。もう1つは64ビットの倍精度浮動小数点数で、しばしばdoubleまたはfloat64と呼ばれます。
国際標準IEEE 754によると、任意の2進浮動小数点数Vは、次の形式の関数として表現することができます:
- 符号:^s は符号ビットを表し、s=0 のとき V は正、s=1 のとき V は負。
- www.imooc.com/article/168
- 序数:2^E は指数ビットを表します。
例
- 10進数の6.0を2進数で書くと110.0となり、1.10×2^2に相当します。 この場合、上記のVの書式に従うと、s=0、M=1.10、E=2となります。
- 10進数の-5.0を2進数で書くと-101.0となり、-1.01×2^2に相当します。
IEEE754では、32ビット浮動小数点数の場合、最上位1ビットが符号ビットs、次の8ビットが指数E、残りの23ビットが有効数字Mと規定されています。64ビット浮動小数点数の場合、最上位1ビットが符号ビットS、次の11ビットが指数E、残りの52ビットが有効数字Mとなります。
Eは8ビットであるため、0から255までの値の範囲を持ちます。 科学的記数法のEは負になる可能性があるため、IEEE 754はEの真の値を中間数で補強しなければならないと規定しています。
例えば、2^10のEは10ですから、32ビット浮動小数点数として保存する場合は、10+127=137、つまり10001001として保存しなければなりません。
指数Eはさらに3つのケースに分けられます:
- この場合、浮動小数点数は上記のルールで表現されます。つまり、指数Eの計算値が127から引かれて真の値になり、有効数Mの前に1の最初の桁が追加されます。
- この時点で、浮動小数点数の指数 E は 1-127 に等しくなり、有効数 M は最初の 1 が加算されなくなり、10 進数の値 0.xxxxxx になります。これは±0や0に近い非常に小さい数を表すために行われます。
- このとき、有効数Mがすべて0であれば、±無限大を意味し、有効数Mがすべて0でなければ、その数は数ではないことを意味します。
このような浮動小数点表現では、符号を無視した浮動小数点数で表現できる最小と最大の数は、ほぼ 1.17 * 10^-38 と 3.40 * 10^38 であり、はるかに大きな値の範囲を表します。この時点で、fは23個の0でeは-126、fは23個の1でeは127です。
浮動小数点数の整数部の10進数から2進数への変換は上の式と同じです。
0.1001のような2進数を10進数に変換すると、次のようになります。
1*2^-1 + 0*2^-2 + 1*2^-3 + 0*2^-4` = 0.2655小数の2進数表現は整数のそれとは異なります。例えば、9.1の9は1001と表現できますし、小数の0.1は整数の2進数除算への変換とは異なります。 小数の場合のやり方は、2を掛けて1以上かどうかを確認し、1以上であれば1を書き出し、1から結果を引いて、さらに循環的に進みます。0.0++0011++1001ここで "0011 "は無限にループします。図のように
そして、整数部分と10進数部分をつなぎ合わせると、9.1この10進数は1001.000110011...となります。これが2進数表現です。浮動小数点数は2進科学記法で表されるので、この数値は1.001000110011...となります。*浮動小数点数の2進科学表記式に対応すると、符号ビットs = 0となり、有効ビットf = 001000110011...となります。fの最長の有効ビットは23ビットしかないので、f = 0010 == 001==.最後の "0011 "ループの最後の "1 "は切り捨てられます。対応する指数は3で、これに127を加えて2進数で表すと130、つまり10000010となります 。
9.1の最終的な2進表現は次のようになります: 0 100000100010100100110011==001====.
この2進数を10進数に直すと、実際の正確な値は9.0999999942779541015625となります。
上記の10進数部分の2進数変換から、これらの9つの数値のうち、0.1~0.9のうち、2進数の浮動小数点数として正確に表現できるのは0.5だけであることがわかります。他は近似値です。
浮動小数点数の加算の原理
まず整列してから計算
2つの浮動小数点数の指数ビットが異なる場合があるので、2つの指数ビットを同じにしてから、有効ビットだけの加算を計算します。
浮動小数点数の足し算では、指数ビットが小さい方の数値を有効桁を右にシフトする必要があり、右にシフトする過程で右端の有効桁が破棄されることがわかります。このため、指数ビットが小さい方の数値は、加算時に精度が低下します。2つの数値の指数ビットの差が大きければ大きいほど、シフトされるビット数が多くなり、精度が失われる可能性が高くなります。32ビット浮動小数点数の有効ビットは合計23ビットしかないため、2つの数値の指数ビットが23ビット異なる場合、小さい方の数値が24ビット右にシフトされた後、すべての有効ビットが失われます。つまり、浮動小数点数は最大3.40×10^38、最小1.17×10^-38までの範囲を表すことができます。しかし実際には、2つの数値の差が2^24、つまりほぼ1600万倍である限り、2つの数値を足し合わせても結果はまったく変わりません。
コードは以下の通り:
0011
=====
0×2^3 + 0×2^2 + 1×2^1 + 1×2^0





