![フューザーからシグナルを受信するCプログラムとシェルスクリプトの違い](https://rvso.com/image/1126777/%E3%83%95%E3%83%A5%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%8B%E3%82%89%E3%82%B7%E3%82%B0%E3%83%8A%E3%83%AB%E3%82%92%E5%8F%97%E4%BF%A1%E3%81%99%E3%82%8BC%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A8%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E9%81%95%E3%81%84.png)
私は大学の研究室用に以下のシェルスクリプトを書きました。これは、別のプロセスから頻繁に更新されるログファイルを調べ、呼び出し時に渡されるコピーをいくつか作成する必要があります。コードは次のとおりです (ログローテーション):
#!/bin/bash
# Usage:
# logrotate.sh [-n int] [-s signal] logfile
# where:
# int is an optional integer used to make int number of copies of logfile
# signal is the name of signal which shell command fuser must send to the process managing logfile
# this script lacks of a strong parameters checking
NCOPIES=4
LOGSIGNAL=USR1
#use of getopts to parse the arguments
while getopts "n:s:" OPTION ; do
case $OPTION in
n) NCOPIES="$OPTARG"
;;
s) LOGSIGNAL="$OPTARG"
;;
?) printf "Usage: %s [-n copies to keep] [-s signal to send] filename\n" $(basename $0) >&2
exit 1
;;
esac
done
#shift to read the last parameter (logfile)
shift $(($OPTIND - 1))
LOGFILE=$1
#create logfile.2 logfile.3 ... logfile.NCOPIES
for i in `seq $NCOPIES -1 1` ; do
test -f $LOGFILE.$i && mv $LOGFILE.$i $LOGFILE.$[ $i + 1 ]
done
mv $LOGFILE $LOGFILE.1
#sending signal to process which is writing to logfile to keep on writing to $LOGFILE(original name, without any extensions)
fuser -k -"$LOGSIGNAL" $LOGFILE.1
そこで私は2つのスクリプトを書き、1秒ごとにファイルに書き込みますログ:
-Cプログラム(ログテスト.c):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("log", O_WRONLY | O_APPEND);
if(fd < 0 ){
printf("Impossible to open file %s.\n", "log");
return -1;
}
for(;;){
if(write(fd, "Ciao bello mio\n", 15) != 15){
write(2, "Error in writing data.\n", 23);
}
sleep(1);
}
close(fd);
exit(0);
}
-そしてシェルスクリプト(ログテスト):
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done
起動すると
./logtest.sh および
./logrotate.sh ログ
スクリプトログローテーション正しい名前のファイルをすべて移動します(ログなるログ1)ファイルを所有するプロセスにシグナルを送信しますログその瞬間(つまりシェルスクリプトログテスト)その後、ファイルに書き込み続けますログさらに、fuser で送信する信号に違いはないようです。常に同じように反応します。
しかし、もし私が
./logtest と
./logrotate.sh ログ
Cプログラムではログテストコマンドからの信号を受信するフューザーそして終了します。
私の質問は、なぜ 2 つのログ プログラムが fuser から送信されたシグナルに対して異なる反応を示すのかということです。つまり、なぜ schell スクリプトは動作し続けるのに、C プログラムは終了するのかということです。
fuserのマニュアルページのRESTRICTIONSセクションにはこう書いてある。
-k オプションはプロセスに対してのみ機能します。
シェルスクリプトはシェル内で実際のプロセスとして扱われないのでしょうか?これは私にとっては新しいことです...インターネットで検索しましたが、それに関するページは見つかりませんでした。フューザー信号セクションの奥深くまで進みます。
答え1
スクリプトはファイル記述子logtest.sh
に書き込み、すぐに閉じるだけです。そのため、を呼び出すと、このファイルのアクティブなファイル記述子を持つプロセスは存在しません。log
fuser
log.1
これをシミュレートするには、while
ループをlist
(while true; do echo $(date); sleep 1; done) >> log
また、 とlogtest.sh
はどちらを送信してlogtest.c
も終了しますSIGNAL
。 はシグナルを処理しないからです。bash
を使用すると、 で実行できますtrap '<COMMAND>' USR1
( を参照してくださいman bash-builtins
)。ただし、これが C でどのように実行されるかはわかりません (C を学んだことはありません)。
答え2
問題は、fuser
カーネル内でファイル記述子が開かれているファイルを現在使用しているプロセスでのみ機能することです。
これはプログラムには当てはまりますがC
、スクリプトには当てはまりませんbash
。
echo $(date) >> log
ファイルを開いて、ファイルに追加し、すぐに閉じます。そのため、カーネルのチェックstdout
時にファイルが開かれているとは見なされません。fuser
bash
簡単な解決策としては、ループが終了するまでファイルを開いたままにするようにスクリプトを変更することですwhile
。
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done < log
この方法では、ループの開始時にファイル記述子log
が作成されwhile
、ループの終了まで開いたままになりますwhile
。