SVID Bağlam Denetimi Örneği
Önceki Bütünsel Bağlam Denetimi Sonraki
SVID Bağlam Denetimi Örneği
Bağlam işleme işlevlerini kullanmanın en kolay yolu setjmp ve longjmp işlevlerinin yerine bunları kullanmaktır.Bağlamın çoğu platformda daha az sürprizle sonuçlanan daha fazla bilgi içermesine rağmen bu işlevlerin kullanımı daha masraflıdır (daha az taşınabilir olması cabası).
int
random_search (int n, int (*fp) (int, ucontext_t *))
{
  volatile int cnt = 0;
  ucontext_t uc;

  /* Geçerli bağlamı güvene alalım.  */
  if (getcontext (&uc) < 0)
    return -1;

  /* Henüz n deneme yapmamışsak, tekrar deneyelim. */
  if (cnt++ < n)
    /* İşlevi yeni bir rasgele sayı ve bağlamla çağıralım. */
    if (fp (rand (), &uc) != 0)
      /* Aradığımızı bulduk.  */
      return 1;

  /* Bulamadık.  */
  return 0;
}
Bağlamların böyle bir yolla kullanımı olağandışılıkların elde edilme benzeşimini etkinleştirir. fp parametresi ile aktarılan arama işlevleri çok büyük, iç içe ve çağrıcıya aktarılan bir hata değeri ile işlevin bırakılması onu karmaşıklaştıracağından (veya en azından biraz daha kod gerekeceğinden), çok karmaşık olabilir. Bağlamı kullanarak arama işlevini tek bir adımda bırakmak ve ayrıca belirgin biçimde daha hızlı olabilen bir yan etkiyle aramanın yeniden başlatılmasına izin vermek mümkündür.
Geçici olarak farklı bir yürütme noktasına geçmek ve sonra yürütmenin durduğu yerden devam etmek gibi bazı şeylerin setjmp ve longjmp ile gerçeklenmesi daha zordur.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <sys/time.h>

/* Buna sinyal eylemci atama yapacak. */
static volatile int expired;

/* Bağlamlar. */
static ucontext_t uc[3];

/* Biz belli sayıda geçiş yapacağız. */
static int switches;


/* Bu işi yapan işlev.  Sadece bir iskelet,
   gerçek kod sonra yerleştirilecek. */
static void
f (int n)
{
  int m = 0;
  while (1)
    {
      /* İşin yapıldığı yer. */
      if (++m % 100 == 0)
        {
          putchar ('.');
          fflush (stdout);
        }

      /* Değişkenin zaman aşımına uğrayıp uğramadığına
         düzenli olarak bakmak lazım. */
      if (expired)
        {
          /* Kodun daha fazla çalışmasını istemiyoruz. */
          if (++switches == 20)
            return;

          printf ("\n%d. bağlamdan %d. bağlama geçiliyor\n", n, 3 - n);
          expired = 0;
          /* Diğer bağlama geçip şimdikini kaydedelim. */
          swapcontext (&uc[n], &uc[3 - n]);
        }
    }
}

/* Sadece değişkene değer atayan bir sinyal eylemci bu. */
void
handler (int signal)
{
  expired = 1;
}


int
main (void)
{
  struct sigaction sa;
  struct itimerval it;
  char st1[8192];
  char st2[8192];

  /* Zamanlayıcının kullanacağı veri yapılarını ilklendirelim. */
  sa.sa_flags = SA_RESTART;
  sigfillset (&sa.sa_mask);
  sa.sa_handler = handler;
  it.it_interval.tv_sec = 0;
  it.it_interval.tv_usec = 1;
  it.it_value = it.it_interval;

  /* Zamanlayıcıyı kuralım ve çalışacağımız bağlamı alalım. */
  if (sigaction (SIGPROF, &sa, NULL) < 0
      || setitimer (ITIMER_PROF, &it, NULL) < 0
      || getcontext (&uc[1]) == -1
      || getcontext (&uc[2]) == -1)
    abort ();

  /* Bağlamı, f işlevinin 1 parametresi ile çağrılmasına
     sebep olan ayrı bir yığıt ile oluşturalım.
     uc_link'in, işlev döndüğü anda yazılımın sonlanmasına
     sebep olan ana bağlamı gösterdiğine dikkat edin. */
  uc[1].uc_link = &uc[0];
  uc[1].uc_stack.ss_sp = st1;
  uc[1].uc_stack.ss_size = sizeof st1;
  makecontext (&uc[1], (void (*) (void)) f, 1, 1);

  /* Aynı ama f'ye parametre olarak 2 aktarılıyor. */
  uc[2].uc_link = &uc[0];
  uc[2].uc_stack.ss_sp = st2;
  uc[2].uc_stack.ss_size = sizeof st2;
  makecontext (&uc[2], (void (*) (void)) f, 1, 2);

  /* İşbaşı! */
  swapcontext (&uc[0], &uc[1]);
  putchar ('\n');

  return 0;
}
Bu kod, bağlam işlevlerinin eş-yordamları veya çok evreli işbirliğini gerçekleştirmekte kullanılabilirliğine bir örnektir. Burada yapılan swapcontext kullanarak yürütmenin her seferinde farklı bir bağlamdan devam ettirilmesidir. Sinyal eylemci içinden ne setcontext ne de swapcontext çağrısı yapıldığından, bağlam değiştirme doğrudan sinyal eylemci tarafından yapılmamaktadır. Bunun yerine sinyal eylemci içinde bir değişkene değer atanıp, işlev içinden bu değişkene bakarak bu işlem gerçekleştirilmektedir. swapcontext geçerli bağlamı kaydettiğinden kod içinde farklı zamanlama noktaları olabilir. Yürütme daima kaldığı yerden devam edecektir.
Önceki Üst Ana Başlık Sonraki
Bütünsel Bağlam Denetimi Başlangıç XXIV. Oylum - Sinyal İşleme
Bir Linux Kitaplığı Sayfası