das IST "sigkill"Zitat:
die mit der höchsten Wahrscheinlichkeit sofort wirkt
es gibts nichts "höheres" in dem Sinne und es garantiert auch dass der Prozess danach weg ist, sonst wäre das ein linux bug
(oder du hast detach aufgerufen)
Druckbare Version
das IST "sigkill"Zitat:
die mit der höchsten Wahrscheinlichkeit sofort wirkt
es gibts nichts "höheres" in dem Sinne und es garantiert auch dass der Prozess danach weg ist, sonst wäre das ein linux bug
(oder du hast detach aufgerufen)
viel erfolg bei der suche
Um so etwas komplett "abschiessen" zu können wäre ein Child Process [3][4] möglich. Damit könntest du das komplett abschießen und neu starten (Forken). Die Kommunikation würde dann über bsw. Fifo [2] laufen.
Ich empfehle dir das Buch: "The Linux Programming Interface: A Linux and UNIX System Programming Handbook"
Leider geht es nicht ohne Theorie. Wenn die Praxis nicht zur Theorie passt, läuft alles total schief und führt zu den verschiedensten Problemen. Computer verhalten sich nun mal nur logisch nach den definierten API's. Bsw. Posix.
[1] ISBN-13: 978-1593272203: The Linux Programming Interface: A Linux and UNIX System Programming Handbook
[2] https://linux.die.net/man/4/fifo
[3] https://linux.die.net/man/2/fork
[4] http://man7.org/linux/man-pages/man2/kill.2.html
Leider auch nur sehr theoretisch, die man pages kenne ich überwiegend schon; außerdem arbeite ich mit wiringPi und seinen file wrappern und seinen handles/fd's, nicht mit nativen files.
Was ich inzwischen aber heraus bekommen habe:
wenn ein realtime thread mit SCHED_RR prio 40 oder 50 hängt (Bereich: 0-99), komme ich auf einem single core Raspi (B+, Zero) überhaupt nicht mehr an irgend etwas heran: das gesamte Programm blockiert dann vollständig.
Auf einem multicore (2B, 3B) geht das schon besser, dann muss es aber wschl von einem höherwertigen thread aus passieren.
Unklar ist, wie SCHED_FIFO / _OTHER etc threads vom Scheduler behandelt werden gegenüber dem hängenden thread, und von einer main() loop aus wird es möglichereise auch nicht gehen, wenn weitere high prio threads laufen, denn (edit) die main loop läuft mit nice=0 (+20...-19, vergleichbar mit SCHED_OTHER (by default), hier gibt es keine prios), und kein Mensch weiß offensichtlich, wie die prios und nices untereinander verrechnet werden ).
Es wird also per zusätzlichem "watcher_thread" laufen müssen, d.h. von einer höheren SCHED_RR prio aus, und es dürfen wschl KEINE SCHED_FIFO threads laufen, wozu du verlinkt hast.
Er muss dann also über einen Thread mit SCHED_RR prio >50 laufen, der alle anderen überwacht (edit: z.B. prio 70-90, aber langen delays/yields zwischendurch),
von dort den anderen mit thread_kill() abbrechen,
dann UART (fd=Serial) beenden,
dann zusätzlich joinen, damit der threadID Handle gelöscht wird,
alles an Variablen /Semaphoren zurücksetzen,
dann UART (fd=Serial) sicher neu starten (geht das?),
dann den vorher abgebrochenen Thread neu starten (geht das mit dem früheren fd=Serial und der früheren threadID, jetzt neu zu vergeben?)
und dann muss die UART Verbindung sich neu mit dem Arduino re-syncen (geht das, auch wenn zwischendurch der virtuelle USB COM Port weg war?)
Ich vermute, viele wird es hier nicht geben, die sich mit der Materie tatsächlich auskennen und einen sicheren Beispielcode posten können, auch wenn ich es gehofft hatte...
a ja, danke, aber die "Richtung" ist mir doch klar...
(gedacht war es ja für das Community Projekt RP6-Nachfolger, da wäre also Mitarbeit mit "echtem Code" auch für alle anderen sehr nützlich...)
(PS, edit: irgendwie "hinwurschteln" kann ich es ntl, die Frage ist nur, wie man es richtig und möglichst failsafe macht)
Ich kann heut Abend ein kleines Sample schreiben ;)
EDIT:
Das mit dem Subprozess kann sicher auch nach einem wegbrechen des ttys wieder starten. Wenn der tty einfach nicht mehr zu öffnen ist, dann wird auch das nichts werden.
Um das Arduino Protokol wieder zu syncen, muss halt das Protokoll "syncbar" sein. Der Arduino hat ja nichts davon mitbekommen.
Wie schaut denn das Protokoll aus?
dankeschön!
das Arduino-Prog iat hier: https://www.roboternetz.de/community...l=1#post652616
- - - Aktualisiert - - -
meine grobe Idee war (der heart beat ist noch nicht ausformuliert, nur der thread kill):
Code:void* WATCHER_thr(void*) // very high priority: thread watcher
{
while(_TASKS_ACTIVE_) {
// to do: UART_HEARTBEAT auswerten!
if (!UART_HEARTBEAT) { // delays sind nur grobe Ideen, als yield für andere threads!
pthread_kill(thread2, SIGKILL); // does not erase handle!
delay(10); // zusätzlich weitere Wartebedingung für kill?
pthread_join(thread2, NULL); // join, erase handle
delay(10);
serialClose( Serial); // close UART, fd=Serial
delay(10);
Serial = serialOpen (uart, UARTclock); // re-open Serial
delay(1);
pthread_create(&thread2, NULL, UART_thr, NULL); // restart UART by medium
param.sched_priority = 40;
pthread_setschedparam(thread2, SCHED_RR, ¶m);
delay(10);
}
delay(100); // long yield
}
return NULL;
}
// andere treads siehe Link oben!
//==================================================================
//==================================================================
int main() {
// threads
pthread_t thread0, thread1, thread2;
struct sched_param param;
printf("starting ..."); printf("\n");
// open UART Serial com port
Serial = serialOpen (uart, UARTclock);
// start multithreading
pthread_create(&thread0, NULL, WATCHER_thr, NULL); // very high priority: thread watcher
param.sched_priority = 80;
pthread_setschedparam(thread0, SCHED_RR, ¶m);
pthread_create(&thread1, NULL, KBD_thr, NULL); // low priority: keyboard monitor
param.sched_priority = 40;
pthread_setschedparam(thread1, SCHED_RR, ¶m);
pthread_create(&thread2, NULL, UART_thr, NULL); // medium priority: UART
param.sched_priority = 40;
pthread_setschedparam(thread2, SCHED_RR, ¶m);
while(_TASKS_ACTIVE_) { delay(10); }
// wait for threads to join before exiting
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
serialClose( Serial);
exit(0);
}
Hier ist mein fork() Sample. Es forkt und überwacht einen Subprozess. Ich habe auch einen tty_dummy für den string gebastelt der dein Protokoll nachahmt bzw. parst. Er sollte so auch wieder auf syncronisieren können (tty_dummy). Der Trick hier ist "find('\n')" und wenn der regex nicht matcht, das wegzuwerfen.
Die Funktion die im Subprozess aufgerufen wird, öffnet dann den tty und spricht mit dem Master nur über die pipes. Die tty_dummy() ist nur ein ungetestetes Skelett. Auf Arduino Seite würde ich das auch so ähnlich machen.
Das ganze wurde auf meinem Debianrechner getestet.
Das Testprogram zeigt:
Code:./restarttest
Started worker: 5433
Slave got command: 14
Got data from worker 4
Slave got command: 11
Got data from worker 3
Slave got command: 11
Got data from worker 3
Slave got command: 11
Got data from worker 3
Slave got command: 11
Got data from worker 2
Slave got command: 13
worker abort
Terminate worker: 5433
Started worker: 5434
Slave got command: 12
Got data from worker 1
Slave got command: 13
Got data from worker 1
Slave got command: 16
Got data from worker 3
Slave got command: 16
Got data from worker 3
Slave got command: 16
...
- - - Aktualisiert - - -Code:/******************************************************************************
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2019 Georg Gast <georg@schorsch-tech.de>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
******************************************************************************/
/******************************************************************************
Sample for Roboternetz.de:
pthread: was genau macht "joinable" und was macht "detached"?
https://www.roboternetz.de/community...macht-detached
Compile with:
-------------
g++ --std=c++11 restart.cpp -o restart
Explanation:
------------
This program creates a master process and forks a child process. These
two processes are connected by pipes. Just use plain POSIX.
+--------------------+
| Master |
+--------------------+
| ^
v |
+--------------------+
| worker |
+--------------------+
They exchange random integer bytes.
The master sends to the child random commands. If it does not get a response
within 100 millisec it sends SIGKILL to the worker that it can not prevent
from termination. After the worker is reaped, close the old pipes, create
new pipes and forks a new worker.
The worker starts to sleep, if its rng generates a 6, if it gets a 7
it calls std::abort(). In any other case it sends it to the master.
but just if SIMULATE_FAIL is 1.
The child rng uses this distribution scheme:
std::normal_distribution<> dis(3,2);
-2
-1 *
0 ***
1 *****
2 ********
3 **********
4 ********
5 ******
6 ***
7 *
8
Comments:
---------
If we need to send more data than PIPE_BUF (linux 64 bit 64kb, 32bit 4kb)
through the pipes, we need to use select(),poll() or epoll() and create a
multiplexed application then we could make sure the while loop in the
read_loop/write_loop can be avoided. It would also make an application
more expandable.
see: man 7 pipe
boost::asio would also be a nice solution to this problem and make it
platform independent.
The regex in the tty_dummy could be optimized to detect automaticly how
many ix=xxx are received.
******************************************************************************/
// if SIMULATE_FAIL is 1, the child simulates random failing
// if SIMULATE_FAIL is 0, the child should run forever
#define SIMULATE_FAIL 1
// std c++
#include <cassert>
#include <chrono> // clock
#include <cmath> // std::round
#include <functional>
#include <iostream>
#include <memory> // shared_ptr for FDHandle
#include <random>
#include <regex>
#include <string>
#include <thread> // sleep_for
#include <tuple>
// abort and runtime_error
#include <exception>
#include <stdexcept>
// linux + posix
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <limits.h> // PIPE_BUF
#include <signal.h>
#include <unistd.h>
// make sure we always close the fd (we MUST ensure
// no fd leak as the process is very long running)
using FDHandle = std::shared_ptr<void>;
FDHandle MakeFDHandle(int fd)
{
return FDHandle(reinterpret_cast<void *>(fd), [fd](void *) {
if (fd >= 0)
{
close(fd);
}
});
}
inline int GetFD(FDHandle handle)
{
size_t p = reinterpret_cast<size_t>(handle.get());
return static_cast<int>(p);
}
// chrono helper
using clk = std::chrono::system_clock;
using timepoint_t = clk::time_point;
inline timepoint_t now() { return clk::now(); }
// blocking write until we wrote all data
size_t write_loop(FDHandle fd, const void *buffer, size_t len)
{
assert(fd.get() != nullptr && GetFD(fd) >= 0);
assert(buffer != nullptr);
assert(len > 0);
assert(len < PIPE_BUF &&
"Because in case we got a signal and interrupt the write, we must "
"be able to get the data into the pipe");
ssize_t wrote_bytes = 0;
size_t total_wrote = 0;
while (total_wrote < len)
{
int f = GetFD(fd);
while ((wrote_bytes =
write(GetFD(fd),
reinterpret_cast<const char *>(buffer) + total_wrote,
len - total_wrote)) < 0 &&
errno == EINTR)
;
// EOF
if (wrote_bytes == 0)
{
throw std::runtime_error("write_loop EOF");
}
else if (wrote_bytes < 0)
{
perror("write()");
throw std::runtime_error("write() failed");
}
total_wrote += wrote_bytes;
}
assert(total_wrote == len);
return total_wrote;
}
// blocking read until we get some data
int read_loop(FDHandle fd, void *buffer, size_t len)
{
assert(fd.get() != nullptr && GetFD(fd) >= 0);
assert(buffer != nullptr);
assert(len > 0);
int read_bytes;
while ((read_bytes = read(GetFD(fd), buffer, len)) < 0 && errno == EINTR)
;
// EOF
if (read_bytes == 0)
{
throw std::runtime_error("read_loop EOF");
}
else if (read_bytes < 0)
{
perror("read()");
throw std::runtime_error("read() failed");
}
assert(read_bytes > 0);
return read_bytes;
}
// peek into the pipe and get the available bytes
int get_avail_bytes(FDHandle fd)
{
assert(fd.get() != nullptr && GetFD(fd) >= 0);
int avail_bytes = 0;
if (ioctl(GetFD(fd), FIONREAD, &avail_bytes) != 0)
{
perror("ioctl()");
throw std::runtime_error("ioctl() failed");
}
return avail_bytes;
}
// start the worker and give back the FDHandles to and from the worker
std::tuple<pid_t, FDHandle /* rfd */, FDHandle /* wfd*/>
start_worker(std::function<int(FDHandle rfd, FDHandle wfd)> fo)
{
assert(static_cast<bool>(fo) == true && "Function object must be valid");
int master_to_worker[2];
int worker_to_master[2];
if (pipe(master_to_worker) != 0)
{
perror("pipe() master_to_worker");
exit(EXIT_FAILURE);
}
if (pipe(worker_to_master) != 0)
{
perror("pipe() worker_to_master");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
switch (pid)
{
// fork failed:
case -1:
perror("fork()");
exit(EXIT_FAILURE);
// child
case 0:
{
// as the child we should close the not needed fds
close(master_to_worker[1]);
close(worker_to_master[0]);
exit(fo(MakeFDHandle(master_to_worker[0]),
MakeFDHandle(worker_to_master[1])));
}
// master
default:
{
// as a master we should close the not needed fds
close(master_to_worker[0]);
close(worker_to_master[1]);
return std::make_tuple(pid, MakeFDHandle(worker_to_master[0]),
MakeFDHandle(master_to_worker[1]));
}
}
// just to silence compiler warning: we never reach this point
return std::make_tuple(pid, MakeFDHandle(-1), MakeFDHandle(-1));
}
void kill_worker(pid_t &worker)
{
if (worker <= 0)
{
return;
}
// SIGKILL _CAN NOT_ be prohibited by the worker. It kills in any case
// the worker (if we have the right to send it this signal)
std::cout << "Terminate worker: " << worker << std::endl;
if (kill(worker, SIGKILL) != 0)
{
perror("kill()");
exit(EXIT_FAILURE);
}
// Just wait for this worker and reap the child process (prevent zombie
// process)
if (waitpid(worker, NULL, 0) != worker)
{
perror("waitpid()");
exit(EXIT_FAILURE);
}
worker = 0;
}
// signal handler for master
// here we can just use very limited number of functions
// see man signal
int selfpipe[2];
void onsignal(int signal)
{
switch (signal)
{
case SIGTERM:
case SIGQUIT:
write(selfpipe[1], " ", 1);
break;
case SIGCHLD:
break;
default:
break;
}
}
// master function. It should not fail or hang.
int master(std::function<int(FDHandle rfd, FDHandle wfd)> worker_fo)
{
assert(static_cast<bool>(worker_fo) == true &&
"Function object must be valid");
// install sighandler for SIGTERM, SIGQUIT, SIGCHLD
// with self pipe trick.
if (pipe2(selfpipe, O_NONBLOCK) != 0)
{
perror("pipe() selfpipe");
exit(EXIT_FAILURE);
}
auto rd_selfpipe = MakeFDHandle(selfpipe[0]);
auto wr_selfpipe = MakeFDHandle(selfpipe[1]);
signal(SIGCHLD, &onsignal);
signal(SIGTERM, &onsignal);
signal(SIGQUIT, &onsignal);
// make sure master and worker dont share the same rng sequence
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(11, 16);
auto last_bytes_read_at = now();
pid_t worker = 0;
FDHandle rfd;
FDHandle wfd;
bool run = true;
while (run)
{
// start the worker
if (worker == 0)
{
std::tie(worker, rfd, wfd) = start_worker(worker_fo);
last_bytes_read_at = now();
std::cout << "Started worker: " << worker << std::endl;
}
// generate a pseudo command
int cmd = dis(gen);
int wrote_bytes = write_loop(wfd, &cmd, sizeof(int));
assert(wrote_bytes == sizeof(int));
// check if the worker has wrote some data
int avail_bytes = 0;
while (avail_bytes == 0 &&
now() - last_bytes_read_at < std::chrono::milliseconds(100))
{
avail_bytes = get_avail_bytes(rfd);
if (avail_bytes == 0)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// check if worker failed
if (avail_bytes == 0)
{
kill_worker(worker);
continue;
}
// got data from the worker
int data;
int read_bytes = read_loop(rfd, &data, sizeof(int));
assert(read_bytes > 0);
std::cout << "Got data from worker " << data << std::endl;
last_bytes_read_at = now();
// check self pipe and reset run flag
if (get_avail_bytes(rd_selfpipe) > 0)
{
run = false;
}
}
// kill the worker, the pipes get closed by the handles
kill_worker(worker);
return EXIT_SUCCESS;
}
int failing_worker(FDHandle rfd, FDHandle wfd)
{
// worker dont get a signal handler as we want to manipulate it by the user
assert(rfd.get() != nullptr && GetFD(rfd) >= 0);
assert(wfd.get() != nullptr && GetFD(wfd) >= 0);
// make sure master and worker dont share the same sequence
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution<> dis(3, 2);
/*
-2
-1 *
0 ***
1 *****
2 ********
3 **********
4 ********
5 ******
6 ***
7 *
8
*/
while (1)
{
// read our command from the master
int cmd;
int read_bytes = read_loop(rfd, &cmd, sizeof(cmd));
assert(read_bytes > 0);
std::cout << "Slave got command: " << cmd << std::endl;
// send a response to the master (or sleep or abort)
int rnd = std::round(dis(gen));
#if SIMULATE_FAIL
if (rnd == 6)
{
// sleep forever: simulate hang
std::cout << "worker sleeping" << std::endl;
std::this_thread::sleep_until(timepoint_t::max());
}
else if (rnd == 7)
{
// just die: abnormal program termination
std::cout << "worker abort" << std::endl;
std::abort();
}
#endif
// simulate a read from uart and give it to master
int wrote = write_loop(wfd, &rnd, sizeof(rnd));
assert(wrote > 0);
}
return EXIT_FAILURE;
}
// an example of the tty receiver on the pi
int tty_dummy(FDHandle rfd, FDHandle wfd)
{
assert(rfd.get() != nullptr && GetFD(rfd) >= 0);
assert(wfd.get() != nullptr && GetFD(wfd) >= 0);
std::string rcv_buffer;
while (1)
{
// read data from the master
std::array<char, 256> tmp;
const int read_bytes = read_loop(rfd, &tmp[0], tmp.size());
for (int i = 0; i < read_bytes; ++i)
{
rcv_buffer += tmp[i];
}
// is there a /n
auto pos = rcv_buffer.find('\n');
if (pos != std::string::npos)
{
std::string line(rcv_buffer.begin(), rcv_buffer.begin() + pos);
// remove the line from the rcv buffer
rcv_buffer.erase(rcv_buffer.begin(), rcv_buffer.begin() + pos + 1);
// math this:
// §&i0=17628;&i1=1;&i2=17434;&i3=33;&i4=-444;&i5=5555;&i6=43690;
std::regex ex(
"^§&i0=([\\-0-9]++);&i1=([\\-0-9]+);&i2=([\\-0-9]+);&i3=([\\-0-"
"9]+);&i4=([\\-0-9]+);&i5=([\\-0-9]+);&i6=([\\-0-9]+);$");
std::smatch match;
if (!std::regex_match(line, match, ex))
{
std::cerr << "Unmatched line: " << line << std::endl;
continue;
}
std::cout << "Matched: ";
for (int i = 0; i < 7; ++i)
{
std::cout << "i" << i << "=" << match[i].str() << std::endl;
}
}
}
}
int main(int argc, char *argv[])
{
try
{
// start the master function
return master(&failing_worker);
}
catch (const std::exception &ex)
{
std::cerr << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception" << std::endl;
}
return EXIT_FAILURE;
}
@HaWe:
man 3 pthread_kill
Das bedeutet, das du nicht den thread töten kannst. Du rufst nur "kill" auf im Kontext des thread thread.Zitat:
NOTES
Signal dispositions are process-wide: if a signal handler is installed, the handler will be invoked in the thread thread, but if the disposition of
the signal is "stop", "continue", or "terminate", this action will affect the whole process.
- - - Aktualisiert - - -
Das hier zeigt es ganz gut:
http://openbook.rheinwerk-verlag.de/.../Kap10-011.htm
und wie geht es dann richtig, so wie beabsichtigt?
https://en.wikipedia.org/wiki/Signal_(IPC)#SIGKILL
- - - Aktualisiert - - -Zitat:
Zitat von wikipedia
Wie es richtig geht, zeigt mein Beispiel: Du kannst einen Thread nicht töten. Du kannst ihn nur so programmieren dass du ihn im jeden Fall sauber beenden kannst (join). Wenn das keine Option ist, geht nur der Subprozess den du mit SIGKILL immer töten kannst.
deinen Code verstehe ich leider überhaupt nicht - was macht der in einfachen Worten?
PS
was meinst du mit "Subprozess" den ich "immer töten kann" ?
Ich würde alle deine Threads als Subprozesse ausführen und mit ihnen mit Pipes, socketpair, posix mq oder was auch immer reden. Dann kannst du jeden deiner Prozesse abschießen und neu starten.
- - - Aktualisiert - - -
Er startet einen Subprozess per fork(), Sendet den Subprozess über eine pipe eine int Zahl. Der Subprozess ließt diese Zahl von der Pipe. Würfelt, bei 6 simuliert der Subprozess ein wait_forever(), bei 7 eine anormale Programmterminierung und in jedem anderen Fall schickt er es über die zweite Pipe zurück.
Das wiederholt sich endlos.
ich verstehe folgende Dinge nicht:
Subprozess
worker
Pipes
socketpair
posix mq
fork
wait_forever()
daher denke ich, das ist 2 Nummern zu schwierig für mich.
Da ich deinen Code nicht verstehe, kann ich auch meinen Code nicht anpassen und umschreiben, leider...
Es soll nun mal ein Thread, der irgendwie geblockt ist oder sich aufgehängt hat warum auch immer (hängt quasi in 1 Programmzeile fest, ohne dass er seine loop- (while() )-Scheife weiter durchläuft), einzeln beendet werden, ohne die Stabilität meines Programms zu stören, und dann soll er neu gestartet werden.
Hast du meine Code übersetzt bekommen?
habe ich ehrlich gesagt noch gar nicht probiert, denn ich weiß noch gar nicht, was da passiert und ob ich es nutzen kann.
Der Code von dir müsste in mein Beispiel eingesetzt werden, für meine Arduino-Raspi-UART-Kommunikation damit es für mich Sinn macht... :rolleyes:
Subprozess: Wenn du in der bash ein Program startest, ist das Program ein Subprozess deiner bash
worker, ein name für den Subprozess. Er soll einfach Arbeit verrichten
Alles IPC (Inter Prozess Kommunikation)
Pipes
socketpair
posix mq
fork(): Startet einen Subprozess. Siehe oben
wait_forever(): Das simuliert deinen hängenden Prozess der auf nichts anderes mehr reagiert.
was ist eine bash und was ist ein bash worker...?? :D
Mein Code ist voll mit Kommentaren. Die einzelnen Funktionen sind recht kurz. Versuch den Code zu übersetzen und beobachte ihn mit htop. Versuche die einzelnen Funktionen zu verstehen. Dann klappt das :)
sorry, was ist htop?
und wie geht das dann später, wenn mein thread (der, der u.U. mal hängt) in 1-ms loops 1 kB-Strings an den Arduino sendet und sofort wieder 1kB-Strings abholt (> 50 GPIOs und Sensorwerte lesen/schreiben in Echtzeit), und diese Strings (und ihre daraus errechneten/extrahierten Zahlenwerte) sofort auch für alle meine anderen Threads in Echtzeit (1ms) zur Verfügung stehen sollen?
htop: https://wiki.ubuntuusers.de/htop/
bash: Shell unter Linux. Wird gestartet wenn duc duch mit ssh oder vom GUI und dem Terminal mit deinem Pi verbindest.
Wenn der hängt, wird der Subprozess gekillt und ein neuer gestartet. Das macht mein Beispiel.
Die Werte die der Kollege per UART/tty/RS232 liefert werden einfach per Inter Prozess Kommunikation an die anderen Prozesse/threads weiter gegeben.
ach so, momentan starte ich mein Programm direkt aus der Geany IDE heraus (F5) - oder aus dem Filemanager (leafpad).
ssh verwende ich nie, ich kompiliere lokal auf dem Pi (mit der Geany IDE).
Ich verstehe aber auch das Konzept mit dem Subprozess nicht: heißt das, mein Programm startet ein 2.Programm?
ja: Aber das Programm das ausgeführt wird, ist nur eine andere Funktion in deinem Hauptprogramm. In meinem Beispiel failing_worker().
ok, danke -
ich werde mich jetzt am besten mit deinem Code mal 2 Jahre in eine Einsiedelei zurückziehen (mit Internet) und versuchen darüber zu meditieren...
(ich hatte ehrlich gesagt gehofft, du könntest meinen obigen Code mit einer Hand voll Zusatzzeilen so verbessern, dass er dennoch meinen hängenden Thread einzeln killt und neu startet... ;) )
Anhang 34223
Du siehst hier den "master()" und im Baum weiter unten den "worker" der die Funktion faiing_worker() ausführt.
- - - Aktualisiert - - -
Empfehlung zur Linux Programmierung hab ich weiter oben schon geschrieben. Das Buch ist sehr gut!
habe jetzt noch mal etwas genauer deinen Code studiert, aber nutzt du denn wirklich pthread? sieht irgendwie wie std::thread aus, das verwende ich aber ja gar nicht...
Nein, ich nutze keinen Thread. Nur fork ()
- - - Aktualisiert - - -
Bzw sleep_for um zu schlafen. Das ist c++11
ok, aber selbst wenn ich sähe, dass es funktioniert, dann wüsste ich dennoch nicht, wie ich das in meinen Code reinbringen soll.
Danke aber auf jeden Fall für die Mühe!
@HaWe: Das hier ist der komplette Code der Laufen soll?
https://www.roboternetz.de/community...l=1#post652711
Das bedeutet du hast nur das Protokoll zum AVR und einen Keyboard Thread und einen UART Thread. Richtig? Das ganze soll dann "irgendwie verheiratet werden" was aber noch nicht da ist.
Das schaut dann so aus:
Dazu soll dann noch vermutlich IO vom PI?Code:Aktueller Stand
+---------+ +-------------+ +--------+
| AVR |+---->ttyACM0----->| UART Thread |+---->| Logik |
+---------+<-----------------++-------------+<----+| |
| |
| |
+---------+ +-------------+ | |
| Keybrd |+----------------->| Kbrd Thread |+---->| |
+---------+<-----------------++-------------+<----+| |
| |
| |
| |
| |
+--------+
Hier kann man ASCII malen ;)
http://stable.ascii-flow.appspot.com
hallo,
das ist erst die Basis, die tokens werden dann ausgewertet und als Sensorwerte verarbeitet oder die entsprechenden GPIOs geschaltet.
Dadurch stehen dann dem Raspi zusätzlich z.B. die 16 ADCs und die 54 digital GPIOS des Mega zur Verfügung plus die Peripherie, die am Mega angeschlossen ist.
Es kommen dann weitere threads hinzu, z.B. für Encder lesen, Navigation aus Odometrie und IMU+GPS, Fernsteuerung, Bildschirmanzeige, PID-Regler, autonome Aktionen wie Wegefindung u/o SLAM.
PS,
ich bin an einer Sache dran, kritische threads asynchron zu starten und mit pthread_cancel ggf. zu stoppen...
Da du pthread_cancel ansprichts, hier gibt es etwas zu lesen für dich:
https://lwn.net/Articles/683118/
Das Problem das ich hier eben sehe, das mein Design mit den Prozessen nicht hat, sind nicht frei gegebene Betriebsystemresourcen wie bsp der offene Filedescriptor für ttyACM0. Da dein Projekt ja der RB6 ist und er recht lange laufen soll, könnte es passieren dass deinem Prozess die Filedescriptoren aus gehen, wenn diese nicht frei gegeben werden. Den Wert kann man erhöhen. Standardmäßig ist er glaub 1024, aber er kann nicht beliebig hoch gesetzt werden wegen begrenzter Resourcen auf dem Zielsystem.
Zur Erklärung ein Beispiel:
Wenn der ttyACM0 vom USB abgezogen wird, während der Thread hier einen Handle hat (auch wiringPi setzt auf dem Kernel auf), ist der FD immer noch offen. Du machst pthread_cancel und restart und du hast 2 offene FD's. Den alten und den neuen FD. Zumindest in der Filedescriptor Tabelle des Prozesses. Das kannst du sehen wenn du ein "ls /proc/${deine PID}/fd" ansiehst.
Da das Projekt auf dem Pi noch recht am Anfang steht, würde ich das versuchen zu beachten. Wenn du sagst, das ist für dich kein Thema, dann ok :)
hallo,
danke für die Infos!
den int fd bekomme ich über wiringSerial: ich öffne ja UART per fd=serialOpen("/dev/ttyACM0", clock);
und wenn ich den Thread abbrechen musste, hänge ich sicherheitshalber noch ein serialClose(fd) hinten dran - dann müsste ja der fd wieder gelöscht/freigegeben sein, oder?
(erst danach öffne ich dann per fd=serialOpen("/dev/ttyACM0", clock) erneut.)
edited
Das könnte klappen... :)
Du musst dich aber um alle Resourcen kümmern. Am besten alles im Heap halten dann bei Close(fd) auch freigeben. std::string und co jegen große Sachen aber auch im Heap ab. Damit wird der Speicher voll und voller...
Edit: Heap: Frei Speicher.
beim FreigebenCode:std::string* buffer = new std::string;
Code:Close(fd);
delete buffer;
...
abere signore, isch 'abe doche gare keine std::threade :p
Das ist egal, auch dein pthread den du mit pthread_cancel abbrichst, muss die Resourcen frei geben.
theoretisch klar, aber ich weiß nicht wie, andererseits: so viel reservierten Speicher gibt es ja gar nicht.
Ich kann auch einige Variablen sicherheitshalber global deklarieren.
Das ist ein grundsätzliches Problem. Am Anfang glaubt man, man kann sich die Interprozesskommunikation sparen, wenn man Threads statt Tasks benutzt. Man kann einfach globale Variable verwenden. Dazu kommt noch die verbreitete Meinung, Taskwechsel verschenken Zeit gegenüber Threadwechseln. Irgendwann, wenn das System unübersichtlich geworden ist und sich z.B. die ersten Memoryleaks oder Deadlocks eingeschlichen haben, fängt man an, eine Thread- und Speicherüberwachung zu programmieren. Zum Schluß stellt man dann fest, daß man ein eigenes Betriebssystem programmiert hat, das aber längst auf dem System vorhanden ist. Das eigene ist auch nicht einfacher als das vorhandene, dafür ist es nur von einem selbst getestet und es fehlen hunderte Seiten man-Pages. Wer schreibt schon sowas zum eigenen Code? Wegen der Threadüberwachung ist es sogar langsamer als ein System, das auf Tasks basiert.
Daher sollte man am Anfang des Systemdesigns gut überlegen, ob es Sinn macht, ein eigenes Tasking-System (genauso wie eigene Kommunikationsprotokolle oder eine eigene Datenserialiesung) zu erfinden. Am Ende lauert immer das komplette Fiasko.
MfG Klebwax