Разница между программами на Си и скриптами оболочки, получающими сигналы от фьюзера

Я написал сценарий оболочки ниже для лаборатории в моем колледже. Он должен смотреть на файл журнала, который часто обновляется другим процессом, и создавать количество копий, переданных при вызове. Вот код (logrotate.sh):

#!/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  

Поэтому я написал два сценария, которые каждую секунду записывают в файл журнала:
-C программа (logtest.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);  
}  

сценарий оболочки (logtest.sh):

#! /bin/bash  

while true 
do
    echo $(date) >> log
    sleep 1
done  

Когда я запускаю

./logtest.sh &
./logrotate.sh log

сценарий logrotate.sh перемещает все файлы с правильными именами (log становится log.1) и отправляет сигнал процессу, которому на данный момент принадлежит файл журнала (так что сценарий оболочки logtest.sh) продолжает записывать файл журнала. Кроме того, кажется, что нет разницы в том, какой сигнал я посылаю с фьюзером: он будет реагировать всегда одинаково.

Тем не менее, если я запускаю

./logtest &
./logrotate.sh log

случается, что logtest программы на C получает сигнал от команды fuser и затем завершает свою работу.

Мой вопрос: почему две программы регистрации имеют разные реакции на сигнал от фьюзера? Я имею в виду, почему скрипт Schell продолжает работать, а программа C завершается?

В справочной странице fuser в разделе ОГРАНИЧЕНИЯ говорится

Опция -k работает только для процессов.

Может ли быть так, что сценарии оболочки не считаются реальными процессами в оболочке? Это было бы новым для меня... Я искал в интернете, но нигде не найдено ни одной страницы о фьюзере.

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 конец цикла

Ваш сценарий 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).

Другие вопросы по тегам