출력 리디렉션 대상이 사라지는 것을 방지

출력 리디렉션 대상이 사라지는 것을 방지

출력이 직렬 콘솔로 리디렉션되는 임베디드 Linux 장치에서 실행되는 데몬이 있습니다.

my_daemon > /dev/ttyS0

그러나 이제 사용자가 exit직렬 인터페이스에서 셸을 실행하면 직렬 장치가 다시 생성되어 직렬 장치가 일시적으로 사라지고 데몬이 충돌하게 됩니다.

이런 일이 발생하지 않도록 하는 (간단한) 방법이 있나요? 파이프 대상이 사라진 후 다시 연결을 시도하는 래퍼(또는 프로세스와 결합된 명명된 파이프)가 중간에 있을 수 있습니까? "오프라인" 시간을 버퍼링할 필요가 없습니다.

답변1

다음 쉘 함수를 사용하십시오.

relay () (
#!/bin/sh
sink="${1:-/dev/ttyS0}"
exec 4<&0 2>/dev/null
while :; do
   cat 3<"$sink" >/proc/self/fd/3
   <&4 cat >/dev/null & sleep 2; kill -s PIPE "$!" || exit
done
)

이와 같이:

my_daemon | relay

#!/bin/shshebang( )과 같은 것이 함수 내에서는 아무런 의미가 없는 것 같습니다 . 단지 코드가 어떤 쉘을 대상으로 하는지를 나타냅니다. 함수 대신 스크립트를 작성하려면 실행 파일에 함수 본문을 저장하면 shebang이 작동합니다.

몇 가지 팁이 있습니다:

  1. 리디렉션이나 유사한 리디렉션을 사용하는 cat >/dev/ttyS0솔루션 에는 결함이 있을 수 있습니다. "직렬 장치를 다시 생성"한다는 것은 /dev/ttyS0존재하지 않는 시간 창이 있다는 것을 의미한다고 가정합니다 . 또한 코드를 실행하는 사용자(아마도 루트)가 에 액세스할 수 있다고 가정합니다 /dev/. >/dev/ttyS0해당 파일이 없으면 일반 파일이 생성됩니다. 이렇게 테스트할 수는 있지만 ! [ -e /dev/ttyS0 ]테스트 전후에 파일이 사라질 수 있습니다 >/dev/ttyS0.

    따라서 나는 cat 3<"$sink" >/proc/self/fd/3기본적 $sink으로 를 사용합니다 /dev/ttyS0. 첫 번째 리디렉션은 읽기 위해 파일을 열려고 시도하고, 두 번째 리디렉션은 stdout을 동일한 파일로 리디렉션하려고 시도합니다. 비결은 두 번째 리디렉션이 새 파일을 생성하지 않는다는 것입니다.

    /dev/ttyS0또 다른 접근 방식은 사용자가 에 액세스할 수 있지만 에서 파일을 생성할 수 없도록 배열하는 것입니다 /dev/. 이 경우 이 트릭은 필요하지 않으며 사용된 솔루션은 >/dev/ttyS0안전해야 합니다. 어떤 이유로든 액세스할 수 없는 경우 이는 효과적인 방법이 될 수 있습니다 /proc/self/fd/3. 첫 번째 cat는 다음과 같습니다.

    cat >"$sink"
    
  2. 첫 번째 고양이의 목적은 수신자에게 데이터를 보내는 것입니다. 리디렉션이 실패하거나 수신자가 결국 사라질 수 있습니다. 여기 두 번째 것이 온다 cat. 두 번째 목적은 cat수신자가 없을 때 데이터를 삭제하는 것입니다. 즉시 다시 생성해야 한다면 /dev/ttyS0두 번째 항목은 필요하지 않습니다 cat. 그러나 귀하의 경우 경로 이름이 빨리 다시 나타날지 확실하지 않습니다. 내 생각에 없으면 대신 계속하고 /dev/ttyS0싶을 것입니다 .my_daemon예방하다. 두 번째는 cat계속 진행됩니다.

    단순한 cat >/dev/null것은 좋은 생각이 아니며 /dev/ttyS0다시 만든 후에도 무기한 실행될 수 있습니다. 비결은 비동기식으로 실행하고 몇 초 후에 종료하는 것입니다. 그러면 코드가 반복되고 첫 번째 코드가 cat싱크를 다시 열려고 시도합니다.

    <&4능력이 필요하기 때문에 필요한 것입니다. 작업 제어가 비활성화되면(기본적으로 함수가 있는 서브셸 또는 스크립트에 있음) 명령은 &표준 입력이 리디렉션되거나 /dev/null동등한 파일로 종료됩니다. 이전 exec 4<&0및 this 덕분에 <&4두 번째 것은 cat어쨌든 함수의 표준 입력에서 읽을 수 있습니다.

  3. kill두 번째 항목이 cat더 이상 존재하지 않으면 실패할 가능성이 높습니다 sleep 2. 나가면 my_daemon이런 일이 발생합니다 . kill … || exitEOF 조건을 감지하는 기술입니다. 예를 들어, date | relayDue에도 불구하고 종료되어야 합니다 sleep. 이 트릭이 없으면 코드는 무한히 반복됩니다.

    EOF의 경우 두 번째 PID가 cat재사용되어 kill잘못된 프로세스를 대상으로 할 수 있습니다. Linux의 AFAIK PID는 순차적으로 할당됩니다. 시퀀스가 ​​약 2초 안에 순환되어 동일한 PID를 얻을 가능성은 거의 없습니다. 트릭은 두 번째 항목이 catEOF로 인해 일찍 종료 되면 kill확인 no such process하고 실패하므로 exit그런 일이 발생한다고 가정합니다.

기본값은 입니다 $sink. 첫 번째 명령줄 인수로 지정하여 /dev/ttyS0다른 경로 이름(예: )을 계속 사용할 수 있습니다 . … | relay foo일반 파일을 테스트하려면 파일이 열려 있는 동안 삭제해야 합니다.그것을 파괴하지 마십시오. 내 테스트에서는 set -x무슨 일이 일어나는지 자주 확인하고 Ctrl+d cat요청 시 쉽게 종료(첫 번째).

내 테스트 플랫폼: Linux 커널 5.15.0, Busybox 1.30.1의 sh( ).ash

답변2

어쩌면 다음과 같은 것일 수도 있습니다.

버퍼 페이스트 프로그램

#!/bin/sh

while true;do
  cat buffer_file > /dev/ttyS0
done

그런 다음 다음을 실행하십시오.

my_daemon > buffer_file
./bufferpaster.sh

답변3

좋습니다. 정보가 없어도 strace유효한 장치 파일을 확인/기다릴 수 있습니다. 데몬이 줄 바꿈(for read)으로 끝나는 줄을 출력한다고 가정하면 다음과 같을 수 있습니다.

tty=/dev/ttyS0
my_daemon | ( 
    # Start with a read of 1 line, to catch when the daemon exits, and then "cat" the rest
    while read line; do 
        # check/wait for device file to exist and be writable
        until [ -w $tty ]; do sleep 1; done
        echo "$line"  >> $tty
        cat >> $tty
    done
)

>>테스트가 아닌 파일을 먼저 테스트해보고 싶으신 경우를 대비해 첨부합니다 .ttyS0

답변4

stdout을 로그 파일에 쓴 다음 initsystem inittab/systemd tail -F 로그 파일을 직렬 포트에 보냅니다. Init는 tail이 직렬 포트에서 실행되는지 확인하고 사용자는 데몬 작성에 집중할 수 있습니다. 로그 회전을 수행해야 하지만 일부 유틸리티(예: Piper, Multilog)를 사용할 수 있습니다. 다음은 stdout을 파이핑하여 로그 파일을 생성하는 방법에 대한 전체 설명입니다.https://superuser.com/questions/291368/log-rotation-of-stdout)

관련 정보