2016-10-27 61 views
10

Dla klasy OS, obecnie muszę utworzyć kolejkę bezpieczną dla wątków w jądrze Linuksa, z którą współdziała się przy użyciu syscalls.Czy istnieją jakiekolwiek korzyści z używania semafora binarnego zamiast muteksu do wzajemnego wykluczania w krytycznej sekcji kolejki?

Teraz dla krytycznych sekcji mam wrażenie, że chciałbym użyć funkcji mutex_lock i mutex_unlock w nagłówku . Jednak powiedziano mi, że zamiast tego mogę użyć binarnego semafora z down_interruptible i up w nagłówku semaphore.h i że będzie lepiej.

Czytałem przez Difference between binary semaphore and mutex: Z nim, rozumiem, że główną zaletą jest to, jak silnie mutex to egzekwuje prawo własności, i że korzyść z semafora jest to, że ponieważ nie wymusza własność można go użyć jako mechanizm synchronizacji między dwoma (wieloma?) różnymi wątkami.

Moje pytanie brzmi: jakie są zalety semafora binarnego, jeśli używasz go w dokładnie taki sam sposób jak muteks. Bardziej wyraźnie gdybym napisał:

down() 
/* critical */ 
up() 

w taki sam sposób, że zrobię

mutex_lock() 
/* critical */ 
mutex_unlock() 

Czy jest jakaś zaleta wydajność, ponieważ jest mniej bezpieczne niż mutex? Czy czegoś brakuje?


Oto niewielki fragment kodu, który chcę zrobić wątku bezpieczny, jeśli chcesz więcej kontekstu (to mój pierwszy C proj kiedykolwiek):

#define MESSAGE_MAX_SIZE 512 

typedef struct list_head list_node; 

/* Create message struct */ 
typedef struct { 
    size_t size; 
    list_node node; 
    char data[MESSAGE_MAX_SIZE]; 
} Message; 

/* Create the linked list queue with dummy head */ 
struct { 
    size_t size; 
    list_node head; 
} my_q = { 0, LIST_HEAD_INIT(my_q.head) }; 

/* 
    Adds a new item to the tail of the queue. 

    @data: pointer to data to add to list 
    @len: size of the data 
*/ 
asmlinkage long sys_enqueue(const void __user *data, long len) { 
    long res = 0; 
    Message *msg = 0; 

    if (len < 0) return EINVAL; 
    if (len > MESSAGE_MAX_SIZE) return E2BIG; 

    msg = kmalloc(sizeof(Message), GFP_KERNEL); 
    if (msg == 0) return ENOMEM; 

    res = copy_from_user(msg->data, data, len); 
    if (res != 0) return EFAULT; 

    /* Start Critical Section */ 

    my_q.size++; 
    list_add_tail(&msg->node, &my_q.head); 

    /* End Critical Section */ 

    return 0; 
} 
+0

Czy Twoja kolejka będzie kiedykolwiek wymagać rekursywnego blokowania lub będzie musiała uporać się z priorytetową inwersją? –

+0

Nie dla odwrócenia priorytetu, i jestem całkiem pewny, że nie będę potrzebował również rekursywnego blokowania. – m0meni

+2

W tym momencie myślę, że sprowadza się to do wydajności. A jeśli chodzi o wydajność, najlepiej jest zrobić profil i zobaczyć, co się stanie. Mam przeczucie, że jeśli wykonają mniej więcej to samo, trzymam się muteksu, ale inni mogą mieć lepsze odpowiedzi na pewno. –

Odpowiedz

11

W przypadku braku dowodów empirycznych, Chciałbym zacytować z książka Linux Kernel Development

on (tj mutex) zachowuje się podobnie do semafora z mnożnikiem jednego, ale ma prostszy interfejs, bardziej wydajne działanie i dodatkowe ograniczenia dotyczące jego użytkowania.

Dodatkowo istnieje wiele ograniczeń, które dotyczą muteksów, ale nie semaforów. Rzeczy takie jak proces nie mogą zakończyć się przy trzymaniu muteksu. Co więcej, jeśli włączona jest opcja jądra CONFIG_DEBUG_MUTEXES, wszystkie ograniczenia dotyczące muteksów są zapewnione przez kontrole debugowania.

Tak więc, chyba że istnieje dobry powód, aby nie używać muteksa, powinien to być pierwszy wybór.

+3

Przejdę przez to również. –

5

Domyślnym prymitywem blokowania jest spinlock. Muteksy mają sens tylko wtedy, gdy musisz spać, trzymając blokadę, czego na pewno nie masz we wspomnianej próbce kodu.

#define MESSAGE_MAX_SIZE 512 

typedef struct list_head list_node; 

Dlaczego?

/* Create message struct */ 
typedef struct { 
    size_t size; 
    list_node node; 
    char data[MESSAGE_MAX_SIZE]; 
} Message; 

Weird order, wskaźnik węzła powinien być pierwszy lub ostatni.

/* Create the linked list queue with dummy head */ 
struct { 
    size_t size; 
    list_node head; 
} my_q = { 0, LIST_HEAD_INIT(my_q.head) }; 

/* 
    Adds a new item to the tail of the queue. 

    @data: pointer to data to add to list 
    @len: size of the data 
*/ 
asmlinkage long sys_enqueue(const void __user *data, long len) { 

Klamra nawiasowa powinna znajdować się w następnej linii. Dlaczego długość podpisanego typu?

long res = 0; 
    Message *msg = 0; 

Dlaczego ją inicjujesz i dlaczego ustawiasz wskaźnik na 0, a nie na NULL?

if (len < 0) return EINVAL; 

Instrukcja return powinna znajdować się w następnym wierszu. Należy również zauważyć, że ten warunek nie byłby istotny, gdyby typ był niepodpisany na początek.

if (len > MESSAGE_MAX_SIZE) return E2BIG; 

    msg = kmalloc(sizeof(Message), GFP_KERNEL); 

Dlaczego nie sizeof (* MSG)

if (msg == 0) return ENOMEM; 

    res = copy_from_user(msg->data, data, len); 
    if (res != 0) return EFAULT; 

to przecieki wiad.

/* Start Critical Section */ 

    my_q.size++; 
    list_add_tail(&msg->node, &my_q.head); 

    /* End Critical Section */ 

    return 0; 
} 
+1

To nie jest "osobista opinia", ale zgodność z istniejącymi wytycznymi dotyczącymi kodowania jądra Linuksa, które naruszasz bez wyraźnego powodu. To, co powinieneś zrobić w odniesieniu do faktycznego pytania, wyjaśniono w pierwszych 2 zdaniach. Powodzenia w twoim nastawieniu. –

+1

Przepraszam, jeśli uznałem to za lekceważące. Przez osobistą opinię miałem na myśli styl, który, ponieważ jest to tylko mój własny projekt, który nie przyczynia się do jądra, nie wydawało mi się ważne. Jeśli chodzi o typ list_head -> list_node typedef, to dlatego, że list_head jest dla mnie tak niezrozumiałą nazwą. Jeśli chodzi o takie zmienne, jak podpisane, to są to podpisy, z którymi miałem pracować, więc je zachowałem. Co do wycieku pamięci, nazywam to kfree() w funkcji usuwania kolejek, której tutaj nie zamieszczałem. Wolę 0 od NULL, ponieważ są one tym samym. Jedno pytanie: dlaczego węzeł powinien być pierwszy w strukturze wiadomości? – m0meni

+0

Usunąłem również moje zgłoszenie. Sądzę, że po prostu denerwowała mnie niechciana recenzja kodu i żałuję, że nie rozwinęliście więcej na pierwszych linijkach, które napisaliście, na przykład, kiedy rzeczywiście trzeba spać, a nie tylko spinlock. – m0meni