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

Address already in useの解決法 Linux版

事象

rails sでサーバを起動しようとすると、エラーが発生して、サーバーが起動できない。
エラー: 「port3000は既に使用されている」

`initialize': Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)

原因

使用したいport3000が過去の古いプロセスにより使用されており、新たなプロセスによるサーバー起動を邪魔している

対処法 : 利用ポートのバッティングを解消する

  1. エラーが発生しているポート番号が使用されているプロセスを確認
mao-no-MacBook-Air:hello_world_sample mao$ lsof -i:3000
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    30406  mao   12u  IPv4 0x982d8086b787a0bb      0t0  TCP localhost:hbci (LISTEN)
ruby    30406  mao   13u  IPv6 0x982d8086b93685bb      0t0  TCP localhost:hbci (LISTEN)

※ 「lsof」でオープンしているファイルを一覧表示 オプション「-i:ポート番号」を指定

2.起動済みプロセスを削除

mao-no-MacBook-Air:hello_world_sample mao$ kill 30406

3.サーバを再起動してエラーが出ないことを確認

mao-no-MacBook-Air:hello_world_sample mao$ rails s
/Users/mao/.rbenv/versions/2.7.1/lib/ruby/2.7.0/x86_64-darwin17/stringio.bundle: warning: already initialized constant StringIO::VERSION
=> Booting Puma
=> Rails 6.0.3.2 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.5 (ruby 2.7.1-p83), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Address already in useのエラーを解決する

事象

rails sでサーバを起動しようとすると、エラーが発生して、サーバーが起動できない。
エラー: 「port3000は既に使用されている」

`initialize': Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)

原因

使用したいport3000が過去の古いプロセスにより使用されており、新たなプロセスによるサーバー起動を邪魔している

対処法 : 利用ポートのバッティングを解消する

  1. エラーが発生しているポート番号が使用されているプロセス(PID)を確認
mao-no-MacBook-Air:hello_world_sample mao$ lsof -i:3000
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    30406  mao   12u  IPv4 0x982d8086b787a0bb      0t0  TCP localhost:hbci (LISTEN)
ruby    30406  mao   13u  IPv6 0x982d8086b93685bb      0t0  TCP localhost:hbci (LISTEN)

※ 「lsof」でオープンしているファイルを一覧表示 オプション「-i:ポート番号」を指定

2.起動済みプロセスを削除

mao-no-MacBook-Air:hello_world_sample mao$ kill 30406

3.サーバを再起動してエラーが出ないことを確認

mao-no-MacBook-Air:hello_world_sample mao$ rails s
/Users/mao/.rbenv/versions/2.7.1/lib/ruby/2.7.0/x86_64-darwin17/stringio.bundle: warning: already initialized constant StringIO::VERSION
=> Booting Puma
=> Rails 6.0.3.2 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.5 (ruby 2.7.1-p83), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ターミナルでは実行できるsendmailがcronで動作しなかった話

Linuxでsendmailを含んだシェルスクリプトがターミナルのコマンドでは動作するのにcronで動作しなかった時の対処法の備忘録です。

次のような書式でシェルを書いた後、cronに定時実行するように書き込みました。すると、コマンド上では正常動作しメールが送信されましたが、cronに書き込んだ定時になってもメールが送信されませんでした。

mail.sh
#!/bin/sh
{
echo "From: <****@gmail.com> "
echo "To: <****@gmail.com> "
echo "Subject: タイトル"
echo "Content-Type: text/plain;charset='UTF-8'"
echo "Content-Transfer-Encoding: base64"
}| sendmail -i -t

そこで、まずcronに問題があるのかと疑い、ファイル権限をchmod 777にした後、ログを確認してみましたがcronは問題なく動作しているようでした。

$ tail /var/log/cron
Aug  7 14:20:01 ip-***-**-*-** CROND[17995]: (root) CMD (/home/***/mail.sh)

問題はsendmailにあるらしいことが断定できたので調べ回ったところ、/usr/sbin/をつけたら問題なく動作しメールが送信され、gmail上では迷惑メールフォルダとして受信できていました。

mail.sh
#!/bin/sh
{
echo "From: <****@gmail.com> "
echo "To: <****@gmail.com> "
echo "Subject: タイトル"
echo "Content-Type: text/plain;charset='UTF-8'"
echo "Content-Transfer-Encoding: base64"
}| /usr/sbin/sendmail -i -t
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxにリンクローカルアドレスをアサインして DNS-SD/SSDPを使ってガサる

DHCPが無い環境で リンクローカルアドレス(IPv4)で自動的に上がってくるようなデバイスの
自動構成をしたくて Ubuntu/Debianで APIPAをふる方法と discoverのやりかた

準備

必要なパッケージのインストール

APIPAをふるだけなら avahi-autoipdがあれば目的としては達成できます

sudo apt-get install -y avahi-autoipd

名前やサービスの解決させたりもあるので 関連ツールとして avahi-daemon と avahi-utils、
さらに SSDPで検出したい場合なんかに gupnp-toolsも入れておく。

sudo apt-get install -y avahi-utils gupnp-tools

設定

eth2で使いたい場合、/etc/network/interfaces に以下を追加する。
この設定で eth2:avahi インターフェースに APIPAがふられる

/etc/network/interfaces
auto eth2
iface eth2 inet manual
  up /usr/sbin/avahi-autoipd -D --force-bind eth2
  down /usr/sbin/avahi-autoipd -k eth2

利用

interface を upするだけ

sudo ifup eth2

確認

root@buster:~# ip -4 a
...
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 169.254.5.253/16 brd 169.254.255.255 scope link eth2:avahi
       valid_lft forever preferred_lft forever

名前がわかっている物のIPを解決する

名前がわかっていれば mDNSで .local ドメインで名前解決する。
例えば fsというマシンを解決したければ ping fs.local

DNS-SDで サービス一覧を表示してみる

avahi-browse -al

実行するとこんな感じ

root@buster:~# avahi-browse -al
+   eth1 IPv4 amzn.dmgr:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:xxxxxxxxxx:##### _amzn-wplay._tcp     local
+   eth1 IPv4 fs - SSH                                      SSH Remote Terminal  local
+   eth1 IPv4 fs - SMB/CIFS                                 Microsoft Windows Network local
+   eth1 IPv4 fs - NFS - home                               Network File System  local
+   eth1 IPv4 Google-Home-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  _googlecast._tcp     local
+   eth1 IPv4 Chromecast-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   _googlecast._tcp     local

オプションにrをつけるとさらにIPやポートまで解決して表示してくれる

SSDPでデバイス一覧を表示してみる

インターフェースを指定して gssdp-discoverで行う

gssdp-discover -i eth2:avahi

root@buster:~# gssdp-discover -i eth2:avahi --timeout=3
Using network interface eth2:avahi
Scanning for all resources
Showing "available" messages
resource available
  USN:      uuid:1a78e91b-d86d-11ea-8157-080027e8f737::upnp:rootdevice
  Location: http://169.254.86.237:1900/ddd.xml
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linux から FPGA のメモリに"キャッシュを有効にして"アクセスするデバイスドライバ

はじめに

お断り

この記事で紹介するデバイスドライバは CPU のデータキャッシュの操作を arm64/arm のアセンブリ言語で実装しています。データキャッシュ操作は本来なら Linux Kernel のAPI を使うべきところですが、残念ながら使えるものがありませんでした(この点は『Linux から FPGA のメモリに"キャッシュを有効にして"アクセスする方法』@Qiita を参照)。

この記事はあくまでもちょっとやってみました的なトライアルの記事であることをご了承ください。

やりたかったこと

ZynqMP(ARM64) や Zynq(ARM) でPS(Processing System)部と PL(Programmable Logic) 部とデータをやりとりする際に、PL 側にBRAM 等のメモリを用意して、PS部のCPUからアクセスする方法があります。その際に次の条件を満たすようにすると便利です。

  1. CPU のデータキャッシュを有効に出来る。
  2. CPU のデータキャッシュの操作(Flush or Invalidiate) が手動でできる。
  3. Device Tree Overlay 等で Linux 起動後に自由に着脱できる。

普通にアクセスするだけならば uio を使えば可能です。しかし uio では条件1のCPU のデータキャッシュを有効に出来ないため、大量のデータを転送する場合は性能的に不利です。

また、参考「Accessing BRAM In Linux」で示す /dev/mem と reserved_memory を使う方法では、データキャッシュを有効にすることは出来ても、手動でキャッシュ操作ができないため、PL側とのデータのやりとりには向いていません。また、reserved_memory はLinux がブートする際にのみ指定できるので、Linux 起動後に自由に着脱できません。

やったこと

Linux から PL 側のメモリにキャッシュを有効にしてアクセスするデバイスドライバを試作してみました。名前は uiomem です。以下の URL で公開しています。(まだアルファ版の試作品です。)

この記事では以下のことについて説明します。

  • サンプルデザインによるデータキャッシュの効果確認
  • uiomem の紹介

データキャッシュの効果

この章では、PS 側から PL 側のメモリをアクセスする際にデータキャッシュがどのていどの効果があるのかを実際に計測して示します。

測定環境

計測に使用した環境は次の通りです。

PL 側には次のようなデザインを実装します。PL側に256KByte分のメモリを BRAM で実装し、そのインターフェースには Xilinx 社の AXI BRAM Controller を使っています。動作周波数は100MHz です。AXI BRAM Controller の AXI I/F と BRAM I/F の波形を観測するためにILA (Integrated Logic Analyzer) を接続しています。

Fig.1 PLBRAM-Ultra96 のブロック図

Fig.1 PLBRAM-Ultra96 のブロック図


これらの環境は github で公開しています。

データキャッシュオフ時のメモリライト

データキャッシュをオフにして memcpy() を使って PL 側の BRAM に 256KByte のデータを書き込むのに要した時間は 0.496 msec でした。約528MByte/sec の書き込み速度です。

そのときの AXI I/F の波形はつぎのようになりました。

Fig.2 データキャッシュオフ時のメモリライトの AXI IF 波形

Fig.2 データキャッシュオフ時のメモリライトの AXI IF 波形


波形をみてわかるとおり、バースト転送が行われていません(AWLEN=00)。1ワード(16bytes) ずつ転送していることがわかります。

データキャッシュオン時のメモリライト

データキャッシュをオンにして memcpy() を使って PL 側の BRAM に 256KByte のデータを書き込むのに要した時間は 0.317 msec でした。約827MByte/sec の書き込み速度です。

そのときの AXI I/F の波形はつぎのようになりました。

Fig.3 データキャッシュオン時のメモリライトの AXI IF 波形

Fig.3 データキャッシュオン時のメモリライトの AXI IF 波形


波形をみてわかるとおり、一回の書き込みで4ワード(64byte)のバースト転送が行われています(AWLEN=03)。

BRAM へのライトが発生するのは、CPU がライトした時ではありません。CPU がライトしたとき、まずデータキャッシュにデータが書き込まれて BRAM へはまだ書き込まれません。そして、マニュアルでデータキャッシュフラッシュ命令が実行されたときか、データキャッシュが一杯になって使われていないキャッシュを空けるときに初めて BRAM へライトが発生します。その際にデータキャッシュのキャッシュラインサイズ(arm64 では64byte) ごとにまとめてライトが行われます。

データキャッシュオフ時のメモリリード

データキャッシュをオフにして memcpy() を使って PL 側の BRAM から 256KByte のデータを読み込むのに要した時間は 3.485 msec でした。約75MByte/sec の読み込み速度です。

そのときの AXI I/F の波形はつぎのようになりました。

Fig.4 データキャッシュオフ時のメモリリードの AXI IF 波形

Fig.4 データキャッシュオフ時のメモリリードの AXI IF 波形


波形をみてわかるとおり、バースト転送が行われていません(ARLEN=00)。1ワード(16bytes) ずつ転送していることがわかります。

データキャッシュオン時のメモリリード

データキャッシュをオンにして memcpy() を使って PL 側の BRAM から 256KByte のデータを読み込むのに要した時間は 0.409 msec でした。約641MByte/sec の読み込み速度です。

そのときの AXI I/F の波形はつぎのようになりました。

Fig.5 データキャッシュオン時のメモリリードの AXI IF 波形

Fig.5 データキャッシュオン時のメモリリードの AXI IF 波形


波形をみてわかるとおり、一回の読み込みで4ワード(64byte)のバースト転送が行われています(ARLEN=03)。

CPU がメモリリードを行った際、データキャッシュにデータが無ければ、BRAM からデータを読んでキャッシュに充填します。その際にデータキャッシュのキャッシュラインサイズ(arm64 では 64byte)分をまとめて BRAM から読み出します。それ以降はデータキャッシュにデータがある限りデータキャッシュからデータがCPUに提供されるので BRAM へのアクセスは発生しません。そのため、データキャッシュがオフの時よりも高速にメモリリードが行われます。この環境ではデータキャッシュオフ時が75MByte/sec に対してデータキャッシュをオンにすると 641MByte/sec と大幅に性能が向上しました。

uiomem の紹介

uiomem とは

uiomemはユーザー空間からLinuxカーネル管理外のメモリ領域にアクセスするためのLinuxデバイスドライバです。uiomemには以下の機能があります。

  • uiomemはCPUキャッシュを有効にできるため、メモリに高速でアクセスできます。
  • uiomemは、CPUキャッシュを手動で無効にしてフラッシュできます。
  • uiomemは、Linuxカーネルから自由にアタッチおよびデタッチできます。

デバイスファイル(/dev/uiomem0など)を使用してユーザーメモリ空間にマッピングするか、read()/ write()関数を使用して、ユーザー空間からメモリにアクセスできます。

メモリ空間の開始アドレスとサイズは、デバイスツリーで指定するか、あるいは insmod コマンドによるデバイスドライバのロード時の引数で指定します。

対応プラットフォーム

  • OS:Linux Kernelバージョン4.19、5.4(作者は5.4でテスト済み)
  • CPU:ARM64 Cortex-A53(ザイリンクスZYNQ UltraScale + MPSoC)
  • CPU:ARMv7 Cortex-A9(ザイリンクスZYNQ)

インストール

insmod で uiomem をロードします。この際に Linuxカーネル管理外のメモリ領域を引数で指定することが出来ます。

shell$ sudo insmod uiomem.ko uiomem0_addr=0x0400000000 uiomem0_size=0x00040000
[  276.428346] uiomem uiomem0: driver version = 1.0.0-alpha.1
[  276.433903] uiomem uiomem0: major number   = 241
[  276.438534] uiomem uiomem0: minor number   = 0
[  276.442980] uiomem uiomem0: range address  = 0x0000000400000000
[  276.448901] uiomem uiomem0: range size     = 262144
[  276.453775] uiomem uiomem.0: driver installed.
shell$ ls -la /dev/uiomem0
crw------- 1 root root 241, 0 Aug  7 12:51 /dev/uiomem0

デバイスツリーによる設定

uiomem は insmod の引数でLinuxカーネル管理外のメモリ領域を指定する以外に、Linux のカーネルが起動時に読み込む device tree ファイルによってメモリ領域を指定する方法があります。device tree ファイルに次のようなエントリを追加しておけばinsmod でロードする際に自動的に /dev/uiomem0 が作成されます。

devicetree.dts
        #address-cells = <2>;
        #size-cells = <2>;
        uiomem_plbram {
            compatible  = "ikwzm,uiomem";
            device-name = "uiomem0";
            minor-number = <0>;
            reg = <0x04 0x00000000 0x0 0x00040000>;
        };

reg プロパティで メモリ領域を示します。reg プロパティの最初の要素(#address-cells が 2 の場合は2要素)がメモリ領域の先頭アドレスを示します。reg プロパティの残りの要素(#size-cells が2の場合は2要素)がメモリ領域の大きさをバイト数で示します。上記の例ではメモリ領域の先頭アドレスは 0x04_0000_0000、メモリ領域のサイズは 0x40000 です。

device-name プロパティでデバイス名を指定します。

minor-number プロパティで uiomem のマイナー番号を指定します。マイナー番号は0から255までつけることができます。ただし、insmodの引数の方が優先され、マイナー番号がかち合うとdevice treeで指定した方が失敗します。minor-number プロパティが省略された場合、空いているマイナー番号が割り当てられます。

デバイス名は次のように決まります。

  1. device-nameが指定されていた場合は、 device-name。
  2. device-nameが省略されていて、かつminor-numberが指定されていた場合は、sprintf("uiomem%d", minor-number)。
  3. device-nameが省略されていて、かつminor-numberも省略されていた場合は、devicetree のエントリー名(例ではuiomem_plbram)。

デバイスファイル

uiomem をカーネルにロードすると、次のようなデバイスファイルが作成されます。<device-name> には、前節で説明したデバイス名が入ります。

  • /dev/<device-name>
  • /sys/class/uiomem/<device-name>/phys_addr
  • /sys/class/uiomem/<device-name>/size
  • /sys/class/uiomem/<device-name>/sync_direction
  • /sys/class/uiomem/<device-name>/sync_offset
  • /sys/class/uiomem/<device-name>/sync_size
  • /sys/class/uiomem/<device-name>/sync_for_cpu
  • /sys/class/uiomem/<device-name>/sync_for_device

/dev/<device-name>

/dev/<device-name> は mmap() を使って、メモリ領域をユーザー空間にマッピングするか、read()、write() を使ってメモリ領域にアクセスする際に使用します。

uiomem_test.c
    if ((fd  = uiomem_open(uiomem, O_RDWR)) != -1) {
        iomem = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        uiomem_sync_for_cpu();
        /* ここで iomem にアクセスする処理を行う */
        uiomem_sync_for_device();
        close(fd);
    }

mmap() を使ってユーザー空間にマッピングする際は、データキャッシュの制御が必要になる場合があります。データキャッシュの制御は sync_for_cpu および sync_for_device で行います。これらについては後述します。

また、dd コマンド等でデバイスファイルを指定することで、shell から直接リードライトすることも出来ます。

shell$ dd if=/dev/urandom of=/dev/uiomem0 bs=4096 count=64
64+0 records in
64+0 records out
262144 bytes (262 kB, 256 KiB) copied, 0.00746404 s, 35.1 MB/s
shell$ dd if=/dev/uiomem0 of=random.bin bs=4096
64+0 records in
64+0 records out
262144 bytes (262 kB, 256 KiB) copied, 0.00578518 s, 45.3 MB/s

phys_addr

/sys/class/uiomem/<device-name>/phys_addr は メモリ領域の先頭アドレスが読めます。

size

/sys/class/uiomem/<device-name>/size はメモリ領域のサイズが読めます。

sync_direction

/sys/class/uiomem/<device-name>/sync_direction は uiomem のキャッシュ制御を手動で行う際のアクセス方向を指定します。

  • 0: リードライト双方向を指定します。
  • 1: ライトオンリー(PS to PL)であることを指定します。
  • 2: リードオンリー(PS from PL)であることを指定します。

sync_offset

/sys/class/uiomem/<device-name>/sync_offset はキャッシュ制御を手動で行う際の範囲の先頭をメモリ領域からのオフセット値で指定します。

sync_size

/sys/class/uiomem/<device-name>/sync_size はキャッシュ制御を手動で行う際の範囲のサイズを指定します。

sync_for_cpu

/sys/class/uiomem/<device-name>/sync_for_cpu は キャッシュ制御を手動で行う際、このデバイスファイルに0以外の値を書き込むことでCPU キャッシュを無効化(Invalidiate) します。このデバイスファイルは書き込みオンリーです。

このデバイスファイルに1を書いた場合、sync_directionが2(=リードオンリー)または0(=リードライト双方向)だった時、 /sys/class/uiomem/<device-name>/sync_offset と /sys/class/uiomem/<device-name>/sync_size で指定された範囲のCPUキャッシュが無効化されます。

uiomem_test.c
void  uiomem_sync_for_cpu(void)
{
    unsigned char  attr[1024];
    unsigned long  sync_for_cpu   = 1;
    if ((fd  = open("/sys/class/uiomem/uiomem0/sync_for_cpu", O_WRONLY)) != -1) {
        sprintf(attr, "%d",  sync_for_cpu);
        write(fd, attr, strlen(attr));
        close(fd);
    }
}

このデバイスファイルに書き込む値には、次のようにsync_offset、sync_size および sync_direction を含めることが出来ます。

uiomem_test.c
void uiomem_sync_for_cpu(unsigned long sync_offset, unsigned long sync_size, unsigned int sync_direction)
{
    unsigned char  attr[1024];
    unsigned long  sync_for_cpu   = 1;
    if ((fd  = open("/sys/class/uiomem/uiomem0/sync_for_cpu", O_WRONLY)) != -1) {
        sprintf(attr, "0x%08X%08X", (sync_offset & 0xFFFFFFFF), (sync_size & 0xFFFFFFF0) | (sync_direction << 2) | sync_for_cpu);
        write(fd, attr, strlen(attr));
        close(fd);
    }
}

この方式で指定した sync_offset、sync_size、sync_direction は一時的なものであり、デバイスファイルの /sys/class/uiomem/<device-name>/sync_offset 、/sys/class/uiomem/<device-name>/sync_size、/sys/class/uiomem/<device-name>/sync_direction の値には影響を与えません。

また、フォーマットの都合上、sync_offset および sync_size で指定できる範囲は32ビットで示せる範囲のみです。

sync_for_device

/sys/class/uiomem/<device-name>/sync_for_device は キャッシュ制御を手動で行う際、このデバイスファイルに0以外の値を書き込むことでCPU キャッシュをフラッシュします。このデバイスファイルは書き込みオンリーです。

このデバイスファイルに1を書いた場合、sync_directionが1(=ライトオンリー)または0(=リードライト双方向)だった時、 /sys/class/uiomem/<device-name>/sync_offset と /sys/class/uiomem/<device-name>/sync_size で指定された範囲のCPUキャッシュがフラッシュされます。

uiomem_test.c
void uiomem_sync_for_device(void)
{
    unsigned char  attr[1024];
    unsigned long  sync_for_device   = 1;
    if ((fd  = open("/sys/class/uiomem/uiomem0/sync_for_cpu", O_WRONLY)) != -1) {
        sprintf(attr, "%d",  sync_for_device);
        write(fd, attr, strlen(attr));
        close(fd);
    }
}

このデバイスファイルに書き込む値には、次のようにsync_offset、sync_size および sync_direction を含めることが出来ます。

uiomem_test.c
void uiomem_sync_for_device(unsigned long sync_offset, unsigned long sync_size, unsigned int sync_direction)
{
    unsigned char  attr[1024];
    unsigned long  sync_for_device  = 1;
    if ((fd  = open("/sys/class/uiomem/uiomem0/sync_for_device", O_WRONLY)) != -1) {
        sprintf(attr, "0x%08X%08X", (sync_offset & 0xFFFFFFFF), (sync_size & 0xFFFFFFF0) | (sync_direction << 2) | sync_for_device);
        write(fd, attr, strlen(attr));
        close(fd);
    }
}

この方式で指定した sync_offset、sync_size、sync_direction は一時的なものであり、デバイスファイルの /sys/class/uiomem/<device-name>/sync_offset 、/sys/class/uiomem/<device-name>/sync_size、/sys/class/uiomem/<device-name>/sync_direction の値には影響を与えません。

また、フォーマットの都合上、sync_offset および sync_size で指定できる範囲は32ビットで示せる範囲のみです。

データキャッシュ制御

PL 側のメモリを CPU からのみアクセス出来るメモリとして使うだけならば、データキャッシュを有効にするだけで済みます。しかし、PL 側のメモリを CPU 以外のデバイスがアクセスする場合や、PL 側のメモリを Linux 起動後に有効にしたり無効にしたりするには、データキャッシュを有効にするだけでは不十分です。データキャッシュと PL 側のメモリとのデータの不一致が起こりうるので、なんらかの方法でデータキャッシュと PL 側のメモリの内容を一致させる必要があります。

uiomemではデータキャッシュの制御を直接 arm64/arm のデータキャッシュ命令を使って実装しています。

uiomem.c
#if (defined(CONFIG_ARM64))
static inline u64  arm64_read_dcache_line_size(void)
{
    u64       ctr;
    u64       dcache_line_size;
    const u64 bytes_per_word = 4;
    asm volatile ("mrs %0, ctr_el0" : "=r"(ctr) : : );
    asm volatile ("nop" : : : );
    dcache_line_size = (ctr >> 16) & 0xF;
    return (bytes_per_word << dcache_line_size);
}
static inline void arm64_inval_dcache_area(void* start, size_t size)
{
    u64   vaddr           = (u64)start;
    u64   __end           = (u64)start + size;
    u64   cache_line_size = arm64_read_dcache_line_size();
    u64   cache_line_mask = cache_line_size - 1;
    if ((__end & cache_line_mask) != 0) {
        __end &= ~cache_line_mask;
        asm volatile ("dc civac, %0" :  : "r"(__end) : );
    }
    if ((vaddr & cache_line_mask) != 0) {
        vaddr &= ~cache_line_mask;
        asm volatile ("dc civac, %0" :  : "r"(vaddr) : );
    }
    while (vaddr < __end) {
        asm volatile ("dc ivac, %0"  :  : "r"(vaddr) : );
        vaddr += cache_line_size;
    }
    asm volatile ("dsb  sy"  :  :  : );
}
static inline void arm64_clean_dcache_area(void* start, size_t size)
{
    u64   vaddr           = (u64)start;
    u64   __end           = (u64)start + size;
    u64   cache_line_size = arm64_read_dcache_line_size();
    u64   cache_line_mask = cache_line_size - 1;
    vaddr &= ~cache_line_mask;
    while (vaddr < __end) {
        asm volatile ("dc cvac, %0"  :  : "r"(vaddr) : );
        vaddr += cache_line_size;
    }
    asm volatile ("dsb  sy"  :  :  : );
}
static void arch_sync_for_cpu(void* virt_start, phys_addr_t phys_start, size_t size, enum uiomem_direction direction)
{
    if (direction != UIOMEM_WRITE_ONLY)
        arm64_inval_dcache_area(virt_start, size);
}
static void arch_sync_for_dev(void* virt_start, phys_addr_t phys_start, size_t size, enum uiomem_direction direction)
{
    if (direction == UIOMEM_READ_ONLY)
        arm64_inval_dcache_area(virt_start, size);
    else
        arm64_clean_dcache_area(virt_start, size);
}
#endif

sync_for_cpu および sync_for_device はそれぞれアーキテクチャに依存した arch_sync_for_cpu() および arch_sync_for_device() を呼び出します。

参考

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

systemdを時間帯で管理したい!!

はいはいはいはい、皆さんこんにちは、○○です。
ええ、自粛期間中はYoutubeが捗りますね、いつも面白い動画をありがとうございます。

systemdを用いてデーモンを特定の時間帯だけ起動させる方法についてです。
意外と需要ありそうなのですが関連する記事が見当たらなかったので備忘録的に試行錯誤を記しておこうと思います。

やりたいこと

8時から23時の間だけデーモン(以下ダエモンさんことMr.daemon)を起動しておきたい。
その他の時間は消費電力削減のために停止しておきたい。
ちなみに、操作したいデーモンは自作のもので、起動中にffmpegでHLSを用いて音声をストリーミング配信し続けるというものです。
余談ですがサービスとデーモンの何が違うのか調べたら、どちらも常駐プログラムで、サービスはWindows系で使われデーモンはUnix系で使われるそうです。

まず思いついた方法

後ほど問題点を記しますがとりあえず。

起動編

systemdを用いたダエモンさんの起動方法は主に二つ挙げられると思います。

  • 自動起動 systemctl enable {Mr.daemon}
  • 手動起動 systemctl start {Mr.daemon}

ですが、これらの方法では時間を指定することはできません。
自動起動ではいつ何時も問答無用で起動してしまいますし、手動起動は言わずもがなです。
そこで時間を指定するためには以下の方法を使えるかと思います。

  • タイマー {Mr.daemon}.timer
  • cron 0 8 * * * systemctl start {Mr.daemon}

systemdのタイマー

systemdのタイマー機能を利用して特定の時間にダエモンさんを起動できます。

{Mr.daemon}.timer
[Unit]
#任意の説明
Description=Run {Mr.daemon}.service

[Timer]
#起動したい時刻
OnCalendar=*-*-* 8:00:00 #year-month-date hour:minute:second
#OnCalenderで指定した時刻にOSが寝ていた場合、次回起動時にデーモンを起動
Persistent=true 

[Install]
WantedBy=timers.target

このようにユニットファイルを記したらsudo systemctl enable {Mr.daemon}.timerでタイマーを有効にします。

管理者権限cron

管理者権限のcronでも時間を指定してsystemctl startを走らせることで同様のことができます。

  1. sudo crontab -e
  2. 右を追記 0 8 * * * systemctl start {Mr.daemon}

停止編

systemdのタイマーでは起動することはできても停止することはできないそうです。
ですので停止するためにはいずれにせよcronを使います。
管理者権限のcron0 23 * * * systemctl stop {Mr.daemon}と追記すればよいです。

上記方法の問題点

さて、上の方法で実装したところ幾つか不都合がありました。
それは8時から23時の間に再起動した後デーモンが起動しないという点です。
当然ですが始まりと終わりのトリガーしか設定していないので、例えばメンテナンス等で9時に再起動した場合は手動で起動しないと翌8時までダエモンさんは眠ったままです。
これでは特定の時間帯だけ稼働するデーモンとしては不十分です。

ではどうするか

デーモンの起動スクリプトをいじることにしました。

stream.sh
###追記部分
NOW=`date +%H` #現在時刻の取得
if [ $NOW -ge 23 -o $NOW -lt 8 ]; then #現在時刻が23時~翌8時の間なら
    systemctl stop {Mr.daemon} #デーモンを停止
    exit 0 #正常終了
fi
###

function fork() {
    #処理
}
fork > /dev/null 2>&1 </dev/null &
echo $! > /run/{Mr.daemon}.pid

追記部分で23時から翌8時の間にこのスクリプトが走ると処理から抜けるようにしてあります。
この状態でsudo systemctl enable {Mr.daemon}として、常に自動起動するようにします。
8時から23時の間では自動起動しますが、23時から翌8時の間は自動起動がキャンセルされるのでダエモンさんは眠ったままです。
最初に試したsystemdのタイマーとデーモンを停止するcronと組み合わせれば特定の時間帯に起動しているダエモンさんの出来上がりです。

解説

当初ifの中はexitだけでしたが、その状態だと起動が失敗したとエラーが吐かれてしまいました。
exit 0として正常終了を返しても同じでした。
別に問題ないといえばそれまでなのですが気持ちが悪いのでsystemctl stop {Mr.daemon}を記述したところ起動キャンセル扱いとなりエラーは出なくなりました。

cronでは?

試していませんがデーモン起動スクリプトをいじれなければ同様のことをcronでも実現できるでしょう。

@reboot if [ $NOW -lt 23 -a $NOW -ge 8 ]; then systemctl start {Mr.daemon}; fi

このように管理者権限cronに追記しておけば23時から8時の間にOSが起動された時ダエモンさんも起動します。
無駄に起動させないのでcronのほうがスマートな気もしますがAfterや依存関係などsystemd関連の順番を調べるのが面倒だったのでsystemdのほうで自動起動するようにしました。

最後に

どうも何れのやり方もスマートな感じがしないので良い方法をご存じの方がいらっしゃいましたらご教授ください。

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

【Linux】実際の職場でyumやrpmのパッケージ依存関係エラーを解決する場合の方法とは?

はじめに

職場にてyumやrpmにてパッケージをインストールする際に、依存関係エラーと格闘するシーンがよくありました。
(例えば、LinuxサーバにGNOMEデスクトップをインストールする場合等です。)
今回は、実際の職場でやっている依存関係エラーの対処方法についてアウトプットしていきたいと思います。

パッケージ依存関係エラーとは?

下記のようなエラーになります。

[root]# rpm -ivh compat-libstdc++-33-3.2.3-69.el6.i686.rpm
警告: compat-libstdc++-33-3.2.3-69.el6.i686.rpm: ヘッダ V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY
エラー: 依存性の欠如:
        libc.so.6 は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libc.so.6(GLIBC_2.0) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libc.so.6(GLIBC_2.1) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libc.so.6(GLIBC_2.1.3) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libc.so.6(GLIBC_2.2) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libc.so.6(GLIBC_2.3) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libgcc_s.so.1 は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libgcc_s.so.1(GCC_3.0) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libgcc_s.so.1(GCC_3.3) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libgcc_s.so.1(GLIBC_2.0) は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています
        libm.so.6 は compat-libstdc++-33-3.2.3-69.el6.i686 に必要とされています

実際の職場での環境

項目 内容
OS RHEL/CentOS
リポジトリ先 社内のローカルリポジトリ/ISOファイル

実際にやっていた対処方法

1.マイナーバージョンが上のローカルリポジトリ/ISOファイルをマウント

現場で一番最初に考えたトラブルシューティング方法になります。

例えば、パッケージインストールの際に依存関係エラーが表示されるとします。

①RHEL7.3(お客様の既存マシン)にRHEL7.3のISOを用いて、yumにてGNOMEデスクトップをインストールする
②依存関係エラーが表示され、インストール不可


このような依存関係エラーの対応の際に、実際にやっていたことはこちらになります。

①マイナーバージョンが上のISOをセッティング(例:RHEL7.6等)
②リポジトリ設定/キャッシュクリア/リポジトリ読み込み
③再度、yumにてGNOMEデスクトップをインストール



このように、マイナーバージョンが上のISOファイル(ローカルリポジトリ)をセッティングした場合、インストールができる場合があります。

※依存関係で必要とされているパッケージが1つだけ存在しない場合、別のマイナーバージョンのISOファイルに存在する可能性があります。

2.複数のリポジトリを利用してパッケージインストール

「1.マイナーバージョンが1つ上のローカルリポジトリ/ISOファイルをマウント」を実施しても、依存関係エラーが表示されてしまった場合の解決方法になります。

対応方法としては、このような形になります。

①複数のリポジトリ先にアクセスできるように設定(リポジトリの設定等)
②再度、yumにてインストール作業実施



例えば、社内でローカルリポジトリを持っている企業さんの場合は、
ローカルリポジトリを1つだけではなく、複数にアクセスできるように設定してやってみる形になります。



このように、複数のリポジトリを設定する形でやってみても、インストールができる場合があります。

3.どうしても依存関係エラーでインストールができない場合(最終手段)

依存関係エラーで検知されているパッケージをアンインストールします。

yum remove <原因のパッケージ>

こちらは、お客様の既存サーバ等では非推奨になります。

なぜなら、サービスに影響が出てしまう可能性があるからです。

パッケージ削除以外の方法を探る方が無難です。

※もしこちらの方法を実施する場合、削除しても問題ないパッケージかどうか必ず調査しましょう!

まとめ

依存関係エラーが出た場合の対処方法

1.マイナーバージョンが上のローカルリポジトリ/ISOファイルをマウント
2.複数のリポジトリを利用してパッケージインストール
3.どうしても依存関係エラーでインストールができない場合(最終手段)

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