質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Q&A

解決済

2回答

3036閲覧

親プロセスを終了させずに、子プロセスを正常に終了させる方法

yoddy

総合スコア10

bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

0グッド

0クリップ

投稿2020/03/23 03:04

お世話になっております。
Raspberry Pi4にて、センサー制御とデータ取得のシステムを作成しています。
今回、教えていただきたいのは、タイトルのとおりで、
「無限ループ(待機)中の親プロセスから、トリガーによって呼び出された子プロセスを、親プロセスを終了させることなく、正常に終了させる方法」
です。

前提・実現したいこと

OS:Raspbian Buster
使用言語:python3.7、bash

ラズパイに複数センサーを接続し、そのデータを取得しています。
/etc/rc.localにbutton_rec.pyを追加して、起動時に実行されるようにしています。
start.shはGPIOピンに繋がったボタンの挙動を監視しており、
特定のボタンが押されると、start.shがセンサーを起動させ、そのデータをファイルに記述していき、stop.shで止める、ということが実現したいです。

ファイル配下の通りです。

button_rec.py ┣ start.sh ┃ ┣ sensor1(C++) ┃ ┗ sensor2.py ┗ stop.sh

前述したとおり、

  1. button_rec.pyはラズパイ起動時に自動起動しGPIOの状態を監視
  2. スタート用ボタンの押下で、start.shを呼び出す
  3. start.shはsensor1とsensor2.pyをバックグラウンドで実行させ、データを取得
  4. ストップ用ボタンの押下で、stop.shを呼び、sensor1とsensor2.pyを終了させる

という流れを期待しています。

  • ここで、sensor1とsensor2.pyはそれぞれ単体では機能しています。

また、当然ですが、それぞれを単体で呼び出したとき、Ctrl+C、あるいはkill -2 PIDで終了させても、期待通りにデータのログファイルは生成・記録されます。

  • start.shで呼び出した後、手動およびstop.shでsensor1、sensor2.pyを終了させると、データのログファイルは生成されるも、データが記録されません。

主なコードは以下のとおりです。

python

1#!/usr/bin/python3 2# -*- coding:utf-8 -*- 3 4import RPi.GPIO as GPIO 5import time 6import sys, os 7import ViewMessage as vm 8 9if __name__ == "__main__": 10 11 vm.set_HelloMsg() 12 13 try: 14 pin_start = 15 15 pin_stop = 23 16 17 GPIO.setmode(GPIO.BCM) 18 GPIO.setup(pin_start, GPIO.IN, pull_up_down = GPIO.PUD_UP) 19 GPIO.setup(pin_stop, GPIO.IN, pull_up_down = GPIO.PUD_UP) 20 21 while True: 22 button_blue = GPIO.input(pin_start) 23 button_yellow = GPIO.input(pin_stop) 24 25 cmd = "" 26 if button_blue == False: 27 GPIO.cleanup() 28 os.system("/home/pi/project/src/start.sh") 29 elif button_yellow == False: 30 GPIO.cleanup() 31 os.system("/home/pi/project/src/stop.sh") 32 if cmd != "": 33 ret = os.popen(cmd).readline().strip() 34 print(ret) 35 time.sleep(1) 36 37 time.sleep(1) 38 39 GPIO.cleanup() 40 41 except KeyboardInterrupt: 42 GPIO.cleanup() 43 sys.exit()

bash

1#!/bin/bash 2 3/home/pi/project/src/sensor1 > /home/pi/project/temp/"`date +%Y%m%d_%H%M%S`"_sensor1.txt & 4python /home/pi/project/src/sensor2.py > /home/pi/project/temp/"`date +%Y%m%d_%H%M%S`"_sensor2.txt &

bash

1#!/bin/bash 2 3PID=`pgrep start.sh` 4PIDS[0]=`pgrep python -n` 5PIDS[1]=`pgrep sensor1` 6 7echo $PID 8echo ${PIDS[0]} 9echo ${PIDS[1]} 10 11echo `sudo kill -2 -${PIDS[0]}` 12echo `sudo kill -2 -${PIDS[1]}`

試したこと

運用時のままの構成とプログラムでは問題がわかりにくいかと思い、以下のような構成でテストを行いました。

start.sh ┣ test1.py ┗ test2.py

コードは以下のとおりです。

bash

1# start.sh 2#!/bin/bash 3 4/usr/bin/python3 test1.py & 5/usr/bin/python3 test2.py & 6 7while :; 8do 9 echo "Now running..." 10 sleep 5 11done

python

1# test1.py 2#!/usr/bin/python3 3# -*- coding:utf-8 -*- 4 5import time 6import sys, os 7 8BASE_DIR = './' 9 10try: 11 path = BASE_DIR + "test1.txt" 12 with open(path, mode='w') as f: 13 while True: 14 msg = "Hello\n" 15 #print(msg) 16 f.write(msg) 17 time.sleep(1) 18 19except KeyboardInterrupt: 20 print("end") 21 #sys.exit() 22 os._exit(0)

python

1# test2.py 2#!/usr/bin/python3 3# -*- coding:utf-8 -*- 4 5import time 6import sys, os 7 8BASE_DIR = "./" 9try: 10 path = BASE_DIR + "test2.txt" 11 with open(path, mode="w") as f: 12 msg = "World!\n" 13 while True: 14 #print(msg) 15 f.write(msg) 16 time.sleep(1) 17 18except KeyboardInterrupt: 19 print("end") 20 #sys.exit() 21 os._exit(0)

実運用のものとの違いとしては、

  • よびだされるプログラムがこちらは両方共pythonのもの。
  • リダイレクトでファイルに記述していた出力を、python内で行う。

この構成でいくつかパターンを変えて実行しました。

  1. test1.pyまたはtest2.pyのみ実行
  2. start.shを実行

この時、1のパターンでは、kill -2 PIDではファイルが作成されますが、kill -KILL PIDではされませんでした。(このあたりは自明なのかもしれませんが、Linuxやシステムの知識が乏しいため、わかりませんでした)
2のパターンでは先にtest1.py、test.2.pyをkillしても、start.shをkillしてもファイルは作られても、記述はされませんでした。

タイトル、および冒頭に記述した通り、** start.shを止めることなく、ファイル作成・記述を行って二つの子プロセスを止める方法**はありますでしょうか?

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2020/03/23 05:00

> start.shを止めることなく、ファイル作成・記述を行って二つの子プロセスを止める方法はありますでしょうか? お書きのコードで出来ているのでは?不足があるなら、 ・こういう結果を期待した ・しかし、実際にはこういう結果だった のように、整理して書いてください。
yoddy

2020/03/23 05:16

ご教示ありがとうございます。 ただkillするだけだと問題ないのですが、記録がされないというのが致命的でした。 最初にご回答くださった方のソースで、解決いたしましたが、otn様にもお心砕きいただいたこと、感謝いたします。
otn

2020/03/23 05:20

では質問は、「子プロセスを止める際に、それまでの結果がファイルに書き込まれるようにする方法」と書くべきでしたね。
yoddy

2020/03/23 08:54

おっしゃるとおりですね。 次回からはタイトルもしっかりと推敲したいと思います。
guest

回答2

0

KeyboardInterruptは、端末に接続されたプロセスでしか効かないようです。代わりにsignal.signal()を使います。

os._exit()は、ファイルへの書き込みバッファのフラッシュを行いませんので、代わりにsys.exit()を使います。

Python

1import time, sys, os, signal 2 3BASE_DIR = './' 4 5def sigint(n,x): 6 print("end") 7 sys.exit(0) #通常のプログラム終了処理を行う 8 9signal.signal(2,sigint) #SIGINT時に実行する関数を登録する 10 11path = BASE_DIR + "test1.txt" 12with open(path, mode='w') as f: 13 while True: 14 msg = "Hello\n" 15 f.write(msg) 16 time.sleep(1)

kill -2 PIDではファイルが作成されますが、kill -KILL PIDではされませんでした。

ファイルがopenされてからkill -KILLされても一旦作られたファイルは消えませんので、ファイルが作成されてなかったと言うことなら、openする前にkillしてしまったのでしょう。

投稿2020/03/23 05:42

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yoddy

2020/03/23 09:02 編集

タイトル詐欺みたいでお手間を取らせたのに、詳しいご回答、ありがとうございます。 sys.exit()については、れこれググったりしている中で、os._exit()を見つけて、藁にもすがる気持ちで使ってみたのですが、バッファのフラッシュを行わないんですね。勉強になりました。 signal.signal() というのは初めて見ました。調べてみたいと思います。
guest

0

ベストアンサー

  1. start.shでバックグラウンドで起動したプロセスidをhoge.pidsに記録する
  2. stop.shは、hoge.pidsをみてkillする
  3. *pyは、write直後に都度flushする

わたしなら、こうするかな。

diff

1diff --git a/start.sh b/start.sh 2index c5a540d..d0a57a0 100644 3--- a/start.sh 4+++ b/start.sh 5@@ -1,8 +1,13 @@ 6 # start.sh 7 #!/bin/bash 8 9+rm ./hoge.pids 10+ 11 /usr/bin/python3 test1.py & 12+echo $! >> hoge.pids 13+ 14 /usr/bin/python3 test2.py & 15+echo $! >> hoge.pids 16 17 while :; 18 do 19diff --git a/stop.sh b/stop.sh 20index a288d3f..7c02e17 100644 21--- a/stop.sh 22+++ b/stop.sh 23@@ -1,12 +1,5 @@ 24-#!/bin/bash 25+#!/bin/sh 26 27-PID=`pgrep start.sh` 28-PIDS[0]=`pgrep python -n` 29-PIDS[1]=`pgrep sensor1` 30+kill -9 $(cat ./hoge.pids) 31 32-echo $PID 33-echo ${PIDS[0]} 34-echo ${PIDS[1]} 35- 36-echo `sudo kill -2 -${PIDS[0]}` 37-echo `sudo kill -2 -${PIDS[1]}` 38\ No newline at end of file 39+rm ./hoge.pids 40\ No newline at end of file 41diff --git a/test1.py b/test1.py 42index fa8398e..7f1b78f 100644 43--- a/test1.py 44+++ b/test1.py 45@@ -11,9 +11,10 @@ try: 46 path = BASE_DIR + "test1.txt" 47 with open(path, mode='w') as f: 48 while True: 49- msg = "Hello\n" 50+ msg = "Hello (%f)\n" % (time.time()) 51 #print(msg) 52 f.write(msg) 53+ f.flush() 54 time.sleep(1) 55 56 except KeyboardInterrupt: 57diff --git a/test2.py b/test2.py 58index 663ad7d..c06a926 100644 59--- a/test2.py 60+++ b/test2.py 61@@ -9,10 +9,11 @@ BASE_DIR = "./" 62 try: 63 path = BASE_DIR + "test2.txt" 64 with open(path, mode="w") as f: 65- msg = "Hello\n" 66+ msg = "Hello (2) (%f)\n" % (time.time()) 67 while True: 68 #print(msg) 69 f.write(msg) 70+ f.flush() 71 time.sleep(1) 72 73 except KeyboardInterrupt:

投稿2020/03/23 04:30

shiketa

総合スコア4061

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yoddy

2020/03/23 05:15

ご教示ありがとうございます。 出先ですが、いまテストしたところ、期待通りの動きをしてくれました。 実運用ファイルは、一つがC++のものですが、おそらくそちらも問題なく記録されるかと思います。 素早く、適切、かつ簡素なソースでご回答頂き、感謝いたします!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問