Cześć. Mam problem z wysłaniem sygnału zamknięcia procesu z programu w języku C.
po numerze pid jest to tak:
kill(pid, SIGTERM)
A jak będzie, żeby nie wpisywać numeru PID tylko nazwę? Bo nie mogę znaleźć :(
Z góry dziękuję za pomoc.
Pozdrawiam.
Cześć. Mam problem z wysłaniem sygnału zamknięcia procesu z programu w języku C.
po numerze pid jest to tak:
kill(pid, SIGTERM)
A jak będzie, żeby nie wpisywać numeru PID tylko nazwę? Bo nie mogę znaleźć :(
Z góry dziękuję za pomoc.
Pozdrawiam.
W katalogu /proc masz pidy, a w numerze cmdline masz teoretycznie nazwę procesu wywołania.
Głównie chodzi o to, ze mam włączone dwa programy o nazwie test.out. Jak je zamknąć z poziomu programu po nazwie? nazwę wystarczy mi jak wpiszę na sztywno.
Kryterium systemu operacyjnego nie jest "nazwa procesu" tylko PID i jedyne co możesz zrobić to wysłać do konkretnego procesu identyfikowanego numerem PID sygnał.
Nie ma w systemie operacyjnym metod, które pozwalałyby wysłać sygnał do "procesu o nazwie XXX" chociażby z tego powodu, że PID danego procesu jest unikalny w danym momencie działania, a nazwa nie.
Jedyne wyjście to grepowanie i selekcjonowanie pidów, a następnie wysyłanie do nich sygnałów, co chociażby robi pkill:
pkill -9 test.out
Z nudów ci napisałem, niby działa.
class psaux(object):
def __init__(self):
self.list = []
self.open()
def printf(self):
for a in self.list:
print 'PID: %s NAME: %s' % (a[0], a[1])
def open(self):
import os
l = os.listdir('/proc')
for a in l:
if a.isdigit():
str = '/proc/%s/comm' % a
temp = open(str, 'rb').read()
self.list.append((a, temp))
if __name__ == "__main__":
a = psaux()
a.printf()
Może bardziej szczegółowo opisze o co chodzi. Napisałem program do obsługi kolejek. Klient - Serwer. I teraz jest taka sprawa, że odpalam 1 serwer i kilku klientów, jeżeli klient wyśle słowo koniec to serwer kończy działanie oraz ten klient który wysłał to słowo. No ale trzeba jeszcze zamknąć jakoś tego drugiego klienta, i właśnie nie bardzo wiem jak..
@marnit: Te odpowiedzi, które tutaj dostajesz są specyficzne dla danego systemu operacyjnego. Dla Linuksa masz katalog /proc z katalogami o numerach zgodnych z PIDami procesów (patrz, kod powyżej). We FreeBSD masz dla C elegancką bibliotekę libprocstat: https://www.freebsd.org/cgi/man.cgi?query=libprocstat&sektion=3. W obu zapewne dałoby się to wyciągnąć ze struktur pracującego kernela przez urządzenie /dev/kmem - tego podejścia gorąco nie polecam. Nieważne, jak tego dokonasz za każdym razem przelatujesz po liście procesów i pobierasz PIDy tych, których nazwa Ci pasuje i do nich ślesz sygnał.
Edit:
niech klient śledzi stan połączenia. Jak mu się serwer rozłączy niech spróbuje się podłączyć jeszcze raz, jak to się nie uda - serwer zmarł koniec pracy.
Edit2:
albo niech przed końcem pracy roześle instrukcję zamknięcia dla pozostałych. Jeśli ten program ma działać na więcej niż jednej maszynie, to nie masz innej drogi.
Zrób getpid i prześlij pid procesu i jak kipnie proces to ten drugi popełni samobojstwo
A kipnięciem możesz tak zrobić.
int func(){
return 1;
}
exit:
func();
asm("int 0x80");
int a = getpid(); // pid klienta
send(a); // wysyłasz do serwera
recv(b);
if (praca_end == true) kill(b);
W sensie, że w serwerze mam dodać:
if(wiadomosc.dziala == 0)
kill(wiadomosc.adres, SIGTERM);
Smród napisał(a):
A kipnięciem możesz tak zrobić.
int func(){
return 1;
}
exit:
func();
asm("int 0x80");
Nie, nie może, bo chociaż to ma szansę zadziałać na konkretnym kompilatorze z wyłączoną optymalizacją, w innej wersji kompilatora albo pod innym kompilatorem może nagle przestać. Nie ma żadnej gwarancji że kompilator wygeneruje mov eax,1 w miejscu wywołania func(), bo o to chyba ci chodziło.
W ogóle najlepiej nie używać wstawek asm(), tylko jeśli ma być fragment kodu w asemblerze, umieścić go w osobnym pliku źródłowym .asm czy .s i napisać funkcję całą w asemblerze.
@marnit pytanie co dokładnie chcesz uzyskać. Bo możesz zrobić kilka rzeczy, np. w momencie kiedy serwer umiera wysyła pozostałym odbiorcom odpowiednią wiadomość, po otrzymaniu której się wyłączą.
Można mordować po nazwie. W Linuksie robi to killall, jakby jego kod źródłowy podejrzeć to tak naprawdę pobiera on tablicę procesów z ich numerami PID, a potem morduje każdego, który spełnia warunki wyrażenia regularnego iterując po tej tablicy zwykłym forem.
Wreszcie opcja nr 3: jeżeli te programy mają działać lokalnie, dopisz sobie jakiś launcher do nich, tak żeby tenże program rozruchowy stał się liderem grupy. Wtedy wysyłasz sygnał do PIDu 0 i cześć pieśni.
W każdym przypadki zastanów się tylko jak mądrze wyłączyć program i po nim posprzątać, tzn. pozamykać deskryptory itd. I uważaj co wołasz w handlerze sygnału, tam są dość spore ograniczenia.
Wrzucam, załącznik bo z tego pdf'a nie da się skopiować normalnie
jeszcze coś takiego mam:
To pokaż jeszcze jak zrobiłeś samą komunikację.
Moim zdaniem to zła droga, ale jak to mówią chcącemu się krzywda nie dzieje:
http://src.gnu-darwin.org/src/usr.bin/killall/killall.c.html
ogólnie to man sysctl
Alternatywnie iterujesz po katalogach w /proc i szukasz tam odpowiedniego pliku cmdline ale to może nie zadziałac na BSD np.
Zacznijmy od tego, że polecenie w zadaniu jest miejscami, delikatnie mówiąc, niemądre: "Niech proces serwer tworzy kolejkę komunikatów, a po zakończeniu działania wszystkich klientów niech ją usuwa". Skąd mamy wiedzieć, że wszyscy klienci się odłączyli? Interfejs nam tego nie umożliwia. Mamy implementować specjalny protokół nad msg z notyfikacjami o podłączaniu/odłączaniu klienta? Ktoś zrobił chyba copy-paste z zadania z TCP.
Opakowywanie msgctl, msgget, msgsnd i msgrcv jest nonsensem moim zdaniem. Po co mamy sobie zduplikować funkcjonalność err(3)? Dodatkowo, pokomplikuje nam to kod, bo rozpoznajemy sytuację errno==EINTR.
Odnośnie reszty, to moim zdaniem takie rozwiązanie jest wystarczająco poprawne:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Można też użyć ftok. */
#define KEY 0xDEADC0DE
#define MAX_LEN 256
#define ROLE_CLIENT 0x00
#define ROLE_SERVER 0x01
/*
* Struktura używana przez nas do komunikacji.
*/
struct comm_msg {
long mtype;
struct msgbody{
pid_t sender;
char txt[MAX_LEN];
} body;
};
/*
* Pusta procedura na potrzeby obsługi SIGINT.
*/
static void
handle_sigint()
{
}
static void
run_client(const int msgid)
{
struct comm_msg msg;
printf("Enter message: ");
while (fgets((char *)&msg.body.txt, sizeof(msg.body.txt), stdin) != NULL) {
msg.mtype = ROLE_SERVER;
msg.body.sender = getpid();
if (msgsnd(msgid, &msg, sizeof(msg.body), 0) == -1)
err(1, "msgrcv(2) failed");
memset((void *)&msg.body.txt, '\0', sizeof(msg.body.txt));
/* Odbieramy komunikaty wyłącznie adresowane do nas. */
if (msgrcv(msgid, &msg, sizeof(msg.body), getpid(), 0) == -1)
err(1, "msgrcv(2) failed");
printf("Got: %sEnter message: ", msg.body.txt);
}
fprintf(stderr, "Client is exiting.\n");
}
static void
run_server(const int msgid)
{
int i;
struct comm_msg msg;
for (;;) {
if (msgrcv(msgid, &msg, sizeof(msg.body), ROLE_SERVER, 0) == -1) {
if (errno != EINTR)
err(1, "msgrcv(2) failed");
else
break;
}
i = 0;
while (i < MAX_LEN && msg.body.txt[i] != '\0') {
msg.body.txt[i] = toupper(msg.body.txt[i]);
i++;
}
msg.mtype = msg.body.sender;
msg.body.sender = ROLE_SERVER;
/* XXX: tutaj też można dostać EINTR. */
if (msgsnd(msgid, &msg, sizeof(msg.body), 0) == -1)
err(1, "msgsnd(2) failed");
}
fprintf(stderr, "Server is exiting.\n");
if (msgctl(msgid, IPC_RMID, NULL) == -1)
err(1, "msgctl(2) failed");
}
int
main(int argc, char **argv)
{
int msgid, role;
msgid = msgget(KEY, 0600 | IPC_CREAT);
if (msgid == -1)
err(1, "msgget(2) failed");
role = ROLE_SERVER;
if (argc > 1) {
if (strcmp(argv[1], "client") == 0) {
role = ROLE_CLIENT;
} else {
fprintf(stderr, "Say what?\n");
exit(1);
}
}
/*
* W sumie to oba procesy powinny mieć ustawiony ten handler. Server
* zareaguje za pomocą EINTR zwróconego z msgrcv. Klient powinien
* reagować na Ctrl+D.
*/
if (signal(SIGINT, handle_sigint) == SIG_ERR)
err(1, "signal(3) failed");
if (role == ROLE_SERVER)
run_server(msgid);
else
run_client(msgid);
return (0);
}