hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

shredコマンドの使い方

1 shredコマンドとは?

shredコマンドは、データの復旧を困難にするためにファイルにランダムデータ(特別なパターン)を繰り返し書き込むコマンドです。通常、rmコマンドでファイルを削除すると、ファイルの内容は見かけ上は表示できなくなりますが、実際にはそのデータがディスク上に残ります。しかし、shredコマンドを使用すると、ディスク上のデータを繰り返しランダムデータで上書きすることでデータの回復ができなくなります。そのため、セキュリティを向上させることができます。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.2 (Turquoise Kodkod)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-284.11.1.el9_2.x86_64

shredコマンドの版数は以下のとおりです。

[root@server ~]# shred --version
shred (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

作者 Colin Plumb。

3 オプション一覧

shredコマンドのオプションは以下のとおりです。

[root@server ~]# shred --help
使用法: shred [OPTION]... FILE...
Overwrite the specified FILE(s) repeatedly, in order to make it harder
for even very expensive hardware probing to recover the data.

If FILE is -, shred standard output.

Mandatory arguments to long options are mandatory for short options too.
  -f, --force    書き込みができるように必要に応じてアクセス権限を変更する
  -n, --iterations=N  N 回上書きを繰り返す (デフォルト: 3 回)
      --random-source=FILE  ランダムバイトのソースを FILE にする
  -s, --size=N   N で指定したバイト数 shred を行う (接尾辞として K, M, G など
                 が使用可能)
  -u             deallocate and remove file after overwriting
      --remove[=HOW]  like -u but give control on HOW to delete;  See below
  -v, --verbose  show progress
  -x, --exact    do not round file sizes up to the next full block;
                   this is the default for non-regular files
  -z, --zero     add a final overwrite with zeros to hide shredding
      --help     この使い方を表示して終了する
      --version  バージョン情報を表示して終了する

4 オプションなしで実行する方法

オプションなしでshredコマンドを実行すると、4096バイトのランダムデータを3回ファイルに上書きします。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

オプションを付けずにshredコマンドを実行します。

[root@server ~]# shred /tmp/test.txt

shredコマンドを実行すると。ファイルは残ったままですが、ファイルサイズが4096byteになっていることがわかります。ファイルの中身を確認すると、ランダムデータで書き換えられていることがわかります。

[root@server ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 4096  9月 28 19:42 /tmp/test.txt

shredコマンド実行時の様子をstraceコマンドで確認すると、/tmp/test.txtをオープンした後、4096バイトのランダムデータを/tmp/test.txtに3回書き込んでいることが確認できます。straceコマンドの使い方は、straceコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# strace -ttT -f -e trace=openat,write shred /tmp/test.txt
-snip
19:57:58.315029 openat(AT_FDCWD, "/tmp/test.txt", O_WRONLY|O_NOCTTY) = 3 <0.000460>
19:57:58.317684 write(3, "\313r\234\10\330\2669\r]\36/\252\25\254V:w\235\3245\3712\270u\26\343\216\276Y\2623P"..., 4096) = 4096 <0.000195>
19:57:58.319256 write(3, "\214W\337H\253,J\22A\227\260\7L\317\301O`;\21Q[\30\5{\301t\32\226\270&\1\332"..., 4096) = 4096 <0.000191>
19:57:58.321464 write(3, "\35\204*O\372\315Z\366\302\26\300L \337\200\247\275\274U\222Rl\343A\320}!\217\301\361S\272"..., 4096) = 4096 <0.000867>
19:57:58.325854 +++ exited with 0 +++

5 ランダムデータのサイズを指定する方法(-s)

-sオプションは、ファイルに書き込むランダムデータのサイズを指定するオプションです。デフォルトでは、ランダムデータのサイズは4096バイトです。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

8192バイトのランダムデータをファイルに書き込んでみました。この操作をstraceコマンドで確認すると、writeシステムコールの戻り値が8192であることから、8192バイトのランダムデータがファイルに書き込まれていることが分かります。

[root@server ~]# strace -ttT -f -e trace=openat,write shred -s 8192 /tmp/test.txt
-snip-
20:07:06.514961 openat(AT_FDCWD, "/tmp/test.txt", O_WRONLY|O_NOCTTY) = 3 <0.000048>
20:07:06.515322 write(3, "\36\10\\2aA", 6) = 6 <0.000096>
20:07:06.517121 write(3, "W\241\346\225\362\322", 6) = 6 <0.000042>
20:07:06.518447 write(3, ".\3414\201\301h", 6) = 6 <0.000036>
20:07:06.521435 write(3, "n\317\371\303}\217\22\277.z\33\354\244K\373\234\325T=\340\234n\262W\263t\274\301\4?\5\35"..., 8192) = 8192 <0.000078>
20:07:06.522752 write(3, "N%\265\3331:Mkj\345f\34V/\20\20\325\205\340\322U\376o\321\365\270Nt\250\3778\f"..., 8192) = 8192 <0.000051>
20:07:06.523538 write(3, "\301\206\240\313f\254\226aV\216\267h\307/E\215\22\375`.o\363urf\350\360\t\212\216\354S"..., 8192) = 8192 <0.000041>
20:07:06.524505 +++ exited with 0 +++

6 進捗状況を表示する方法(-v)

-vオプションは、shredコマンドの進捗状況を表示するオプションです。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

ファイルにランダムデータを3回書き込んでいることが分かります。

[root@server ~]# shred -v /tmp/test.txt
shred: /tmp/test.txt: 経過 1/3 (random)...
shred: /tmp/test.txt: 経過 2/3 (random)...
shred: /tmp/test.txt: 経過 3/3 (random)...

7 ファイルを削除する方法(-u)

-uオプションは、ファイルにランダムにデータを書き込んだ後、ファイルを削除するオプションです。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

-uオプションを指定してshredコマンドを実行します。

[root@server ~]# shred -v -u /tmp/test.txt
shred: /tmp/test.txt: 経過 1/3 (random)...
shred: /tmp/test.txt: 経過 2/3 (random)...
shred: /tmp/test.txt: 経過 3/3 (random)...
shred: /tmp/test.txt: 削除しています
shred: /tmp/test.txt: /tmp/00000000 に名前が変更されました
shred: /tmp/00000000: /tmp/0000000 に名前が変更されました
shred: /tmp/0000000: /tmp/000000 に名前が変更されました
shred: /tmp/000000: /tmp/00000 に名前が変更されました
shred: /tmp/00000: /tmp/0000 に名前が変更されました
shred: /tmp/0000: /tmp/000 に名前が変更されました
shred: /tmp/000: /tmp/00 に名前が変更されました
shred: /tmp/00: /tmp/0 に名前が変更されました
shred: /tmp/test.txt: 削除しました

テスト用に作成したファイルを確認すると、ファイルが削除されたことがわかります。

[root@server ~]# ls -l /tmp/test.txt
ls: '/tmp/test.txt' にアクセスできません: そのようなファイルやディレクトリはありません

8 ランダムデータの書き込み回数を指定する方法(-n)

-nオプションは、ランダムデータをファイルに書き込む回数を指定するオプションです。オプションを指定しないとランダムデータを3回ファイルに書き込みます。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

straceコマンドを使用して、ランダムデータをファイルに書き込む回数を確認してみました。テスト用ファイルをオープンした後、テスト用ファイルと同じサイズ(6バイト)のランダムデータを5回書き込んだ後、4096バイトのランダムデータを5回書き込んでいることが分かります。

[root@server ~]# strace -ttT -f -e trace=openat,write shred -n 5 /tmp/test.txt
-snip-
10:30:12.169526 openat(AT_FDCWD, "/tmp/test.txt", O_WRONLY|O_NOCTTY) = 3 <0.000160>
10:30:12.170725 write(3, "\317;\236\216\"-", 6) = 6 <0.000230>
10:30:12.172615 write(3, "\0\0\0\0\0\0", 6) = 6 <0.000339>
10:30:12.174522 write(3, "~\263J\277\214\360", 6) = 6 <0.000169>
10:30:12.176208 write(3, "\377\377\377\377\377\377", 6) = 6 <0.000202>
10:30:12.178187 write(3, "\265\300-\330kB", 6) = 6 <0.000238>
10:30:12.180110 write(3, "0\274\344\337\225*\221\225\10\320\360\200\273\10@\341tc\251\"`\26\30\326\3z\233elTB1"..., 4096) = 4096 <0.000167>
10:30:12.182488 write(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096 <0.000234>
10:30:12.184468 write(3, "\226\v\320\26\20\314\232B\371\345\233r:\217~D\37\206\30r\225h\334\241\351\340j\210\252\36\32\310"..., 4096) = 4096 <0.000183>
10:30:12.187589 write(3, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"..., 4096) = 4096 <0.000182>
10:30:12.189164 write(3, ".)\260^ U\30[+h!\263\206\3655\377\304\222\335j\4+@)\16Ij\273\210\n8\263"..., 4096) = 4096 <0.000357>
10:30:12.192114 +++ exited with 0 +++

9 ランダムデータで上書きをしたあと0で上書きする方法(-z)

-zオプションは、ランダムデータをファイルに書き込んだ後、最後にゼロをファイルに書き込むオプションです。

テスト用ファイルを作成します。

[root@server ~]# echo "12345" > /tmp/test.txt

作成したテスト用ファイルを確認します。

[root@server ~]# cat /tmp/test.txt
12345

-zオプションを指定してshredコマンドを実行します。このとき、-vオプションも使用してみます。ランダムデータをファイルに3回書き込んだ後、最後にゼロを書き込んでいることが分かります。

[root@server ~]# shred -v -z /tmp/test.txt
shred: /tmp/test.txt: 経過 1/4 (random)...
shred: /tmp/test.txt: 経過 2/4 (random)...
shred: /tmp/test.txt: 経過 3/4 (random)...
shred: /tmp/test.txt: 経過 4/4 (000000)...

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

BeautifulSoupライブラリの使い方

1 Beautiful Soupとは?

Beautiful Soupは、HTMLファイルを解析および処理するためのライブラリです。このライブラリを使って、まずはローカルマシン上で基本的な使い方を練習し、その後、以下のウェブサイトから情報を抽出するテストプログラムを作成してみます。なお作成するテストプログラムですが、見やすくするため、例外処理等は実装していません。

  1. Anacondaのインストールシェルプログラムの一覧
  2. 歴代首相の名前の一覧
  3. 夏の高校野球全国高等学校野球選手権大会)歴代優勝校の一覧

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.2 (Turquoise Kodkod)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-284.11.1.el9_2.x86_64

pythonの版数は以下のとおりです。

[root@server ~]# python -V
Python 3.9.16

3 BeautifulSoupのインストール方法

pipコマンドでbeautifulsoup4ライブラリをインストールします。

[root@server ~]# pip install beautifulsoup4

beautifulsoup4の版数を確認します。

[root@server ~]# pip show beautifulsoup4
Name: beautifulsoup4
Version: 4.12.2
Summary: Screen-scraping library
Home-page:
Author:
Author-email: Leonard Richardson <leonardr@segfault.org>
License:
Location: /usr/local/lib/python3.9/site-packages
Requires: soupsieve
Required-by:

4 事前準備

BeautifulSoupの使い方に慣れるため、ローカルマシンでHTMLファイルを作成して、それを読み込むテストプログラムを作成してみます。

[root@server ~]# vi index.html
[root@server ~]# cat index.html
<!DOCTYPE html>
<html>
<head>
    <title>サンプルHTML</title>
</head>
<body>
    <div id="chapter1">
        <h1>これは見出し1です</h1>
        <h2>これは見出し2です</h2>
        <h3>これは見出し3です</h3>
        <p>これは段落です。段落はテキストを表示するために使用されます。</p>
        <ul>
            <li>リスト項目1</li>
            <li>リスト項目2</li>
        </ul>
        <ol>
            <li>項目1</li>
            <li>項目2</li>
        </ol>
        <dl>
            <dt>用語1</dt>
            <dd>用語1の説明</dd>
        </dl>
    </div>
    <div id="chapter2">
      <a href="https://www.example.com" target="_blank">www.example.comへのリンクです。aタグにtarget属性が設定されています。クリックすると新しいタブが開きます</a>
      <br>
      <a href="https://www.kantei.go.jp" title="首相官邸">首相官邸へのリンクです。aタグにtitle属性が設定されています。マウスカーソルをリンクに合わせると、ツールチップが表示されます。</a>
    </div>
</body>
</html>

作成したHTMLファイルを/var/www/htmlにコピーします。

[root@server ~]# cp index.html /var/www/html/

httpdサービスを起動します。

[root@server ~]# systemctl start httpd

httpdプロセスがListenしているTCPの80番ポートを解放します。なお、firewall-cmdコマンドの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# firewall-cmd --add-port=80/tcp
success

解放しているポート番号を確認します。80番ポートが解放されていることがわかります。

[root@server ~]# firewall-cmd --list-ports
80/tcp

httpdに対してcurlコマンドを実行すると、HTMLファイルを読み出していることがわかります。なお、curlコマンドの使い方は、curlコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# curl http://192.168.1.200
<!DOCTYPE html>
<html>
<head>
    <title>サンプルHTML</title>
-snip-

5 メソッドの使い方

Beautiful Soupオブジェクトは、HTMLファイルから特定の条件を満たすタグを取得するのに、findやfind_allというメソッドを利用することができます。

メソッド 概要
find 引数に指定した条件に合致する最初の要素を取得します。一致する要素が複数ある場合でも、最初に見つかった要素だけが返されます
find_all 指定した条件に合致するすべての要素を取得します。複数の要素が一致する場合、それらの要素がリストとして返されます。

5.1 findメソッドの使い方(タグ名を指定した場合)

指定したタグのタグオブジェクトおよびテキストを返すテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("http://192.168.1.200/index.html")
soup = BeautifulSoup(response.content, "html.parser")
print(soup.find("title"))
print(soup.find("h1"))
print(soup.find("li"))

print(soup.find("title").text)
print(soup.find("h1").text)
print(soup.find("li").text)

テストプログラムを実行すると、1行目から3行目までがHTMLのタグを表しており、4行目から6行目までがそれらのタグに含まれるテキストを表示していることが確認できます。

[root@server ~]# ./test.py
<title>サンプルHTML</title>
<h1>これは見出し1です</h1>
<li>リスト項目1</li>
サンプルHTML
これは見出し1です
リスト項目1

5.2 findメソッドの使い方(属性を指定した場合)

属性がchapter2のコンテンツだけを表示するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("http://192.168.1.200/index.html")
soup = BeautifulSoup(response.content, "html.parser")
content = soup.find(id="chapter2")
print(content)

テストプログラムを実行すると、chapter2という属性を持つコンテンツだけが表示されます。chapter1属性を持つコンテンツは表示されません。

[root@server ~]# ./test.py
<div id="chapter2">
<a href="https://www.example.com" target="_blank">www.example.comへのリンクです。<a>タグにtarget属性が設定されています。クリックすると新しいタブが開きます</a>
<br/>
<a href="https://www.kantei.go.jp" title="首相官邸">首相官邸へのリンクです。<a>タグにtitle属性が設定されています。マウスカーソルをリンクに合わせると、ツールチップが表示されます。</a>
</div>

5.3 find_allメソッドの使い方

liタグのテキストを全て表示するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("http://192.168.1.200/index.html")
soup = BeautifulSoup(response.content, "html.parser")
for element in soup.find_all("li"):
    print(element.text)

テストプログラムを実行すると、全てのliタグのテキストが表示されていることがわかります。

[root@server ~]# ./test.py
リスト項目1
リスト項目2
項目1
項目2

6 タグから情報を取得する方法

タグから情報を取得するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("http://192.168.1.200/index.html")
soup = BeautifulSoup(response.content, "html.parser")
content = soup.find("a")
print(content)
print(content.name)
print(content.text)
print(content.attrs)
print(content.get("href"))

テストプログラムを実行します。1行目はタグ、2行目はタグ名、3行目はタグのテキスト、4行目は属性、5行目は指定した属性の値が表示されていることがわかります。

[root@server ~]# ./test.py
<a href="https://www.example.com" target="_blank">www.example.comへのリンクです。aタグにtarget属性が設定されています。クリックすると新しいタブが開きます</a>
a
www.example.comへのリンクです。aタグにtarget属性が設定されています。クリックすると新しいタブが開きます
{'href': 'https://www.example.com', 'target': '_blank'}
https://www.example.com

7 実践練習1(Anacondのソースコードダウンロード)

Anacondaのウェブページで提供されている以下のソースコード(赤枠内)から、拡張子が.shのファイルをダウンロードするテストプログラムを作成します。
https://repo.anaconda.com/archive/

AnacondaのwebページからHTMLファイルを取得するテストプログラムを作成します。このテストプログラムを実行することで、Anacondaのwebページがどのような構成になっているかを確認することができます。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("https://repo.anaconda.com/archive/")
soup = BeautifulSoup(response.content, "html.parser")
print(soup)

テストプログラムを実行します。このときlessコマンドを併用しながら、AnacondaのwebサイトのHTMLファイルの構成を確認します。拡張子が.shのファイルは、タグ内に存在することがわかります。

<tr>
<td><a href="Anaconda3-2023.07-2-Linux-x86_64.sh">Anaconda3-2023.07-2-Linux-x86_64.sh</a></td>
<td class="s">1015.6M</td>
<td>2023-08-04 10:56:05</td>
<td>589fb34fe73bc303379abbceba50f3131254e85ce4e7cd819ba4276ba29cad16</td>
</tr>
<tr>

テストプログラムを改良して、AnacondaのウェブサイトのHTMLからタグだけを表示するようにします。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("https://repo.anaconda.com/archive/")
soup = BeautifulSoup(response.content, "html.parser")
a_tags = soup.find_all("a")
for a_tag in a_tags:
    print(a_tag)

テストプログラムを実行すると、タグの内容が表示されていることがわかります。

[root@server ~]# ./test.py
<a href=".winzip/">.winzip/</a>
<a href="Anaconda3-2023.07-2-Windows-x86_64.exe">Anaconda3-2023.07-2-Windows-x86_64.exe</a>
<a href="Anaconda3-2023.07-2-MacOSX-x86_64.sh">Anaconda3-2023.07-2-MacOSX-x86_64.sh</a>
<a href="Anaconda3-2023.07-2-MacOSX-x86_64.pkg">Anaconda3-2023.07-2-MacOSX-x86_64.pkg</a>
-snip-

テストプログラムを改良して、タグからhref属性で参照されるリンクだけを表示し、かつ拡張子が "sh" のリンクだけを抽出するようにします。

[root@server ~]# cat test.py
#!/usr/bin/python3

import requests
from bs4 import BeautifulSoup

response = requests.get("https://repo.anaconda.com/archive/")
soup = BeautifulSoup(response.content, "html.parser")

file_links = []
a_tags = soup.find_all("a")
for a_tag in a_tags:
    url = a_tag.get("href")
    if url.endswith(".sh"):
        file_links.append(url)
        print(url)

テストプログラムを実行すると、表示される内容は拡張子が.shであるリンクだけであることがわかります。

[root@server ~]# ./test.py
Anaconda3-2023.07-2-MacOSX-x86_64.sh
Anaconda3-2023.07-2-MacOSX-arm64.sh
Anaconda3-2023.07-2-Linux-x86_64.sh
-snip-

テストプログラムを改良して、ダウンロードするファイルを保存するディレクトリを作成します。そして、作成したディレクトリに拡張子が.shのファイルを2つだけ保存します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os
import requests
from bs4 import BeautifulSoup

# AnacondaのサイトからHTMLファイルを取得する。
archive_url = "https://repo.anaconda.com/archive/"
response = requests.get(archive_url)
soup = BeautifulSoup(response.content, "html.parser")

# 拡張子がshのファイルだけリンクをfile_linksに格納する。
file_links = []
a_tags = soup.find_all("a")
for a_tag in a_tags:
    url = a_tag.get("href")
    if url.endswith(".sh"):
        file_links.append(url)

# ダウンロードしたファイルを格納するディレクトリを作成する
download_dir = "./download/"
if not os.path.exists(download_dir):
    os.mkdir(download_dir)

# Anacondaのサイトから拡張子がshのファイルを2つダウンロードする。
for i, file_link in enumerate(file_links[:2], 1):
    file_name = file_link.split("/")[-1]
    file_url = archive_url + file_link
    file_path = download_dir + file_name
    print(f"ダウンロード {i}: {file_path} : {file_url}")
    response = requests.get(file_url)
    with open(file_path, "wb") as file:
        file.write(response.content)

テストプログラムを実行します。拡張子がshのファイルを2つだけダウンロードします。

[root@server ~]# ./test.py
ダウンロード 1: ./download/Anaconda3-2023.07-2-MacOSX-x86_64.sh : https://repo.anaconda.com/archive/Anaconda3-2023.07-2-MacOSX-x86_64.sh
ダウンロード 2: ./download/Anaconda3-2023.07-2-MacOSX-arm64.sh : https://repo.anaconda.com/archive/Anaconda3-2023.07-2-MacOSX-arm64.sh

ダウンロードしたファイルを確認します。

[root@server ~]# ls -l download/*
-rw-r--r--. 1 root root 676990792  9月 16 23:37 download/Anaconda3-2023.07-2-MacOSX-arm64.sh
-rw-r--r--. 1 root root 641855956  9月 16 23:36 download/Anaconda3-2023.07-2-MacOSX-x86_64.sh

8 実践練習2(歴代首相の一覧)

以下のページに記載されている歴代首相の名前と年を一覧として表示してみます。
https://www.kantei.go.jp/jp/rekidainaikaku/index.html

歴代首相が記載されていwebページからHTMLファイルを取得するテストプログラムを作成します。このテストプログラムを実行することで、webページがどのような構成になっているかを確認することができます。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os
import requests
from bs4 import BeautifulSoup

url = "https://www.kantei.go.jp/jp/rekidainaikaku/index.html"
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
print(soup)

テストプログラムを実行すると、歴代首相の代と名前が以下の構成で繰り返し出現していることがわかります。

<div class="his-profile">
<h3 class="his-generation">第101代</h3>
<p class="his-name"><a href="/jp/rekidainaikaku/101.html">岸田 文雄</a></p>
</div>

テストプログラムを修正します。h3タグ、pタグのclass属性を指定して歴代首相の名前と代を抽出します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os
import requests
from bs4 import BeautifulSoup

url = "https://www.kantei.go.jp/jp/rekidainaikaku/index.html"
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")

prime_minister_elements = soup.find_all("div", class_="his-profile")

for i, prime_minister_element in enumerate(prime_minister_elements, start=1):
    generation_element = prime_minister_element.find("h3", class_="his-generation")
    name_element = prime_minister_element.find("p", class_="his-name")

    if generation_element and name_element:
        generation = generation_element.text.strip()
        name = name_element.text.strip()
        print(f"{generation}:{name}")

テストプログラムを実行します。

[root@server ~]# ./test.py
第101代:岸田 文雄
第100代:岸田 文雄
第99代:菅 義偉
第98代:安倍 晋三
第97代:安倍 晋三
第96代:安倍 晋三
第95代:野田 佳彦
第94代:菅 直人
第93代:鳩山 由紀夫
第92代:麻生 太郎
-snip-

9 実践練習3(高校野球歴代優勝校の一覧)

以下のページに記載されている歴代の優勝校と優勝年を一覧として表示してみます。
https://www.hanshin.co.jp/koshien/highschool/past/champion/summer.html

8章のようなテストプログラムを作成して実行すると、歴代の優勝高校と優勝年が以下の構成で繰り返し出現していることがわかります。

<tr>
<td>1</td>
<td>大正4年(1915)</td>
<td>京都二中(京都)</td>
</tr>

HTMLファイルの構成を考慮してテストプログラムを作成します。優勝年と優勝校はタグに含まれていることがわかります。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os
import requests
from bs4 import BeautifulSoup

url = "https://www.hanshin.co.jp/koshien/highschool/past/champion/summer.html"

response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")

champion_list = soup.find_all("tr")
current_year_group = None

for row in reversed(champion_list):
    columns = row.find_all("td")
    if len(columns) == 3:
        year_text = columns[1].text.strip()
        school_text = columns[2].text.strip()

        year_start = year_text.find("(")
        year_end = year_text.find(")")
        if year_start != -1 and year_end != -1:
            year = year_text[year_start + 1:year_end]
            school = school_text.split("(")[0]
            print(f"{year}: {school}")

テストプログラムを実行すると、歴代の優勝高校と優勝年が表示されていることがわかります。

[root@server ~]# ./test.py
2022: 仙台育英
2021: 智辯和歌山
2020: 新型コロナウイルス感染症の流行により中止
2019: 履正社
2018: 大阪桐蔭
-snip-

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

記事作成に参考にした書籍です。

  • Pythonコードレシピ集

サンプルコードが短く、分かりやすいです。
Pythonコードレシピ集(単行本)


Pythonコードレシピ集(Kindle版)

  • スッキリわかるPython入門 (スッキリわかる入門シリーズ)

簡潔な説明で分かりやすいです。

スッキリわかるPython入門 (スッキリわかる入門シリーズ)(単行本)

スッキリわかるPython入門 (スッキリわかるシリーズ)(Kindle版)

osモジュール,os.pathモジュールの使い方

1 はじめに

ファイルとディレクトリ操作の機能を提供するosモジュールとos.pathモジュールを使ったプログラムを作成して動作確認をしてみます。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.2 (Turquoise Kodkod)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-284.11.1.el9_2.x86_64

pythonの版数は以下のとおりです。

[root@server ~]# python -V
Python 3.9.16

3 osモジュールの使い方

osモジュールは、OSに依存する機能を提供するモジュールです。 ファイルやディレクトリの操作を行うことができます。osモジュールが提供する関数(一部)を以下に示します。

関数 概要
getcwd カレントディレクトリ取得する
chdir カレントディレクトリを変更する
listdir ディレクトリの中身をリストで取得する
walk ディレクトリツリーを取得する
mkdir ディレクトリを作成する
rmdir ディレクトリを削除する
makedirs ディレクトリを再帰的に作成する
rename ファイルやディレクトリの名前を変更する
chmod ファイルのパミッションを変更する

3.1 カレントディレクトリを取得、変更する方法(getcwd,chdir)

カレントディレクトリのパスを取得したあと、テストプログラムの引数に指定したパスにカレントディレクトリを変更するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

dir = os.getcwd()
print(dir)

dir = input("ディレクトリ名を入力してください: ")
os.chdir(dir)
dir = os.getcwd()
print(dir)

テストプログラムを実行すると、カレントディレクトリを表示したあと、指定したディレクトリにカレントディレクトリを変更したことがわかります。

[root@server ~]# ./test.py
/root
ディレクトリ名を入力してください: /var/log
/var/log

3.2 ファイル一覧を取得する方法(listdir)

引数に指定したディレクトリのファイル一覧を表示するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

dir = input("ディレクトリ名を入力してください: ")
files = os.listdir(dir)
print(files)

テストプログラムを実すると、/boot直下のファイルがリスト形式で表示されることがわかります。

[root@server ~]# ./test.py
ディレクトリ名を入力してください: /boot
['efi', 'grub2', 'loader', 'vmlinuz-5.14.0-284.11.1.el9_2.x86_64', 'System.map-5.14.0-284.11.1.el9_2.x86_64', 'config-5.14.0-284.11.1.el9_2.x86_64', '.vmlinuz-5.14.0-284.11.1.el9_2.x86_64.hmac', 'symvers-5.14.0-284.11.1.el9_2.x86_64.gz', 'initramfs-5.14.0-284.11.1.el9_2.x86_64.img', 'vmlinuz-0-rescue-ae58d7b0f9374e8d801cd68b6296a573', 'initramfs-0-rescue-ae58d7b0f9374e8d801cd68b6296a573.img', 'initramfs-5.14.0-284.11.1.el9_2.x86_64kdump.img']

リスト形式ではなく、lsコマンドを実行した時のように表示するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

dir = input("ディレクトリ名を入力してください: ")
files = os.listdir(dir)
for file in files:
    print(file)

テストプログラムを実行すると、/boot直下のファイルがlsコマンドを実行したときのように表示されることがわかります。

[root@server ~]# ./test.py
ディレクトリ名を入力してください: /boot
efi
grub2
loader
-snip-

3.3 ディレクトリのファイルを再帰的に表示する方法(walk)

テスト用のディレクトリ、ファイルを作成します。

[root@server ~]# mkdir -p dir1/dir2
[root@server ~]# touch dir1/file1.txt
[root@server ~]# touch dir1/dir2/file2.txt

テストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

for root, dirs, files in os.walk("dir1"):
    for dir in dirs:
        print("Directory:", os.path.join(root, dir))
    for file in files:
        print("File:", os.path.join(root, file))

テストプログラムを実行します。

[root@server ~]# ./test.py
Directory: dir1/dir2
File: dir1/file1.txt
File: dir1/dir2/file2.txt

3.4 ディレクトリを作成・削除する方法(mkdir,rmdir)

引数に指定したディレクトリの作成、削除をするテストプログラムを作成します。指定したディレクトリが存在しなければ作成しますが、存在すれば作成しません。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

new_dir = input("ディレクトリ名を入力してください: ")
if os.path.exists(new_dir):
  print(f"'{new_dir}' が存在するので、作成できません")
else:
  print(f"'{new_dir}' を作成します ")
  os.mkdir(new_dir)

テストプログラムを実行します。初回実行時は/tmp/testが存在しないので、ディレクトリを作成できます。

[root@server ~]# ./test.py
ディレクトリ名を入力してください: /tmp/test
'/tmp/test' を作成します

2回目は/tmp/testが存在するので、ディレクトリが作成できないことがわかります。

[root@server ~]# ./test.py
ディレクトリ名を入力してください: /tmp/test
'/tmp/test' が存在するので、作成できません

3.5 ディレクトリを再帰的に作成する方法(makedirs)

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

new_dir = input("ディレクトリ名を入力してください: ")
if not os.path.exists(new_dir):
    os.makedirs(new_dir)
    print(f"'{new_dir}' を作成します ")
else:
    print(f"'{new_dir}' が存在するので、作成できません")
[root@server ~]# ./test.py
ディレクトリ名を入力してください: /tmp/dir1/dir2/dir3
'/tmp/dir1/dir2/dir3' を作成します

作成したディレクトリを確認します。/tmp直下にdir1/dir2/dir3が作成されていることがわかります。

[root@server ~]# ls -ld /tmp/dir1/dir2/dir3
drwxr-xr-x. 2 root root 6  9月  9 22:25 /tmp/dir1/dir2/dir3

3.6 ファイルやディレクトリの名前を変更する方法(rename)

renameはファイルやディレクトリの名前を変更する関数です。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

old_filename = input("ファイル名を入力してください: ")

with open(old_filename, "w") as file:
    file.write("0123456789\n")

with open(old_filename, "r") as file:
    old_data = file.read()
    print(f"作成したファイルの内容:{old_data}")

new_filename = input("変更後のファイル名を入力してください: ")
os.rename(old_filename, new_filename)

テストプログラムを実行します。

[root@server ~]# ./test.py
ファイル名を入力してください: old.txt
作成したファイルの内容:0123456789

変更後のファイル名を入力してください: new.txt
[root@server ~]# cat new.txt
0123456789

4 os.pathモジュールの使い方

os.path モジュールはファイルパスやディレクトリパスを操作するための関数を提供するモジュールです。

関数 概要
exists ファイルやディレクトリが存在するかどうかを確認します
dirname ファイルパスからディレクトリ名を取得する
basename ファイルパスからファイル名を取得する
splitext ファイル名から拡張子を取り出します
relpath 相対パスを取得します
abspath 相対パス絶対パスに変換します
join パスを結合します
split パス名からディレクトリ名とファイル名を取り出します

4.1 ファイルやディレクトリの存在確認する方法(exitst)

引数に指定したディレクトリ名やファイル名が存在するかどうかを確認するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

path = input("パス名を入力してください: ")

if os.path.exists(path):
    print(f"{path} は存在します.")
else:
    print(f"{path} は存在しません.")

ディレクトリの存在確認をしてみます。

[root@server ~]# ./test.py
パス名を入力してください: /etc
/etc は存在します.

ファイルの存在確認をしてみます。

[root@server ~]# ./test.py
パス名を入力してください: /etc/chrony.conf
/etc/chrony.conf は存在します.

4.2 パス名からディレクトリ名とファイル名を取り出す方法(dirname,basename)

dirnameはパス名からディレクトリ名を取り出す関数、basenameはパス名からファイル名を取り出す関数です。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

path = input("パス名を入力してください: ")

dir = os.path.dirname(path)
file = os.path.basename(path)

print(f"dir={dir}, file={file}")

テストプログラムを実行します。パス名に/etc/chrony.confを指定すると、ディレクトリ名が/efc、ファイル名がchrony.confであることがわかります。

[root@server ~]# ./test.py
パス名を入力してください: /etc/chrony.conf
dir=/etc, file=chrony.conf

4.3 拡張子を取り出す方法(splitext)

拡張子を取りだすテストプログラムを実行します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

path = input("パス名を入力してください: ")
file_name, file_extension = os.path.splitext(path)

print(f"ファイル名:{file_name}, 拡張子:{file_extension}")

chrony.confファイルの拡張子を確認します。拡張子がconfであることがわかります。

[root@server ~]# ./test.py
パス名を入力してください: /etc/chrony.conf
ファイル名:/etc/chrony, 拡張子:.conf

カーネルモジュールの拡張子を確認します。拡張子がxzであることがわかります

[root@server ~]# ./test.py
パス名を入力してください: /usr/lib/modules/5.14.0-284.11.1.el9_2.x86_64/kernel/net/ipv4/ip_gre.ko.xz
ファイル名:/usr/lib/modules/5.14.0-284.11.1.el9_2.x86_64/kernel/net/ipv4/ip_gre.ko, 拡張子:.xz

4.4 パス名からディレクトリ名とファイル名を求めるする方法(split)

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

dir, file = os.path.split("/var/log/messages")
print(f"dir={dir}, file={file}")

テストプログラムを実行すると、/var/log/messagesからディレクトリ名(/var/log)とファイ名(messages)がそれぞれ取得できていることがわかります。

[root@server ~]# ./test.py
dir=/var/log, file=messages

4.5 パスを結合する方法(join)

joinメソッドは、引数で指定したパスを結合するメソッドです。この方法を使うと、ファイルシステムの階層構造に関する違いを気にせずにパスを作成できます。また、プラットフォーム間での互換性が確保されます。

joinメソッドの書式は以下のとおりです。

os.path.join("path1", "path2", "path3", ...)

"/"、"var"、"log”を結合するテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

log = os.path.join("/", "var", "log")
print(log)

テストプログラムを実行すると、引数に指定したパスが結合されて/var/logと表示されることがわかります。

[root@server ~]# ./test.py
/var/log

4.6 相対パス絶対パスを求める方法(relpath,abspath)

テスト用のディレクトリを作成します。

[root@server ~]# mkdir dir1/dir2/dir3

テスト用に作成したディレクトリの相対パス絶対パスを求めるテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3
import os

relative_path = "./dir1/dir2/dir3"
absolute_path = os.path.abspath(relative_path)

print(f"相対パス:{relative_path}, 絶対パス:{absolute_path}")

テストプログラムを実行すると、テスト用に作成したディレクトリの相対パス絶対パスが表示されることがわかります。

[root@server ~]# ./test.py
相対パス:./dir1/dir2/dir3, 絶対パス:/root/dir1/dir2/dir3

5 ファイルオブジェクトの使い方

5.1 read系メソッド

5.1.1 ファイル全体を読み込む方法(read)

readメソッドは、ファイル全体または指定したバイト数を読み込むためのメソッドです。readメソッドを引数なしで呼び出すとファイル全体を読み込みます。readメソッドに引数を指定すると、指定したバイト数を読み出します。

ファイル全体を読み出すテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

with open("/tmp/test.txt", "r") as file:
    file_contents = file.read()
    print(file_contents)

テストプログラムが読み込むファイルを作成します。

[root@server ~]# vi /tmp/test.txt
[root@server ~]# cat /tmp/test.txt
111 111
222 222 222

テストプログラムを実行すると、ファイル全体読み込んでいることがわかります。

[root@server ~]# ./test.py
111 111
222 222 222

[root@server ~]#

次は、readメソッドの引数に2を指定して、ファイルから2バイトを読み込むテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

with open("/tmp/test.txt", "r") as file:
    file_contents = file.read(2)
    print(file_contents)

テストプログラムを実行すると、ファイルから2バイト読み込んでいることがわかります。

[root@server ~]# ./test.py
11
5.1.2 ファイルから1行ずつ読み込む方法(readline)

readlineメソッドはファイルから1行ずつ読み込むためのメソッドです。ファイル終端に到達すると空文字を返します。

[root@server ~]# cat test.py
#!/usr/bin/python3

with open("/tmp/test.txt", "r") as f:
    line = f.readline()
    while line:
        print(line, end="")
        line = f.readline()

テストプログラムを実行します。

[root@server ~]# ./test.py
111 111
222 222 222
5.1.3 1行をリスト形式で取得する方法(readlines)

readlinesメソッドはファイルから全ての行を一度に読み取り、各行を文字列のリストとして返すためのメソッドです。

[root@server ~]# cat test.py
#!/usr/bin/python3

with open("/tmp/test.txt", "r") as f:
    lines = f.readlines()
    print(lines)

テストプログラムを実行します。

[root@server ~]# ./test.py
['111 111\n', '222 222 222\n']

5.2 write系メソッド

5.2.1 ファイルにデータを書き込む方法(write)

writeメソッドは、ファイルにデータを書き込むためのメソッドです。

引数に指定したファイルにデータを書き込むテストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

file = input("ファイル名を入力してください: ")
with open(file, "w") as file:
    file.write("0123456789\n")

テストプログラムを実行して、/tmp/sample.txtを作成してみます。

[root@server ~]# ./test.py
ファイル名を入力してください: /tmp/sample.txt

作成したファイルの中身を確認します。

[root@server ~]# cat /tmp/sample.txt
0123456789

5.3 seekメソッド

seekメソッドは、ファイルオブジェクト内のカーソル位置を移動するためのメソッドです。書式は以下の通りす。

seek(offset, whence)
引数 意味
offset 移動するバイト数を指定します。正の整数値はファイルの先頭から移動し、負の整数値はファイルの末尾から逆方向に移動します
whence デフォルトは0でファイルの先頭からの相対位置を示します。1は現在の位置からの相対位置を示し、2はファイルの末尾からの相対位置を示します

テストプログラムを作成します。

[root@server ~]# cat test.py
#!/usr/bin/python3

seek = int(input("移動するバイト数を入力してください: "))
byte = int(input("読み込むバイト数を入力してください: "))

with open("/tmp/test.txt", "r") as f:
    f.seek(seek)
    data = f.read(byte)
    print(data)

テストプログラムが読み込むファイルを作成します。

[root@server ~]# vi /tmp/test.txt
[root@server ~]# cat /tmp/test.txt
0123456789

ファイル先頭から2バイト読み出してみます。

[root@server ~]# ./test.py
移動するバイト数を入力してください: 0
読み込むバイト数を入力してください: 2
01

ファイル先頭から1バイト移動してから5バイト読み出してみます。

[root@server ~]# ./test.py
移動するバイト数を入力してください: 1
読み込むバイト数を入力してください: 5
12345

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

記事作成に参考にした書籍です。

サンプルコードが短く、分かりやすいです。

Pythonコードレシピ集(Kindle版)

Pythonコードレシピ集(単行本)

  • スッキリわかるPython入門 (スッキリわかる入門シリーズ)

簡潔な説明で分かりやすいです。

スッキリわかるPython入門 (スッキリわかる入門シリーズ)(単行本)

スッキリわかるPython入門 (スッキリわかるシリーズ)(Kindle版)

ipsetコマンドの使い方

1 ipsetコマンドとは?

ipsetコマンドは、IPアドレスやポート番号などをまとめて管理するためのツールです。ipsetを使用することで、これらの要素をセットとしてまとめることができます。作成したセットは、iptablesのsetモジュールで使用することが可能です。これにより、iptablesコマンドで個々のIPアドレスを1つずつ登録するのではなく、セットを使用して一括登録することができるようになります。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.1 (Lime Lynx)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-162.6.1.el9_1.x86_64

ipsetコマンドの版数は以下のとおりです。

[root@server ~]# ipset -v
ipset v7.11, protocol version: 7

3 オプション一覧

[root@server ~]# ipset help
ipset v7.11

Usage: ipset [options] COMMAND

Commands:
create SETNAME TYPENAME [type-specific-options]
        Create a new set
add SETNAME ENTRY
        Add entry to the named set
del SETNAME ENTRY
        Delete entry from the named set
test SETNAME ENTRY
        Test entry in the named set
destroy [SETNAME]
        Destroy a named set or all sets
list [SETNAME]
        List the entries of a named set or all sets
save [SETNAME]
        Save the named set or all sets to stdout
restore
        Restore a saved state
flush [SETNAME]
        Flush a named set or all sets
rename FROM-SETNAME TO-SETNAME
        Rename two sets
swap FROM-SETNAME TO-SETNAME
        Swap the contect of two existing sets
help [TYPENAME]
        Print help, and settype specific help
version
        Print version information
quit
        Quit interactive mode

Options:
-o plain|save|xml
       Specify output mode for listing sets.
       Default value for "list" command is mode "plain"
       and for "save" command is mode "save".
-s
        Print elements sorted (if supported by the set type).
-q
        Suppress any notice or warning message.
-r
        Try to resolve IP addresses in the output (slow!)
-!
        Ignore errors when creating or adding sets or
        elements that do exist or when deleting elements
        that don't exist.
-n
        When listing, just list setnames from the kernel.

-t
        When listing, list setnames and set headers
        from kernel only.
-f
        Read from the given file instead of standard
        input (restore) or write to given file instead
        of standard output (list/save).

Supported set types:
    list:set            3       skbinfo support
    list:set            2       comment support
    list:set            1       counters support
    list:set            0       Initial revision
-snip-

4 セットの作成、削除方法

セットはメソッドとデータタイプを指定して作成します。書式は以下のとおりです。

TYPENAME := method:datatype[,datatype[,datatype]]

・method:list,hash,bitmapを指定できます。
・datatype:ip,net,mac,port,ifaceを指定できます。

メソッドの各意味を以下のとおりです。

メソッド 意味
list セットをリスト形式で格納します
hash ハッシュテーブルを使用してセットを格納します。ハッシュメソッドは、IPアドレスやネットワークを格納するときに使用します
bitmap ビットマップを使用してセットを格納します。ポート番号やポート範囲を格納するときに使用します

4.1 セットを作成する方法(nまたはcreate)

名前がtest1、タイプがhash:ipのセットを作成してみます。

[root@server ~]# ipset n test1 hash:ip

作成したセットを確認します。test1という名前のセットが作成できたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xcbb84ee8
Size in memory: 216
References: 0
Number of entries: 0
Members:

4.2 セットを削除する方法(xまたはdel)

セットの一覧を確認します。test1という名前のセットが存在することがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xcbb84ee8
Size in memory: 216
References: 0
Number of entries: 0
Members:

test1という名前のセットを削除します。

[root@server ~]# ipset x test1

セットの一覧を確認します。test1という名前のセットが削除されたことがわかります。

[root@server ~]# ipset l
[root@server ~]#

4.3 セットを一括削除する方法(xまたはdel)

事前準備として、test1,test2という名前のセットを2つ作成しておきます。

[root@server ~]# ipset n test1 hash:ip
[root@server ~]# ipset n test2 hash:ip

セットの一覧を確認します。test1,test2という名前のセットが作成されたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xa5a2e8c8
Size in memory: 216
References: 0
Number of entries: 0
Members:

Name: test2
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x0dad6e43
Size in memory: 216
References: 0
Number of entries: 0
Members:

2つのセットを一括で削除します。

[root@server ~]# ipset x

セットの一覧を確認します。test1,test2という名前のセットが削除されたことがわかります。

[root@server ~]# ipset l
[root@server ~]#

5 セットの名前を変更する方法(eまたはrename)

事前準備として、test1という名前のセットを作成しておきます。

[root@server ~]# ipset n test1 hash:ip

作成したセットを確認します。test1という名前のセットが作成できたことがわかります。

[root@server ~]# ipset l -n
test1

test1という名前のセットをtest2に変更します。

[root@server ~]# ipset e test1 test2

セットの一覧を確認します。test1という名前のセットがtest2に変更されたことがわかります。

[root@server ~]# ipset l -n
test2

あと始末をします。

[root@server ~]# ipset x

6 セットにエントリを追加/削除する方法

6.1 エントリを追加する方法(aまたはadd)

事前準備として、名前がtest1、タイプがhash:ipのセットを作成してみます。

[root@server ~]# ipset n test1 hash:ip

作成したセットを確認します。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 120
References: 0
Number of entries: 0
Members:
[root@server ~]#

セットにIPアドレスのエントリを2つ追加します。

[root@server ~]# ipset add test1 192.168.10.10
[root@server ~]# ipset add test1 192.168.10.20

セットを確認します。追加したエントリが2つ作成されたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 216
References: 0
Number of entries: 2
Members:
192.168.10.20
192.168.10.10

6.2 エントリを削除する方法(dまたはdel)

セットからエントリを1つ削除します。

[root@server ~]# ipset d test1 192.168.10.10

セットを確認します。エントリが1つ削除されたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 168
References: 0
Number of entries: 1
Members:
192.168.10.20

セットからもう1つエントリを削除します。

[root@server ~]# ipset d test1 192.168.10.20

セットの中身を確認します。エントリが全て削除されたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 120
References: 0
Number of entries: 0
Members:

6.3 エントリを一括削除する方法(fまたはflush)

セットを確認します。エントリが2つ存在することがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 216
References: 0
Number of entries: 2
Members:
192.168.10.20
192.168.10.10

エントリを一括で削除します。

[root@server ~]# ipset f test1

セットの中身を確認します。エントリが全て削除されたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 120
References: 0
Number of entries: 0
Members:

7 セーブ、リストア方法

セットの内容をファイルに保存したり、ファイルに保存したセットをリストアすることができます。

7.1 セットの内容をファイルに保存する方法(save)

テスト用のセットを作成します。

[root@server ~]# ipset n test1 hash:ip
[root@server ~]# ipset a test1 192.168.10.1

作成したセットをファイルに保存します。

[root@server ~]# ipset save > ip_set.txt

ファイルに保存したセットの内容を確認します。

[root@server ~]# cat ip_set.txt
create test1 hash:ip family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x1266b74c
add test1 192.168.10.1

7.2 リストアする方法(restore)

セットの一覧を確認します。セットが登録されていないことを確認します。

[root@server ~]# ipset l
[root@server ~]#

ファイルに保存されているセットを確認します。

[root@server ~]# cat ip_set.txt
create test1 hash:ip family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x1266b74c
add test1 192.168.10.1

ファイルに保存されているセットをリストアします。

[root@server ~]# ipset restore < ip_set.txt

セットを確認します。ファイルに保存されていたセットがリストアされたことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x1266b74c
Size in memory: 256
References: 0
Number of entries: 1
Members:
192.168.10.1

8 タイムアウトの設定方法(timeout)

エントリにタイムアウトを設定することができます。指定した時間を経過すると、エントリが削除されます。ここでは、エントリに10秒のタイムアウトを設定してみます。10秒経過すると、エントリが削除されることを確認してみます。

[root@server ~]# ipset n test1 hash:ip timeout 10

セット(test1)にエントリ(192.168.10.10)を追加します。

[root@server ~]# ipset a test1 192.168.10.10

セットに追加されたエントリを確認します。エントリの削除まで4秒であることがわかります。

[root@server ~]# ipset l test1
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 timeout 10 bucketsize 12 initval 0x728c1f29
Size in memory: 280
References: 0
Number of entries: 1
Members:
192.168.10.10 timeout 4

セットに追加されたエントリを確認します。エントリが削除されたことがわかります。

[root@server ~]# ipset l test1
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 timeout 10 bucketsize 12 initval 0x728c1f29
Size in memory: 280
References: 0
Number of entries: 0
Members:

9 カウンタの設定方法(counters)

カウンタ付のセットを作成します。

[root@server ~]# ipset n test1 hash:ip counters

セットにエントリを追加します。

[root@server ~]# ipset a test1 192.168.122.213

セットの内容を表示します。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 counters bucketsize 12 initval 0x1707fb58
Size in memory: 296
References: 0
Number of entries: 1
Members:
192.168.122.213 packets 0 bytes 0

セットをINPUTチェインに追加します。

[root@server ~]# iptables -A INPUT -m set --match-set test1 src -p icmp -j LOG

INPUTチェインのルールを確認します。test1セットがINPUTチェインに追加されたことがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            match-set test1 src LOG flags 0 level 4

クライアントからサーバに対してpingコマンドを1回実行します。なお、pingコマンドの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client~]# ping -c 1 192.168.122.16

セットの内容を確認します。192.168.122.213から84バイトのパケットを1つ受信したことがわかります。

[root@server ~]# ipset l
Name: test1
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 counters bucketsize 12 initval 0x1707fb58
Size in memory: 296
References: 1
Number of entries: 1
Members:
192.168.122.213 packets 1 bytes 84

INPUTチェインを確認します。ICMPパケットを受信したことがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        1    84 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            match-set test1 src LOG flags 0 level 4

ipsetコマンドを実行したときのカーネルの動作をsystemtapで確認する。

[root@server ~]# ipset n test1 hash:ip
pp=module("ip_set_hash_ip").function("hash_ip_create@net/netfilter/ipset/ip_set_hash_gen.h:1207")
pp=module("ip_set_hash_ip").function("htable_size@net/netfilter/ipset/ip_set_hash_gen.h:103")

シグナルの受信処理について

1 はじめに

シグナルの受信処理について、以下のことを確認してみます。
・同じシグナルを複数回受信した場合の挙動
・通常シグナル、リアルタイムシグナルの受信処理の順序

シグナルについて以下の記事も書きました。
シグナルについて - hana_shinのLinux技術ブログ

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.1 (Lime Lynx)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-162.6.1.el9_1.x86_64

3 テストプログラム

テストプログラムの内容は次のとおりです。
SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)の各シグナルについて、シグナルハンドラを登録し、それらシグナルを受信した際にはシグナル番号を表示します。シグナルハンドラでは、fprintf関数やprintf関数ではなく、シグナルセーフなwriteシステムコールを使ってメッセージを出力します。また、main関数ではfprintfの出力先にstderrを指定しています。これは、メッセージ出力をバッファリングせず、即座に端末に出力できるようにするためです。

[root@server tp]# cat signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sig_int(int sig) {
    char msg[100];
    snprintf(msg, sizeof(msg), "Signal number:%d\n", sig);
    write(STDERR_FILENO, msg, strlen(msg));
}

int main() {
    struct sigaction sa[4];
    int signals[] = {1, 11, 63, 64};

    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = sig_int;
        sigemptyset(&sa[i].sa_mask);
        sa[i].sa_flags = 0;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "PID=%d\n", getpid());
    while(1) {
        sleep(600);
    }
}

テストプログラムをコンパイルします。

[root@server tp]# gcc -Wall -o signal signal.c

4 同じシグナルを複数回受信した場合について

4.1 通常シグナルを複数回受信した場合

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1189

Ctrl+zを押下して、signalプロセスを停止状態にします。なお、SIGSTOPシグナルをsignalプロセスに送信しても同じ状態になります。

[root@server tp]# ./signal
PID=1189
^Z
[1]+  停止                  ./signal

psコマンドを使用してsignalプロセスの状態を確認すると、停止状態(STATがT)であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signalプロセスにSIGHUPシグナルを3つ送信します。

[root@server ~]# kill -SIGHUP 1189
[root@server ~]# kill -SIGHUP 1189
[root@server ~]# kill -SIGHUP 1189

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGが0000000000000001であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000001 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

シグナルについて - hana_shinのLinux技術ブログに記載したプログラム(signal.py)を実行すると、SIGHUPシグナルが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:0000000000000001
Active signals:
SIGHUP(1)

fgコマンドを実行して、signalプロセスを再開します。signalプロセスにSIGHUPシグナルを3つ送信したのですが、シグナルハンドラが1回しか実行されていないので、処理されシグナルは1回だけであることがわかります。

[root@server tp]# fg
./signal
Signal number:1

psコマンドを使用してsignalプロセスの状態を確認すると、PENDINGが0000000000000000であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/0      0:00 ./signal

テストCtrl+cを押下して、テストプログラムを終了します。

[root@server tp]# fg
./signal
Signal number:1
^C

4.2 リアルタイムシグナルを複数回受信した場合

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1230

psコマンドを実行して、プロセスの状態を確認します。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/1      0:00 ./signal

Ctrl+zキーを押下して、signalプロセスを停止状態にします。

[root@server tp]# ./signal
PID=1230
^Z
[1]+  停止                  ./signal

psコマンドを使用してsignalプロセスの状態を確認すると、停止状態(STATがT)であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signalプロセスにSIGRTMAXシグナルを3つ送信します。

[root@server ~]# kill -SIGRTMAX 1230
[root@server ~]# kill -SIGRTMAX 1230
[root@server ~]# kill -SIGRTMAX 1230

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGが8000000000000000であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 8000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

シグナルについて - hana_shinのLinux技術ブログに記載したプログラムを実行すると、SIGRTMAXシグナルが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:8000000000000000
Active signals:
SIGRTMAX(64)

fgコマンドを実行して、signal プロセスを再開します。シグナルハンドラが3回実行されたため、処理されたシグナルは3回であることがわかります。通常のシグナルとは異なり、リアルタイムシグナルは受信したシグナルの回数を実行することができます。

[root@server tp]# fg
./signal
Signal number:64
Signal number:64
Signal number:64

signalプロセスが再開して、保留中のシグナルを処理したので、PENDINGが0になったことがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/1      0:00 ./signal

5 通常シグナルとリアルタイムシグナルの優先度について

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1321

Ctrl+zを押下して、signalプロセスを停止状態にします。

[root@server tp]# ./signal
PID=1321
^Z
[1]+  停止                  ./signal

signalプロセスにSIGHUP, SIGSEGV, SIGRTMAX-1, SIGRTMAX を送信します。SIGHUP, SIGSEGVは通常シグナルです。SIGRTMAX-1, SIGRTMAXはリアルタイムシグナルです。

[root@server ~]# kill -SIGHUP 1321
[root@server ~]# kill -SIGSEGV 1321
[root@server ~]# kill -SIGRTMAX-1 1321
[root@server ~]# kill -SIGRTMAX 1321

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGがc000000000000401であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1321 c000000000000401 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signal.pyを実行すると、SIGHUP, SIGSEGV, SIGRTMAX-1, SIGRTMAXが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:c000000000000401
Active signals:
SIGHUP(1), SIGSEGV(11), SIGRTMAX-1(63), SIGRTMAX(64)

fgコマンドを実行して、signal プロセスを再開します。リアルタイムシグナル、通常シグナルの順でシグナルが処理されていることがわかります。また、リアルタイムシグナルは番号の大きいものから、通常シグナルは番号の小さいものから処理されていることがわかります。

[root@server tp]# fg
./signal
Signal number:64
Signal number:63
Signal number:1
Signal number:11

6 まとめ

項目 説明
通常シグナルとリアルタイムシグナルの優先度 リアルタイムシグナルが通常シグナルより優先して処理されます
リアルタイムシグナル同士の優先度 番号の大きいリアルタイムシグナルが優先して処理されます
通常シグナル同士の優先度 番号の小さい通常シグナルが優先して処理されます
同じシグナルの実行回数 通常シグナルの場合、同じシグナルをN回受信しても処理するのは1回です。リアルタイムシグナルの場合、同じシグナルをN回受信したら、N回受信処理をします

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

シグナルについて

1 はじめに

シグナルに関して様々な実験を実施してみます。

他にも、以下の記事を投稿しました。
シグナルの受信処理について - hana_shinのLinux技術ブログ

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.1 (Lime Lynx)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-162.6.1.el9_1.x86_64

3 シグナル一覧を表示する方法

killコマンドを実行すると、シグナルの一覧を表示することができます。通常シグナルは1~32までの範囲で、リアルタイムシグナルは33~64までの範囲にあります。

[root@server ~]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

4 シグナルを送信する方法

killコマンドを実行すると、プロセスにシグナルを送信することができます。ここでは、sleepプロセスにSIGKILLシグナルを送信してみます。まず、sleepコマンドを実行します。sleepコマンドを実行すると、sleepプロセスが起動され、その後600秒スリープします。

[root@server ~]# sleep 600

psコマンドを実行して、sleepプロセスのPIDを確認します。sleepプロセスのPIDは19876であることがわかります。なお、psコマンドの使い方は、psコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ps -C sleep
    PID TTY          TIME CMD
  19867 pts/2    00:00:00 sleep

4.1 シグナル名を指定する方法

シグナル名を指定してkillコマンドを実行します。

[root@server ~]# kill -SIGKILL 19867

sleepプロセスがSIGKILLシグナルを受信すると、強制終了することがわかります。

[root@server ~]# sleep 600
強制終了

4.2 シグナル番号を指定する方法

シグナル番号を指定してkillコマンドを実行します。

[root@server ~]# kill -9 1188

sleepプロセスがSIGKILLシグナルを受信すると、強制終了することがわかります。

[root@server ~]# sleep 600
強制終了

5 シグナルの状態を表示する方法

psコマンドにsオプションを指定すると、シグナルの各種状態(PENDING、BLOCKED、IGNORED、CAUGHT)を表示することができます。ここでは、これらの各種状態をシグナル情報と呼ぶことにします。シグナル情報は64ビットのデータです。通常シグナルには32ビットが割り当てられており、リアルタイムシグナルにも32ビットが割り当てられています。

[root@server ~]# ps s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0     672 0000000000000000 0000000000000000 0000000000000006 0000000000000000 Ss+  tty1       0:00 /sbin/agetty -o -p
    0    1124 0000000000000000 0000000000010000 0000000000384004 000000004b813efb Ss   pts/0      0:00 -bash
    0    1195 0000000000000000 0000000000000000 0000000000384004 000000004b813efb Ss+  pts/1      0:00 -bash
    0   19660 0000000000000000 0000000000010000 0000000000384004 000000004b813efb Ss   pts/2      0:00 -bash
    0   19908 0000000000000000 0000000000000000 0000000000000000 0000000000000000 S+   pts/2      0:00 sleep 600
    0   19915 0000000000000000 0000000000000000 0000000000000000 0000000073d1fef9 R+   pts/0      0:00 ps s

シグナル情報の意味は以下のとおりです。

シグナル情報 意味
PENDING プロセスが受信したがまだ処理していないシグナルを示します
BLOCKED プロセスが特定のシグナルを受信しないように一時的にシグナルの処理をブロックするシグナルを示します。ブロックを解除するとシグナルを処理します
IGNORED プロセスが特定のシグナルを受信しても無視するシグナルを示します
CAUGHT シグナルハンドラを登録しているシグナルを示します

PIDを指定すると、特定プロセスのシグナル情報を表示することができます。ここでは、systemd(PID=1)のシグナル情報を表示してみます。

[root@server ~]# ps s -p 1
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0       1 0000000000000000 7fe3c0fe28014a03 0000000000001000 00000001000004ec Ss   ?          0:05 /usr/lib/systemd/s

次のようにフォーマットを明に指定しても、プロセスのシグナル情報を表示することができます。

[root@server ~]# ps -p 1 -o comm,pid,sig,sigmask,sigignore,sigcatch
COMMAND             PID          PENDING          BLOCKED          IGNORED           CAUGHT
systemd               1 0000000000000000 7fe3c0fe28014a03 0000000000001000 00000001000004ec

-Cオプションでプロセス名を指定すると、指定したプロセスのシグナル情報を表示することができます。chronydプロセスのシグナル情報を表示してみます。

[root@server ~]#  ps -C chronyd -o comm,pid,sig,sigmask,sigignore,sigcatch
COMMAND             PID          PENDING          BLOCKED          IGNORED           CAUGHT
chronyd             656 0000000000000000 0000000000000000 0000000000001000 0000000100004007

記事末尾のサンプルプログラム(signal.py)を使用してIGNOREDのシグナル名を確認すると、SIGPIPEが無視されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:0000000000001000
Active signals:
SIGPIPE(13)

次に、chronydプロセスがSIGPIPEシグナルを無視するかどうかを確認するため、chronydプロセスにSIGPIPEシグナルを送信してみます。通常、SIGPIPEを受信するとプロセスは終了しますが、chronydプロセスはSIGPIPEシグナルを無視する設定になっているため、SIGPIPEシグナルを受信してもchronydプロセスは終了しません。

[root@server ~]# kill -SIGPIPE 656

chronydプロセスの状態を確認します。chronydプロセスのPIDは656のままで、chronydプロセスが終了していないことがわかります。

[root@server ~]# ps -C chronyd -o comm,pid,sig,sigmask,sigignore,sigcatch
COMMAND             PID          PENDING          BLOCKED          IGNORED           CAUGHT
chronyd             656 0000000000000000 0000000000000000 0000000000001000 0000000100004007

6 CAUGHTの確認方法

6.1テストプログラムの作成

CAUGHTを確認するためのテストプログラムを作成します。SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)の各シグナルについて、シグナルハンドラを登録し、それらシグナルを受信した際にはシグナル番号を表示します。シグナルハンドラでは、fprintf関数やprintf関数ではなく、シグナルセーフなwriteシステムコールを使ってメッセージを出力します。また、main関数ではfprintfの出力先にstderrを指定しています。これは、メッセージ出力をバッファリングせず、即座に端末に出力できるようにするためです。

[root@server ~]# cat signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sig_int(int sig) {
    char msg[100];
    snprintf(msg, sizeof(msg), "Signal number:%d\n", sig);
    write(STDERR_FILENO, msg, strlen(msg));
}

int main() {
    struct sigaction sa[4];
    int signals[] = {1, 2, 63, 64};

    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = sig_int;
        sigemptyset(&sa[i].sa_mask);
        sa[i].sa_flags = 0;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "PID=%d\n", getpid());
    while(1) {
        sleep(600);
    }
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o signal signal.c

6.2 確認結果

テストプログラムを実行します。

[root@server ~]# ./signal
PID=1421

psコマンドを実行してシグナル情報を確認すると、CAUGHTがc000000000000003であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1421 0000000000000000 0000000000000000 0000000000000000 c000000000000003 S+   pts/1      0:00 ./signal

signal.pyを実行すると、CAUGHTのc000000000000003は、テストプログラムで定義した4つのシグナルを補足することを示していることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:c000000000000003
Active signals:
SIGHUP(1), SIGINT(2), SIGRTMAX-1(63), SIGRTMAX(64)

7 PENDINGの確認方法

6章と同じテストプログラムを実行します。signalプロセスのPIDが1207であることがわかります。

[root@server ~]# ./signal
PID=1207

Ctrl+zキーを押下して、signalプロセスを停止状態にします。なお、SIGSTOPシグナルをsignalプロセスに送信しても同じ状態になります。

[root@server ~]# ./signal
PID=1207
^Z
[1]+  停止                  ./signal

psコマンドを使用してsignalプロセスの状態を確認すると、停止状態(STATがT)であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1207 0000000000000000 0000000000000000 0000000000000000 c000000000000003 S+   pts/1      0:00 ./signal

signalプロセスにSIGHUP(1)シグナルを送信します。

[root@server ~]# kill -1 1207

psコマンドを実行してシグナル情報を確認すると、PENDINGが0000000000000001になったことがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1207 0000000000000001 0000000000000000 0000000000000000 c000000000000003 T    pts/1      0:00 ./signal

signal.pyを実行すると、SIGHUPシグナルが保留されていることがわかります。これは、シグナルを受信するプロセスが停止状態になっているため、SIGHUPシグナルを受信しても処理できずに保留しているからです。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:0000000000000001
Active signals:
SIGHUP(1)

次に、signalプロセスにSIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)を送信します。

[root@server ~]# kill -2 1207
[root@server ~]# kill -63 1207
[root@server ~]# kill -64 1207

psコマンドを実行してシグナル情報を確認すると、PENDINGがc000000000000003になっていることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1207 c000000000000003 0000000000000000 0000000000000000 c000000000000003 T    pts/1      0:00 ./signal

signal.pyを使ってPENDINGのシグナルを確認すると、SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)が保留中であることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:c000000000000003
Active signals:
SIGHUP(1), SIGINT(2), SIGRTMAX-1(63), SIGRTMAX(64)

fgコマンドを実行して、signalプロセスを停止状態からスリープ状態に戻します。

[root@server ~]# fg
./signal
Signal number:64
Signal number:63
Signal number:2
Signal number:1

psコマンドを実行してシグナル情報を確認します。signalプロセスが起床して、保留中のシグナルを処理したので、PENDINGが0になったことがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1207 0000000000000000 0000000000000000 0000000000000000 c000000000000003 S+   pts/1      0:00 ./signal

8 IGNOREDの確認方法

8.1テストプログラムの作成

IGNOREDを確認するためのテストプログラムを作成します。実行開始から30秒間は、SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)を受信しても無視します。30秒経過すると、無視していたシグナルを実行します。

[root@server ~]# cat signal1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sig_int(int sig) {
    char msg[100];
    snprintf(msg, sizeof(msg), "Signal number:%d\n", sig);
    write(STDERR_FILENO, msg, strlen(msg));
}

int main() {
    struct sigaction sa[4];
    int signals[] = {1, 2, 63, 64};
    int block_time = 30;

    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = SIG_IGN;
        sigemptyset(&sa[i].sa_mask);
        sa[i].sa_flags = 0;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "PID=%d\n", getpid());
    sleep(block_time);
    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = sig_int;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "Signals unblocked.\n");
    while(1) {
        sleep(600);
    }
    return 0;
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o signal1 signal1.c

8.2 確認結果

テストプログラムを実行します。

[root@server ~]# ./signal1
PID=1318

プロセスの状態を確認すると、IGNOREDがc000000000000003になっていることがわかります。

[root@server ~]# ps -C signal1 s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1318 0000000000000000 0000000000000000 c000000000000003 0000000000000000 S+   pts/1      0:00 ./signal1

signalプロセスにSIGHUP(1), SIGINT(2), SIGRTMAX-1(63), SIGRTMAX(64)を送信します。

[root@server ~]# kill -1 1318
[root@server ~]# kill -2 1318
[root@server ~]# kill -63 1318
[root@server ~]# kill -64 1318

シグナルを受信してもシグナル番号が表示されないことから、シグナルが無視されていることがわかります。

[root@server ~]# ./signal1
PID=1318

30秒経過したあと、プロセスの状態を確認するとIGNOREDが0000000000000000になっていることがわかります。

[root@server ~]# ps -C signal1 s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1318 0000000000000000 0000000000000000 0000000000000000 c000000000000003 S+   pts/1      0:00 ./signal1

signalプロセスにSIGHUP(1), SIGINT(2), SIGRTMAX-1(63), SIGRTMAX(64)を送信します。

[root@server ~]# kill -1 1318
[root@server ~]# kill -2 1318
[root@server ~]# kill -63 1318
[root@server ~]# kill -64 1318

シグナルを受信してシグナルハンドラが実行されたため、シグナル番号が表示されていることがわかります。

[root@server ~]# ./signal1
PID=1318
Signals unblocked.
Signal number:1
Signal number:2
Signal number:63
Signal number:64

9 BLOCKEDの確認方法

9.1テストプログラムの作成

BLOCKEDを確認するためのテストプログラムを作成します。テストプログラムを実行すると30秒間シグナルの受信処理をブロックします。30秒経過するとブロックを解除してシグナルの受信処理を実行します。

[root@server ~]# cat signal2.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sig_int(int sig) {
    char msg[100];
    snprintf(msg, sizeof(msg), "Signal number:%d\n", sig);
    write(STDERR_FILENO, msg, strlen(msg));
}

int main() {
    struct sigaction sa[4];
    int signals[] = {1, 2, 63, 64};
    int block_time = 30;
    sigset_t set;

    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = sig_int;
        sigemptyset(&sa[i].sa_mask);
        sa[i].sa_flags = 0;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "PID=%d\n", getpid());

    sigemptyset(&set);
    for (int i = 0; i < 4; ++i) {
        sigaddset(&set, signals[i]);
    }
    sigprocmask(SIG_BLOCK, &set, NULL);

    fprintf(stderr, "Signals blocked for %d seconds.\n", block_time);
    sleep(block_time);

    sigprocmask(SIG_UNBLOCK, &set, NULL);
    fprintf(stderr, "Signals unblocked.\n");

    while (1) {
        sleep(600);
    }
    return 0;
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o signal2 signal2.c

9.2 確認結果

テストプログラムを実行します。

[root@server ~]# ./signal2
PID=1383

プロセスの状態を確認すると、BLOCKEDがc000000000000003になっており、SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)がブロックされれいることを示しています

[root@server ~]# ps -C signal2 s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1383 0000000000000000 c000000000000003 0000000000000000 c000000000000003 S+   pts/0      0:00 ./signal2

signalプロセスにSIGHUP(1), SIGINT(2), SIGRTMAX-1(63), SIGRTMAX(64)を送信します。

[root@server ~]# kill -1 1383
[root@server ~]# kill -2 1383
[root@server ~]# kill -63 1383
[root@server ~]# kill -64 1383

プロセスの状態を確認すると、PENDINGがc000000000000003になっており、SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)が保留中であることを示しています。これは、シグナルの受信処理がブロックされていてシグナルが保留されているからです。

[root@server ~]# ps -C signal2 s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1383 c000000000000003 c000000000000003 0000000000000000 c000000000000003 S+   pts/0      0:00 ./signal2

テストプログラムを実行して30秒経過するとブロックしているシグナルのシグナルハンドラが実行されます。シグナルの処理はリアルタイムシグナルが通常シグナルの処理より優先されます。また、シグナル番号の大きいシグナルから処理されていることがわかります。

[root@server ~]# ./signal2
PID=1383
Signals blocked for 30 seconds.
Signal number:64
Signal number:63
Signal number:2
Signal number:1
Signals unblocked.

プロセスの状態を確認すると、BLOCKEDが0000000000000000 になっていることがわかります。これは、30秒経過してブロックしていたシグナルを解除したからです。

[root@server ~]# ps -C signal2 s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1383 0000000000000000 0000000000000000 0000000000000000 c000000000000003 S+   pts/0      0:00 ./signal2

X シグナル名とシグナル番号を表示するプログラム

以下は、シグナル情報の16進数をシグナル名とシグナル番号で表示するプログラムです。

[root@server ~]# cat signal.py
#!/usr/bin/python3

def binary_to_signals(hex_input):
    signals = [
        "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
        "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
        "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2",
        "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT",
        "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP",
        "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU",
        "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
        "SIGIO", "SIGPWR", "SIGSYS", "SIGUNUSED", "SIGUNUSED",
        "SIGRTMIN", "SIGRTMIN+1", "SIGRTMIN+2", "SIGRTMIN+3",
        "SIGRTMIN+4", "SIGRTMIN+5", "SIGRTMIN+6", "SIGRTMIN+7",
        "SIGRTMIN+8", "SIGRTMIN+9", "SIGRTMIN+10", "SIGRTMIN+11",
        "SIGRTMIN+12", "SIGRTMIN+13", "SIGRTMIN+14", "SIGRTMIN+15",
        "SIGRTMAX-14", "SIGRTMAX-13", "SIGRTMAX-12", "SIGRTMAX-11",
        "SIGRTMAX-10", "SIGRTMAX-9", "SIGRTMAX-8", "SIGRTMAX-7",
        "SIGRTMAX-6", "SIGRTMAX-5", "SIGRTMAX-4", "SIGRTMAX-3",
        "SIGRTMAX-2", "SIGRTMAX-1", "SIGRTMAX",
    ]

    signals_activated = []
    binary_str = bin(int(hex_input, 16))[2:]
    binary_str = binary_str.zfill(64)

    for i, bit in enumerate(reversed(binary_str)):
        if bit == "1":
            if i < len(signals):
                signal_name = signals[i]
                signal_number = i + 1
                signals_activated.append(f"{signal_name}({signal_number})")

    return signals_activated

if __name__ == "__main__":
    hex_input = input("Please enter a hexadecimal number:")

    if not all(ch in "0123456789abcdefABCDEF" for ch in hex_input):
        print("Input error: Please enter a hexadecimal number.")
    else:
        activated_signals = binary_to_signals(hex_input)
        if not activated_signals:
            print("There are no active signals:")
        else:
            print("Active signals:")
            for i in range(0, len(activated_signals), 5):
                signals_line = ", ".join(activated_signals[i:i+5])
                print(signals_line)

実行権を付与します。

[root@server ~]# chmod 744 signal.py

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

lscpuコマンドの使い方

1 lscpuコマンドとは?

lscpuコマンドは、CPUの数、スレッド数、コア数、ソケット数、およびNUMAノードなどの情報を表示するコマンドです。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.1 (Lime Lynx)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-162.6.1.el9_1.x86_64

3 パッケージのインストール方法

util-linuxパッケージをインストールします。

[root@server ~]# dnf -y install util-linux

lscpuコマンドの版数は以下のとおりです。

[root@server ~]# lscpu -V
lscpu from util-linux 2.37.4

4 オプション一覧

lscpuコマンドのオプション一覧は以下のとおりです。

[root@server ~]# lscpu -h

使い方:
 lscpu [オプション]

CPUアーキテクチャについての情報を表示します。

オプション:
 -a, --all               オンラインの CPU とオフラインの CPU を両方表示します (-e を指定した場合の既定値)
 -b, --online            オンラインの CPU のみを表示します (-p を指定した場合の既定値)
 -B, --bytes             print sizes in bytes rather than in human readable format
 -C, --caches[=<list>]   info about caches in extended readable format
 -c, --offline           オフラインの CPU のみを表示します
 -J, --json              use JSON for default or extended format
 -e, --extended[=<リスト>] 読みやすい拡張形式で表示します
 -p, --parse[=<list>]    プログラムなどで処理しやすい形式で表示します
 -s, --sysroot <dir>     指定したディレクトリをシステムルートとみなすようにします
 -x, --hex               CPU をリストではなく 16 進数のマスク形式で表示します
 -y, --physical          print physical instead of logical IDs
     --output-all        print all available columns for -e, -p or -C

 -h, --help              このヘルプを表示します
 -V, --version           バージョンを表示します

Available output columns for -e or -p:
-snip-

5 CPUに関する情報を表示する方法

lscpuコマンドを引数なしで実行すると、CPUに関する情報を表示することができます。

[root@server ~]# lscpu
アーキテクチャ:                        x86_64
  CPU 操作モード:                      32-bit, 64-bit
  Address sizes:                       45 bits physical, 48 bits virtual
  バイト順序:                          Little Endian
CPU:                                   4
  オンラインになっている CPU のリスト: 0-3
ベンダー ID:                           GenuineIntel
  BIOS Vendor ID:                      Red Hat
  モデル名:                            Intel Core Processor (Skylake, IBRS)
    BIOS Model name:                   RHEL 7.6.0 PC (i440FX + PIIX, 1996)
    CPU ファミリー:                    6
    モデル:                            94
    コアあたりのスレッド数:            1
    ソケットあたりのコア数:            1
    ソケット数:                        4
    ステッピング:                      3
    BogoMIPS:                          5616.01
    フラグ:                            fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mm
                                       x fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopolo
                                       gy cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic mov
                                       be popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dno
                                       wprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust
                                       bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsa
                                       ves arat umip md_clear arch_capabilities
Virtualization features:
  ハイパーバイザのベンダー:            KVM
  仮想化タイプ:                        完全仮想化
Caches (sum of all):
  L1d:                                 128 KiB (4 instances)
  L1i:                                 128 KiB (4 instances)
  L2:                                  16 MiB (4 instances)
  L3:                                  64 MiB (4 instances)
NUMA:
  NUMA ノード数:                       1
  NUMA ノード 0 CPU:                   0-3
Vulnerabilities:
  Itlb multihit:                       Not affected
  L1tf:                                Mitigation; PTE Inversion
  Mds:                                 Mitigation; Clear CPU buffers; SMT Host state unknown
  Meltdown:                            Mitigation; PTI
  Mmio stale data:                     Vulnerable: Clear CPU buffers attempted, no microcode; SMT Host state unknown
  Retbleed:                            Mitigation; IBRS
  Spec store bypass:                   Mitigation; Speculative Store Bypass disabled via prctl
  Spectre v1:                          Mitigation; usercopy/swapgs barriers and __user pointer sanitization
  Spectre v2:                          Mitigation; IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS Not affected
  Srbds:                               Unknown: Dependent on hypervisor status
  Tsx async abort:                     Not affected

6 CPUの状態を表示する方法

事前準備としてCPU0の状態をオフラインに変更しておきます。

CPU0の状態を確認します。CPU0はオンラインであることがわかります。

[root@server ~]# cat /sys/devices/system/cpu/cpu0/online
1

CPU0の状態をオフラインに変更します。

[root@server ~]# echo 0 > /sys/devices/system/cpu/cpu0/online

CPU0の状態を確認します。CPU0の状態がオフラインになったことがわかります。

[root@server ~]# cat /sys/devices/system/cpu/cpu0/online
0

6.1 オンライン/オフラインのCPUを表示する方法(-a)

-aはオンライン/オフラインのCPUを全て表示するオプションです。以下の例ではCPU0がオフライン、他のCPUはオンラインであことがわかります。

[root@server ~]# lscpu -ae
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  0    -      -    - -                 no
  1    0      0    0 1:1:1:1          yes
  2    0      1    1 2:2:2:2          yes
  3    0      2    2 3:3:3:3          yes

6.2 オンラインのCPUを表示する方法(-b)

-bオプションは、オンラインのCPUのみを表示するオプションです。CPUの状態を確認すると、オンラインのCPU1、2、3が表示されていることが分かります。

[root@server ~]# lscpu -be
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  1    0      0    0 1:1:1:1          yes
  2    0      1    1 2:2:2:2          yes
  3    0      2    2 3:3:3:3          yes

6.3 オフラインのCPUを表示する方法(-c)

-cオプションは、オフラインのCPUを表示するためのオプションです。CPUの状態を確認すると、オフラインのCPU0が表示されていることが分かります。

[root@server ~]# lscpu -ce
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  0    -      -    - -                 no

CPU0の状態をオンラインに戻します。

[root@server ~]# echo 1 > /sys/devices/system/cpu/cpu0/online

CPUの状態を確認します。CPU0がオンラインになったことがわかります。

[root@server ~]# lscpu -ae
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  0    0      0    0 0:0:0:0          yes
  1    0      1    1 1:1:1:1          yes
  2    0      2    2 2:2:2:2          yes
  3    0      3    3 3:3:3:3          yes

7 特定の情報を表示する方法(-p)

-pは指定したCPUの情報を表示するオプションです。指定できる情報として以下のものがあります。

Available output columns for -e or -p:
      BOGOMIPS  crude measurement of CPU speed
           CPU  論理 CPU 番号
          CORE  論理コア番号
        SOCKET  論理ソケット番号
       CLUSTER  logical cluster number
          NODE  論理 NUMA ノード番号
          BOOK  論理ブック番号
        DRAWER  logical drawer number
         CACHE  CPU 間でどのようにキャッシュが共有されるかの表示
  POLARIZATION  仮想ハードウェア上での CPU ディスパッチモード
       ADDRESS  CPU の物理アドレス
    CONFIGURED  ハイパーバイザが CPU を割り当てたかどうかの表示
        ONLINE  Linux が CPU を利用しているかどうかの表示
           MHZ  shows the currently MHz of the CPU
        MAXMHZ  CPU の最大 MHz を表示します
        MINMHZ  CPU の最小 MHz を表示します

論理CPUを表示してみます。

[root@server ~]# lscpu -p=CPU
# The following is the parsable format, which can be fed to other
# programs. Each different item in every column has an unique ID
# starting usually from zero.
# CPU
0
1
2
3

次に、CPUの論理番号とクロック数を表示してみます。

[root@server ~]# lscpu -p=CPU,MHZ
# The following is the parsable format, which can be fed to other
# programs. Each different item in every column has an unique ID
# starting usually from zero.
# CPU,Mhz
0,2808.006
1,2808.006
2,2808.006
3,2808.006

8 CPUのキャッシュの情報を表示する方法(-C)

-CはCPUのキャッシュ情報を表示するオプションです。

[root@server ~]# lscpu -C
NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL  SETS PHY-LINE COHERENCY-SIZE
L1d       32K     128K    8 Data            1    64        1             64
L1i       32K     128K    8 Instruction     1    64        1             64
L2         4M      16M   16 Unified         2  4096        1             64
L3        16M      64M   16 Unified         3 16384        1             64

-Cに指定できるオプションは以下のとおりです。

Available output columns for -C:
      ALL-SIZE  size of all system caches
         LEVEL  cache level
          NAME  cache name
      ONE-SIZE  size of one cache
          TYPE  キャッシュタイプ
          WAYS  ways of associativity
  ALLOC-POLICY  allocation policy
  WRITE-POLICY  write policy
      PHY-LINE  number of physical cache line per cache t
          SETS  number of sets in the cache; set lines has the same cache index
 COHERENCY-SIZE  minimum amount of data in bytes transferred from memory to cache

キャッシュの名前とタイプを表示してみます。

[root@server ~]# lscpu -C=NAME,TYPE
NAME TYPE
L1d  Data
L1i  Instruction
L2   Unified
L3   Unified

9 CPUの情報をJSON形式で表示する方法(-J)

-JはCPUの情報をJSON形式で表示するオプションです。

[root@server ~]# lscpu -J
{
   "lscpu": [
      {
         "field": "アーキテクチャ:",
         "data": "x86_64",
-snip-

10 物理 ID を表示する代わりに論理 ID を表示する方法(-y)

[root@server ~]# lscpu -y
アーキテクチャ:                        x86_64
  CPU 操作モード:                      32-bit, 64-bit
  Address sizes:                       45 bits physical, 48 bits virtual
  バイト順序:                          Little Endian
CPU:                                   4
  オンラインになっている CPU のリスト: 0-3
ベンダー ID:                           GenuineIntel
  BIOS Vendor ID:                      Red Hat
  モデル名:                            Intel Core Processor (Skylake, IBRS)
    BIOS Model name:                   RHEL 7.6.0 PC (i440FX + PIIX, 1996)
    CPU ファミリー:                    6
    モデル:                            94
    コアあたりのスレッド数:            1
    ソケットあたりのコア数:            1
    ソケット数:                        4
    ステッピング:                      3
    BogoMIPS:                          5616.00
    フラグ:                            fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2
                                        ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology cpuid tsc_known_freq pni pc
                                       lmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f1
                                       6c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stib
                                       p fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xget
                                       bv1 xsaves arat umip md_clear arch_capabilities
Virtualization features:
  ハイパーバイザのベンダー:            KVM
  仮想化タイプ:                        完全仮想化
Caches (sum of all):
  L1d:                                 128 KiB (4 instances)
  L1i:                                 128 KiB (4 instances)
  L2:                                  16 MiB (4 instances)
  L3:                                  64 MiB (4 instances)
NUMA:
  NUMA ノード数:                       1
  NUMA ノード 0 CPU:                   0-3
Vulnerabilities:
  Itlb multihit:                       Not affected
  L1tf:                                Mitigation; PTE Inversion
  Mds:                                 Mitigation; Clear CPU buffers; SMT Host state unknown
  Meltdown:                            Mitigation; PTI
  Mmio stale data:                     Vulnerable: Clear CPU buffers attempted, no microcode; SMT Host state unknown
  Retbleed:                            Mitigation; IBRS
  Spec store bypass:                   Mitigation; Speculative Store Bypass disabled via prctl
  Spectre v1:                          Mitigation; usercopy/swapgs barriers and __user pointer sanitization
  Spectre v2:                          Mitigation; IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS Not affected
  Srbds:                               Unknown: Dependent on hypervisor status
  Tsx async abort:                     Not affected

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ