20200219のLinuxに関する記事は9件です。

ubuntuとは

Ubuntuとは

Linuxディストリビューションのこと

Linux:
広義ではOSの名前(Windows的な)。狭義では、OSの中核(カーネル)のこと。

ディストリビューション:
OSとしてのLinux(つまり広義)。狭義の、カーネルとしてのLinuxだけではPCは何もできないので、色々な機能を持ったソフトウェア(例えば電卓とか)を盛り込む必要がある。
つまり、Linux(狭義) + 色んなソフトウェア = ディストリビューション(=広義のLinux)ってこと。
そしてディストリビューションって言葉が出てきたら、ほぼLinuxの話だと思っていいみたい。

だから、
Linuxディストリビューション: 
カーネルとしてのLinuxに他の仕事をするソフトウェアを盛り込んだ、OSとしてのLinux(広義)のこと。
で、Linuxディストリビューションというのはいくつも種類がある。

まとめ

まとめるとUbuntuは…
「カーネルとしてのLinux(狭義)」と「色んな機能を持ったソフトウェア」をまとめた広義の方のOSとしてのLinux(=Linuxディストリビューション)の一つこと。
むずかしTT ブチギレみけんコネコネ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

archlinuxのインストール方法

biosモードで起動している場合の方法です。

usbメディアを作る

https://www.archlinux.jp/download/
から落としてきたisoファイルを適当なツールでUSBに焼く。

パーティションを作る

fdiskを使ってパーティションを1つ作る。
パーティションをフォーマットする。

# mkfs.ext4 /dev/sda1

パーティションをマウントする。

# mount /dev/sda1 /mnt

スワップ領域はあとからスワップファイルで設定できるので今は作らない。

ベースシステムをインストール

/etc/pacman.d/mirrorlist を編集して日本サーバーの行を一番上に移動する。
pacstrapを使ってマウントしたパーティションにベースシステムをインストールする。

# pacstrap /mnt base base-devel linux linux-headers linux-firmware

fstabを生成する

# genfstab -U /mnt >> /mnt/etc/fstab

chroot

chrootする

# arch-chroot /mnt

wheelグループのユーザーがsudoコマンドを実行できるようにする

# visudo

を実行し

%wheel ALL=(ALL) ALL

のコメントアウトを外す。

rootユーザーのパスワードを設定する

# passwd

一般ユーザーを追加し、wheelグループに追加する。

# useradd -m ユーザー名
# usermod -aG wheel ユーザー名
# passwd ユーザー名

必要なパッケージを入れる。
ドライバーやエディター、ttyは自分の環境や好みに応じて変えてください

# pacman -S sudo nvidia networkmanager plasma vim alacritty

sddmとnetworkmanagerを有効化

# systemctl enable sddm
# systemctl enable NetworkManager

grubをインストール

# grub-install --target=i386-pc /dev/sda
# grub-mkconfig -o /boot/grub/grub.cfg

chrootを抜ける

# exit

電源を切ってUSBメモリを抜いてから起動する。

タイムゾーンなどの設定

まだタイムゾーンやローケーションの設定をしていないのでkdeの設定から設定する。
日本語で使う場合は日本語フォントも入れる。(ファイルを落として開けばwindowsと同じように簡単にインストールできる。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rust で vmlinux を起動できる x86 ブートローダーを作ってみた話

Rust を勉強し始めたので冬休みの間に Linux の boot protocol を喋る x86ブートローダー(自称:Krabs)を作ってみました。この記事では、開発に至った動機や、作成した Krabs の特徴とか仕組み、開発中におきた嬉しかったことなどについて書きたいと思います。

Krabs とは

Krabs は、Rustで書かれた x86/x86_64(Legacy BIOS) 向けの4段ロケット構成のチェインローダーです。
bzip2 で圧縮された ELF 形式のカーネルを起動できます。bzip2 圧縮されたイメージを解凍して、次に展開してでてきた ELF イメージを再配置してからの、カーネルの起動となります。
内部では libbzip2 の C ライブラリを利用していますが、それ以外は全て Rust で記述されています。

GitHub - ellbrid/krabs: An x86 bootloader written in Rust.

以下の特徴があります。

  • legacy BIOS に対応
  • 64bit ロングモード, 32 bit プロテクトモードの両方に対応
  • 最小限の x86/x86_64 Linux boot protocol に対応
  • Linux boot protocol に従って kernel コマンドラインを指定し、OS 起動時の動作を制御可能
  • Linux boot protocol に従って initramsfs/initrd などのモジュールをロードできる
  • マルチブート仕様には対応していない。

kernel command line と initrd を活用する 64bit vmlinux の起動例は以下になります。

./tools/build.sh -k vmlinux -i initramfs.cpio.gz -c "clocksource=tsc" disk.img 
qemu-system-x86_64 --hda disk.img -m 1G

cmdline.gif

Krabs を作ろうと思った動機

Rust の練習と Linux の習熟が目的です。OSスタックより下のレベルでのcodingもRust を使用することにより、よりモダンにできるのではないかと考えました。また。Linuxカーネル起動までの過程から、必要最小限のエッセンス部分だけを抽出し、最終的に自分にとってブラックボックスが一切存在しないブート環境を作り上げてみたいと言う思いもありました。基本的には、以下の混迷する部分、詰まりがちな部分を取り除くことを目標としました。

  • GRUB/2ブートローダーや bzImage など、チェインローダーの仕組みや技法が難解
  • また、そこでのアセンブリ<->Cとの往復は、素人目には超スパゲッティ状態
  • 起動コードの読破は結構時間と努力が必要。かりに読破できたとしても、得られるものがあるのかどうか..
  • Rust は成果物が大きくなりがちでブートローダーの記述には向いていないと言われているが、本当か?

以上を踏まえて、ブートローダーの読解や bzImage コードの追跡はいさぎよく諦め、自ら Rust でブートローダーを書き下ろすことにしました。

仕組み

Linux カーネル起動機構

Linux カーネル起動機構を bzImage や GRUB ブートローダーから眺めてしまうと難しく感じてしまいますが、実態は意外にも単純です。基本的なことは、2点です。圧縮イメージを展開し、ELF 形式のイメージを取り出したら、それをプログラムヘッダーに従って再配置すること、そして、The Linux/x86 Boot Protocol に従って初期化処理を行い、パラメーターの設定をすること。これだけです。

あっけないほど簡単に思えますが、具体的には、以下の4種類の初期化処理を行います。

ハードウェア初期化:

  • キーボードリピート頻度の設定(最大値)
  • 割り込みの禁止およびすべての割り込みレベルのマスク
  • 割り込みディスクプリタ(IDT), セグメントディスクプリタ(GDT)の設定
  • すべてのセレクタ(CS, DS, ES, FS, GS)は4Gバイトフラットのリニアアドレス空間を参照
  • アドレスバスの32bit化(A20ラインの有効化)
  • プロテクトモードへの移行
  • ターゲットがELF64の場合、4Gブートページテーブルを設定し、ロングモードに移行

ソフトウェア初期化:

  • BIOS 呼び出しによるシステム搭載メモリ容量の取得

カーネルへの情報伝達:

  • カーネルパラメータ用にZero Pageを設定し、OSに送信

イメージの配置:

  • ターゲットはELFファイルだが、Krabsはbzip2圧縮後にそれを使用する。したがって、2段階の再配置が必要:1つはbzip2解凍で、もう1つはELF再配置
  • initrd/initramfsなどのモジュールをロードして配置する

上記の処理を行うのに Krabs は4つのステージに分かれたプログラムで対応します。

Krabsの構造と概要

  1. stage1:
    stage1はブートセクターに書き込まれた446バイトのプログラムです。セグメントレジスタ(CSDSESSS)は0x07C0に設定され、スタックポインター(ESP)は0xFFF0に初期化されます。その後、stage2をアドレス0x07C0:0x0200にロードし、アドレス0x07C0:0x0280にジャンプします。 stage1の末端2byteには、stage2プログラムのセクター長(512バイト単位)を格納するための領域があります。Rust でも 446 byte に収まる1stステージローダーが書けるのです!
  2. stage2:
    stage3をアドレス0x07C0:0x6000にロードします。bzip2圧縮されたカーネルイメージは拡張メモリ領域のアドレス0x0350_0000にロードされ、initrdは0x0560_0000にロードされます。これらのファイルの転送には、アドレス0x07C0:0xEE00から4Kバイトのトラックバッファーを使用しました。ディスクから読み取ったものを一時ここに格納し、さらにINT 15h BIOSファンクション0x87hを使用して適切なアドレスに転送します。 stage3、initrd、および圧縮カーネルイメージの読み込みが完了したら、アドレス0x07C0:0x6000にジャンプします。なお、カーネルコマンドラインは、アドレス0x280から120バイトの領域に保持されています。
  3. stage3 + stage4:
    Stage3とStage4は、bss領域を使用する可能性があるため、.bssセクションのゼロクリアをサポートする必要があります。一連のハードウェアおよびソフトウェアの初期化の後、Zero Page 情報を 0x07C0:0x00000x07C0:0x0FFFに準備します。 A20 line を有効にし、アドレスバスを32ビットに変更して、保護モードに移行します。bzip2 解凍関数を呼び出し、bzip2圧縮されたELFカーネルイメージを拡張メモリアドレス0x100000以降に復元します。そして、ELF32 / ELF64ファイルをパースして、ロードします。もし、ターゲットがELF64だった場合には、4Gブートページテーブルを設定し、ロングモードに移行します。最後に、エントリポイントにジャンプしてカーネルを起動します。このとき、下位メモリに用意された Zero Page 情報の物理アドレス(0x00007C00)をESIまたはRSIレジスタに設定しておきます。
  4. plankton:
    planktonはstage1 ~ stage4 に共通しているライブラリです。

Disk 構造

KrabsはHDDとSSDに対応していますが、必ず MBR を持つ必要があります。また、いずれかのパーティションは必ず boot flag が設定されている必要があります。stage3,4, kernel, initrd は bootflag のついたブートパーティションに格納されます。

layout.png

補足

ところで、なぜ legacy BIOS に対応しているのか気になっている方がいるかもしれないので、こちらについて補足させてください。以下の3点の理由から今回は legacy BIOSのみサポートしています。

  • このブートローダーはもともとThinkPad 600Xと言う古の?PCで使うことを目的として作ってた。
  • 現時点においては、UEFI よりもレガシーBIOS に対応したほうが幅広い環境で使える。
  • 自分の PC 以外では主にクラウド環境で使用することを目的としている。クラウド環境では レガシーBIOS が主流で、またこれをUEFIに置き換えるメリットもなさそうだったので、当分生存できそう。

Rust で書いてみた感想

Rust は低レベルなコードを書く上で C よりも圧倒的に楽。と言うのが個人的な感想です。

コンパイルが通った時の安心感がすごい

例え問題が起きても本当にunsafe部分を疑うだけで済む。今回はこれ本当だった。

パッケージ、モジュールの考え方が最高

Cみたいにオブジェクトファイルどれとどれリンクするんだっけってイライラしなくていい。

モダンなコードをかけて楽

no_std の低レベルのコードも比較的モダンにかける。
また、あれが使えないとかこれが使えないとかそんなに悩むことはない。
体感ですが、開発速度はCよりも早いかも?
何より、Rust は書いてて楽しいです。
型を意識して書くのいいです。また、いろいろな機能を使うのも楽しいです。

Rust でもチェインローダーが書ける

かけます。16bit/32bit もあまり不自由なくチェインローダーがかけます。
チェインローダーはそのロケット構造上、次のステージに行くために必ず unsafe な部分を記述しなければなりませんが、その unsafe な部分には、Cのときによく使う技法がそのまま使えるのもいいなと思いました。
ただし、ブートセクタ内部ではマクロとクロージャは要注意です。サイズが爆発しやすい気がします。

Cの資産の活用と提供

FFIによりCの資産をあまり意識せずに活用できるのは大変素晴らしいと思います。
今回libbzip2を内部で使用していますが、Rust から簡単に使うことができました。
また、逆にCにRustの資産を提供することも容易かったです。libbzip2 の使用のためにはmallocが必要ですが、これについて Rust で簡単に実装したものを C 側に提供できました

ハマったところ

ページテーブルを設定するのに、アラインメントをリンカースクリプトstructの属性で設定しようとしましたが、いずれもうまくいかず。。。(他のデータ構造が壊れてしまっていたように見えた)。Rust のアラインメント、何かおかしいのかと思いつつそのまま放置。結局、ページテーブルを設定したい領域を手動で確保して、そこのアドレスをベタ打ちすることで対応しました。

超大物エンジニアが助けてくれた!

Krabs を作成して単純な ELF 形式のカーネルを起動できるようになってから しばらくは、なぜか vmlinux を正しく起動できず、開発が少しの期間停止していました。その間、READMEを英語で書いたり、単純な動作例をテストしたり、long mode に対応したりして、twitter 上で英語で宣伝を行っていました。

なんでうまくいかないんだろうと毎日悩んでいて、bzImage のソースを読み込んでいたある日、とても嬉しいことが起きました。

何気なく日本語で呟いたツイートに、なんと AWS の超大物エンジニア @_msw_ と x86/x86-64 Linux kernel treeの長年のメンテナとして有名な@LinuxHPAHans Peter Anvin - Wikipedia) が反応してアドバイスくれたのです!思わず嬉しすぎてスクショしました。
このアドバイスのおかげで、問題は一気に解決し、また、マルチブート仕様には対応しない決意を新たにしました(もともともkernel内部にパラメータを埋め込むマルチブート仕様は僕はあまり好きじゃないです)。

advice2.png

今回は日本語に反応してくれましたが、もともと英語で宣伝していたものにリアクション頂いてから繋がっていました。英語で発信しておくの大事だなと改めて思いました。

あと英語で呟くと、twitter で直接 DM をくれたり、リプライで応援メッセージを送ってもらえる頻度が上がる気がします。大変励みになります。嬉しかったメッセージを紹介します。Writing an OS in Rust の方もメッセージくれました。

...これで終わるともったいないので、Krabs を体験していただくためにも、最後に 最小構成の Linux システムを構築してこれを Krabs で起動する例を扱いたいと思います。

Minimal な Linux を起動しよう

  • ブートローダー作りたい人
  • 最小構成のLinuxシステムをビルドしてみたい人
  • 簡単なinitramfs作ってみたい人

とかの参考になるのではと思います。(以下、CentOS7で作業してます)

minimal な vmlinux を作ろう!

1: Linuxのソースを持ってきます。

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.5.2.tar.gz
tar xf linux-5.5.2.tar.gz 
cd linux-5.5.2

2: Linuxのconfigを設定します。

make allnoconfig
make menuconfig

以下の設定を使用します。

64-bit kernel ---> yes
General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support ---> yes
General setup ---> Configure standard kernel features ---> Enable support for printk ---> yes
Executable file formats / Emulations ---> Kernel support for ELF binaries ---> yes
Executable file formats / Emulations ---> Kernel support for scripts starting with #! ---> yes
Enable the block layer ---> yes
Device Drivers ---> Generic Driver Options ---> Maintain a devtmpfs filesystem to mount at /dev ---> yes
Device Drivers ---> Generic Driver Options ---> Automount devtmpfs at /dev, after the kernel mounted the rootfs ---> yes
Device Drivers ---> Character devices ---> Enable TTY ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> 8250/16550 and compatible serial support ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> Console on 8250/16550 and compatible serial port ---> yes
Device Drivers ---> Block devices ---> yes
Device Drivers ---> PCI Support --> yes
Device Drivers ---> Serial ATA and Parallel ATA drivers (libata) ---> yes
Device Drivers ---> Serial ATA and Parallel ATA drivers (libata) ---> Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support ---> yes
Device Drivers ---> Serial ATA and Parallel ATA drivers (libata) ---> Generic ATA support ---> yes   
Device Drivers ---> SCSI device support ---> SCSI disk support
File systems ---> The Extended 4 (ext4) filesystem ---> yes
File systems ---> Pseudo filesystems ---> /proc file system support ---> yes
File systems ---> Pseudo filesystems ---> sysfs file system support ---> yes

もしくは、上記をすでに設定してある my recommended config を用意してあるので、これを.configにコピーしてきます。

wget https://raw.githubusercontent.com/ellbrid/krabs/master/resources/.config -O .config
make menuconfig

3: vmlinuxをビルドします。

make vmlinux

4: カレントディレクトリに vmlinuxが出来上がっています。

initramfsを作ろう!

1: まず./src/initramfsディレクトリを作成してここに基本的なディレクトリを構築していきます。

cd ..
mkdir --parents src/initramfs/{bin,dev,etc,lib,lib64,mnt/root,proc,root,sbin,sys}

2: 基本的なデバイスノードもコピーしていきます。

    1.  sudo cp --archive /dev/{null,console,tty,tty[0-4],sda,sda[1-8],mem,kmsg,random,urandom,zero} src/initramfs/dev/

3: 動的ライブラリなど導入して環境を構築する代わりに、busyboxを使用します。

curl -L 'https://www.busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64' > src/initramfs/bin/busybox
sudo chmod +x src/initramfs/bin/busybox
./src/initramfs/bin/busybox --list | sed 's:^:src/initramfs/bin/:' | xargs -n 1 ln -s busybox

4: initスクリプトを用意します。

cat >> src/initramfs/init << EOF
#!/bin/sh
mount -t devtmpfs  devtmpfs  /dev
mount -t proc      proc      /proc
mount -t sysfs     sysfs     /sys
sleep 2
cat <<END
Boot took $(cut -d' ' -f1 /proc/uptime) seconds

_____           _        __    _             
|   __|___ ___ _| |_ _   |  |  |_|___ _ _ _ _ 
|__   | .'|   | . | | |  |  |__| |   | | |_'_|
|_____|__,|_|_|___|_  |  |_____|_|_|_|___|_,_|
                  |___|                       


Welcome to Sandy Linux
END
exec sh
EOF
sudo chmod +x src/initramfs/init

5: initramfsを作ります。

cd src/initramfs
find . | cpio -o -H newc | gzip > ../../initramfs.cpio.gz

diskイメージを作ろう!

1: イメージファイルを qemu-img で作成します。ddでもいいです。

qemu-img create disk.img 512M

2: fdiskでパーティションを作成します。

1st partition:

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-1048575, default 2048): 2048
Last sector, +sectors or +size{K,M,G} (2048-1048575, default 1048575): 206848
Partition 1 of type Linux and of size 100 MiB is set

ブートフラグを第1パーティションに作ります:

Command (m for help): a
Selected partition 1

2nd partition:

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
Partition number (2-4, default 2): 
First sector (206849-1048575, default 208896): 
Using default value 208896
Last sector, +sectors or +size{K,M,G} (208896-1048575, default 1048575): 
Using default value 1048575
Partition 2 of type Linux and of size 410 MiB is set

write out:

Command (m for help): w
The partition table has been altered!
Syncing disks.

3: 第2パーティションにext4ファイルシステムを作る

$ sudo kpartx -av disk.img 
lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0              11:0    1 1024M  0 rom  
loop0             7:0    0  512M  0 loop 
├─loop0p1       253:2    0  100M  0 part 
└─loop0p2       253:3    0  410M  0 part 
$ sudo mkfs.ext4 /dev/mapper/loop0p2
$ sudo kpartx -d disk.img 

Krabsで起動しよう!

以下のコマンドを実行するだけです。これで vmlinux が bzip2 圧縮されてブートローダーとともに disk.img に書き込まれます。

$ pwd
path/to/krabs
$ ./tools/build.sh -k path/to/vmlinux -i path/to/initramfs.cpio.gz path/to/disk.img 

それでは起動しましょう!

$ qemu-system-x86_64 --hda disk.img -m 1G

cmdline.gif

あとがき

そういえば reddit のコメントで、なんで xz や gzip にしなかったのか聞かれました。あまり深く考えてませんでしたが、単に bzip2 を持ってくるのが簡単だったという理由があります。また、bzip2 を Rust に移植中のニュースを聞いていたのでそれが楽しみであるのも理由の一つです。
ただ、上記の Minimal Linux を起動する例をお試しいただいた方はお分かりいただけるかと思いますが、bzip2は大変遅いです。このため将来的には gzip もしくは xz に移行する可能性があります。

ちなみに僕はスポンジボブが好きで、このブートローダーについてカーニさんとプランクトンから名前を拝借してます。僕の起動する Linux では GRUB じゃなくて Krabs を使うのが密かな目標です。(spongeと命名した自作OSも作り始める予定です)。

Amazon EC2 で、このブートローダーを使用するオリジナルのジョーク Linux AMI を作成することも目指してます(その名も Sandy Linux AMI 笑)。Rust製のcoreutilsに、init に Rust製のsystemdであるrustysdを使用しようかなと考えています。sshはこれでイケる? 夢が広がりますね。

あと、githubのstarはすごい嬉しいものであることがわかったので、これからどんどんstarしていこうと思いました。

最後まで読んでいただきありがとうございました。
いいね、コメント、アドバイス、issue、プルリクとかありましたら、ぜひお願いしますmm。

GitHub - ellbrid/krabs: An x86 bootloader written in Rust.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SDL_mixerを使ったプログラムでMIDIがならないときの確認点

SimutransというゲームではBGMがMIDIファイルになっており、そのBGMがならなくて困ったので解決策を探していたらLinuxでSDL_mixerを使っている際に共通する(と思われる)解決策で解決でき、また日本語情報が見つからなかったので共有します。

動作環境

上記の通り、今回は一つのゲームでしか試していません。そのうえで今回上手く動作した環境は以下です。

  • OS: Arch Linux (Linux 5.5.4-arch1-1)
  • SDL_mixer: libSDL_mixer-1.2.so.0.12.0
  • サウンドフォント: FluidR3_GM.sf2
  • Simutrans: Simutrans 121.0

また、PulseAudio環境下での話です。

確認点

TiMidity++またはFluidSynth

まずそもそも、LinuxでMIDIを再生するためにこれらのうちの少なくとも一方を入れる必要があります。今回の場合どちらも入っている状態で片方ずつ確認しましたが、それぞれ正常に動作したのでどちらでも良いと思われます。また、単に導入するだけでも駄目で、timidity -iAfluidsynth -a pulseaudio -m alsa_seq -o midi.autoconnect 1 [SOUND_FONT]等でデーモン化させておく必要があります。個々のオプションは環境に合わせて調節してください。

サウンドフォント

OSにより異なると思いますが、少なくともArchLinuxではTiMidity++やFluidSynthを導入してもサウンドフォントは入らないため、別に入れる必要があります。導入したサウンドフォントはデーモン化する際にTiMidity++の場合は/etc/timidity++/timidity.conf等、FluidSynthの場合は引数に指定してください。

SDL_SOUNDFONTS

SDL_mixerを使ったプログラムを実行する際に、上で設定したサウンドフォントのパスをSDL_SOUNDFONTS=/path/to/soundfont.sf2 ./programのように指定してください。試していないですが、恐らく.bashrc等で設定しても大丈夫だと思います。

ミュート

ここまでの作業で再生されるはずですが、もし再生されない場合は念の為ミュートになっていないか確認してください。今回の場合はここまでで上手く再生されました。

最後に

SDL_SOUNDFONTSの情報が見つからずに嵌ってしまい、また見つかった際もFluidSynthをSDL_mixerと組み合わせる場合の情報として書いてあったので、探しづらかったです。なお、動作環境にも書きましたが、SDL2ではなくSDLでの話なのでSDL2では変わっている可能性があります。

情報元

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

fork()関数、execve()関数について

  • 自分用メモ
  • カーネルのプロセス生成について学んだことを残しておく
  • C言語で実行

fork()関数について

  • fork()関数を実行すると、親プロセスから子プロセスを生み出す
fork.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

static void child()
{
    printf("Child. pid: %d.\n", getpid());
    exit(EXIT_SUCCESS);
}

static void parent(pid_t pid_child)
{
    printf("Parent. parent's pid: %d. child's pid: %d\n", getpid(), pid_child);
    exit(EXIT_SUCCESS);
}

int main()
{
    pid_t pid;
    pid = fork();

    if(pid == -1) err(EXIT_FAILURE, "fork() failed");

    //子プロセスは返り値が0
    if(pid == 0)
        child();
    //親プロセスが返り値が子プロセスのプロセスID(> 0)
    else 
        parent(pid);
}
  • 実行結果
Parent. parent's pid: 27701. child's pid: 27702
Child. pid: 27702.

親プロセスから子プロセスが生成され、それぞれ別のプロセスIDを持っていることが分かる

execv()関数について

  • 以下の流れでカーネルはプロセスを実行する
  1. 実行ファイルを読み込む
  2. 現在のプロセスのメモリを新たなプロセスのデータで上書き
  3. 新しいプロセスを実行
execve.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

static void child()
{
    char *args[] = {"/bin/echo", "こんにちは", NULL};
    printf("child. child's pid: %d.\n", getpid());
    execve("/bin/echo", args, NULL);
    err(EXIT_FAILURE, "execve() failed");
}

static void parent(pid_t pid_child)
{
    printf("parent. parent's pid: %d. child's pid: %d\n", getpid(), pid_child);
    exit(EXIT_SUCCESS);
}


int main(void)
{
    pid_t pid;
    pid = fork();
    if(pid == -1)
        err(EXIT_FAILURE, "fork() failed");

    if(pid == 0)
        child();
    else
        parent(pid);

}
  • 実行結果
parent. parent's pid: 28034. child's pid: 28035
child. child's pid: 28035.
こんにちは

fork()関数でプロセスが分岐したあと、子プロセスでexecve()関数によって、/bin/echoが実行されていることが分かる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新しいシェルスクリプトの教科書 ~リダイレクトとパイプ~

標準入出力

3つの標準入出力

名前 内容
標準入力 標準的な入力元。通常はキーボード。
標準出力 標準的な出力先。通常は端末ディスプレイ。
標準エラー出力 標準的なエラーの出力先。通常は端末ディスプレイ。

リダイレクト

  • コマンドを実行する時に標準入出力の入力元、出力先を置き換えるシェルの機能

出力のリダイレクト>

  • >を使う
例 標準出力をresult.txtにリダイレクト
$ ps > result.txt
$ cat result.txt
   PID TTY          TIME CMD
12202 pts/0     00:00:00 bash
34100 pts/0     00:00:00 ps
例 標準出力をhello.txtにリダイレクト
$ echo hello > hello.txt
$ cat hello.txt
hello

標準エラー出力のリダイレクト2>

例 標準エラー出力をファイルにリダイレクト
$ ls /xxx 2> error.txt
$ cat error.txt
ls: /xxx にアクセスできません: そのようなファイルやディレクトリはありません

入力のリダイレクト<

  • <を使う
例 標準入力ファイルをword.txtとしてtrコマンドを実行
$ cat word.txt
abcd         ←word.txtの内容
my book
$ tr b B < word.txt    ←word.txtを入力元としてリダイレクト
aBcd
my Book

noclobberと出力のリダイレクト

  • >によって出力をリダイレクトした場合、リダイレクト先のファイルが存在しない場合はファイルが新規作成される
  • 一方、ファイルが存在する場合、ファイルの内容は失われて上書きされてしまう
  • 既存ファイルの上書きを防ぐためにnoclobberをsetコマンドで設定する
  • 無効にする場合は、set +o noclobber
  • noclobberを無視して出力リダイレクトで上書きは、>|を使う
例 bashのnoclobberを設定する
$ set -o noclobber

例 noclobberを設定した状態で既存ファイルに出力リダイレクト
$ touch result.txt
$ ps > resultx.txt
-bash: result.txt: 存在するファイルを上書きできません

例 bashのnoclobberの設定を無効にする
$ set +o noclobber

例 noclobberの設定を無視してリダイレクトで上書き
$ ps >| result.txt
$ cat resultxtxt
  PID TTY          TIME CMD
11272 pts/0    00:00:00 bash
11384 pts/1    00:00:00 ps

リダイレクトと追記

  • >による上書きではなく、元のファイルへ追記したい場合は、>>を使う
  • 指定したファイルが存在しなければ、新規作成される
例 echo.txtへリダイレクトによる追記
$ echo line1 >> echo.txt
$ echo line2 >> echo.txt
$ echo line3 >> echo.txt
$ echo cat echo.txt
line1
line2
line3

標準出力と標準エラー出力をまとめる

  • リダイレクトの記号を1行に複数指定することで、標準出力と標準エラー出力をそれぞれ別のファイルに出力することができる
  • 標準出力と標準エラー出力を同じファイルにリダイレクトしたい場合は、末尾に2>&1を書く
  • 指定した同じファイルに、標準出力と標準エラー出力をリダイレクトしたい場合は、ファイル名の前に>>と指定する
  • エラーメッセージは、標準エラー出力にしておくとエラーの原因の調査がしやすい
例 標準出力と標準エラー出力を分けてリダイレクト
$ ls /usr /xxx > result.txt >2 error.txt

例 標準出力と標準エラー出力を同じファイルにリダイレクト
$ ls /usr /xxx > ls_result.txt 2>&1

ちなみに、
$ ls /usr /xxx > ls_result.txt 2>&1
$ ls /usr /xxx &> ls_result.txt
は、同じ

ちなみに、
$ ls /usr /xxx >> ls_result.txt 2>&1
$ ls /usr /xxx &>> ls_result.txt
は、同じ
echo_stderr.sh
例 エラーメッセージを標準エラー出力へ出力する
# !/bin/bash
echo message
echo 'error message' 1>&2    ←この場合も、1は省略しても同じ意味
$ ./echo_stderr.sh > result.txt
error message

/dev/null

  • リダイレクトと組み合わせてよく使われる
  • /dev/nullから読み込んでも、何もデータは返されない
  • /dev/nullにデータを書き込んでもどこにも書き込まれることはなく、データは消える
  • 出力リダイレクト先に/dev/nullを指定すると出力は捨てられ、何も出力されない
    • 不要なメッセージを取り除くため
  • コマンドからの出力に不要なメッセージが含まれてる場合や、通常のメッセージの中にエラーメッセージが埋もれてしまってエラー情報がわからない時などに使うと便利
例 /dev/nullの表示
$ cat /dev/null
$                     ←何も表示されない

例 lsコマンドの出力を/den/nullにリダイレクトして捨てる
$ ls / > /dev/null

例 標準出力を/dev/nullにリダイレクトして標準エラー出力だけ表示
$ ls / /xxx > /dev/null
ls: /xxx にアクセスできません: そのようなファイルやディレクトリはありません

例 標準エラー出力を/dev/nullにリダイレクトして標準出力だけ表示
$ ls / /xxx 2> /dev/null
(ls / の結果だけ表示される)

リダイレクトに関する注意点

スペースを入れてはいけないところにスペースを入れる

  • リダイレクト記号の左に書くファイルディスクリプタ番号と>の間に、スペースを入れてはいけない
例 標準エラー出力を/dev/nullへリダイレクトしようとした失敗例
$ ls /usr /xxx 2 > /dev/null

リダイレクト先とリダイレクト元

  • リダイレクト元とリダイレクト先を同一とすると、ファイルの中身が空になってしまう

リダイレクト記号のまとめ

記号 内容
< file 標準入力をfileに変更する
> file 標準出力をfileに変更する
>> file 標準出力をfileの末尾に追加する
> file
2> file 標準エラー出力をfileに変更する
2>> file 標準エラー出力をfileの末尾に追加する
2> file
>&m 標準出力をファイルディスクリプタm番のコピーにする
n>&m ファイルディスクリプタn番をファイルディスクリプタm番のコピーにする
&> file 標準出力を標準エラー出力の両方をfileに変更する
&>> file 標準出力と標準エラー出力の両方をfileの末尾に追加する

ヒアドキュメント

あるコマンドの入力をファイルとして指定するのではなく、直接シェルスクリプト内に記述する機能

  • ヒアドキュメントの内容が標準入力にあるものとして、コマンドが実行される
  • 標準入力としてコマンドに渡したい内容が複数行になる場合によく使う
  • 最後の終了文字列は、1つの行に単独で書かなければならない
  • ヒアドキュメントの内側では、パラメータ展開・コマンド置換・算術式展開が行われる
  • ヒアドキュメントの中で$,`,\という展開に使用する記号をそのまま文字として扱う場合は、\でエスケープする
  • ヒアドキュメント全体で展開が行われないようにするには、<<の右に書く終了文字列をクォート
    • 終了文字列のクウォートは、シングルクォーテーション・ダブルクォーテーション・バックスラッシュなど
  • <<の代わりに<<-と書くと、ヒアドキュメントの内容と最後の終了文字列の行頭タブが無視される

ヒアドキュメントの構造

コマンド << 終了文字列
ヒアドキュメントの構造
終了文字列
ls.sh
例 catコマンドでヒアドキュメントの内容を表示
# !/bin/bash
cat << END
Usage: ls [OPTION]... [FILE]...
List information about the FILEs
Sort entries alphabetically
END
$ ./ls.sh
Usage: ls [OPTION]... [FILE]...
List information about the FILEs
Sort entries alphabetically
here_param.sh
例 ヒアドキュメントの中でパラメータ展開
# !/bin/bash
script_name=ls

cat << END
Usage: $script_name [OPTION]... [FILE]...    ←ヒアドキュメント内でパラメータ展開
List information about the FILEs
Sort entries alphabetically
END
here_noparam.sh
例 ヒアドキュメントの中でパラメータ展開させない
# !/bin/bash
script_name=ls

cat << END
Usage: \$script_name [OPTION]... [FILE]...    ←$の前に\でエスケープした
List information about the FILEs
Sort entries alphabetically
END
here_noparam2.sh
例 ヒアドキュメントの中でパラメータ展開させない
# !/bin/bash
script_name=ls

cat << 'END'                               ←終了文字列をクウォート
Usage: \$script_name [OPTION]... [FILE]...    
List information about the FILEs
Sort entries alphabetically
END
 $ ./here_noparam.sh
Usage: $script_name [OPTION]... [FILE]...   ←$script_nameが展開されていない
List information about the FILEs
Sort entries alphabetically

ヒアストリング

  • ヒアドキュメントを1行にした書き方
  • <<<の右側に書いた文字列が標準入力にあるものとしてコマンドが実行される
  • <<<の右側に書いた要素に対しては、パラメータ展開・コマンド置換・算術式展開・ブレース展開・チルダ展開が行われる

ヒアストリングの構造

コマンド <<< 文字列
例 ヒアストリングによる標準入力
$ str=abc
$ tr b B <<< "$str"
aBc

パイプライン

複数のコマンドを一つに繋げる役割

  • |の直後に改行を入れると、シェルはまだパイプラインの途中と解釈する
  • 標準出力だけが次のコマンドに渡されて、標準エラー出力は渡されない
    • 標準出力と標準エラー出力を合わせて次のコマンドに渡したい場合は、2>&1でリダイレクトする

パイプラインの構造

コマンド1 | コマンド2
例 lsコマンドの標準出力をlessコマンドで表示
$ ls | less
...
ドキュメント
ビデオ
音楽
画像
公開
(END)
例 grepコマンドでpyを含む行を抽出、wcコマンドで行数をカウントする
$ ls /usr/bin | grep 'py' | wc -l
6
例 標準出力と標準エラー出力の両方がパイプラインで次のコマンドに渡される
$ ls /usr /xxx 2>&1 | less
ls: /xxx にアクセスできません: そのようなファイルやディレクトリはありません
/usr:
bin
etc
...
share
src
tmp
(END)

コマンドのグループ化

複数のコマンドを{}で囲んで1つのコマンドとしてまとめる機能

  • 1行でまとめる際は、それぞれのコマンドの後ろに;をつける
group2.sh
例 グループコマンドを使ってリダイレクト
# !/bin/bash
{
    date +%Y-%m-%d
    echo '/usr list'
    ls /usr
} > result.txt
例 グループコマンドを1行で記述
$ { date +%Y-%m-%d; echo '/usr list'; ls /usr; } > result.txt

()で囲む書き方

  • ()の内側のコマンドがサブシェル内で実行される
  • サブシェルを利用すると、現在のシェルから子プロセスとして新しくシェルのプロセスが起動され、その中で()の内側に書いたコマンドが実行される
    • ()の内側でカレントディレクトリを変更しても、親ディレクトリのシェルプロセスには影響を与えない
    • ディレクトリを移動して処理を行った後、元のディレクトリに戻りたい場合に便利
    • 親のシェルプロセスで設定していた変数はサブシェルで参照できるが、サブシェルで改めて値を代入しても、親のシェルには影響がない
subshell.sh
サブシェルを使ってリダイレクト
# !/bin/bash
(
    date +%Y-%m-%d
    echo '/usr list'
    ls /usr
) > result.txt
subshell_cd.sh
例 サブシェルの中でディレクトリを移動
# !/bin/bash
cd /usr
pwd

)
    echo ''
    echo 'sub shell'
    pwd

    echo 'cd in sub shell'
    echo /tmp
    pwd
    echo ''
)

pwd
$ ./subshell_cd.sh
/usr

sub shell
/usr
cd in sub shell
/tmp              ←サブシェルの中で移動した

/usr              ←元の/usrディレクトリに戻ってきている
subshell_param.sh
例 サブシェルの中で変数代入
# !/bin/bash
name=miyake
echo "$name"

(
    echo 'sub shell'
    echo "$name"
    name=okita
    echo "$name"
)

echo "$name"
例 サブシェルの中で代入した値が影響していない
$ ./subshell_param.sh
miyake
    sub shell
    miyake
    okita
miyake
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

scpコマンドでEC2ストレージからファイルを取り出す

備忘録的に記述

サーバー上でpwdを実行し、コピーしたいディレクトリまたはファイルを確認し、コピペ

$ pwd
> /home/ubuntu/directory/to/folder

ローカルに戻り、scpをssh接続しながら実行する

$ pwd
> ペーストしたいディレクトリの確認、今回はデスクトップにコピペ
> /Users/username/desktop

$ scp -i ~/.ssh/filename.pem -r ubuntu@x.xxx.xxx.x:/home/ubuntu/directory/to/folder /Users/username/desktop

ローカルのディレクトリと、ubuntu@以下の部分を逆にして書くと、ローカルのフォルダがサーバーにアップされてしまうため、注意!!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VMware vSphere ESXi 5.5 に CentOS 6.10 x86_64を導入する

VMware vSphere ESXi 5.5 に CentOS 6.10 x86_64を導入する

前提条件

  • ネットワークアドレスは、192.168.222.0/24
  • ゲートウェイは、192.168.222.254
  • DNSサーバは、192.168.222.1
  • VMware vSphere ESXi 5.5 の利用が可能であること

私のポリシー(とっても個人的な)

  • CentOSを導入する際は、netinstallする。
  • netinstallの際に利用するimageデータ取得先は、ftp.riken.jp様にする。理研様、バンザイ。
  • LANGは、en_US.UTF-8にする。
  • キーボードレイアウトは、jp106にする。

新規仮想マシンの作成

構成は、カスタムを選択しました。
image.png

名前と場所の入力

仮想マシン名は、適宜任意で入力する。私は、ZabbixServer4.0LTSonCentOS6.10x86_64としました。
検証環境だと、一目で目的の仮想マシンを探したくなるので、ホスト名を入れるよりも目的や機能名・OS、6432bit辺りを書いたがいいですね。
image.png

ストレージの選択

画像ではローカルストレージしかありませんので、そちらを選択しています。
image.png

仮想マシンのバージョンを選択

仮想マシンのバージョンは、ESXi5.5で選択出来る最新バージョン(ver8)を選択します。
現行最新ESXi(2020/02/19時点)だと、6.7系になりサポート範疇となります。
参考: ESXi/ESX ホストおよび互換性のある仮想マシンのハードウェア バージョンのリスト (2007240)
image.png

ゲストOSの選択

LinuxCentOS 4/5/6 (64ビット)を選択します。
image.png

CPUの選択

適宜、利用している物理環境のリソース状況を鑑みて、設定します。
image.png

メモリの設定

メモリサイズを設定します。今回は小さなLinuxサーバを立てる目的だったので、2GB程にしました。512MBでもよかったかも。
image.png

ネットワークの選択

適宜、適切なネットワークを選択します。
アダプタに関しては、VMXNET3を選択します。デフォルトで選択されているはずです。
image.png

SCSI コントローラの設定

LSI Logic SASを設定します。
image.png

ディスクの選択

新規仮想ディスクを作成を選択します。
image.png

ディスクの作成

HDDサイズを設定します。今回は、小さ目なサイズでしたのでシックで作成しています。
ケースに併せて、シンか、シックか、判断します。
image.png

詳細オプション

メッセージどおり、特段変更しません。
image.png

終了準備の完了

特にありません。これまでの設定サマリーが見れます。
下部にある完了前に仮想マシンの設定を編集にチェックを付けておくと、次の画像の画面が見れます。image.png

仮想マシンのプロパティ

netinstall用にISOをマウントしておきます。
また、FDDは利用しませんので、デバイスから削除しておきます。(※この部分は、私の好みです。)
image.png

CentOSの導入(インストール)の開始

Install or upgrade an existing systemを選択します。
時限式で、放っておくとInstall or upgrade an existing systemが選択されます。
image.png

メディアテスト

ブータブルメディア(ISOメディア)を検知したら、テストをするかどうか問われます。
チェックしません。
image.png

言語の選択

冒頭で書いたとおり、英語圏を選択しますのでEnglishを選択します。
image.png

キーボードの選択

こちらも冒頭で書いたとおり、jp106を選択します。
image.png

インストールメディアを指定

netinstallをするので、URLを指定します。
個人的にこれがなぜ好みなのかというと、設定したIPをそのまま継承してCentOSが起動するので、そのままssh接続に移行出来るからですね。
image.png

TCP/IPの設定

IPv6は無効にし、IPv4を手動設定します。
つまり、Enable IPv6 Supportをチェックアウトし、Enable IPv4 Supportをチェックインして、Manual Configurationをチェックインします。
image.png

IP設定

冒頭のとおり、ネットワーク設定をします。
image.png

URL設定

理研様のftpサーバのURLを指定します。
image.png

Imageのロード

Imageがロードされます。この際、ロードできない場合は、ネットワーク(IP,GW,DNS)を確認します。あとは、URLを再確認します。
image.png

GUIベースのインストールウィザード開始

Nextを押します。
image.png

インストール先を指定

ローカルディスクか、ネットワークにあるディスクか、選択します。
今回は、ローカルディスクですね。なので、Basic Storage Deviceを選択します。
image.png

フォーマットの確認

指定したディスクをフォーマットするかどうか、問われます。
フォーマットします。
image.png

ホスト名の設定

適宜、ホスト名を指定します。
image.png

タイムゾーンの設定

サーバは、日本になるのでTokyo, Asiaを指定します。
image.png

rootパスワードの設定

rootパスワードを設定します。
パスワード強度が低い場合やパスワードポリシーに抵触する場合は、「それはどうなの?それにするの?本当に?」と聞かれますが、適宜判断してください。
image.png

パーティションの設定

自動的にパーティションを作成してくれますが、その適応範囲を決めます。
今回は、新規ディスクですので、Use All Spaceを指定します。
パーティションのレイアウトを見たい場合は、Review and modify partitioning layoutをチェックしてから、進めます。
image.png

パーティションの書き込み確認

パーティション書き込んじゃうけどいいの?と問われているので、そのまま進めます。
つまり、Write changes to diskを押します。
image.png

デフォルトインストールの選択

私は、GUIが嫌いなのでCLIベースになるようにします。
但し、Minimalだと悲惨な目にあうので、Web Serverを選択します。
image.png

インストールの開始

インストールが開始されました。待ちましょう。
image.png

完了

完了です。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Linux]自分のIPを変数に入れる方法

概要

シェルスクリプト内などで、自分のIPを変数に入れたい場合のTips
IPv4のみ対応です。

環境

$ cat /etc/os-release 
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
$ ip a|grep -e inet |grep -v inet6
    inet 127.0.0.1/8 scope host lo
    inet 192.168.1.1/24 brd 192.168.1.255 scope global dynamic eth0
    inet 192.168.2.1/24 brd 192.168.2.255 scope global dynamic eth1
$ hostname -i #eth0のみ表示
192.168.1.1
$ hostname -I #loを除く全てを表示
192.168.1.1 192.168.2.1

コマンド

#一番シンプルな方法
$ MyIP=`hostname -i` 
$ echo $MyIP 
192.168.1.1

#IPが複数ある場合
$ MyIPeth0=`hostname -I | cut -f1 -d' '`
$ echo $MyIPeth0 
192.168.1.1
$ MyIPeth1=`hostname -I | cut -f2 -d' '`
$ echo $MyIPeth1
192.168.2.1

コマンド2

hostnameコマンドは危険だから使うなって言われた場合はこちら

$ MyIPeth0=`ip -f inet -o addr show eth0|cut -d\  -f 7 | cut -d/ -f 1`
$ echo $MyIPeth0
192.168.1.1

$ MyIPeth1=`ip -f inet -o addr show eth1|cut -d\  -f 7 | cut -d/ -f 1`
$ echo $MyIPeth1
192.168.2.1

補足

rootでhostnameコマンド実行時、'hostname -i'とするところを'hostname i'とすると
ホスト名がiになるので注意。

$ hostname
hogehoge
$ hostname i
hostname: you must be root to change the host name

$ sudo su -
#

# hostname i
# hostname
i

# MyIPeth0=`hostname I | cut -f1 -d' '` 
# hostname
I

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む