Penjadwalan waktu nyata

Penjadwalan waktu nyata: prioritas, ceiling locking, dan kebijakan dispatching.

Daftar isi

Penjadwalan waktu nyata menentukan task mana yang berjalan pada suatu saat. Pada sistem dengan tenggat, keputusan tersebut tidak boleh diserahkan kepada kebetulan. Bahasa menyediakan kerangka penjadwalan yang dinyatakan di dalam program, yaitu prioritas task, protokol penguncian dengan plafon prioritas, dan kebijakan penjadwalan (dispatching). Topik ini membahas ketiganya, dimulai dari penetapan prioritas secara statis, dilanjutkan dengan perubahan prioritas saat program berjalan, lalu ditutup dengan kebijakan penguncian dan dispatching yang dapat dipilih. Setiap nilai prioritas pada contoh diperiksa langsung dengan runtime GNAT pada host.

Prioritas dasar pada task#

Prioritas task dinyatakan dengan pragma Priority pada deklarasi task, dan nilainya bertipe System.Priority. Nilai yang lebih besar menyatakan task yang lebih penting, dan penjadwal memilih task siap dengan prioritas dasar tertinggi ketika prosesor bebas. Pada target Linux, GNAT memetakan System.Priority ke rentang 0 sampai 97, menetapkan Default_Priority bernilai 48, dan menyisakan nilai 98 untuk Interrupt_Priority. Rentang ini dapat berbeda pada target lain karena didefinisikan dalam package System milik kompilator.

ada prioritas_task.adb
with Ada.Text_IO;
with Ada.Dynamic_Priorities;
with System;

procedure Prioritas_Task is
   task Pemantau is
      pragma Priority (20);
   end Pemantau;

   task body Pemantau is
      use Ada.Dynamic_Priorities;
   begin
      Ada.Text_IO.Put_Line ("prioritas task Pemantau = "
         & System.Any_Priority'Image (Get_Priority));
   end Pemantau;
begin
   null;
end Prioritas_Task;
bash
# kompilasi program
gnatmake -q prioritas_task.adb
# jalankan program
./prioritas_task
text Keluaran
prioritas task Pemantau =  20

Prosedur utama menunggu task yang menjadi miliknya sampai berakhir, sehingga baris keluaran selalu muncul sebelum program selesai. Task Pemantau membaca prioritas dasarnya sendiri melalui fungsi Get_Priority dari package Ada.Dynamic_Priorities, dan hasilnya tetap 20 sesuai pragma Priority. Pembacaan semacam ini berguna ketika suatu prosedur menerima objek task dari pihak lain, yaitu ketika prioritas task tersebut perlu diverifikasi tanpa melihat deklarasi aslinya. Pragma Priority juga berlaku pada prosedur utama, dan nilainya menjadi prioritas environment task yang menjalankan prosedur tersebut.

Perubahan prioritas saat berjalan#

Prioritas dasar dapat berubah saat program berjalan melalui package Ada.Dynamic_Priorities. Prosedur Set_Priority menetapkan nilai baru dan berlaku segera, sedangkan fungsi Get_Priority membaca nilai saat itu. Perubahan ini memungkinkan penyesuaian prioritas terhadap beban, misalnya menaikkan prioritas task pengolahan saat antrean bacaan menumpuk, lalu menurunkannya kembali setelah antrean kosong.

ada ubah_prioritas.adb
with Ada.Text_IO;
with Ada.Dynamic_Priorities;
with System;
use Ada.Dynamic_Priorities;

procedure Ubah_Prioritas is
   procedure Tampilkan (Tahap : String) is
   begin
      Ada.Text_IO.Put_Line (Tahap & " = "
         & System.Any_Priority'Image (Get_Priority));
   end Tampilkan;
begin
   Tampilkan ("prioritas awal");
   Set_Priority (25);
   Tampilkan ("prioritas setelah dinaikkan");
   Set_Priority (10);
   Tampilkan ("prioritas setelah diturunkan");
end Ubah_Prioritas;
bash
# kompilasi program
gnatmake -q ubah_prioritas.adb
# jalankan program
./ubah_prioritas
text Keluaran
prioritas awal =  48
prioritas setelah dinaikkan =  25
prioritas setelah diturunkan =  10

Nilai awal 48 adalah Default_Priority pada target Linux, dan setiap Set_Priority menggantinya segera tanpa penundaan. Keluaran menegaskan bahwa prioritas dasar adalah properti task pada model bahasa, bukan properti kernel, karena nilainya terbaca kembali dengan tepat. Kernel sistem operasi tetap berwenang atas eksekusi sesungguhnya, dan pembatasan privilege pada desktop biasa dapat meredam efek prioritas waktu nyata, sedangkan nilai yang terbaca oleh program tetap benar. Set_Priority juga menerima parameter identifikasi task secara opsional, sehingga task yang mengatur jadwal dapat mengubah prioritas task lain.

Ceiling locking dan kebijakan dispatching#

Setiap protected object memiliki plafon prioritas (priority ceiling), yaitu batas atas prioritas pemanggilnya. Protokol plafon prioritas segera (immediate ceiling priority protocol) menaikkan prioritas task pemanggil ke nilai plafon selama protected action berlangsung, sehingga task berprioritas menengah tidak dapat menyisipi di antara pemanggil dan operasi terlindungi yang sedang berlangsung. Mekanisme ini mencegah inversi prioritas (priority inversion) dan menjaga waktu tunggu pada objek terlindungi dalam batas yang dapat dihitung. Kebijakan penguncian dipilih dengan pragma Locking_Policy, sedangkan kebijakan dispatching dipilih dengan pragma Task_Dispatching_Policy. Nilai yang umum untuk kebijakan dispatching adalah FIFO_Within_Priorities untuk penjadwalan prioritas tetap atau Round_Robin_Within_Priorities untuk pembagian waktu.

ada Contoh: kebijakan penguncian dan plafon prioritas
-- tidak-dijalankan
pragma Task_Dispatching_Policy (FIFO_Within_Priorities);
pragma Locking_Policy (Ceiling_Locking);

package Kendali_Pompa is
   protected Pompa with Priority => 30 is
      procedure Aktifkan (Durasi : Natural);
      function Menyala return Boolean;
   private
      Aktif : Boolean := False;
   end Pompa;
end Kendali_Pompa;

Aspek Priority => 30 menetapkan plafon objek Pompa secara eksplisit, sehingga task dengan prioritas sampai 30 boleh memanggilnya. Kebijakan Ceiling_Locking menerapkan protokol plafon pada seluruh protected object, dan pragma Task_Dispatching_Policy menegakkan urutan prioritas tetap tanpa pembagian waktu. Kedua pragma biasanya ditulis sebagai configuration pragma pada tingkat proyek agar kebijakannya seragam untuk seluruh program. Kebijakan pembagian waktu hanya dipertimbangkan apabila tenggat sistem memang menuntut porsi prosesor yang merata.

Kesalahan umum#

Empat kekeliruan berikut paling sering muncul saat penjadwalan waktu nyata dipraktikkan.

  • Menganggap pragma Priority mengubah perilaku kernel secara langsung. Nilai prioritas adalah kontrak di dalam model bahasa, sedangkan pemetaannya ke kernel bergantung pada runtime dan konfigurasi sistem operasi. Uji perilaku penjadwalan pada target tempat program akan ditempatkan.
  • Menetapkan plafon lebih rendah daripada prioritas tertinggi pemanggil. Panggilan semacam itu memicu Program_Error, dan error ini baru terlihat pada jalur eksekusi yang bersangkutan. Kumpulkan seluruh pemanggil objek terlindungi lebih dahulu, lalu tetapkan plafonnya.
  • Menganggap Round_Robin_Within_Priorities aktif dengan sendirinya. Tanpa pragma Task_Dispatching_Policy, dispatching mengikuti kebijakan bawaan FIFO_Within_Priorities tanpa pembagian waktu. Nyatakan kebijakan yang diinginkan secara eksplisit pada konfigurasi program.
  • Mengabaikan Default_Priority pada task tanpa pragma Priority. Task-task tersebut berbagi prioritas 48 pada target Linux, sehingga urutan eksekusi di antara task berprioritas sama tidak tertentu. Beri pragma Priority eksplisit pada setiap task yang terikat tenggat, karena nilai default juga berlaku bagi environment task sebelum prioritasnya diubah.

Ringkasan#

  • Prioritas task ditetapkan dengan pragma Priority, bertipe System.Priority, dan dapat diubah saat berjalan melalui package Ada.Dynamic_Priorities.
  • Pada target Linux, GNAT menetapkan Default_Priority 48 dengan rentang Priority 0 sampai 97.
  • Protected object memiliki plafon prioritas yang bekerja melalui protokol plafon segera untuk mencegah inversi prioritas.
  • Kebijakan penguncian dan dispatching dinyatakan dengan pragma Locking_Policy dan Task_Dispatching_Policy.

Referensi#