Linux(Ubuntu)メモ その1

Linux(Ubuntu)の構成や、当方がLinux(Ubuntu)を操作した際の設定や対応などを記載しています。なお、そのときの環境は、Windows10のPCに、VirtualBox7.0(7.0.14 r161095)をインストールし、VirtualBox7.0のVMの1つとしてUbuntu 22.04LTSを実装しました。


Linux(Ubuntu)の構成

Linuxの構成は下図の通り。アプリ(ユーザからのコマンド)とカーネルとのやり取りを支えるのが、シェル。シェルがアプリ(ユーザからのコマンド)のやりたいことを解釈して、カーネルに伝えて、カーネルが処理。カーネルの処理結果をシェルを介して、アプリに返信(レスポンス)することもある。有名なシェルはbash。
Linuxは、ユーザがLinuxのターミナル(端末)からコマンド入力して操作することが多いので、下図の右側をイメージした方が良いかもしれません。

図1.Linux(Ubuntu)の構成

Linux豆知識

①Linuxはいろいろな種類があります。Debian系のUbuntuや、RedHat系のRedHatEnterpriseLinux(CentOSもありますが終了)などが一般的で、SSDやHDDに入ったファイルシステム(容量はGB単位)から起動します。また、組み込みシステムを意識したYocto系もあり、従来のLinuxカーネルの機能から取捨選択するなど作り込みが必要になりますが、フラッシュROMに入ったファイルシステム(容量はMB単位)から起動可能です。自動車向けLinuxのAGL(AutomotiveGradeLinux)もYocto系になります。

②LinuxカーネルはPOSIXに準拠したシステムコールが提供されています。POSIXは、IEEEで、UNIX系OSの間でアプリの移植性を高めることを目的に作られた共通API(ApplicationProgrammingInterface)になります。

③Linuxに限った話ではないですが、ソフトを実行すると、ジョブ(もしくはタスク)、プロセス、スレッドという言葉が出てきます。イメージは以下の通り。
・ジョブ(もしくはタスク)=プロセス+スレッド
・プロセスとスレッドは親子の関係で、親がプロセス、子がスレッド。
・プロセスが作られると、メモリ空間が確保され、データ処理が行われる。
・プロセスは干渉しないように各プロセスは独立して実行される。
・スレッドは親のプロセスや、親が共通なスレッドとメモリ空間を共有できる。スレッド間の動作は共有部分が多いので、コンテキストスイッチが高速に処理される(異なるプロセスの切替に比べて)。

ジョブ、プロセス、スレッドの確認方法

ジョブとプロセスの関係を確認するには、Linuxのターミナルで
 $ cat output0.txt | less &
を実行して下さい。lessの前の「|」はパイプ(コマンドの連結)になり、catコマンドの出力をlessコマンドの入力として引き渡します。output0.txtは適当なテキストファイルをご準備下さい。通常は「&」(バックグラウンドで実行される)を使って上記のように実行することはありませんが、ジョブとプロセスの確認のためバックグラウンドで実行しています。次に、
 $ jobs -l
を実行下さい。-l(lはLの小文字です)はプロセスIDを表示。下図のように、1つのジョブに対し([1])、2つのプロセスが実行されたことがわかります(プロセスID3217、3218)。

図2.jobsコマンドの実行結果(抜粋)

プロセスとスレッドの関係を確認するには、Linuxのターミナルで
 $ ps auxww -L
を実行して下さい。aは全ユーザのプロセス情報を表示、uはユーザ名と開始時間を表示、xは制御端末のないプロセス情報を表示、wはプロセス情報の表示行数を増やす(wが多いと長いコマンドが切れずに表示されます)。-LはスレッドID(LWP)やスレッド数(NLWP)を表示。
下図の場合、プロセスID577は(PID)、3つのスレッドから構成され(NLWP)、スレッドIDは577、614、659だとわかります(LWP)。

図3.psコマンドの実行結果(抜粋)

bashrcについて

bashの起動時に毎回読み込む設定ファイルがあり、bashrcと言います(rcはRun Commandsから来ています)。
bashrcには、エイリアス(ユーザが独自のコマンドを設定)、PATH(コマンドが入っているディレクトリを設定)、環境変数を定義することができる。
例えば、環境変数の設定は、ターミナルでviやnanoを使い、
 $ vi ~/.bashrc
を実行する。bashrcが開くので、最終行に、
 export ftp_proxy=http://プロキシサーバ:ポート番号
を追記し、保存する(これで毎回、環境変数ftp_proxyが設定される)。なお、「~/.bashrc」の「~/」はホームディレクトリ(/home/ユーザ名)、「.」は隠しファイルを意味します。

プロキシ設定の参考ですが、/etc/apt/apt.confに下記を記述すればプロキシ設定できます。まず、上記同様、viやnanoを使って、/etc/apt/apt.confを開き、下記を追記し、保存します。
 Acquire::ftp::proxy "http://プロキシサーバ:ポート番号";

Linux(Ubuntu)が起動しない場合の対応

Linux起動時に、/dev/sda2等のファイルシステムがエラーを起こし、「initramfs」のプロンプトが表示され、Linuxが起動しない場合があります。このとき、例えば、エラー発生が/dev/sda2の場合、「fsck /dev/sda2」を実行すれば、ファイルシステムが修復されます。

Hello.cのコンパイル

まず、Hello.cの内容は以下の通りです。
  #include <stdio.h>
    void main(){
      printf("Hello, World\n");
   }
GNUのCコンパイラを使って、Linuxのターミナルで「Hello, World」を表示させます。ターミナルで
 $ gcc -o hello hello.c
を実行し、hello.cをコンパイルし、
 $ ./hello
を実行すると、「Hello, World」が表示されます。このとき、
 $ file hello
を実行すると、「ELF 64-bit LSB pie executable, x86-64, …」というようにx86マイコンの形式とわかります(インテルのマイコンが載ったPCで実行しているので当然です)。

クロス環境とQEMU

次に、
 $ sudo apt install g++-arm-linux-gnueabihf
を実行して、Windows(x86マイコン)上で、組み込みシステムでよく使用されているARMマイコンのコンパイル環境(クロスコンパイル環境)をインストールします。なお、gnueabihfのgnuはGNU、eabiはARMマイコンのEmbeddedApplicationBinaryInterfaceで組み込みシステムのソフトのデータ型やファイルフォーマットなどを規定しており同じeabi規定なら別コンパイラのコードもリンク可能、hfはHardFloatでソフトでなくハードで浮動小数点の処理、を意味します。ターミナルで、
 $ arm-linux-gnueabihf-gcc -o hello_arm hello.c
を実行し、
 $ ./hello_arm
を実行しても「Hello, World」は表示されません。ここで
 $ file ./hello_arm
を実行すると、「ELF 32-bit LSB pie executable, ARM, …」というようにARMマイコンの形式とわかります(クロス環境なので当然です)。次に、
 $ sudo apt install qemu-user-binfmt
を実行し、QEMU(キューエミュ、プロセッサのエミュレータ)をインストールします。binfmtはLinuxのカーネル機能で任意の実行ファイルを認識してエミュレータで実行可能、を意味します。ここで、
 $ qemu-arm -L /usr/arm-linux-gnueabihf/ ./hello_arm/
を実行すると、「Hello, World」が表示されます。
なお、-LでARMのライブラリ「/usr/arm-linux-gnueabihf/」を指定して、「./hello_arm/」をエミュレーションしています。エミュレーションは、ハードのARMマイコンがない代わりに、インテルマイコンのPC上のソフトで検証できることを意味します。

Linux(Ubuntu)の仮想アドレス

Linuxのターミナルで、
 $ xeyes &
を実行し、下図の左側(動眼のアプリ)を表示させました。このとき、プロセスIDは3817だったので、
 $ sudo cat /proc/3817/maps
を実行し、下図の右側のように仮想アドレスを確認。64bitのLinuxになりますが、仮想アドレスは48bit(アドレス空間…256TB)とわかります。

図4.Linux(Ubuntu)の仮想アドレス

複数コマンドの実行例 その1

複数のコマンドを「|(パイプ)」で連結したときの動作と結果を記載します。Linuxのターミナルで、
 $ apt list --installed | awk -F/ '{print $1}' | tr '\n' ' ' | tee result.txt
を実行しました。まず、「|」で連結した各コマンドの動作を説明します。
<apt list --installed>
 Linuxにインストールされたパッケージ一覧を抽出。なお、図5は、説明のために「apt list --installed」のみを単独で実行した結果になります。

図5.aptコマンド実行結果

<awk -F/ '{print $1}'>
 各行を「/」区切りで認識し、各行の1列目を抽出。この場合、上図の緑部分を抽出。

<tr '\n' ' '>
 「\n(改行)」を「' '(スペース)」に変換。この場合、上図の緑部分を「' '(スペース)」を区切りにして結合。

<tee result.txt>
 Linuxのターミナルと、result.txtに結果を出力。

下図はteeコマンドの実行後の結果になるが、上図の緑部分のインストールされたパッケージ情報が「' '(スペース)」を区切りに結合していることがわかる(accountsservice、acl、acpi-support…が順に並んでいる)。

図6.teeコマンド実行結果

複数コマンドの実行例 その2

Linuxのターミナルで、
 $ apt list --installed | awk -F/ '{print $1}' | sed -e '1d' | sed 's/^/sudo apt-get install /' | tee sed_result.txt
を実行しました。まず、「|」で連結した各コマンドの動作を説明します。
<apt list --installed>
 上記の「その1」同様、Linuxにインストールされたパッケージ一覧を抽出。

<awk -F/ '{print $1}'>
 上記の「その1」同様、各行を「/」区切りで認識し、各行の1列目を抽出。

<sed -e '1d'>
 1行目はパッケージ情報でなく「一覧表示…」だったので、1行目を削除。

<sed 's/^/sudo apt-get install /'>
 各行の文頭に「sudo apt-get install 」を挿入。なお、文末に挿入したい場合は、「^」を「$」に変更する。
お詫び…すでにインストールされたパッケージ情報の前に「sudo apt-get install 」を挿入するのは変ですが、文頭に文字を挿入する方法とお考え下さい。

<tee sed_result.txt>
 Linuxのターミナルと、sed_result.txtに結果を出力。

下図はteeコマンドの実行後の結果になるが、インストールされたパッケージ情報の前に「sudo apt-get install 」が挿入されていることがわかる(accountsserviceやaclの前に、sudo apt-get install が挿入されている)。

図7.teeコマンド実行結果

シェルスクリプト

シェルスクリプトは、コマンドをテキストファイルに記述し、まとめて実行する機能です。例えば、Linuxのホームディレクトリ(/home/ユーザ名)にtest.shを作成し、test.shの中身を
 #!/bin/bash
 pwd
 cat /etc/os-release
で保存して下さい。「#!/bin/bash」は、シバン(シェバン)といい、シェルbashのパスを作成します。
次に、Linuxのターミナルで、
 $ chmod +x test.sh
 $ ./test.sh
を実行すると、pwdコマンドの実行結果「/home/ユーザ名」と、catコマンドの実行結果「Linux(Ubuntu)情報」を確認することができます。なお、「$ chmod +x test.sh」は、Linuxの全ユーザにtest.shの実行権限を与えています。

この記事が気に入ったらサポートをしてみませんか?