2017-12-09 28 views
2

Linux 커널 4.4.0-57에서 C++ 11을 사용하여 고정 된 두 개의 루 프 루핑 프로세스 (예 : p1, p2)를 고정하려고합니다 (pthread_setaffinity_np)을 사용하고 POSIX 세마포어 (세마포어 h)와 sched_yield()을 사용하여 인터리빙 실행 순서를 확인하십시오. 하지만 잘 돌아 가지 않았습니다.POSIX 세마포어가 높은 경쟁 상태/부하에서 작동하지 않습니다.

다음은 2 개의 프로세스를 생성하고 각각이 하위 작업 코드를 실행하는 상위 코드 (상위 작업)입니다.

#include <stdio.h>               
#include <cstdlib>               
#include <errno.h>  // errno           
#include <iostream>  // cout cerr          
#include <semaphore.h> // semaphore          
#include <fcntl.h>  // O_CREAT           
#include <unistd.h>  // fork            
#include <string.h>  // cpp string          
#include <sys/types.h> //             
#include <sys/wait.h> // wait()           

int init_semaphore(){           
    std::string sname = "/SEM_CORE";           
    sem_t* sem = sem_open (sname.c_str(), O_CREAT, 0644, 1);    
    if (sem == SEM_FAILED) {            
     std::cerr << "sem_open failed!\n";         
     return -1;               
    }                  

    sem_init(sem, 0, 1);             
    return 0;                
}                   

// Fork and exec child-task.             
// Return pid of child              
int fork_and_exec(std::string pname, char* cpuid){      
    int pid = fork();              
    if (pid == 0) {               
     // Child                
     char* const params[] = { "./child-task", "99", strdup(pname.c_str()), cpuid, NULL };        
     execv(params[0], params);           
     exit(0);                
    }                  
    else {                 
     // Parent                
     return pid;               
    }                  
}                   

int main(int argc, char* argv[]) {          
    if (argc <= 1)               
     printf("Usage ./parent-task <cpuid> \n");       

    char* cpuid = argv[1];             
    std::string pnames[2] = { "p111", "p222" };        

    init_semaphore();              

    int childid[ 2 ] = { 0 };            
    int i = 0;                
    for(std::string pname : pnames){          
     childid[ i ] = fork_and_exec(pname, cpuid); 
    }                  

    for (i=0; i<2; i++)             
     if (waitpid(childid[i], NULL, 0) < 0)        
     perror("waitpid() failed.\n");         

    return 0;                
} 

아이 태스크 코드는 다음과 같습니다 : 아이 태스크 코드에서

#include <cstdlib>                
#include <stdio.h>                
#include <sched.h>                
#include <pthread.h>                
#include <stdint.h>                
#include <errno.h>               
#include <semaphore.h>               
#include <iostream>                
#include <sys/types.h>               
#include <fcntl.h>  // O_CREAT            

sem_t* sm;                  

int set_cpu_affinity(int cpuid) {            
    pthread_t current_thread = pthread_self();         
    cpu_set_t cpuset;                
    CPU_ZERO(&cpuset);               
    CPU_SET(cpuid, &cpuset);             
    return pthread_setaffinity_np(current_thread,        
            sizeof(cpu_set_t), &cpuset);    
}                    

int lookup_semaphore() {               
    sm = sem_open("/SEM_CORE", O_RDWR);           
    if (sm == SEM_FAILED) {              
     std::cerr << "sem_open failed!" << std::endl ;        
     return -1;                 
    }                    
}                    

int main(int argc, char* argv[]) {            
    printf("Usage: ./child-task <PRIORITY> <PROCESS-NAME> <CPUID>\n");    
    printf("Setting SCHED_RR and priority to %d\n", atoi(argv[1]));   

    set_cpu_affinity(atoi(argv[3]));           

    lookup_semaphore();               

    int res;                  
    uint32_t n = 0;                
    while (1) {                 
     n += 1;                 
     if (!(n % 1000)) {                               
     res = sem_wait(sm); 

     if(res != 0) {              
      printf(" sem_wait %s. errno: %d\n", argv[2], errno);     
     }                 
     printf("Inst:%s RR Prio %s running (n=%u)\n", argv[2], argv[1], n); 
     fflush(stdout);              

     sem_post(sm);               

     sched_yield();               
     }                   

     sched_yield();                
    }                    

    sem_close(sm);                
} 

, 내가 기다리고 세마포어를 올리기에 경합/부하를 줄이는 실험 if (!(n % 1000)) 있습니다. 내가 얻은 결과는 n % 1000 일 때 자식 프로세스 중 하나가 항상 잠자기 상태 ( 상위)이고 다른 자식 프로세스가 제대로 실행된다는 것입니다. 그러나,로드/경쟁이 적은 n % 10000을 설정하면 두 프로세스가 모두 실행되어 출력이 인터리빙 방식으로 출력되므로 이는 예상 한 결과입니다.

이것이 semaphore.h의 제한 사항인지 또는 프로세스 실행 순서를 보장하는 더 나은 방법이 있는지 알고 계십니까?

+0

'main'에있는 어린이의 의견에서 알 수 있듯이 예약 정책과 우선 순위를 설정 하시겠습니까? 그것들을 어디에서 설정한다는 것은 무엇입니까? – pilcrow

답변

0

업데이트 : 스레드와 세마포어로 간단한 예제를 만들었습니다. sched_yield가 작업을 수행하기 위해 '차례로'는 스레드의 불필요한 웨이크 업을 피하는 데 도움이 될 수 있지만 보증하지 않습니다. 또한 mutex/condvar를 사용하여 작동이 보장되고 수율이 필요없는 예제를 보여줍니다.

#include <stdexcept> 
#include <semaphore.h> 
#include <pthread.h> 
#include <thread> 
#include <iostream> 

using std::thread; 
using std::cout; 

sem_t sem; 
int count = 0; 

const int NR_WORK_ITEMS = 10; 

void do_work(int worker_id) 
{ 
    cout << "Worker " << worker_id << '\n'; 
} 

void foo(int work_on_odd) 
{ 
    int result; 
    int contention_count = 0; 
    while (count < NR_WORK_ITEMS) 
    { 
     result = sem_wait(&sem); 
     if (result) { 
      throw std::runtime_error("sem_wait failed!"); 
     } 
     if (count % 2 == work_on_odd) 
     { 
      do_work(work_on_odd); 
      count++; 
     } 
     else 
     { 
      contention_count++; 
     } 
     result = sem_post(&sem); 
     if (result) { 
      throw std::runtime_error("sem_post failed!"); 
     } 
     result = sched_yield(); 
     if (result < 0) { 
      throw std::runtime_error("yield failed!"); 
     } 
    } 
    cout << "Worker " << work_on_odd << " terminating. Nr of redundant wakeups from sem_wait: " << 
     contention_count << '\n'; 
} 

int main() 
{ 
    int result = sem_init(&sem, 0, 1); 

    if (result) { 
     throw std::runtime_error("sem_init failed!"); 
    } 

    thread t0 = thread(foo, 0); 
    thread t1 = thread(foo, 1); 

    t0.join(); 
    t1.join(); 

    return 0; 
} 

여기 조건 변수 및 뮤텍스를 사용하는 한 가지 방법이 있습니다. C++ 표준 스레드에서 pthread 로의 변환은 간단해야합니다. 프로세스간에이를 수행하려면 프로세스간에 공유 될 수있는 pthread 뮤텍스 유형을 사용해야합니다. 어쩌면 condvar와 뮤텍스를 공유 메모리에 둘 수있어 스레드와 동일한 작업을 수행 할 수 있습니다.

도 참조 맨 pthread_condattr_setpshared (3) 한편 http://manpages.ubuntu.com/manpages/wily/man3/pthread_condattr_setpshared.3posix.html

는, 어쩌면 단지 두 개의 작업자 프로세스 사이의 SOCK_STREAM 유닉스 도메인 소켓을 사용하고, 단지 때까지 소켓에 차단하는 간단 피어 (peer) 작업자가 소켓을 통해 ping (즉, 하나의 char 보내기)합니다.

#include <cassert> 
#include <iostream> 
#include <thread> 
#include <condition_variable> 
#include <unistd.h> 

using std::thread; 
using std::condition_variable; 
using std::mutex; 
using std::unique_lock; 
using std::cout; 

condition_variable cv; 
mutex mtx; 
int count; 

void dowork(int arg) 
{ 
    std::thread::id this_id = std::this_thread::get_id(); 

    cout << "Arg: " << arg << ", thread id: " << this_id << '\n'; 
} 

void tfunc(int work_on_odd) 
{ 
    assert(work_on_odd < 2); 

    auto check_can_work = [&count, &work_on_odd](){ return ((count % 2) == 
                  work_on_odd); }; 
    while (count < 10) 
    { 
     unique_lock<mutex> lk(mtx); 
     cv.wait (lk, check_can_work); 
     dowork(work_on_odd); 
     count++; 
     cv.notify_one(); 
     // Lock is unlocked automatically here, but with threads and condvars,                                             
     // it is actually better to unlock manually before notify_one.                                               
    } 
} 

int main() 
{ 
    count = 0; 
    thread t0 = thread(tfunc, 0); 
    thread t1 = thread(tfunc, 1); 
    sleep(1); 
    cv.notify_one(); 

    t0.join(); 
    t1.join(); 
} 
+0

Erik에게 의견을 보내 주시면 감사하겠습니다. 그러나 문제는 해결되지 않았습니다. – anthonyaje

+0

이유에 대해 자세히 설명해 주시겠습니까? –