Mô phỏng một quá trình không thành công ở trạng thái D


14

Đối với các kịch bản thử nghiệm thảm họa trên môi trường máy chủ, chúng tôi đang tìm kiếm một cách dễ dàng để làm cho quá trình bị kẹt trong trạng thái D (giấc ngủ không bị gián đoạn).

Cách nào dễ? Một ví dụ mã mẫu C sẽ là một điểm cộng :)

Chỉnh sửa - câu trả lời đầu tiên là bán chính xác, vì quá trình được hiển thị ở trạng thái D, nhưng nó vẫn nhận được tín hiệu và có thể bị giết



Trên hệ điều hành nào? Hoặc bạn đang tìm kiếm một giải pháp di động (không chắc chắn nếu có)?
derobert

@mr_tron - đây không phải là "không bị gián đoạn" :)
er453r

1
@derobert - xin lỗi vì không chính xác - máy chủ Ubuntu 12.04.4
er453r

1
Đối với những người đang tìm kiếm một giải pháp "làm việc", hãy truy cập stackoverflow.com/a/22754979/2182622
noname

Câu trả lời:


2

Tôi đã có cùng một vấn đề và giải quyết nó bằng cách tạo một mô-đun hạt nhân bị kẹt ở trạng thái D.

Vì tôi không có bất kỳ kinh nghiệm nào về các mô-đun, tôi đã lấy mã từ khu vực này với một số sửa đổi được tìm thấy ở đâu đó .

Kết quả là một thiết bị bật / dev / bộ nhớ bị kẹt khi đọc nhưng có thể bị đánh thức khi viết trên thiết bị (nó cần hai lần ghi, tôi không biết tại sao nhưng tôi không quan tâm).

Để sử dụng nó chỉ:

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

Để bỏ chặn, từ một thiết bị đầu cuối khác:

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

Trang điểm:

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

Mã cho bộ nhớ.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;
}

10

Từ https://bloss.oracle.com/ksplice/entry/disown_zombie_children_and_the

Một quá trình được đặt trong một giấc ngủ không bị gián đoạn (STAT D) khi nó cần chờ đợi một thứ gì đó (thường là I / O) và không nên xử lý tín hiệu trong khi chờ đợi. Điều này có nghĩa là bạn không thể kill, bởi vì tất cả các lệnh giết đều gửi tín hiệu. Điều này có thể xảy ra trong thế giới thực nếu bạn rút máy chủ NFS trong khi các máy khác có kết nối mạng mở với nó.

Chúng ta có thể tạo các quy trình liên tục trong thời gian giới hạn của riêng mình bằng cách tận dụng vforkcuộc gọi hệ thống. vforkgiống như fork, ngoại trừ không gian địa chỉ không được sao chép từ cha mẹ vào con, với dự đoán execsẽ xóa dữ liệu được sao chép. Thuận tiện cho chúng tôi, khi bạn vforkcha mẹ chờ đợi liên tục (bằng cách wait_on_completion) trên đứa trẻ exechoặc 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

Chúng tôi thấy đứa trẻ ( PID 1973, PPID 1972) trong một giấc ngủ gián đoạn và cha mẹ ( PID 1972, PPID 13291- vỏ) trong một giấc ngủ không bị gián đoạn trong khi nó chờ đợi 60 giây trên đứa trẻ.

Một điều gọn gàng (tinh nghịch?) Về kịch bản này là các quá trình trong một giấc ngủ không bị gián đoạn góp phần vào mức trung bình tải cho một máy. Vì vậy, bạn có thể chạy tập lệnh này 100 lần để tạm thời cung cấp cho máy một tải trung bình tăng 100, như báo cáo của uptime.


Chính xác những gì đang tìm kiếm! Cảm ơn rât nhiều!
er453r

3
điều đáng buồn là quá trình này ở trạng thái D, nhưng tôi có thể giết nó bằng kill: /
er453r

@ er453r - xin lỗi, anh bạn. Tôi không biết nhiều về nó một cách trung thực - câu trả lời chỉ là một bản sao / dán, đó là lý do tại sao tôi đặt nó làm nội dung wiki cộng đồng . Tôi đọc câu hỏi của bạn, bản thân tôi tò mò, sau đó làm một số việc và bật lên những gì tôi nghĩ là một số thông tin khá thú vị. Đó là những gì bạn thấy ở trên. Nhưng phiếu bầu và phần còn lại không đóng góp cho danh tiếng của tôi vì nó bị ảnh hưởng và vì tôi đã đánh cắp nó. Có lẽ có thêm thông tin trên trang đó có thể giải thích tại sao?
mikeerv

Cảm ơn - Tôi đã đọc nó giống như bạn đã đăng nó. Tôi đã tìm kiếm trên internet về điều này, nhưng mọi người đều cố gắng loại bỏ các quy trình này, không tạo ra chúng: P Nói chung, trao đổi ngăn xếp luôn là phương sách cuối cùng đối với tôi :)
er453r

Phải, tôi vẫn có thể giết cái này: - /
Leo Ufimtsev

2

Về cơ bản, bạn không thể. Đọc bài viết này, có tiêu đề: TASK_KILLABLE: Trạng thái quy trình mới trong Linux .

đoạn trích

Linux® kernel 2.6.25 đã giới thiệu một trạng thái quy trình mới để đưa các quy trình vào trạng thái ngủ gọi là TASK_KILLABLE, cung cấp một giải pháp thay thế cho TASK_UNINTERRUPTIBLE hiệu quả nhưng có khả năng đánh thức dễ dàng nhưng an toàn hơn nhưng TASK_INTERRUPTIBLE.

Câu hỏi và trả lời SO này có tiêu đề: một quá trình không thể gián đoạn là gì? cũng giải thích nó.

Tôi phát hiện ra điều này trong cuốn sách rất thú vị này có tiêu đề: Giao diện lập trình Linux: Cẩm nang lập trình hệ thống Linux và UNIX .


Điều đó không có nghĩa là bạn không thể tạo ra một quy trình không thành công. Điều đó chỉ có nghĩa là số lượng cuộc gọi hệ thống không thể thực hiện được sẽ giảm xuống khi những cuộc gọi đó chuyển sang sử dụng TASK_KILLABLEtrạng thái mới .
Martijn Pieters
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.