2016-04-11 28 views
2

Çok işlemcili bir senaryoda, aynı işlemin sonraki iş parçacığının uyandırılması gereken (yalnızca bir iş parçacığının çalıştığı ve tüm diğerleri) bir POSIX zamanlayıcıyı uygulamaya çalışıyorum. blok halindedir). Zamanlayıcıda, sigev_notify = SIGEV_THREAD_ID numaralı telefonu kullanıyorum, çünkü herhangi bir işleyicinin sinyale hizmet vermesini istemiyorum veya zamanlayıcı sona erdikten sonra yeni bir iş parçacığı oluşturmak istiyorum.POSIX zamanlayıcı ile sigev_notify = SIGEV_THREAD_ID yöntemi

//Import 
#define _GNU_SOURCE 
#define _POSIX_C_SOURCE 199309 
#include <sched.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <signal.h> 
#include <errno.h> 
#include <semaphore.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <syscall.h> 
#define NUM_THREADS 10 

#define CLOCKID CLOCK_REALTIME 

int ret; 
//pthread_cond_t  condA[NUM_THREADS+1] = PTHREAD_COND_INITIALIZER; 
pthread_cond_t  condA = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; 

sem_t sem[NUM_THREADS]; 
sem_t mute; 

timer_t timer1; 
pthread_t tid[NUM_THREADS]; 
int state = 0; 
int thread_count = 1; 
int arr_tid[NUM_THREADS]; 
struct itimerspec new_value, old_value; 

struct sigaction action; 
struct sigevent sevent; 
sigset_t set; 
int signum = SIGALRM; 

void *threadA(void *data_) 
{ 
    cpu_set_t my_set;   
    CPU_ZERO(&my_set);  
    CPU_SET(2, &my_set);  
    sched_setaffinity(0, sizeof(cpu_set_t), &my_set); 

    //struct itimerspec new_value, old_value; 

    int i = 0, value; 
    int sid; 
    FILE *fp; 
    fp=fopen("ipc.out","a");  

    long int loopNum; 
    int turn = (intptr_t)data_; 
    struct timespec tval_result, tval_result2; 

    if(thread_count < NUM_THREADS) 
    { 
     thread_count++; 
     sid = syscall(SYS_gettid); 
     arr_tid[turn] = sid; 
     fprintf(fp,"thread_%d %d\n", turn, sid); 
     //printf("Blocked %d ->%d\n", turn, thread_count); 
     pthread_mutex_lock(&mutex); 
     pthread_cond_wait(&condA, &mutex); 
     pthread_mutex_unlock(&mutex); 
    } 
    else 
    { 
     arr_tid[turn] = syscall(SYS_gettid); 
    } 

    for (value = 0; value < NUM_THREADS; ++value) 
    { 
     printf("%d\n",arr_tid[value]); 
    } 
    //printf("rpg\n"); 
    pthread_mutex_lock(&mutex); 
    pthread_cond_broadcast(&condA); 
    pthread_mutex_unlock(&mutex); 
    //printf("unblocked\n"); 
    fclose(fp); 

    if (turn > 0) 
     { 
      if (sigwait (&set, &signum) == -1) 
       perror ("sigwait"); 
      //sleep(1); 
      //printf("thread %d is sleeping\n", turn); 
     } 
    while(1) 
    { 
     ret = sem_wait(&sem[turn]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     //printf("this isn't the end of the world!!!\n"); 
     sevent.sigev_notify = SIGEV_THREAD_ID; 
     sevent._sigev_un._tid = arr_tid[(turn+1)%10]; 
     sevent.sigev_signo = signum; 

     sigemptyset(&set); 
     sigaddset(&set, signum); 
     sigprocmask(SIG_BLOCK, &set, NULL); 

     printf("Thread # -> %d\n", turn); 
     clock_gettime(CLOCKID, &tval_result); 
     do 
     { 
      clock_gettime(CLOCKID, &tval_result2); 
     } while((tval_result2.tv_sec - tval_result.tv_sec)*1000000000+(tval_result2.tv_nsec - tval_result.tv_nsec)<=12000);  
       //printf("Timestamp : %ld %ld\n", tval_result2.tv_sec, tval_result2.tv_nsec); 
     // printf("Before creating timer\n"); 

     new_value.it_interval.tv_sec = 0; 
     new_value.it_interval.tv_nsec = 0; 
     new_value.it_value.tv_sec = 0; 
     new_value.it_value.tv_nsec = 15000; 
     printf("next thread to be signalled %d\n", arr_tid[turn+1]); 
     if (timer_settime (timer1, 0, &new_value, NULL) == -1) 
      perror ("timer_settime"); 
     printf("yy\n"); 
     ret = sem_post(&sem[(state+1)%NUM_THREADS]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     state++; 
     //printf("yy\n"); 
     //sleep(1); 
     if (sigwait (&set, &signum) == -1) 
      perror ("sigwait"); 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    int data = 0; 
    int err, i; 
    int sid = syscall(SYS_gettid); 
    //struct itimerspec new_value, old_value; 

    FILE *fp; 
    fp=fopen("ipc.out","a");  
    fprintf(fp,"Mainthread %d\n",sid); 
    fclose(fp); 
    if (timer_create (CLOCK_REALTIME, &sevent, &timer1) == -1) 
     perror ("timer_create"); 

    sem_init(&sem[0], 0, 1); 
    //sem_init(&sem[1], 0, 0); 
    //sem_init(&sem[2], 0, 0); 
    for (i = 1; i < NUM_THREADS; ++i) 
     { 
      sem_init(&sem[i], 0, 0); 
     } 
    while(data < NUM_THREADS) 
    { 
     //create our threads 
     err = pthread_create(&tid[data], NULL, threadA, (void *)(intptr_t)data); 
     if(err != 0) 
      printf("\ncan't create thread :[%s]", strerror(err)); 
     data++; 
    } 
    pthread_exit(NULL); 
} 

Derleme:$ gcc filename.c -lrt -lpthread Ben ipler belirli bir sırayla çalıştırmak, böylece konuları eşitlemek için semaforları kullandık

.

İstenilen çıktıyı alamıyorum. 15 mikrosaniyeden sonra olması gerektiği gibi olmalıdır, zamanlayıcı sona ermeli ve sonraki iplik uyanmalı ama gerçekleşmiyor. Konuları engellemek için sigwait() kullandım. Bir iş parçacığını engellemek ve bir zamanlayıcı kullanarak onları uyandırmak için başka alternatifler var mı? signum'da atanabilen sinyaller nelerdir? SIGALRM yerine pthread_cond_signal() kullanabilir miyim?

+0

Küçük bir anlamsız görünüyor .... –

+0

@MartinJames Sigwait() man sayfasına göre, sinyali beklemek için sinyal bekleyen herhangi bir sayıda iplik alınabilir.Benim durumumda hangisi istenmiyor ki. Yani, zamanlayıcı son kullanma tarihinde bir ipliği özel olarak uyandırmak için bir mekanizma varsa, o zaman benim sorunum için yeterli olacaktır. (pthread_cond_signal() öğesinde, bir koşul değişkeninde bekleyen belirli bir iş parçacığına sinyal gönderebildiğimiz gibi) – Scissor

cevap

1

Um .. Tamam.

Semaforunuzu bir üniteye sıfırlayın ve tüm dişlerin beklemesini bekleyin.

Bir iş parçacığı alırsa, geçerli 'startTime' süresini bazı statik/gobal/neyse, (güvenlidir, çünkü tek bir iş parçacığı her seferinde semaunit olabilir) depolar ve işini yapmak için gider./s'dir. Tamamlandığında, 'endTime' zamanını alır, 'endTime-startTime' olarak ne kadar sürdüğünü hesaplar ve istenen iplik arası zaman aralığından çıkarır - artık bir sonraki iş parçacığı geçmeden önce ne kadar süre kaldı - ' remainingInterval'. KalanInterval 0'dan küçükse, 0 yapar. Bu, timeInterval'i, uykuda()/Sleep() için gerekli olan herhangi birime dönüştürür ve o kadar uzun süre uyur, sonra ünitesini semafor'a gönderir. Başka bir iş parçacığı daha sonra çalışabilir ve işini yapan iş parçacığı döngü yapabilir ve eğer istenirse semaforda bekleyebilir.

Birden fazla iş parçacığının çalışabilmesi, işin/işin/ne olursa olsun, bir seferde birden gerçekleştirilemez.

Açık bir zamanlayıcı, mutex, condvar vb. Gerekli değildir, sadece bir semafor.

+0

Önceden yazılan ... Belirtmiş olduğunuz 'kalanInterval' 1 mikrosaniyeden veya hatta daha az olmak istenir. Yani, pratik olarak, sleep() veya usleep() kullanamıyorum. Clock_nanosleep() ile devam etsem bile, mevcut işlemcilerde bu çözünürlüğü başaramıyorum (yanılıyorsam beni düzelt). Bu yüzden bunu başarmak için çok iş parçacıklı bir mekanizmaya ihtiyacım var. Zamanlayıcı ile o kadar güzel bir ayrıntıya ulaşabileceğimi umuyordum. – Scissor

+0

1 Bize? Bildiğim herhangi bir işletim sisteminde olamazsınız. Yine de neden tüm bu konulara ihtiyacın olduğunu anlayamıyorum - sadece bir seferde koşabilirsin, o zaman neden rahatsızsın? –

+0

Okey, birden çok iş parçacığı kullanıyorum çünkü her bir iş parçacığının sanal çalışma zamanı, 'kalanInterval' içinde çalışacak olan işlemden daha az olacaktır. Bütün ipler aynı işi birbiri ardına sırayla yapıyor. Bu inter-thread süresi (yaklaşık 1 biz istiyorum) aynı çekirdek üzerinde bir süreç için ayrılmalıdır. Böylelikle, bütün bu düzen, bir iş parçacığının işi bitirdiği anda, blok haline gelecektir. Bu arada, diğer süreç 1 us için çalışmaya başlayacak. Bu zamana kadar sonraki iş parçacığı zamanlayıcı tarafından uyandırılmalıdır (benim uygulamamda). – Scissor

1

Tüm iş parçacıklarının zaman aşımına uğradığı anda bir kerede bir sinyal almasını bekliyorsunuz, ancak zamanlayıcı ayarı (timer_settime) kuralı bozan tüm iş parçacıklarında gerçekleştirildi. zamanlayıcı ve zaman ayarı, süreç seviyesi tek seferde olması gereken değil, başına iş parçacığı:

Bu yapılması gereken budur.

Ardından, tüm konuları oluşturun ve iş parçacığı dizilerini bir dizide saklayın ve bazı koşullara göre bekletin (pthread_cond_wait).

Zamanlayıcı sona erdiğinde ve bir sinyal gönderdiğinde, iş parçacığı boyunca döngü yapın ve uyandırın, her seferinde bir iş parçacığı.

+0

Yani, zamanlayıcı sona erdikten sonra gelen sinyale hizmet vermek için her iş parçacığı için bir sinyal işleyici oluşturmak zorundayım? Ayrıca, bu sinyal işleyicide, bir koşul değişkeninde bekleyen sonraki iş parçacığını (işleyiciden aramak güvenli ise) uyanmak için pthread_cond_signal() kullanmalıyım. Ama aynı çalışan iş parçacığının bir sonraki işleyiciyi yaratmasına gerek kalmaması için SIGEV_THREAD_ID yöntemini kullanıyorum. O zaman pthread_cond_wait() 'de bekleyen ipliği nasıl uyandırmalıyım? Kodumda SIGALRM yerine pthread_cond_signal() kullanabilir miyim? – Scissor

+0

Sanırım anlamadın. Tek sinyal işleyici olacak. Timer_create ve timer_settime, main() öğesinde bir kez gerçekleşir. Zamanlayıcının süresi bittiğinde ve sinyal işleyicisi onu yakaladığında, bu sinyal işleyicisinin içinde, bir sonraki iş parçacığını iplik dizilerinizden sırayla bulmanız ve uyandırmanız yeterlidir. – kingsmasher1