Symuluj niemożliwy do zabicia proces w stanie D.

14

W przypadku scenariuszy testowania katastrof w środowisku serwera szukamy łatwego sposobu, aby proces utknął w stanie D (nieprzerwany sen).

Jakieś proste sposoby? Przykładowy kod C byłby plusem :)

Edycja - pierwsza odpowiedź jest częściowo poprawna, ponieważ proces jest w stanie D, ale nadal odbiera sygnały i może zostać zabity

er453r
źródło
W jakim systemie operacyjnym? A może szukasz przenośnego rozwiązania (nie jesteś pewien, czy takie istnieje)?
derobert
@mr_tron - to nie jest „nieprzerwane” :)
er453r
1
@derobert - przepraszam, że nie jestem precyzyjny - serwer Ubuntu 12.04.4
er453r
1
Dla tych, którzy szukają „działającego” rozwiązania, przejdź do stackoverflow.com/a/22754979/2182622
noname

Odpowiedzi:

2

Miałem ten sam problem i rozwiązałem go, tworząc moduł jądra, który utknął w stanie D.

Ponieważ nie mam żadnego doświadczenia w modułach, wziąłem kod z tego turorialu z pewnymi modyfikacjami znalezionymi gdzieś w esle .

Rezultatem jest urządzenie w / dev / memory, które zacina się podczas odczytu, ale można go obudzić, pisząc na nim (potrzebuje dwóch zapisów, nie wiem dlaczego, ale mnie to nie obchodzi).

Aby go użyć, po prostu:

# make
# make mknod
# make install
# cat /dev/memory   # this gets blocked

Aby odblokować, z innego terminala:

# echo -n a > /dev/memory
# echo -n a > /dev/memory

Makefile:

obj-m += memory.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

install:
    sudo insmod memory.ko

uninstall:
    sudo rmmod memory

mknod:
    sudo mknod /dev/memory c 60 0
    sudo chmod 666 /dev/memory

Kod pamięci. C:

/* Necessary includes for device drivers */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <linux/sched.h>

MODULE_LICENSE("Dual BSD/GPL");

/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file */
/* access functions */
ssize_t memory_write( struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
struct file_operations memory_fops = {
    .read = memory_read,
    .write = memory_write,
    .open = memory_open,
    .release = memory_release
};

/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);

/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;

int memory_init(void) {
    int result;

    /* Registering device */
    result = register_chrdev(memory_major, "memory", &memory_fops);
    if (result < 0) {
        printk(
                "<1>memory: cannot obtain major number %d\n", memory_major);
        return result;
    }

    /* Allocating memory for the buffer */
    memory_buffer = kmalloc(1, GFP_KERNEL); 
    if (!memory_buffer) { 
        result = -ENOMEM;
        goto fail; 
    } 
    memset(memory_buffer, 0, 1);

    printk("<1>Inserting memory module\n"); 
    return 0;

fail: 
    memory_exit(); 
    return result;
}

void memory_exit(void) {
    /* Freeing the major number */
    unregister_chrdev(memory_major, "memory");

    /* Freeing buffer memory */
    if (memory_buffer) {
        kfree(memory_buffer);
    }

    printk("<1>Removing memory module\n");

}

int memory_open(struct inode *inode, struct file *filp) {

    /* Success */
    return 0;
}

int memory_release(struct inode *inode, struct file *filp) {

    /* Success */
    return 0;
}
static DECLARE_WAIT_QUEUE_HEAD(wq);
static volatile int flag = 0;

ssize_t memory_read(struct file *filp, char *buf, 
        size_t count, loff_t *f_pos) { 

    printk("<1>going to sleep\n");
    flag = 0;
    //wait_event_interruptible(wq, flag != 0);
    wait_event(wq, flag != 0);

    printk("<1>Reading from memory module\n");
    /* Transfering data to user space */ 
    copy_to_user(buf,memory_buffer,1);

    /* Changing reading position as best suits */ 
    if (*f_pos == 0) { 
        *f_pos+=1; 
        return 1; 
    } else { 
        return 0; 
    }
}

ssize_t memory_write( struct file *filp, char *buf,
        size_t count, loff_t *f_pos) {

    char *tmp;

    printk("<1>wake someone up\n");
    flag = 1;
    //wake_up_interruptible(&wq);
    wake_up(&wq);

    printk("<1>Writting to memory module\n");
    tmp=buf+count-1;
    copy_from_user(memory_buffer,tmp,1);
    return 1;
}
Ryc
źródło
Niestety oba linki są martwe, a skopiowane tutaj pliki nie zawierają wszystkiego.
Dunatotatos
10

Od https://blogs.oracle.com/ksplice/entry/disown_zombie_children_and_the

Proces jest wprowadzany w nieprzerwany sen, (STAT D) gdy musi na coś czekać (zwykle we / wy) i nie powinien obsługiwać sygnałów podczas oczekiwania. Oznacza to, że nie możesz tego killzrobić, ponieważ wszystko, co zabija, to wysyłanie sygnałów. Może się to zdarzyć w prawdziwym świecie, jeśli odłączysz serwer NFS, podczas gdy inne komputery mają do niego otwarte połączenia sieciowe.

Możemy stworzyć własne nieprzerwane procesy o ograniczonym czasie trwania, wykorzystując vforkwywołanie systemowe. vforkjest jak fork, ale przestrzeń adresowa nie jest kopiowana od rodzica do dziecka, w oczekiwaniu na taki, execktóry po prostu wyrzuci skopiowane dane. Dogodnie dla nas, gdy vforkrodzic czeka nieprzerwanie (w drodze wait_on_completion) na dziecko execlub exit:

jesstess@aja:~$ cat uninterruptible.c 
int main() {
    vfork();
    sleep(60);
    return 0;
}
jesstess@aja:~$ gcc -o uninterruptible uninterruptible.c
jesstess@aja:~$ echo $$
13291
jesstess@aja:~$ ./uninterruptible
and in another shell:

jesstess@aja:~$ ps -o ppid,pid,stat,cmd $(pgrep -f uninterruptible)

13291  1972 D+   ./uninterruptible
 1972  1973 S+   ./uninterruptible

Widzimy dziecko ( PID 1973, PPID 1972) we śnie przerywanym, a rodzic ( PID 1972, PPID 13291- skorupkę) podczas nieprzerwanego snu, podczas gdy dziecko czeka 60 sekund.

Jedną zgrabną (psotną?) Cechą tego skryptu jest to, że procesy w nieprzerwanym trybie uśpienia przyczyniają się do średniej obciążenia komputera. Możesz więc uruchomić ten skrypt 100 razy, aby tymczasowo nadać maszynie średnią wartość obciążenia podwyższoną o 100, jak podaje uptime.

mikeserv
źródło
Dokładnie to, czego szukałem! Dziękuję Ci bardzo!
er453r
3
smutne jest to, że proces jest w stanie D, ale jestem w stanie go zabić za pomocą kill: /
er453r
@ er453r - przepraszam, stary. Nie wiem zbyt wiele na ten temat - odpowiedzią było kopiowanie / wklejanie, dlatego ustawiłem ją jako zawartość wiki społeczności . Przeczytałem twoje pytanie, sam byłem ciekawy, potem googlowałem i odkryłem coś, co uważałem za całkiem interesującą informację. To, co widzisz powyżej. Ale głosy i reszta nie przyczyniają się do mojej własnej reputacji, ponieważ jest ona opublikowana na wiki i dlatego, że ją ukradłem. Może na tej stronie znajduje się więcej informacji, które mogłyby wyjaśnić dlaczego?
mikeserv
Dzięki - przeczytałem to tak, jak napisałeś. Już szukałem tego w Internecie, ale każdy próbuje pozbyć się tych procesów, a nie ich tworzyć: P Ogólnie rzecz biorąc, wymiana stosów jest zawsze dla mnie ostatecznością :)
er453r
Tak, nadal mogę to zabić: - /
Leo Ufimtsev
2

Zasadniczo nie możesz. Przeczytaj ten artykuł, zatytułowany: TASK_KILLABLE: nowy stan procesu w systemie Linux .

fragment

Jądro Linux® 2.6.25 wprowadziło nowy stan procesu uśpienia procesów o nazwie TASK_KILLABLE, który oferuje alternatywę dla wydajnego, ale potencjalnie niemożliwego do zabicia TASK_UNINTERRUPTIBLE oraz łatwego do przebudzenia, ale bezpieczniejszego TASK_INTERRUPTIBLE.

To SO Pytania i odpowiedzi zatytułowane: Co to jest nieprzerwany proces? wyjaśnia to również.

Odkryłem to w bardzo interesującej książce zatytułowanej: Interfejs programistyczny systemu Linux: Podręcznik programowania systemu Linux i UNIX .

Florent
źródło
To nie znaczy, że nie możesz wyprodukować nie do zabicia procesu. Oznacza to po prostu, że liczba wywołań systemowych, których nie można zabić, spada, gdy te połączenia przechodzą na używanie nowego TASK_KILLABLEstanu.
Martijn Pieters