20200220のLinuxに関する記事は7件です。

Linux カーネルにおけるInstruction Pointer (=プログラムカウンタ)取得方法

Linuxカーネル・ドライバにおいて、Instruction Pointer (=プログラムカウンタ)を取得したい場合、linux/kernel.hで定義された_THIS_IP_マクロを使います

定義は下記のとおり。

#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })

LinuxカーネルにおけるInstruction Pointerの簡単な歴史

かつては各アーキテクチャで独自にcurrent_text_addr()を定義していたのですが、v4.20あたりでそれらが全て撤廃され、linux/kernel.hにある_THIS_IP_を使うように統一されたようです。

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.6-rc2&id=de0d22e50cd3d57277f073ccf65d57aa519d6888

このときのpatchを見ると、各アーキテクチャのアセンブラによるプログラムカウンタの取得方法が見れておもしろいです。
アセンブラを書いたことがある人なら当たり前の話なのですが、プロセッサのプログラムカウンタを保持するレジスタは、アーキテクチャ毎に異なります。そのため、アーキテクチャ毎に個別の実装を用意するというのは、理にかなっているような気がします。ではなぜ、マクロ1本に統一されたのか?

なんだこのマクロは?

まずマクロがまったく理解できなかったのでググってみたところ、stackoverflowで優しいお兄様方が解説してくれていました。さすがです。

https://stackoverflow.com/questions/13902431/this-ip-macro-in-linux-kernel

要約すると、

  • __label__ __here;はローカルラベル名の宣言
  • __here:はローカルラベル
  • &&__here;は上記ローカルラベルのアドレス (unsigned long型にキャストされている)

ということのようです。カッコ()の中が1行だとわかりにくいので改行を入れると、

{
    __label__ __here;
__here:
    (unsigned long)&&__here;
}

となり、1行目がローカルラベル名の宣言、2行目がラベルです。
ローカルラベルは、どうやらGCCで使えるものらしいです。
https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Local-Labels.html

ブロック内でのみ参照できるラベルということですね。

3行目の'&&'はラベルのアドレスを示すための特別な演算子です(参考:https://docs.oracle.com/cd/E19205-01/821-0386/bjabt/index.html)

このマクロを埋め込んだ位置では、ブロック内のローカルラベルとして__hereが貼られ、そのラベルのアドレスを取得することが、詰まるところ現在のプログラムの位置(=プログラムカウンタ)となるわけです。
よって、プログラムの現在の位置を、アセンブラに頼らずC言語のみで表現できるため、カーネル内でも_THIS_IP_マクロ1本に統一できたというわけです。(なお、gccを使う限りはLinuxに限らず様々な環境に移植可能なはずです。)

そしてC言語ようわからんという愚痴

問題はこの外側について。正直よくわからない。通常、ブロック文{}は式として使用できない。試しに、下記のようなコードを書くとコンパイルエラーになる。

int i = {1;};

ところが、ブロック文をカッコ()で括るとなぜかコンパイルが通る。

int i = ({1;});

このとき、カッコ()式の値はブロック{}文の最後に書いた式を評価した時の値が使用される。試しに下記のコードをgccでコンパイルして実行してみると、コンソールにはブロック文最後の式dの値(=5)が表示される。

#include <stdio.h>
int main(void)
{
        int a = ({
                int a = 2;
                int b = 3;
                int c = 4;
                int d = 5;
                a;
                b;
                c;
                d;
        });
        printf("%d\n", a); 
        return 0;
}

これは文法的にどうなんだ。。。

以上、かれこれ10年以上はC言語を扱ってるのに未だに文法的に知らない・読めないものが出てくることに対する無力感から、備忘録としてまとめました。...精進します。

でもC言語は綺麗じゃないと思う。だって'&&'、お前ずっと二項演算子として俺と付き合ってたはずだよな...?
('&&'もGCCの拡張ですかね https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Labels-as-Values.html#Labels-as-Values)

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

systemd unit ファイルのパスを得る 

$ systemctl show sshd|grep ^FragmentPath|sed -e "s/^.*=//"
/usr/lib/systemd/system/sshd.service
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

環境毎にプロンプトの色を変える

「「入れ替わってる〜〜!?!?」」

とならないために、開発環境、ステージング環境、本番環境のシェルはひと目で判別できるようにしておきたいですね。

bashzsh でプロンプト色を変更してみましょう。

bash編

RHEL系のデフォルトプロンプト

2020-02-20 at 13.03.png

[\u@\h \W]\$

白いですね。
bashでは PS1 という環境変数を変更する事でプロンプトの変更が可能です。

文字色を変える

2020-02-20 at 14.21.png

\e[36m

で色付けを開始して

\e[m

でリセットしています。

36 が水色、 33 が黄、 35 が紫に対応しています。
開発環境は水色、ステージングは黄、本番環境は紫、などの設定ができますね。

文字を太字にする

2020-02-20 at 14.35.png

\e[1m

1 だと文字を太字にする事ができます。

太字で色も変える

2020-02-20 at 14.25.png

\e[1;36m

136 の両方を設定して太字フォントかつ文字色が変更になります。

さらに色々変える

2020-02-20 at 14.43.png

1 太字で
2 薄色で
3 イタリックで
4 下線を引き
9 取り消し線を入れ
36 文字色を水色に
45 背景色を紫に
できたりします。

仕組み

ANSIエスケープシーケンスという特殊文字を使ってターミナルでの表示を制御しています。プロンプトに限らず、プログラムの出力にも使われます。
文字スタイルや、実際の表示色はクライアントのOSやターミナルによって微妙に異なってきます。

参考
ANSIエスケープシーケンス チートシート
https://qiita.com/PruneMazui/items/8a023347772620025ad6

文字スタイル指定の一覧
https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters

色指定の一覧と各ターミナルでの表示色例
https://en.wikipedia.org/wiki/ANSI_escape_code#Colors

zsh編

2020-02-20 at 15.15.png

zsh では PROMPT 環境変数で設定します。
設定方法もbashとは異なり、zsh独自のタグのようなものを使います。
%B太字
%b で太字解除
%F{cyan} で文字色を水色にし
%f で文字色リセット

参考
プロンプト拡張
http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Prompt-Expansion
表示効果
http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Visual-effects
文字装飾
http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting

マシン全体に設定

/etc/profile.d/

bashなら

export PS1="\e[1;36m\][\u@\h \W]\e[m\$ "

zshなら

export PROMPT="%B%F{cyan}[%n@%m]%f%b%~%# "

のようなシェルスクリプトを入れておくと、全てのユーザのデフォルトプロンプトにできます。

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

bcコマンドでPiを求める

恒例

https://www.gnu.org/software/bc/
bcコマンドは、任意の精度の数値を求めることができる

コマンド

echo "scale=5000; 4*a(1)" | bc -l -q
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BashでPiを求める

恒例

https://www.gnu.org/software/bc/
bcコマンドは、任意の精度の数値を求めることができる

コマンド

echo "scale=5000; 4*a(1)" | bc -l -q
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

scpコマンド メモ

メモ

scpコマンド。いつも忘れるので。

$ scp -i path_to_identity_file remote_user_name@remote_ip:path_to_file ./
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ざっくりsystemctlコマンド

systemctlとはざっくり・・・

サービスを管理するコマンド。
※nginxを起動したり、firewallの自動起動したり、一時的に止めたりとか。
主にLinuxで使われていて、CentOS7やUbuntuで使われているみたい(CentOS6以前はchkconfigコマンドらしい)。

ちなみに対象のサービス名は〜.serviceと入れても入れなくてもOK。

例 ↓はどちらも同じ
systemctl status httpd
systemctl status httpd.service

ざっくりsystemctlコマンドを即時反映、自動起動、状態確認の3つの系統に分けてメモ。

1. 即時反映系

  • 開始
$ systemctl start サービス名
  • 停止
$ systemctl stop サービス名
  • 再起動
$ systemctl restart サービス名
  • リロード
$ systemctl reload サービス名
  • 強制終了
$ systemctl kill サービス名

2. 自動起動系

  • サービスの自動起動を有効にする。 ※止まっている状態で有効にしてもサービスは止まったまま
$ systemctl enable サービス名
  • サービスの自動起動を無効にする。 ※動いている状態で無効にしてもサービスは動いたまま
$ systemctl disable サービス名

3. 状態確認系

  • サービスの状態を確認
$ systemctl status サービス名
  • サービスの詳細を確認
$ systemctl show サービス名
  • アクティブかどうかの確認
$ systemctl is-active サービス名
  • 自動起動の設定確認
$ systemctl is-enabled サービス名
  • 全サービスの一覧
$ systemctl list-units --all
  • アクティブなサービスの一覧
$ systemctl list-units --type=service
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む