Apa manfaat dari OS non-preemptive? dan harga untuk manfaat ini?

14

Untuk MCU logam berduri, Dibandingkan dengan kode buatan sendiri dengan latar belakang loop ditambah arsitektur interupsi timer, apa manfaat dari OS non-preemptive? Apa di antara manfaat ini yang cukup menarik bagi sebuah proyek untuk mengadopsi OS non-preemptive, daripada menggunakan kode buatan sendiri dengan arsitektur loop latar belakang?
.

Penjelasan untuk Pertanyaan:

Saya sangat menghargai semua yang telah menjawab pertanyaan saya. Saya merasa jawabannya sudah hampir sampai. Saya menambahkan penjelasan ini ke pertanyaan saya di sini yang menunjukkan pertimbangan saya sendiri dan dapat membantu mempersempit pertanyaan atau membuatnya lebih tepat.

Apa yang saya coba lakukan adalah memahami bagaimana memilih RTOS yang paling tepat untuk proyek secara umum.
Untuk mencapai ini, pemahaman yang lebih baik tentang konsep dasar dan manfaat paling menarik dari berbagai jenis RTOS dan harga yang sesuai akan membantu, karena tidak ada RTOS terbaik untuk semua aplikasi.
Saya membaca buku tentang OS beberapa tahun yang lalu tetapi saya tidak memilikinya lagi. Saya mencari di internet sebelum memposting pertanyaan saya di sini dan menemukan informasi ini sangat membantu: http://www.ustudy.in/node/5456 .
Ada banyak informasi bermanfaat lainnya seperti pengantar di situs RTOS yang berbeda, artikel yang membandingkan penjadwalan pre-emptive dan penjadwalan non-preemptive, dan lain-lain.
Tapi saya tidak menemukan topik yang disebutkan ketika memilih RTOS non-preemptive dan kapan lebih baik hanya menulis kode Anda sendiri menggunakan timer interrupt dan loop latar belakang.
Saya punya jawaban sendiri, tetapi saya tidak cukup puas dengan jawaban itu.
Saya benar-benar ingin mengetahui jawaban atau pandangan dari orang-orang yang lebih berpengalaman, terutama dalam praktik industri.

Pemahaman saya sejauh ini adalah:
tidak masalah menggunakan atau tidak menggunakan OS, jenis kode penjadwalan tertentu selalu diperlukan, bahkan itu dalam bentuk kode seperti:

    in the timer interrupt which occurs every 10ms  
    if(it's 10ms)  
    {  
      call function A / execute task A;  
    }  
    if(it's 50ms)  
    {  
      call function B / execute task B;  
    }  

Manfaat 1:
OS non-preemptive menunjuk cara / gaya pemrograman untuk kode penjadwalan, sehingga insinyur dapat berbagi pandangan yang sama bahkan jika mereka tidak berada dalam proyek yang sama sebelumnya. Kemudian dengan pandangan yang sama tentang tugas konsep, insinyur dapat mengerjakan tugas yang berbeda dan mengujinya, membuat profil mereka secara mandiri sebanyak mungkin.
Tapi berapa banyak yang bisa kita dapatkan dari ini? Jika insinyur bekerja di proyek yang sama, mereka dapat menemukan cara berbagi pandangan yang sama dengan baik tanpa menggunakan OS non-preemptive.
Jika seorang insinyur berasal dari proyek atau perusahaan lain, ia akan mendapatkan keuntungan jika ia mengetahui OS sebelumnya. Tetapi jika dia tidak melakukannya, sekali lagi, sepertinya tidak ada perbedaan besar baginya untuk belajar OS baru atau sepotong kode baru.

Manfaat 2:
Jika kode OS telah diuji dengan baik, sehingga menghemat waktu dari debugging. Ini benar-benar manfaat yang bagus.
Tetapi jika aplikasi hanya memiliki sekitar 5 tugas, saya pikir itu tidak benar-benar berantakan untuk menulis kode Anda sendiri menggunakan penghenti waktu dan loop latar belakang.

OS non-preemptif di sini disebut sebagai OS komersial / bebas / lawas dengan penjadwal non-preemptif.
Ketika saya memposting pertanyaan ini, saya terutama memikirkan OS tertentu seperti:
(1) KISS Kernel (RTOS NonPreemptive Kecil - diklaim oleh situs webnya)
http://www.frontiernet.net/~rhode/kisskern.html
(2) uSmartX (RTOS ringan - diklaim oleh situs webnya)
(3) FreeRTOS (Ini adalah RTOS preemptive, tapi seperti yang saya pahami, ia dapat dikonfigurasikan sebagai RTOS non-preemptive juga)
(4) uC / OS (mirip dengan FreeRTOS)
(5 ) kode OS / scheduler lawas di beberapa perusahaan (biasanya dibuat dan dikelola oleh perusahaan secara internal)
(Tidak dapat menambahkan lebih banyak tautan karena batasan dari akun StackOverflow baru)

Seperti yang saya mengerti, OS non-preemptive adalah kumpulan kode-kode ini:
(1) scheduler menggunakan strategi non-preemptive.
(2) fasilitas untuk komunikasi antar tugas, mutex, sinkronisasi dan kontrol waktu.
(3) manajemen memori.
(4) fasilitas / perpustakaan bermanfaat lainnya seperti Sistem File, tumpukan jaringan, GUI, dll. (FreeRTOS dan uC / OS menyediakan ini, tetapi saya tidak yakin apakah mereka masih berfungsi ketika scheduler dikonfigurasi sebagai non-preemptive)
Beberapa mereka tidak selalu ada di sana. Tetapi scheduler adalah suatu keharusan.

hailang
sumber
Singkatnya, singkatnya. Jika Anda memiliki beban kerja yang perlu multithreaded dan Anda mampu membayar overhead, gunakan OS threading. Kalau tidak, waktu sederhana atau "penjadwal" berbasis tugas sudah cukup untuk sebagian besar kasus. Dan untuk mengetahui apakah multitasking preemptive atau kooperatif adalah yang terbaik ... Saya kira itu turun ke overhead dan berapa banyak kontrol yang ingin Anda miliki atas multitasking yang perlu Anda lakukan.
akohlsmith

Jawaban:

13

Baunya agak di luar topik, tetapi saya akan mencoba mengarahkannya kembali ke jalurnya.

Pre-emptive multitasking berarti sistem operasi atau kernel dapat menangguhkan utas yang saat ini berjalan dan beralih ke yang lain berdasarkan heuristik penjadwalan apa pun yang ada. Sering kali utas yang berjalan tidak memiliki konsep bahwa ada hal-hal lain yang terjadi pada sistem, dan apa artinya ini untuk kode Anda adalah Anda harus berhati-hati untuk mendesainnya sehingga jika kernel memutuskan untuk menangguhkan utas di tengah sebuah operasi multi-langkah (katakanlah mengubah output PWM, memilih saluran ADC baru, membaca status dari perangkat I2C, dll.) dan membiarkan utas lain berjalan untuk sementara waktu, bahwa kedua utas ini tidak saling mengganggu.

Contoh sewenang-wenang: katakanlah Anda baru mengenal sistem tertanam multithread dan Anda memiliki sedikit sistem dengan I2C ADC, LCD SPI, dan EEPROM I2C. Anda memutuskan bahwa itu akan menjadi ide yang baik untuk memiliki dua utas: satu yang membaca dari ADC dan menulis sampel ke EEPROM, dan satu yang membaca 10 sampel terakhir, rata-rata mereka dan menampilkannya pada LCD SPI. Desain yang tidak berpengalaman akan terlihat seperti ini (sangat disederhanakan):

char i2c_read(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_READ);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

char i2c_write(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_WRITE);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

adc_thread()
{
    int value, sample_number;

    sample_number = 0;

    while (1) {
        value = i2c_read(ADC_ADDR);
        i2c_write(EE_ADDR, EE_ADDR_REG, sample_number);
        i2c_write(EE_ADDR, EE_DATA_REG, value);

        if (sample_number < 10) {
            ++sample_number;
        } else {
            sample_number = 0;
        }
    };
}

lcd_thread()
{
    int i, avg, sample, hundreds, tens, ones;

    while (1) {
        avg = 0;
        for (i=0; i<10; i++) {
            i2c_write(EE_ADDR, EE_ADDR_REG, i);
            sample = i2c_read(EE_ADDR, EE_DATA_REG);
            avg += sample;
        }

        /* calculate average */
        avg /= 10;

        /* convert to numeric digits for display */
        hundreds = avg / 100;
        tens = (avg % 100) / 10;
        ones = (avg % 10);

        spi_write(CS_LCD, LCD_CLEAR);
        spi_write(CS_LCD, '0' + hundreds);
        spi_write(CS_LCD, '0' + tens);
        spi_write(CS_LCD, '0' + ones);
    }
}

Ini adalah contoh yang sangat kasar dan cepat. Jangan kode seperti ini!

Sekarang ingat, pre-emptive OS multitasking dapat menangguhkan salah satu utas ini pada baris mana pun dalam kode (sebenarnya pada instruksi perakitan apa pun) dan memberikan waktu utas lainnya untuk dijalankan.

Berpikir tentang itu. Bayangkan apa yang akan terjadi jika OS memutuskan untuk menunda adc_thread()antara pengaturan alamat EE untuk menulis dan menulis data aktual. lcd_thread()akan berjalan, berkeliaran dengan periferal I2C untuk membaca data yang dibutuhkan, dan ketika adc_thread()mendapat giliran untuk berjalan lagi, EEPROM tidak akan berada dalam keadaan yang sama seperti saat dibiarkan. Semuanya tidak akan bekerja dengan baik sama sekali. Lebih buruk lagi, ini bahkan mungkin bekerja sebagian besar waktu, tetapi tidak semua waktu, dan Anda akan menjadi gila mencoba mencari tahu mengapa kode Anda tidak berfungsi ketika TERLIHAT seperti seharusnya!

Itu adalah contoh kasus terbaik; OS mungkin memutuskan untuk mencegah i2c_write()dari adc_thread()konteks dan mulai menjalankannya lagi dari lcd_thread()konteks! Banyak hal bisa menjadi sangat berantakan dengan sangat cepat.

Ketika Anda menulis kode untuk bekerja di lingkungan multitasking yang pre-emptive, Anda harus menggunakan mekanisme penguncian untuk memastikan bahwa jika kode Anda ditangguhkan pada waktu yang tidak tepat sehingga tidak terjadi kerusakan besar.

Multitasking kooperatif, di sisi lain, berarti bahwa setiap utas mengendalikan kapan ia menyerahkan waktu pelaksanaannya. Pengkodeannya lebih sederhana, tetapi kode harus dirancang dengan hati-hati untuk memastikan semua utas mendapatkan cukup waktu untuk berjalan. Contoh lain yang dibuat-buat:

char getch()
{
    while (! (*uart_status & DATA_AVAILABLE)) {
        /* do nothing */
    }

    return *uart_data_reg;
}

void putch(char data)
{
    while (! (*uart_status & SHIFT_REG_EMPTY)) {
        /* do nothing */
    }

    *uart_data_reg = data;
}

void echo_thread()
{
    char data;

    while (1) {
        data = getch();
        putch(data);
        yield_cpu();
    }
}

void seconds_counter()
{
    int count = 0;

    while (1) {
        ++count;
        sleep_ms(1000);
        yield_cpu();
    }
}

Kode itu tidak akan berfungsi seperti yang Anda pikirkan, atau bahkan jika itu tampaknya berhasil, itu tidak akan berfungsi ketika laju data dari thread gema meningkat. Sekali lagi, mari kita luangkan waktu sebentar untuk melihatnya.

echo_thread()menunggu byte muncul di UART dan kemudian mendapatkannya dan menunggu sampai ada ruang untuk menulisnya, kemudian menulisnya. Setelah itu selesai memberi giliran lain untuk menjalankan thread. seconds_counter()akan menambah hitungan, menunggu 1000 ms dan kemudian memberikan thread lain kesempatan untuk berjalan. Jika dua byte masuk ke UART selama sleep()itu terjadi, Anda bisa melewatkannya karena UART hipotetis kami tidak memiliki FIFO untuk menyimpan karakter sementara CPU sibuk melakukan hal-hal lain.

Cara yang benar untuk menerapkan contoh yang sangat buruk ini adalah menempatkan di yield_cpu()mana pun Anda memiliki lingkaran sibuk. Ini akan membantu hal-hal bergerak, tetapi dapat menyebabkan masalah lain. mis. jika timing sangat penting dan Anda menghasilkan CPU ke utas lain yang membutuhkan waktu lebih lama dari yang Anda harapkan, Anda bisa membatalkan timing Anda. OS multitasking pre-emptive tidak akan memiliki masalah ini karena secara paksa menangguhkan utas untuk memastikan semua utas dijadwalkan dengan benar.

Sekarang apa hubungannya dengan pengatur waktu dan latar belakang? Pengatur waktu dan latar belakang sangat mirip dengan contoh kerja sama multitasking di atas:

void timer_isr(void)
{
    ++ticks;
    if ((ticks % 10)) == 0) {
        ten_ms_flag = TRUE;
    }

    if ((ticks % 100) == 0) {
        onehundred_ms_flag = TRUE;
    }

    if ((ticks % 1000) == 0) {
        one_second_flag = TRUE;
    }
}

void main(void)
{
    /* initialization of timer ISR, etc. */

    while (1) {
        if (ten_ms_flag) {
            if (kbhit()) {
                putch(getch());
            }
            ten_ms_flag = FALSE;
        }

        if (onehundred_ms_flag) {
                    get_adc_data();
            onehundred_ms_flag = FALSE;
        }

        if (one_second_flag) {
            ++count;
                    update_lcd();
            one_second_flag = FALSE;
        }
    };
}

Ini terlihat cukup dekat dengan contoh threading koperasi; Anda memiliki pengatur waktu yang mengatur acara dan loop utama yang mencari mereka dan bertindak pada mereka dengan cara atom. Anda tidak perlu khawatir tentang "benang" ADC dan LCD saling mengganggu karena yang satu tidak akan pernah mengganggu yang lain. Anda masih harus khawatir tentang "utas" terlalu lama; misal apa yang terjadi jika get_adc_data()butuh 30 ms? Anda akan kehilangan tiga peluang untuk memeriksa karakter dan menggemakannya.

Implementasi loop + timer seringkali jauh lebih mudah untuk diimplementasikan daripada mikrokernel multitask yang kooperatif karena kode Anda dapat dirancang lebih spesifik untuk tugas yang dihadapi. Anda tidak benar-benar melakukan banyak tugas seperti merancang sistem tetap di mana Anda memberi setiap subsistem waktu untuk melakukan tugasnya dengan cara yang sangat spesifik dan dapat diprediksi. Bahkan sistem multitugas kooperatif harus memiliki struktur tugas generik untuk setiap utas dan utas berikutnya untuk menjalankan ditentukan oleh fungsi penjadwalan yang bisa menjadi sangat kompleks.

Mekanisme penguncian untuk ketiga sistem adalah sama, tetapi overhead yang diperlukan untuk masing-masing sistem sangat berbeda.

Secara pribadi, saya hampir selalu kode ke standar terakhir ini, implementasi loop + timer. Saya menemukan threading adalah sesuatu yang harus digunakan sangat hemat. Tidak hanya lebih kompleks untuk menulis dan debug, tetapi juga membutuhkan lebih banyak overhead (mikrokernel multitasking preemptive selalu akan lebih besar dari timer bodoh sederhana dan pengikut acara loop utama).

Ada juga yang mengatakan bahwa siapa pun yang mengerjakan utas akan menghargai:

if you have a problem and use threads to solve it, yoeu ndup man with y pemro.bls

:-)

akohlsmith
sumber
Terima kasih banyak atas balasan Anda dengan contoh-contoh terperinci, akohlsmith. Namun, saya tidak dapat menyimpulkan dari jawaban Anda mengapa Anda memilih pengatur waktu sederhana dan arsitektur latar belakang daripada multitasking yang kooperatif . Jangan salah sangka. Saya sangat menghargai balasan Anda, yang menyediakan banyak informasi berguna tentang penjadwalan yang berbeda. Aku hanya tidak mengerti intinya.
hailang
Bisakah Anda mengerjakan ini lebih banyak?
hailang
Terima kasih, akohlsmith. Saya suka kalimat yang Anda masukkan di akhir. Butuh beberapa saat untuk mengenalinya :) Kembali ke titik jawaban Anda, Anda hampir selalu kode ke implementasi loop + timer. Kemudian, dalam kasus-kasus yang Anda hentikan implementasi ini dan beralih ke OS non-preemptive, apa yang membuat Anda melakukannya?
hailang
Saya menggunakan sistem multitasking kooperatif dan preemptive ketika saya menjalankan OS orang lain. Baik Linux, ThreadX, ucOS-ii atau QNX. Bahkan dalam beberapa situasi tersebut, saya telah menggunakan pengatur waktu + acara yang sederhana dan efektif ( poll()langsung diingat).
akohlsmith
Saya bukan penggemar threading atau multitasking di embedded, tapi saya tahu bahwa untuk sistem yang kompleks itu satu-satunya pilihan yang waras. Sistem operasi mikro kalengan memberi Anda cara cepat untuk menjalankan dan menjalankan dan seringkali menyediakan driver perangkat juga.
akohlsmith
6

Multi-tasking dapat menjadi abstraksi yang berguna dalam banyak proyek mikrokontroler, meskipun penjadwal pre-emptive yang benar akan terlalu berat dan tidak perlu dalam kebanyakan kasus. Saya telah melakukan lebih dari 100 proyek mikrokontroler. Saya telah menggunakan penugasan kooperatif beberapa kali, tetapi penukaran tugas pre-emptive dengan bagasi terkait sejauh ini belum tepat.

Masalah-masalah dengan pre-emptive tasking sebagaimana ditambahkan ke tasking kooperatif adalah:

  1. Jauh lebih berat. Penjadwal tugas pre-emptive lebih rumit, mengambil lebih banyak ruang kode, dan mengambil lebih banyak siklus. Mereka juga memerlukan setidaknya satu interupsi. Itu sering menjadi beban yang tidak dapat diterima pada aplikasi.

  2. Mutex diperlukan di sekitar struktur yang dapat diakses secara bersamaan. Dalam sistem kooperatif, Anda tidak perlu menelepon TASK_YIELD di tengah apa yang seharusnya menjadi operasi atom. Ini mempengaruhi antrian, berbagi keadaan global, dan masuk ke banyak tempat.

Secara umum, mendedikasikan tugas untuk pekerjaan tertentu masuk akal ketika CPU dapat mendukung ini dan pekerjaan itu cukup rumit dengan operasi yang bergantung pada sejarah yang cukup sehingga memecahnya menjadi beberapa peristiwa terpisah akan menjadi rumit. Ini umumnya terjadi ketika menangani aliran input komunikasi. Hal-hal seperti itu biasanya sangat didorong oleh negara tergantung pada beberapa input sebelumnya. Misalnya, mungkin ada byte opcode diikuti oleh byte data unik untuk setiap opcode. Lalu ada masalah byte ini datang pada Anda ketika sesuatu yang lain ingin mengirimnya. Dengan tugas terpisah menangani aliran input, Anda dapat membuatnya muncul dalam kode tugas seolah-olah Anda keluar dan mendapatkan byte berikutnya.

Secara keseluruhan, tugas berguna ketika ada banyak konteks negara. Tugas pada dasarnya adalah mesin negara dengan PC menjadi variabel keadaan.

Banyak hal yang harus dilakukan oleh mikro dapat diekspresikan sebagai respons terhadap serangkaian peristiwa. Sebagai hasilnya, saya biasanya memiliki loop acara utama. Ini memeriksa setiap peristiwa yang mungkin terjadi secara berurutan, kemudian melompat kembali ke atas dan melakukan semuanya lagi. Ketika menangani suatu acara membutuhkan lebih dari beberapa siklus, saya biasanya melompat kembali ke awal perulangan acara setelah menangani acara tersebut. Ini berlaku berarti acara memiliki prioritas tersirat berdasarkan di mana mereka diperiksa dalam daftar. Pada banyak sistem sederhana, ini cukup baik.

Terkadang Anda mendapatkan tugas yang sedikit lebih rumit. Ini sering dapat dipecah menjadi urutan sejumlah kecil hal yang terpisah untuk dilakukan. Anda dapat menggunakan bendera internal sebagai peristiwa dalam kasus tersebut. Saya telah melakukan hal semacam ini berkali-kali pada PIC low end.

Jika Anda memiliki struktur acara dasar seperti di atas tetapi juga harus menanggapi aliran perintah di atas UART, misalnya, maka berguna untuk memiliki tugas terpisah menangani aliran UART yang diterima. Beberapa mikrokontroler memiliki sumber daya perangkat keras yang terbatas untuk multi-tasking, seperti PIC 16 yang tidak dapat membaca atau menulis tumpukan panggilannya sendiri. Dalam kasus seperti itu, saya menggunakan apa yang saya sebut tugas semu untuk prosesor perintah UART. Event loop utama masih menangani yang lainnya, tetapi salah satu event yang harus ditangani adalah byte baru diterima oleh UART. Dalam hal ini ia melompat ke rutin yang menjalankan tugas semu ini. Modul perintah UART berisi kode tugas, dan alamat eksekusi serta beberapa nilai register tugas disimpan dalam RAM dalam modul itu. Kode yang dilompati oleh loop peristiwa menyimpan register saat ini, memuat register tugas yang disimpan, dan melompat ke alamat restart tugas. Kode tugas memanggil makro YIELD yang melakukan kebalikannya, yang kemudian pada akhirnya melompat kembali ke awal perulangan acara utama. Dalam beberapa kasus, loop acara utama menjalankan pseudo-task sekali per pass, biasanya di bagian bawah untuk menjadikannya acara dengan prioritas rendah.

Pada PIC 18 dan lebih tinggi, saya menggunakan sistem penugasan kooperatif sejati karena tumpukan panggilan dapat dibaca dan ditulis oleh firmware. Pada sistem ini, alamat mulai ulang, beberapa bagian lainnya dari negara, dan penumpukan tumpukan data disimpan dalam buffer memori untuk setiap tugas. Untuk membiarkan semua tugas lain berjalan satu kali, sebuah tugas memanggil TASK_YIELD. Ini menyimpan status tugas saat ini, mencari daftar tugas berikutnya yang tersedia, memuat statusnya, lalu menjalankannya.

Dalam arsitektur ini, loop acara utama hanyalah tugas lain, dengan panggilan ke TASK_YIELD di bagian atas loop.

Semua kode multi-tasking saya untuk PIC tersedia secara gratis. Untuk melihatnya, instal rilis Alat Pengembangan PIC di http://www.embedinc.com/pic/dload.htm . Cari file dengan "tugas" dalam namanya di direktori SOURCE> PIC untuk PIC 8 bit, dan direktori SOURCE> DSPIC untuk PIC 16 bit.

Olin Lathrop
sumber
mutex masih mungkin diperlukan dalam sistem multitugas kooperatif, meskipun jarang. Contoh khas adalah ISR yang membutuhkan akses ke bagian kritis. Ini hampir selalu dapat dihindari melalui desain yang lebih baik atau memilih wadah data yang sesuai untuk data penting.
akohlsmith
@ Akoh: Ya, saya telah menggunakan mutex pada beberapa kesempatan untuk menangani sumber daya bersama, seperti akses ke bus SPI. Maksud saya adalah bahwa mutex tidak secara inheren diperlukan sejauh mereka berada dalam sistem pre-emptive. Saya tidak bermaksud mengatakan bahwa mereka tidak pernah dibutuhkan atau tidak pernah digunakan dalam sistem koperasi. Juga, mutex dalam sistem kooperatif dapat sesederhana memutar dalam loop TASK_YIELD memeriksa bit tunggal. Dalam sistem pre-emptive mereka umumnya perlu dibangun ke dalam kernel.
Olin Lathrop
@ OlinLathrop: Saya pikir keuntungan paling signifikan dari sistem non-preemptive ketika datang ke mutex adalah bahwa mereka hanya diperlukan baik ketika berinteraksi secara langsung dengan interupsi (yang pada dasarnya bersifat preemptive) atau ketika salah satu waktu perlu memiliki sumber daya yang dijaga melebihi waktu yang ingin dihabiskan antara panggilan "menghasilkan", atau seseorang ingin memegang sumber daya yang dijaga di sekitar panggilan yang "mungkin" menghasilkan (misalnya "menulis data ke file"). Pada beberapa kesempatan ketika memiliki hasil dalam panggilan "tulis data" akan menjadi masalah, saya telah memasukkan ...
supercat
... metode untuk memeriksa berapa banyak data yang dapat ditulis segera, dan metode (yang mungkin menghasilkan) untuk memastikan bahwa ada jumlah yang tersedia (mempercepat reklamasi blok flash kotor, dan menunggu hingga jumlah yang sesuai telah direklamasi) .
supercat
Hai Olin, saya sangat menyukai balasan Anda. Informasinya jauh melampaui pertanyaan saya. Ini termasuk banyak pengalaman praktis.
hailang
1

Sunting: (Saya akan meninggalkan posting saya sebelumnya di bawah; mungkin itu akan membantu seseorang suatu hari nanti.)

OS Multitasking dalam bentuk apa pun dan Rutinitas Layanan Interupsi tidak - atau tidak seharusnya - arsitektur sistem yang bersaing. Mereka dimaksudkan untuk pekerjaan yang berbeda di berbagai tingkat sistem. Interupsi benar-benar dimaksudkan untuk urutan kode singkat untuk menangani tugas-tugas langsung seperti memulai kembali perangkat, mungkin polling perangkat non-interupsi, ketepatan waktu dalam perangkat lunak, dll. Biasanya diasumsikan bahwa latar belakang akan melakukan pemrosesan lebih lanjut yang tidak lagi kritis waktu setelah kebutuhan mendesak telah terpenuhi. Jika semua yang perlu Anda lakukan adalah me-restart timer dan beralih LED atau pulsa perangkat lain, ISR biasanya dapat melakukan semuanya di latar depan dengan aman. Kalau tidak, ia harus memberi tahu latar belakang (dengan mengatur bendera, atau mengantri pesan) bahwa sesuatu perlu dilakukan, dan lepaskan prosesor.

Saya telah melihat struktur program yang sangat sederhana yang lingkaran latar belakang hanya loop menganggur: for(;;){ ; }. Semua pekerjaan dilakukan di timer ISR. Ini dapat bekerja ketika program perlu mengulangi beberapa operasi konstan yang dijamin akan selesai dalam waktu kurang dari periode waktu; beberapa jenis pemrosesan sinyal tertentu muncul di pikiran.

Secara pribadi, saya menulis ISR yang membersihkan keluar, dan membiarkan latar belakang mengambil alih hal lain yang perlu dilakukan, bahkan jika itu sesederhana perkalian dan penambahan yang dapat dilakukan dalam sepersekian periode waktu. Mengapa? Suatu hari, saya akan mendapatkan ide cemerlang untuk menambahkan fungsi "sederhana" ke program saya, dan "huh, itu hanya akan memerlukan ISR singkat untuk melakukannya" dan tiba-tiba arsitektur sederhana saya sebelumnya menumbuhkan beberapa interaksi yang tidak saya rencanakan dan terjadi secara tidak konsisten. Itu tidak terlalu menyenangkan untuk di-debug.


(Sebelumnya diposting perbandingan dua jenis multi-tasking)

Peralihan tugas: Pre-emptive MT menangani pengalihan tugas untuk Anda termasuk memastikan tidak ada utas yang kekurangan CPU, dan bahwa utas prioritas tinggi dapat berjalan segera setelah siap. MT Kooperatif membutuhkan pemrogram untuk memastikan tidak ada utas yang menjaga prosesor terlalu lama. Anda juga harus memutuskan berapa lama terlalu lama. Itu juga berarti bahwa setiap kali Anda memodifikasi kode, Anda harus mengetahui apakah segmen kode sekarang melebihi kuantum waktu itu.

Melindungi operasi non-atom: Dengan PMT, Anda harus memastikan pertukaran ulir tidak terjadi di tengah operasi yang tidak boleh dibagi. Membaca / menulis pasangan perangkat-register tertentu yang harus ditangani dalam urutan tertentu atau dalam waktu maksimum, misalnya. Dengan CMT sangat mudah - hanya saja jangan menghasilkan prosesor di tengah operasi seperti itu.

Debugging: Umumnya lebih mudah dengan CMT, karena Anda berencana kapan / di mana switch ulir akan terjadi. Kondisi ras antara utas dan bug yang terkait dengan operasi non-aman dengan PMT sangat sulit untuk di-debug karena perubahan utas adalah probabilistik, jadi tidak dapat diulang.

Memahami kode: Utas yang ditulis untuk PMT cukup banyak ditulis seolah-olah bisa berdiri sendiri. Thread yang ditulis untuk CMT ditulis sebagai segmen dan tergantung pada struktur program yang Anda pilih, mungkin lebih sulit untuk diikuti pembaca.

Menggunakan kode pustaka non thread-safe: Anda harus memverifikasi bahwa setiap fungsi pustaka yang Anda panggil di bawah PMT-safe-thread. printf () dan scanf () dan variannya hampir selalu tidak aman. Dengan CMT, Anda akan tahu bahwa tidak ada perubahan utas yang terjadi kecuali saat Anda secara khusus menghasilkan prosesor.

Sistem terbatas yang digerakkan mesin untuk mengontrol perangkat mekanis dan / atau melacak peristiwa eksternal seringkali merupakan kandidat yang baik untuk CMT, karena di setiap acara, tidak banyak yang bisa dilakukan - memulai atau menghentikan motor, mengatur bendera, memilih negara berikutnya , dll. Dengan demikian, fungsi state-change pada dasarnya singkat.

Pendekatan hibrida dapat bekerja dengan sangat baik dalam sistem semacam ini: CMT untuk mengelola mesin negara (dan karenanya, sebagian besar perangkat keras) berjalan sebagai satu utas, dan satu atau dua utas lainnya untuk melakukan komputasi yang berjalan lagi yang dimulai oleh suatu keadaan perubahan.

JRobert
sumber
Terima kasih atas balasan Anda, JRobert. Tapi itu tidak disesuaikan dengan pertanyaan saya. Ini membandingkan OS preemptive vs non-preemptive OS, tetapi tidak membandingkan OS non-preemptive vs non-OS.
hailang
Benar - maaf. Hasil edit saya harus menjawab pertanyaan Anda dengan lebih baik.
JRobert