📄 sem.c
字号:
/** * sem.c - Semaphore Mechanism. * * Copyright (C) 2008 ZhangHu * All rights reserved. * E-MAIL: anmnmnly@gmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */#include "include/types.h"#include "include/list.h"#include "include/mem.h"#include "include/core.h"#include "include/sem.h"#include "include/irq.h"static list_t sem_head; /* pointer to the head and rear of semaphore queue *//** * initsem - Initialize sem_head. * @return: * * @notes: */void initsem(void) { sem_head.next = &sem_head; sem_head.prev = &sem_head;}/** * check_id - Check the ID is whether existed. * @semid: The semaphore ID that need to check out. * @return: Semaphore or NULL. * * @notes: If the ID has existed in semaphore queue, and then return * semaphore's address, otherwise return NULL. */static sem_t *check_id(semid_t semid) { sem_t *psem; list_t *tmp_list = sem_head.next; if(sem_head.next != &sem_head && sem_head.prev != &sem_head) { do { psem = mac_find_entry(tmp_list, sem_t, sem_link); if(semid == psem->sem_id) { return psem; } else { tmp_list = tmp_list->next; } } while(tmp_list != sem_head.next); } return NULL;}/** * rdy_to_sem_wait - Move TCB from ready queue to semaphore waiting queue. * @psem: Pointer to semaphore. * @ptcb: Pointer to TCB. * @return: * * @notes: */static void rdy_to_sem_wait(sem_t *psem, tcb_t *ptcb) { uword_t prio = ptcb->prio; list_t *del_tcb; if((del_tcb = del_tcb_rdy(ptcb)) != NULL) { add_node_list_rear(&psem->task_link[prio], del_tcb); }}/** * sem_wait_to_rdy - Move TCB form semaphore waiting queue to ready queue * @psem: Pointer to semaphore. * @del_tcb: Pointer to TCB. * @return: * * @notes: */static void sem_wait_to_rdy(sem_t *psem, list_t *del_tcb) { list_t *plist; tcb_t *ptcb; ptcb = mac_find_entry(del_tcb, tcb_t, link); plist = del_node_list(&psem->task_link[ptcb->prio], del_tcb); ptcb = mac_find_entry(plist, tcb_t, link); add_tcb_rdy(ptcb);}/** * find_max_prio_tcb - Find the highest priority task in semaphore waiting queue * and return the link_address. * @psem: Pointer to semaphore. * @return: The 'link' member of TCB. otherwise return NULL. * * @notes: */static list_t *find_max_prio_tcb(sem_t *psem) { uword_t i; for(i = 0; i < MAX_PRIO; i++) { if(psem->task_link[i].next != &psem->task_link[i]) { return psem->task_link[i].next; } } return NULL;}/* The following functions are application program interface *//** * semInit - Creating a semaphore. * @SemID: The ID of new semaphore. * @InitCnt: The initial count value of new semaphore. * @err: Return reasons about this operation. * @return: The entry of new semaphore. * * @notes: */sem_t *semInit(semid_t SemID, uword_t InitCnt, uword_t *err) { uword_t i; sem_t *psem; psem = (sem_t*)kmalloc(sizeof(sem_t)); if(psem == NULL) { *err = SEM_FALSE; return NULL; } mac_disable_irq(); if(check_id(SemID) == NULL) { psem->sem_link.next = &psem->sem_link; psem->sem_link.prev = &psem->sem_link; psem->sem_id = SemID; psem->sem_cnt = InitCnt; psem->sem_type = SEM_NUM; psem->task_cnt = 0; psem->tcb_link.next = NULL; for(i = 0; i < MAX_PRIO; i++) { psem->task_link[i].next = &psem->task_link[i]; psem->task_link[i].prev = &psem->task_link[i]; } add_node_list_rear(&sem_head, &psem->sem_link); mac_enable_irq(); *err = SEM_OK; return psem; } else { *err = SEM_ID_CONFLICT; mac_enable_irq(); kfree(psem); return NULL; }}/** * semInitSyn - Creating a synchronized semaphore. * @SemID: The ID of new semaphore. * @err: Return reasons about this operation. * @return: The entry of new semaphore, otherwise NULL for failed. * * @notes: */sem_t *semInitSyn(semid_t SemID, uword_t *err) { sem_t *psem; psem = semInit(SemID, 0, err); if(psem != NULL) { psem->sem_type = SEM_SYN_INIT; return psem; } else { return NULL; }}/** * semInitMutex - Creating a mutex semaphore. * @SemID: The ID of new semaphore. * @err: Return reasons about this operation. * @return: The entry of new semaphore, otherwise NULL for failed. * * @notes: */sem_t *semInitMutex(semid_t SemID, uword_t *err) { sem_t *psem; psem = semInit(SemID, 1, err); if(psem != NULL) { psem->sem_type = SEM_BIN; return psem; } else { return NULL; }}/** * semGain - Get semaphore, if operating is failed, * the task will be block, and its TCB will be * deleted from ready queue, and then insert it * into waiting queue of semaphore. * @SemID: The ID of semaphore. * @pSem: Pointer to semaphore. * @err: Return reasons about this operation. * @return: * * @notes: SemID or pSemaphore is valide to specify a semaphore. */void semGain(semid_t SemID, sem_t *pSemaphore, uword_t *err) { sem_t *psem; sem_t *sem; slist_t *slist; /* In Interruption Context, so return directly */ if(if_isr() > 0) { *err = SEM_FALSE_ISR; return ; } /* Get the real entry of semaphore */ psem = pSemaphore; if(psem == NULL) { psem = check_id(SemID); if(psem == NULL) { *err = SEM_NOT_CREATE; return ; } } mac_disable_irq(); /* semaphore is available */ if(psem->sem_cnt > 0) { psem->sem_cnt--; add_node_single_list_head(&(current()->sem_link), &psem->tcb_link); mac_enable_irq(); *err = SEM_OK; return ; } /* semaphore is unavailable, so current task blocked. */ current()->status = T_BLOCK; psem->task_cnt++; rdy_to_sem_wait(psem, current()); mac_enable_irq(); scheduler(); /* other task have a opportunity to run. */ /* current task restarts running, it has got the semaphore. even so, we should confirm it. */ slist = current()->sem_link.next; while(slist != NULL) { sem = mac_find_entry(slist, sem_t, tcb_link); if(sem == psem) { *err = SEM_OK; return ; } else { slist = slist->next; } } *err = SEM_FALSE; return ;}/** * semGainFree - Get semaphore, if the operating is failed, * the task should not be blocked. * @SemID: The ID of semaphore. * @pSem: Pointer to semaphore. * @err: Return reasons about this operation. * @return: * * @notes: */void semGainFree(semid_t SemID, sem_t *pSemaphore, uword_t *err) { sem_t *psem; psem = pSemaphore; if(psem == NULL) { psem = check_id(SemID); if(psem == NULL) { *err = SEM_NOT_CREATE; return ; } } mac_disable_irq(); if(psem->sem_cnt > 0) { psem->sem_cnt--; add_node_single_list_head(&(current()->sem_link), &psem->tcb_link); mac_enable_irq(); *err = SEM_OK; return ; } mac_enable_irq(); *err = SEM_FALSE;}/** * semPost - Unlock a semaphore. * @SemID: The ID of semaphore. * @pSem: Pointer to semaphore. * @return: Return results about this operation. * * @notes: */uword_t semPost(semid_t SemID, sem_t *pSemaphore) { sem_t *psem; sem_t *sem; list_t *plist; slist_t *slist; tcb_t *ptcb; word_t flag; flag = FALSE; psem = pSemaphore; if(psem == NULL) { psem = check_id(SemID); if(psem == NULL) { return SEM_NOT_CREATE; } } mac_disable_irq(); slist = &(current()->sem_link); slist = slist->next; while(slist != NULL) { sem = mac_find_entry(slist, sem_t, tcb_link); if(sem == psem) { flag = TRUE; del_node_single_list(&(current()->sem_link), slist); break; } slist = slist->next; } if(flag == FALSE) { /* Release a Synchronized semaphore firstly. */ if(psem->sem_cnt == 0 && psem->sem_type == SEM_SYN_INIT) { psem->sem_type = SEM_BIN; } else { mac_enable_irq(); return SEM_FALSE; } } psem->sem_cnt++; if(psem->sem_cnt <= 0) { mac_enable_irq(); return SEM_OK; } if((plist = find_max_prio_tcb(psem)) == NULL) { mac_enable_irq(); return SEM_OK; } psem->task_cnt--; ptcb = mac_find_entry(plist, tcb_t, link); add_node_single_list_head(&ptcb->sem_link, &psem->tcb_link); psem->sem_cnt--; ptcb->status = T_READY; sem_wait_to_rdy(psem, plist); mac_enable_irq(); scheduler(); return SEM_OK;}/** * semDel - Deleting a semaphore. * @SemID: The ID of semaphore. * @pSem: Pointer to semaphore. * @return: Return results of this operation. * * @notes: */void semDel(semid_t SemID, sem_t *pSemaphore) { uword_t i; sem_t *psem; list_t *plist; tcb_t *ptcb; psem = pSemaphore; if(psem == NULL) { psem = check_id(SemID); if(psem == NULL) { return ; } } mac_disable_irq(); /* move all tasks in waiting queue to sleep queue. */ for(i=0; i<MAX_PRIO; i++) { do { if((plist = psem->task_link[i].next) == &psem->task_link[i]) { break; } else { del_node_list(&psem->task_link[i], plist); ptcb = mac_find_entry(plist, tcb_t, link); ptcb->status = T_SLEEP; add_tcb_sleep(ptcb); } } while(1); } del_node_list(&sem_head, &psem->sem_link); mac_enable_irq(); kmemset(psem, 0, sizeof(sem_t)); kfree(psem); return ;}/** * release_sem - Unlock all semaphores that a task locked. * @ptcb: Pointer to a TCB. * @return: * * @notes: */void release_sem(tcb_t *ptcb) { sem_t *psem; slist_t *slist; slist = &ptcb->sem_link; slist = slist->next; while(slist != NULL) { psem = mac_find_entry(slist, sem_t, tcb_link); semPost(0, psem); slist = slist->next; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -