Nuttx信號(hào)機(jī)制


Nuttx相關(guān)的歷史文章


介紹

信號(hào)是在軟件層次上對(duì)中斷機(jī)制的模擬赚窃,在原理上說(shuō),一個(gè)任務(wù)接收到一個(gè)信號(hào),與CPU接收到中斷請(qǐng)求是一致的锰悼。信號(hào)是異步的,任務(wù)不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá)团赏,它甚至不知道信號(hào)何時(shí)會(huì)到達(dá)箕般。
信號(hào)的來(lái)源包括:

  • 硬件來(lái)源,比如按鍵觸發(fā)
  • 軟件來(lái)源舔清,比如kill丝里,raise等系統(tǒng)函數(shù),比如一些非法運(yùn)算操作等

任務(wù)和信號(hào)

Nuttx為進(jìn)程和線程提供了信號(hào)接口体谒,可以在任務(wù)上下文中或在任務(wù)上下文之間杯聚,通過(guò)信號(hào)這種異步通信機(jī)制,來(lái)改變?nèi)蝿?wù)的控制流营密。在任何一個(gè)任務(wù)中或中斷處理函數(shù)中械媒,可以給指定TASK ID的其他任務(wù)發(fā)送信號(hào)。接收到信號(hào)的任務(wù)將在具有優(yōu)先級(jí)時(shí)執(zhí)行任務(wù)指定的信號(hào)處理函數(shù)。信號(hào)處理程序是一個(gè)用戶提供的函數(shù)纷捞,它綁定到一個(gè)特定的信號(hào)痢虹,并在接收到信號(hào)時(shí)執(zhí)行任何必要的操作。默認(rèn)情況下主儡,沒有對(duì)任何信號(hào)設(shè)置預(yù)定義動(dòng)作奖唯,所有信號(hào)的默認(rèn)操作都是忽略(如果用戶沒有提供信號(hào)處理函數(shù)),從這個(gè)意義上說(shuō)糜值,所有Nuttx默認(rèn)情況下都是實(shí)時(shí)信號(hào)丰捷。

任務(wù)組

Nuttx既支持任務(wù)task,又支持線程pthreads寂汇。taskpthreads的主要區(qū)別在于task之間的獨(dú)立性要高得多病往。task可以創(chuàng)建pthreads,這些pthreads將共享task的資源骄瓣。主task線程和它所包含的pthreads停巷,一起被稱為任務(wù)組,在Nuttx中使用任務(wù)組來(lái)模擬POSIX的進(jìn)程榕栏。

發(fā)送信號(hào)給多線程任務(wù)組

多線程任務(wù)組中的信號(hào)行為是復(fù)雜的畔勤。Nuttx使用任務(wù)組模擬進(jìn)程,并遵循POSIX規(guī)則進(jìn)行信號(hào)發(fā)送扒磁。通常庆揪,當(dāng)向一個(gè)任務(wù)組發(fā)送信號(hào)時(shí),需要向創(chuàng)建該任務(wù)組的主task線程的ID號(hào)發(fā)送(實(shí)際上妨托,其他任務(wù)不應(yīng)該知道該任務(wù)組中創(chuàng)建的內(nèi)部線程ID)缸榛。任務(wù)組會(huì)記住該ID(即使主任務(wù)線程退出)。

當(dāng)向一個(gè)多線程任務(wù)組發(fā)送信號(hào)時(shí)兰伤,會(huì)出現(xiàn)以下情況:

  • 當(dāng)任務(wù)組接收到一個(gè)信號(hào)仔掸,那么任務(wù)組中只有一個(gè)不阻塞該信號(hào)的不確定線程會(huì)接收到信號(hào)。
  • 當(dāng)任務(wù)組接收到一個(gè)信號(hào)医清,并且有多個(gè)線程在等待該信號(hào),有且只有一個(gè)不確定的線程將接收該信號(hào)卖氨。

可以使用sigprocmask()pthread_sigmask()接口來(lái)屏蔽信號(hào)会烙。信號(hào)被屏蔽后,將不會(huì)在具有屏蔽該信號(hào)的線程中接收到筒捺。在創(chuàng)建新的線程時(shí)柏腻,新線程將會(huì)繼承父線程的信號(hào)掩碼,因此如果在一個(gè)線程上阻塞某個(gè)信號(hào)系吭,那么在它所創(chuàng)建的線程中也會(huì)阻塞該信號(hào)五嫂。

可以通過(guò)信號(hào)掩碼來(lái)控制哪個(gè)線程接收信號(hào),例如,創(chuàng)建一個(gè)線程沃缘,該線程的唯一目的是捕獲某個(gè)特定的信號(hào)并且做出響應(yīng):在主任務(wù)中阻塞信號(hào)躯枢;這時(shí)該信號(hào)會(huì)在任務(wù)組中被所有的線程阻塞。在一個(gè)“信號(hào)處理線程”中槐臀,使能了信號(hào)锄蹂,這個(gè)線程將是唯一接收信號(hào)的線程。

API接口

本來(lái)想一上來(lái)就分析數(shù)據(jù)結(jié)構(gòu)水慨,看了一圈源代碼得糜,發(fā)現(xiàn)還是先從應(yīng)用層的API入手,有個(gè)全面的認(rèn)識(shí)后晰洒,再逐層去分析底層的原理朝抖。

API如下:

int sigemptyset(sigset_t *set) ;    /* 清空set信號(hào)集, 排除所有信號(hào) */
int sigfillset(sigset_t *set);    /* 置位set信號(hào)集谍珊,包含所有信號(hào) */
int sigaddset(sigset_t *set, int signo);    /* 將信號(hào)signo添加進(jìn)set信號(hào)集 */
int sigdelset(sigset_t *set, int signo);    /* 將signo信號(hào)從set信號(hào)集中刪除 */
int  sigismember(const sigset_t *set, int signo);    /* 判斷signo信號(hào)是否屬于set信號(hào)集 */
int sigaction(int signo, const struct sigaction *act,
                  struct sigaction *oact);    /* 信號(hào)安裝函數(shù)治宣,將sigaction與一個(gè)特定的信號(hào)進(jìn)行綁定,sigaction結(jié)構(gòu)體在下文會(huì)介紹 */
int sigignore(int signo);    /* 忽略signo信號(hào) */
void (*sigset(int signo, void (*disp)(int)))(int);    /* 改變signo信號(hào)的配置, disp可以是SIG_DFL周偎、SIG_IGN鸥鹉,或者信號(hào)處理Handler */
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);    /* 根據(jù)how的策略,來(lái)改變當(dāng)前阻塞的信號(hào)集 */
int sighold(int signo);    /* 將signo信號(hào)添加進(jìn)進(jìn)程的阻塞信號(hào)集 */
int sigrelse(int signo);    /* 將sigo信號(hào)從進(jìn)程的阻塞信號(hào)集中移除 */
int sigpending(sigset_t *set);    /* 返回在阻塞期間收到的阻塞信號(hào)的集合 */
int sigsuspend(const sigset_t *set);    /* 在接收到某個(gè)信號(hào)之前豌拙,臨時(shí)用set替換進(jìn)程的信號(hào)掩碼,并暫停進(jìn)程執(zhí)行,直到收到信號(hào)為止 */
int sigpause(int signo);    /* 將signo信號(hào)從信號(hào)掩碼中移除题暖,暫停進(jìn)程按傅,直到收到信號(hào)為止 */
int sigwaitinfo(const sigset_t *set, struct siginfo *info);    /* 調(diào)用的sigtimedwait */
int sigtimedwait(const sigset_t *set, struct siginfo *info,
                     const struct timespec *timeout);    /* 將set作為阻塞信號(hào)集,當(dāng)多個(gè)信號(hào)到達(dá)時(shí)胧卤,返回最小的返回唯绍,如果沒有信號(hào)到達(dá),在timeout時(shí)間內(nèi)枝誊,進(jìn)程會(huì)暫停况芒,直到收到信號(hào)或者時(shí)間到期 */
int sigqueue (int tid, int signo, union sigval value);    /* 向tid Task發(fā)送signo信號(hào),信號(hào)攜帶value數(shù)據(jù) */
int kill(pid_t pid, int sig);    /* 向pid Task發(fā)送sig信號(hào) */
int pause(void);    /* 暫停當(dāng)前調(diào)用線程叶撒,直到收到一個(gè)non-blocked信號(hào) */

從上述接口中可以看出绝骚,大致可以分為以下幾類:

  • 對(duì)信號(hào)集/信號(hào)本身的操作:比如信號(hào)集的清空與置位、將信號(hào)從信號(hào)集中刪除祠够、增加信號(hào)到信號(hào)集中压汪、判斷信號(hào)是否屬于信號(hào)集等
  • 對(duì)信號(hào)的行為響應(yīng):比如首先需要信號(hào)安裝、設(shè)置信號(hào)的Handler古瓤、忽略某個(gè)信號(hào)止剖、阻塞某些信號(hào)腺阳、在接收到某些信號(hào)前暫停當(dāng)前進(jìn)程(會(huì)涉及到任務(wù)的切換)等
  • 發(fā)送信號(hào):向某個(gè)特定的task發(fā)送信號(hào),信號(hào)中還能攜帶數(shù)據(jù)

數(shù)據(jù)結(jié)構(gòu)

數(shù)據(jù)結(jié)構(gòu)又分為兩部分:Kernel部分和User部分穿香,其中Kernel部分也需要用到User部分的定義亭引。

User部分,定義在include/signal.h中扔水,主要描述信號(hào)的基本數(shù)據(jù)結(jié)構(gòu)以及API接口

  • 信號(hào)集的定義痛侍,總共包含32中信號(hào),Nuttx提供了部分信號(hào)魔市,其余的用戶可以自定義
/* This defines a set of 32 signals (numbered 0 through 31).
 * REVISIT: Signal 0 is, however, not generally usable since that value has
 * special meaning in some circumstances (e.g., kill()).
 */

typedef uint32_t sigset_t;   /* Bit set of 32 signals */
#define __SIGSET_T_DEFINED 1

/* Signal set management definitions and macros. */

#define NULL_SIGNAL_SET ((sigset_t)0x00000000)
#define ALL_SIGNAL_SET  ((sigset_t)0xffffffff)
#define MIN_SIGNO       0
#define MAX_SIGNO       31
#define GOOD_SIGNO(s)   ((((unsigned)(s))<=MAX_SIGNO))
#define SIGNO2SET(s)    ((sigset_t)1 << (s))

/* A few of the real time signals are used within the OS.  They have
 * default values that can be overridden from the configuration file. The
 * rest are all user signals.
 *
 * The signal number zero is wasted for the most part.  It is a valid
 * signal number, but has special meaning at many interfaces (e.g., Kill()).
 *
 * These are the semi-standard signal definitions:
 */
#define SIGUSR1       1  /* User signal 1 */
#define SIGUSR2       2  /* User signal 2 */
#define SIGALRM       3  /* Default signal used with POSIX timers (used only */
                           /* no other signal is provided) */
#define SIGCHLD     4  /* Used by child threads to signal parent thread */
#define SIGPOLL     5  /* Sent when an asynchronous I/O event occurs */

/* The following are non-standard signal definitions */
#define SIGCONDTIMEDOUT 16  /* Used in the implementation of pthread_cond_timedwait */
#define SIGWORK     17  /* Used to wake up the work queue */
  • 信號(hào)事件的定義主届,主要用于向消息隊(duì)列發(fā)送信號(hào),通知某個(gè)task隊(duì)列中已經(jīng)有消息了
/* Values for the sigev_notify field of struct sigevent */

#define SIGEV_NONE      0 /* No asynchronous notification is delivered */
#define SIGEV_SIGNAL    1 /* Notify via signal,with an application-defined value */
#ifdef CONFIG_SIG_EVTHREAD
#define SIGEV_THREAD    3 /* A notification function is called */
#endif

/* This defines the type of the siginfo si_value field */

union sigval
{
  int       sival_int;       /* Integer value */
  FAR void *sival_ptr;       /* Pointer value */
};

/* This structure contains elements that define a queue signal. The following is
 * used to attach a signal to a message queue to notify a task when a message is
 * available on a queue
 */

#ifdef CONFIG_CAN_PASS_STRUCTS
typedef CODE void (*sigev_notify_function_t)(union sigval value);
#else
typedef CODE void (*sigev_notify_function_t)(FAR void *sival_ptr);
#endif

struct sigevent
{
  uint8_t      sigev_notify; /* Notification method: SIGEV_SIGNAL, SIGEV_NONE, or SIGEV_THREAD */
  uint8_t      sigev_signo;  /* Notification signal */
  union sigval sigev_value;  /* Data passed with notification */

#ifdef CONFIG_SIG_EVTHREAD
  sigev_notify_function_t sigev_notify_function; /* Notification function */
  FAR pthread_attr_t *sigev_notify_attributes;   /* Notification attributes (not used) */
#endif
};
  • 信號(hào)的定義待德,描述信號(hào)的內(nèi)部細(xì)節(jié)信息君丁,用于在信號(hào)Handler中的參數(shù)傳遞
/* These are the possible values of the signfo si_code field */

#define SI_USER         0  /* Signal sent from kill, raise, or abort */
#define SI_QUEUE        1  /* Signal sent from sigqueue */
#define SI_TIMER        2  /* Signal is result of timer expiration */
#define SI_ASYNCIO      3  /* Signal is the result of asynch IO completion */
#define SI_MESGQ        4  /* Signal generated by arrival of a message on an */
                           /* empty message queue */
#define CLD_EXITED      5  /* Child has exited (SIGCHLD only) */
#define CLD_KILLED      6  /* Child was killed (SIGCHLD only) */
#define CLD_DUMPED      7  /* Child terminated abnormally (SIGCHLD only) */
#define CLD_TRAPPED     8  /* Traced child has trapped (SIGCHLD only) */
#define CLD_STOPPED     9  /* Child has stopped (SIGCHLD only) */
#define CLD_CONTINUED   10 /* Stopped child had continued (SIGCHLD only) */

/* The following types is used to pass parameters to/from signal handlers */

struct siginfo
{
  uint8_t      si_signo;     /* Identifies signal */
  uint8_t      si_code;      /* Source: SI_USER, SI_QUEUE, SI_TIMER, SI_ASYNCIO, or SI_MESGQ */
  uint8_t      si_errno;     /* Zero or errno value associated with signal */
  union sigval si_value;     /* Data passed with signal */
#ifdef CONFIG_SCHED_HAVE_PARENT
  pid_t        si_pid;       /* Sending task ID */
  int          si_status;    /* Exit value or signal (SIGCHLD only). */
#endif
};

typedef struct siginfo siginfo_t;
#define __SIGINFO_T_DEFINED 1
  • 信號(hào)action的定義,當(dāng)信號(hào)deliver的時(shí)候将宪,Task所采取的行動(dòng)绘闷,其中sigaction中sa_mask位域,表示的是當(dāng)Handler在執(zhí)行期間较坛,需要阻塞的信號(hào)
/* struct sigaction flag values */

#define SA_NOCLDSTOP    (1 << 0) /* Do not generate SIGCHILD when
                                  * children stop (ignored) */
#define SA_SIGINFO      (1 << 1) /* Invoke the signal-catching function
                                  * with 3 args instead of 1
                                  * (always assumed) */
#define SA_NOCLDWAIT    (1 << 2) /* If signo=SIGCHLD, exit status of child
                                  * processes will be discarded */

/* Special values of of sa_handler used by sigaction and sigset.  They are all
 * treated like NULL for now.  This is okay for SIG_DFL and SIG_IGN because
 * in NuttX, the default action for all signals is to ignore them.
 */

#define SIG_ERR         ((_sa_handler_t)-1)  /* And error occurred */
#define SIG_DFL         ((_sa_handler_t)0)   /* Default is SIG_IGN for all signals */
#define SIG_IGN         ((_sa_handler_t)0)   /* Ignore the signal */
#define SIG_HOLD        ((_sa_handler_t)1)   /* Used only with sigset() */

/* Non-standard convenience definition of signal handling function types.
 * These should be used only internally within the NuttX signal logic.
 */

typedef CODE void (*_sa_handler_t)(int signo);
typedef CODE void (*_sa_sigaction_t)(int signo, FAR siginfo_t *siginfo,
                                     FAR void *context);

/* The following structure defines the action to take for given signal */

struct sigaction
{
  union
  {
    _sa_handler_t   _sa_handler;
    _sa_sigaction_t _sa_sigaction;
  } sa_u;
  sigset_t          sa_mask;
  int               sa_flags;
};

/* Definitions that adjust the non-standard naming */

#define sa_handler   sa_u._sa_handler
#define sa_sigaction sa_u._sa_sigaction

Kernle部分印蔗,定義在include/sched/signal/signal.h中,主要描述了Kernel中是如何實(shí)現(xiàn)信號(hào)機(jī)制的

  • 描述一個(gè)信號(hào)的action的結(jié)構(gòu)丑勤,指針flink將sigactq鏈接起來(lái)管理华嘹,系統(tǒng)注冊(cè)一個(gè)信號(hào),底層將用一個(gè)sigactq結(jié)構(gòu)體來(lái)對(duì)應(yīng)
/* The following defines the sigaction queue entry */

struct sigactq
{
  FAR struct sigactq *flink;     /* Forward link */
  struct sigaction act;          /* Sigaction data */
  uint8_t   signo;               /* Signal associated with action */
};
typedef struct sigactq  sigactq_t;
  • 描述pending信號(hào)(未決信號(hào))的結(jié)構(gòu)法竞,其中info中包括信號(hào)的詳細(xì)信息耙厚,該信號(hào)會(huì)通過(guò)flink鏈接管理
/* The following defines the queue structure within each TCB to hold pending
 * signals received by the task.  These are signals that cannot be processed
 * because:  (1) the task is not waiting for them, or (2) the task has no
 * action associated with the signal.
 */

struct sigpendq
{
  FAR struct sigpendq *flink;    /* Forward link */
  siginfo_t info;                /* Signal information */
  uint8_t   type;                /* (Used to manage allocations) */
};
typedef struct sigpendq sigpendq_t;
  • 描述需要被執(zhí)行的信號(hào)節(jié)隊(duì)列點(diǎn)結(jié)構(gòu),當(dāng)任務(wù)注冊(cè)了信號(hào)并接收到信號(hào)后岔霸,會(huì)分配一個(gè)信號(hào)隊(duì)列節(jié)點(diǎn)薛躬,將該節(jié)點(diǎn)掛載到任務(wù)tcb->sigpendactionq鏈表上等待運(yùn)行信號(hào)服務(wù)函數(shù)。其中action指向信號(hào)處理函數(shù)呆细,mask用于當(dāng)信號(hào)處理函數(shù)運(yùn)行時(shí)阻塞其他信號(hào)型宝,info是信號(hào)的詳細(xì)信息
/* The following defines the queue structure within each TCB to hold queued
 * signal actions that need action by the task
 */

struct sigq_s
{
  FAR struct sigq_s *flink;      /* Forward link */
  union
  {
    void (*sighandler)(int signo, siginfo_t *info, void *context);
  } action;                      /* Signal action */
  sigset_t  mask;                /* Additional signals to mask while the
                                  * the signal-catching function executes */
  siginfo_t info;                /* Signal information */
  uint8_t   type;                /* (Used to manage allocations) */
};
typedef struct sigq_s sigq_t;

上述三種結(jié)構(gòu),struct sigactq描述信號(hào)的action絮爷, struct sigpendq描述未決的信號(hào)诡曙, struct sigq_s描述的是信號(hào)與action的對(duì)應(yīng)關(guān)系,可以認(rèn)為是一個(gè)紐帶略水,將信號(hào)和Action綁定到一起。這幾個(gè)結(jié)構(gòu)的名字讓我懵逼了好久劝萤。還有更懵逼的在下邊渊涝。

基于上述的三個(gè)結(jié)構(gòu)體,系統(tǒng)維護(hù)了5個(gè)全局隊(duì)列,用于最終信號(hào)的處理跨释,信號(hào)處理過(guò)程中胸私,這三個(gè)結(jié)構(gòu)體的節(jié)點(diǎn),將在這5個(gè)全局隊(duì)列中進(jìn)行流動(dòng)鳖谈,有點(diǎn)類似于任務(wù)調(diào)度中任務(wù)隊(duì)列的意思岁疼。

  1. 存放action的隊(duì)列,存放sigactq_t缆娃,用于Action資源的分配
/* The g_sigfreeaction data structure is a list of available signal action
 * structures.
 */

extern sq_queue_t  g_sigfreeaction;
  1. 存放信號(hào)隊(duì)列節(jié)點(diǎn)的隊(duì)列捷绒,存放sigq_t,此時(shí)Action和信號(hào)已經(jīng)完成了綁定贯要,用于sigq_t資源的分配暖侨。存放信號(hào)隊(duì)列節(jié)點(diǎn)的隊(duì)有兩種:用于普通分配的隊(duì)列和用于中斷中分配的隊(duì)列。
/* The g_sigpendingaction data structure is a list of available pending
 * signal action structures.
 */

extern sq_queue_t  g_sigpendingaction;

/* The g_sigpendingirqaction is a list of available pending signal actions
 * that are reserved for use by interrupt handlers.
 */

extern sq_queue_t  g_sigpendingirqaction;
  1. 存放未決信號(hào)的隊(duì)列崇渗,存放sigpendq_t字逗,用于未決信號(hào)資源的分配。同2相似宅广,它也存在兩種隊(duì)列:用于普通分配的隊(duì)列和用于中斷中分配的隊(duì)列葫掉。
/* The g_sigpendingsignal data structure is a list of available pending
 * signal structures.
 */

extern sq_queue_t  g_sigpendingsignal;

/* The g_sigpendingirqsignal data structure is a list of available pending
 * signal structures that are reserved for use by interrupt handlers.
 */

extern sq_queue_t  g_sigpendingirqsignal;

那么這三種數(shù)據(jù)結(jié)構(gòu)以及幾個(gè)全局隊(duì)列又是怎么對(duì)應(yīng)到Task數(shù)據(jù)結(jié)構(gòu)中的呢,先看看Task中與信號(hào)相關(guān)的位域吧跟狱,有兩部分:

第一部分:

struct tcb_s 
{
...
#ifndef CONFIG_DISABLE_SIGNALS
  sigset_t   sigprocmask;                /* Signals that are blocked            */
  sigset_t   sigwaitmask;                /* Waiting for pending signals         */
  sq_queue_t sigpendactionq;             /* List of pending signal actions      */
  sq_queue_t sigpostedq;                 /* List of posted signals              */
  siginfo_t  sigunbinfo;                 /* Signal info when task unblocked     */
#endif
...
}

上述代碼中位域介紹如下:

  • sigprocmask:任務(wù)Tcb的阻塞信號(hào)集俭厚,如果某個(gè)信號(hào)屬于這個(gè)阻塞信號(hào)集,那么發(fā)送該信號(hào)到Tcb時(shí)兽肤,信號(hào)被阻塞套腹。除非該信號(hào)是等待的信號(hào),或者Tcb任務(wù)取消了對(duì)該信號(hào)的阻塞资铡,信號(hào)才會(huì)被deliver电禀。
  • sigwaitmask:該任務(wù)等待的信號(hào)集。
  • sigpendactionq:用于掛載該任務(wù)需要服務(wù)的信號(hào)節(jié)點(diǎn)sigq_t笤休,任務(wù)開始執(zhí)行時(shí)尖飞,sigpendactionq中的節(jié)點(diǎn)所代表的信號(hào)處理函數(shù)將被運(yùn)行。在信號(hào)處理函數(shù)運(yùn)行前店雅,該信號(hào)的sigq_t節(jié)點(diǎn)將從sigpendactionq隊(duì)列中轉(zhuǎn)移到sigpostedq隊(duì)列中政基。
  • sigpostedq:用于掛載該任務(wù)正在執(zhí)行信號(hào)處理函數(shù)的信號(hào)節(jié)點(diǎn)sigq_t,當(dāng)信號(hào)處理函數(shù)執(zhí)行完畢后闹啦,信號(hào)節(jié)點(diǎn)sigq_t將被從隊(duì)列中移除沮明,然后被釋放。
  • sigunbinfo:用于記錄信號(hào)信息

從上可以看出窍奋,上述結(jié)構(gòu)中的sigpendactionq會(huì)存放sigq_t資源荐健,也就是已經(jīng)完成了信號(hào)和Action綁定后的節(jié)點(diǎn)酱畅。顯然,sigq_t資源會(huì)在tcb->sigpendactionq字段指向的隊(duì)列和g_sigpendingaction/g_sigpendingirqaction之間流動(dòng)江场。

第二部分纺酸,在struct task_group_s中,如果定義了TASK_GROUP的話就會(huì)包含址否。

struct task_group_s
{
...
#ifndef CONFIG_DISABLE_SIGNALS
  /* POSIX Signal Control Fields ************************************************/

  sq_queue_t tg_sigactionq;         /* List of actions for signals              */
  sq_queue_t tg_sigpendingq;        /* List of pending signals                  */
#endif
...
}

上述兩個(gè)位域含義很清晰餐蔬,一個(gè)用于放置Action,一個(gè)用于放置信號(hào)佑附。對(duì)應(yīng)到前邊的五個(gè)全局隊(duì)列樊诺,可以知道:sigactq_t資源在task_group->tg_sigactionq指向的隊(duì)列和g_sigfreeaction隊(duì)列中流動(dòng);sigpendq_t資源在task_group->tg_sigpendingq指向的隊(duì)列和g_sigpendingsignal/g_sigpendingirqsignal隊(duì)列中流動(dòng)帮匾。

到這里為止啄骇,基本上將所有的數(shù)據(jù)結(jié)構(gòu)及資源捋清了。注冊(cè)信號(hào)就是關(guān)聯(lián)一個(gè)Task和某個(gè)信號(hào)處理函數(shù)瘟斜,當(dāng)Task接收到信號(hào)后缸夹,對(duì)應(yīng)信號(hào)的信號(hào)處理函數(shù)被運(yùn)行。而涉及到這個(gè)處理流程的所有資源(上述結(jié)構(gòu)體描述)螺句,就是在這些資源隊(duì)列中進(jìn)行流轉(zhuǎn)虽惭。
來(lái)一張圖吧


信號(hào)機(jī)制

注冊(cè)信號(hào)

通過(guò)int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *oact)接口可以查詢和設(shè)置信號(hào)關(guān)聯(lián)的處理方式。在該函數(shù)中蛇尚,完成了以下幾個(gè)功能:

  • act對(duì)應(yīng)的sigaction設(shè)置進(jìn)本task中芽唇,并將之前的sigactionoact的形式傳遞出來(lái)。
  • 根據(jù)signo查詢task_group->tg_sigactionq中是否有對(duì)應(yīng)的sigactq_t取劫,沒有的話從系統(tǒng)g_sigfreeaction鏈表中分配一個(gè)匆笤。
  • 根據(jù)act對(duì)應(yīng)的sigactionHandler的處理方式(忽略信號(hào),還是提供處理函數(shù))谱邪,更新sigactq_t結(jié)構(gòu)炮捧,最終將sigactq_t的結(jié)構(gòu)插入到task_group->tg_sigactionq中。

整個(gè)過(guò)程惦银,就是將sigaction注冊(cè)進(jìn)Task中的鏈表中咆课,還是直接看源代碼來(lái)得更清晰,代碼里有詳盡的注釋扯俱,理解起來(lái)比較容易书蚪。

/****************************************************************************
 * Name: sigaction
 *
 * Description:
 *   This function allows the calling process to examine and/or specify the
 *   action to be associated with a specific signal.
 *
 *   The structure sigaction, used to describe an action to be taken, is
 *   defined to include the following members:
 *
 *   - sa_u.sa_handler:  Pointer to a signal-catching function
 *   - sa_u.sa_sigaction:  Alternative form of the signal-catching function
 *   - sa_mask: An additional set of signals to be blocked during execution
 *       of a signal catching function
 *   - sa_flags.  Special flags to affect the behavior of a signal.
 *
 *   If the argument 'act' is not NULL, it points to a structure specifying
 *   the action to be associated with the specified signal.  If the argument
 *   'oact' is not NULL, the action previously associated with the signal
 *   is stored in the location pointed to by the argument 'oact.'
 *
 *   When a signal is caught by a signal-catching function installed by
 *   sigaction() function, a new signal mask is calculated and installed for
 *   the duration of the signal-catching function.  This mask is formed by
 *   taking the union of the current signal mask and the value of the
 *   sa_mask for the signal being delivered and then including the signal
 *   being delivered.  If and when the user's signal handler returns, the
 *   original signal mask is restored.
 *
 *   Once an action is installed for a specific signal, it remains installed
 *   until another action is explicitly requested by another call to sigaction().
 *
 * Parameters:
 *   sig - Signal of interest
 *   act - Location of new handler
 *   oact - Location to store only handler
 *
 * Return Value:
 *   0 (OK), or -1 (ERROR) if the signal number is invalid.
 *   (errno is not set)
 *
 * Assumptions:
 *
 * POSIX Compatibility:
 * - There are no default actions so the special value SIG_DFL is treated
 *   like SIG_IGN.
 * - All sa_flags in struct sigaction of act input are ignored (all
 *   treated like SA_SIGINFO). The one exception is if CONFIG_SCHED_CHILD_STATUS
 *   is defined; then SA_NOCLDWAIT is supported but only for SIGCHLD
 *
 ****************************************************************************/

int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *oact)
{
  FAR struct tcb_s *rtcb = this_task();
  FAR struct task_group_s *group;
  FAR sigactq_t *sigact;

  /* Since sigactions can only be installed from the running thread of
   * execution, no special precautions should be necessary.
   */

  DEBUGASSERT(rtcb != NULL && rtcb->group != NULL);
  group = rtcb->group;

  /* Verify the signal number */

  if (!GOOD_SIGNO(signo))
    {
      set_errno(EINVAL);
      return ERROR;
    }

  /* Find the signal in the signal action queue */

  sigact = sig_findaction(group, signo);

  /* Return the old sigaction value if so requested */

  if (oact)
    {
      if (sigact)
        {
          COPY_SIGACTION(oact, &sigact->act);
        }
      else
        {
          /* There isn't an old value */

          oact->sa_u._sa_handler = NULL;
          oact->sa_mask = NULL_SIGNAL_SET;
          oact->sa_flags = 0;
        }
    }

  /* If the argument act is a null pointer, signal handling is unchanged;
   * thus, the call can be used to enquire about the current handling of
   * a given signal.
   */

  if (!act)
    {
      return OK;
    }

#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)

  /* Handle a special case.  Retention of child status can be suppressed
   * if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT.
   *
   * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated
   * when a child process terminates.  In NuttX, a SIGCHLD signal is
   * generated in this case; but in some other implementations, it may not
   * be.
   */

  if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0)
    {
      irqstate_t flags;

      /* We do require a critical section to muck with the TCB values that
       * can be modified by the child thread.
       */

      flags = enter_critical_section();

      /* Mark that status should be not be retained */

      rtcb->group->tg_flags |= GROUP_FLAG_NOCLDWAIT;

      /* Free all pending exit status */

      group_removechildren(rtcb->group);
      leave_critical_section(flags);
    }
#endif

  /* Handle the case where no sigaction is supplied (SIG_IGN) */

  if (act->sa_u._sa_handler == SIG_IGN)
    {
      /* Do we still have a sigaction container from the previous setting? */

      if (sigact)
        {
          /* Yes.. Remove it from signal action queue */

          sq_rem((FAR sq_entry_t *)sigact, &group->tg_sigactionq);

          /* And deallocate it */

          sig_releaseaction(sigact);
        }
    }

  /* A sigaction has been supplied */

  else
    {
      /* Do we still have a sigaction container from the previous setting?
       * If so, then re-use for the new signal action.
       */

      if (!sigact)
        {
          /* No.. Then we need to allocate one for the new action. */

          sigact = sig_allocateaction();

          /* An error has occurred if we could not allocate the sigaction */

          if (!sigact)
            {
              set_errno(ENOMEM);
              return ERROR;
            }

          /* Put the signal number in the queue entry */

          sigact->signo = (uint8_t)signo;

          /* Add the new sigaction to signal action queue */

          sq_addlast((FAR sq_entry_t *)sigact, &group->tg_sigactionq);
        }

      /* Set the new sigaction */

      COPY_SIGACTION(&sigact->act, act);
    }

  return OK;
}

發(fā)送信號(hào)

發(fā)送信號(hào)以kill()函數(shù)來(lái)解釋是再合適不過(guò)了。
kill()函數(shù)中迅栅,根據(jù)傳進(jìn)來(lái)的PID號(hào)殊校,找到對(duì)應(yīng)的Task,并向該Task發(fā)送信號(hào)读存,關(guān)鍵代碼如下:

int kill(pid_t pid, int signo)
{
...
  /* Keep things stationary through the following */

  sched_lock();

  /* Create the siginfo structure */

  info.si_signo           = signo;
  info.si_code            = SI_USER;
  info.si_errno           = EINTR;
  info.si_value.sival_ptr = NULL;
#ifdef CONFIG_SCHED_HAVE_PARENT
  info.si_pid             = rtcb->pid;
  info.si_status          = OK;
#endif

  /* Send the signal */

  ret = sig_dispatch(pid, &info);
  sched_unlock();
...
}

調(diào)用到sig_dispatch()接口箩艺,完成信號(hào)的分發(fā)窜醉,而在sig_dispatch()接口中,又將調(diào)用sig_tcbdispatch()接口艺谆,最核心的部分在于sig_tcbdispatch(),事實(shí)上上層信號(hào)最終的分發(fā)都在這個(gè)接口中實(shí)現(xiàn)拜英。

sig_tcbdispatch()函數(shù)静汤,主要完成以下幾點(diǎn)功能:

  • 如果分發(fā)的信號(hào)在目標(biāo)Task中是是masked,而且Task的狀態(tài)沒有變成等待該信號(hào)的話居凶,就將信號(hào)添加進(jìn)pending隊(duì)列中虫给,也就是task_group->tg_sigpendingq隊(duì)列中;而如果Task的狀態(tài)變成了需要等待這個(gè)之前mask掉的信號(hào)侠碧,這時(shí)候就調(diào)用up_unblock_task()接口抹估,完成任務(wù)的切換。
  • 如果分發(fā)的信號(hào)在目標(biāo)Task中是unmask弄兜,此時(shí)需要調(diào)用sig_queueaction()接口药蜻,將一個(gè)sigq_t結(jié)構(gòu)添加進(jìn)tcb->sigpendactionq隊(duì)列中。當(dāng)然替饿,在sig_queueaction()接口中语泽,會(huì)去從上文中提到過(guò)的全局隊(duì)列中獲取sigq_t結(jié)構(gòu)資源。加入到tcb->sigpendactionq隊(duì)列后视卢,調(diào)用up_schedule_sigaction()接口踱卵,該接口主要是更新Task對(duì)應(yīng)的Tcb中的內(nèi)容,最終調(diào)用up_unblock_task()進(jìn)行任務(wù)切換的時(shí)候据过,能去處理信號(hào)惋砂。

最終信號(hào)發(fā)送成功,有兩件事完成了:1)在目標(biāo)Tasktcb->sigpendactionq隊(duì)列中绳锅,成功的添加了sigq_t結(jié)構(gòu)西饵,該結(jié)構(gòu)完成了信號(hào)和Action的匹配;2)更新了目標(biāo)Tasktcb->xcp中的內(nèi)容榨呆,更新完這個(gè)后罗标,當(dāng)完成任務(wù)切換的時(shí)候,Context Restore的時(shí)候會(huì)將tcb->xcp中的內(nèi)容恢復(fù)到寄存器中积蜻,因此也就能跳轉(zhuǎn)到信號(hào)處理函數(shù)中執(zhí)行闯割。

關(guān)鍵代碼如下:

/****************************************************************************
 * Name: sig_tcbdispatch
 *
 * Description:
 *   All signals received the task (whatever the source) go through this
 *   function to be processed.  This function is responsible for:
 *
 *   - Determining if the signal is blocked.
 *   - Queuing and dispatching signal actions
 *   - Unblocking tasks that are waiting for signals
 *   - Queuing pending signals.
 *
 *   This function will deliver the signal to the task associated with
 *   the specified TCB.  This function should *not* typically be used
 *   to dispatch signals since it will *not* follow the group signal
 *   deliver algorithms.
 *
 * Returned Value:
 *   Returns 0 (OK) on success or a negated errno value on failure.
 *
 ****************************************************************************/

int sig_tcbdispatch(FAR struct tcb_s *stcb, siginfo_t *info)
{
...
  /************************* MASKED SIGNAL HANDLING ************************/

  /* Check if the signal is masked -- if it is, it will be added to the list
   * of pending signals.
   */

  if (sigismember(&stcb->sigprocmask, info->si_signo))
    {
      /* Check if the task is waiting for this pending signal.  If so, then unblock it.
       * This must be performed in a critical section because signals can be queued
       * from the interrupt level.
       */

      flags = enter_critical_section();
      if (stcb->task_state == TSTATE_WAIT_SIG &&
          sigismember(&stcb->sigwaitmask, info->si_signo))
        {
          memcpy(&stcb->sigunbinfo, info, sizeof(siginfo_t));
          stcb->sigwaitmask = NULL_SIGNAL_SET;
          up_unblock_task(stcb);
          leave_critical_section(flags);
        }

      /* Its not one we are waiting for... Add it to the list of pending
       * signals.
       */

      else
        {
          leave_critical_section(flags);
          ASSERT(sig_addpendingsignal(stcb, info));
        }
    }

  /************************ UNMASKED SIGNAL HANDLING ***********************/

  else
    {
#ifdef CONFIG_SMP
      int cpu;
#endif
      /* Queue any sigaction's requested by this task. */

      ret = sig_queueaction(stcb, info);

      /* Deliver of the signal must be performed in a critical section */

      flags = enter_critical_section();

#ifdef CONFIG_SMP
      /* If the thread is running on another CPU, then pause that CPU.  We can
       * then setup the for signal delivery on the running thread.  When the
       * CPU is resumed, the signal handler will then execute.
       */

      cpu = sched_cpu_pause(stcb);
#endif /* CONFIG_SMP */

      /* Then schedule execution of the signal handling action on the
       * recipient's thread.
       */

      up_schedule_sigaction(stcb, sig_deliver);

#ifdef CONFIG_SMP
      /* Resume the paused CPU (if any) */

      if (cpu >= 0)
        {
          /* I am not yet sure how to handle a failure here. */

          DEBUGVERIFY(up_cpu_resume(cpu));
        }
#endif /* CONFIG_SMP */

      /* Check if the task is waiting for an unmasked signal.  If so, then
       * unblock it. This must be performed in a critical section because
       * signals can be queued from the interrupt level.
       */

      if (stcb->task_state == TSTATE_WAIT_SIG)
        {
          memcpy(&stcb->sigunbinfo, info, sizeof(siginfo_t));
          stcb->sigwaitmask = NULL_SIGNAL_SET;
          up_unblock_task(stcb);
        }

      leave_critical_section(flags);

      /* If the task neither was waiting for the signal nor had a signal
       * handler attached to the signal, then the default action is
       * simply to ignore the signal
       */

      /*********************** OTHER SIGNAL HANDLING ***********************/

      /* If the task is blocked waiting for a semaphore, then that task must
       * be unblocked when a signal is received.
       */
...
}

傳遞信號(hào)

信號(hào)最終的deliver,需要理解兩個(gè)函數(shù)竿拆,以及一個(gè)過(guò)程宙拉。兩個(gè)函數(shù)指的是up_schedule_sigaction()up_sigdeliver(),在arch/arm/src/arm下實(shí)現(xiàn)丙笋,一個(gè)過(guò)程指的是Context切換的過(guò)程谢澈,參考我之前的一篇文章:Nuttx Task Schedule

先來(lái)看看up_schedule_sigaction()函數(shù)煌贴,在函數(shù)中又分為幾種情況:1)發(fā)送的信號(hào)是給當(dāng)前任務(wù)的,且當(dāng)前任務(wù)不在中斷上下文中锥忿;2)發(fā)送的信號(hào)是給當(dāng)前任務(wù)的牛郑,且當(dāng)前任務(wù)在中斷上下文中;3)發(fā)送的信號(hào)是給其他任務(wù)的敬鬓。

  • 發(fā)送給當(dāng)前任務(wù)淹朋,且不在中斷中,直接調(diào)用sig_deliver()函數(shù)钉答,完成信號(hào)的deliver础芍。
  • 發(fā)送給當(dāng)前任務(wù),且在中斷中数尿,需要先將中斷上下文中保存的PCCPSR值保存到tcb->xcp.saved_pctcb->xcp.saved_cpsr中仑性,然后再將up_sigdeliver()函數(shù)指針值及新設(shè)置的CPSR覆蓋中斷上下文中的PCCPSR值,因此當(dāng)中斷返回的時(shí)候右蹦,中斷上下文將恢復(fù)到寄存器中诊杆,此時(shí)便會(huì)去執(zhí)行up_sigdeliver()函數(shù),在up_sigdeliver()中調(diào)用sig_deliver()完成信號(hào)處理嫩实。這時(shí)候可能會(huì)有疑問(wèn)刽辙,那之前的中斷上下文被破壞了,怎么恢復(fù)呢甲献?別忘了tcb->xcp.saved_pctcb->xcp.saved_cpsr宰缤,這個(gè)結(jié)構(gòu)保存了最開始的中斷上下文,因此在up_sigdeliver()執(zhí)行中晃洒,會(huì)去恢復(fù)原來(lái)的中斷上下文慨灭。
  • 發(fā)送給其他任務(wù),處理的方式與在中斷中發(fā)送給當(dāng)前任務(wù)的情況類似球及。
/****************************************************************************
 * Name: up_schedule_sigaction
 *
 * Description:
 *   This function is called by the OS when one or more
 *   signal handling actions have been queued for execution.
 *   The architecture specific code must configure things so
 *   that the 'igdeliver' callback is executed on the thread
 *   specified by 'tcb' as soon as possible.
 *
 *   This function may be called from interrupt handling logic.
 *
 *   This operation should not cause the task to be unblocked
 *   nor should it cause any immediate execution of sigdeliver.
 *   Typically, a few cases need to be considered:
 *
 *   (1) This function may be called from an interrupt handler
 *       During interrupt processing, all xcptcontext structures
 *       should be valid for all tasks.  That structure should
 *       be modified to invoke sigdeliver() either on return
 *       from (this) interrupt or on some subsequent context
 *       switch to the recipient task.
 *   (2) If not in an interrupt handler and the tcb is NOT
 *       the currently executing task, then again just modify
 *       the saved xcptcontext structure for the recipient
 *       task so it will invoke sigdeliver when that task is
 *       later resumed.
 *   (3) If not in an interrupt handler and the tcb IS the
 *       currently executing task -- just call the signal
 *       handler now.
 *
 ****************************************************************************/

void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
{
  irqstate_t flags;

  sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);

  /* Make sure that interrupts are disabled */

  flags = enter_critical_section();

  /* Refuse to handle nested signal actions */

  if (!tcb->xcp.sigdeliver)
    {
      /* First, handle some special cases when the signal is
       * being delivered to the currently executing task.
       */

      sinfo("rtcb=0x%p CURRENT_REGS=0x%p\n", this_task(), CURRENT_REGS);

      if (tcb == this_task())
        {
          /* CASE 1:  We are not in an interrupt handler and
           * a task is signalling itself for some reason.
           */

          if (!CURRENT_REGS)
            {
              /* In this case just deliver the signal now. */

              sigdeliver(tcb);
            }

          /* CASE 2:  We are in an interrupt handler AND the
           * interrupted task is the same as the one that
           * must receive the signal, then we will have to modify
           * the return state as well as the state in the TCB.
           *
           * Hmmm... there looks like a latent bug here: The following
           * logic would fail in the strange case where we are in an
           * interrupt handler, the thread is signalling itself, but
           * a context switch to another task has occurred so that
           * CURRENT_REGS does not refer to the thread of this_task()!
           */

          else
            {
              /* Save the return lr and cpsr and one scratch register
               * These will be restored by the signal trampoline after
               * the signals have been delivered.
               */

              tcb->xcp.sigdeliver       = sigdeliver;
              tcb->xcp.saved_pc         = CURRENT_REGS[REG_PC];
              tcb->xcp.saved_cpsr       = CURRENT_REGS[REG_CPSR];

              /* Then set up to vector to the trampoline with interrupts
               * disabled
               */

              CURRENT_REGS[REG_PC]      = (uint32_t)up_sigdeliver;
              CURRENT_REGS[REG_CPSR]    = SVC_MODE | PSR_I_BIT | PSR_F_BIT;

              /* And make sure that the saved context in the TCB
               * is the same as the interrupt return context.
               */

              up_savestate(tcb->xcp.regs);
            }
        }

      /* Otherwise, we are (1) signaling a task is not running
       * from an interrupt handler or (2) we are not in an
       * interrupt handler and the running task is signalling
       * some non-running task.
       */

      else
        {
          /* Save the return lr and cpsr and one scratch register
           * These will be restored by the signal trampoline after
           * the signals have been delivered.
           */

          tcb->xcp.sigdeliver       = sigdeliver;
          tcb->xcp.saved_pc         = tcb->xcp.regs[REG_PC];
          tcb->xcp.saved_cpsr       = tcb->xcp.regs[REG_CPSR];

          /* Then set up to vector to the trampoline with interrupts
           * disabled
           */

          tcb->xcp.regs[REG_PC]      = (uint32_t)up_sigdeliver;
          tcb->xcp.regs[REG_CPSR]    = SVC_MODE | PSR_I_BIT | PSR_F_BIT;
        }
    }

  leave_critical_section(flags);
}

當(dāng)調(diào)用up_schedule_sigaction()接口氧骤,更新了tcb->xcp的值后,當(dāng)遇到任務(wù)切換點(diǎn)的時(shí)候吃引,比如up_unblock_task()筹陵,就有可能去執(zhí)行信號(hào)處理函數(shù)了,這個(gè)就是up_sigdeliver()函數(shù)的作用了镊尺。
up_sigdeliver()函數(shù)主要完成以下幾個(gè)功能:

  • 在棧上邊regs中將現(xiàn)場(chǎng)保存好朦佩,并且將之前tcb->xcp.saved_pc和tcb->xcp.saved_cpsr中的值恢復(fù)到regs棧上邊對(duì)應(yīng)位置。
  • 恢復(fù)中斷狀態(tài)
  • 調(diào)用信號(hào)處理Handler進(jìn)行處理
  • 調(diào)用up_fullcontextrestore(regs)庐氮,進(jìn)行任務(wù)的恢復(fù)语稠。

可以看出,這個(gè)過(guò)程很像中斷處理的過(guò)程,因此信號(hào)的處理機(jī)制仙畦,有的地方稱為軟中斷處理機(jī)制

還是放上代碼吧:

/****************************************************************************
 * Name: up_sigdeliver
 *
 * Description:
 *   This is the a signal handling trampoline.  When a signal action was
 *   posted.  The task context was mucked with and forced to branch to this
 *   location with interrupts disabled.
 *
 ****************************************************************************/

void up_sigdeliver(void)
{
  struct tcb_s  *rtcb = this_task();
  uint32_t regs[XCPTCONTEXT_REGS];
  sig_deliver_t sigdeliver;

  /* Save the errno.  This must be preserved throughout the signal handling
   * so that the user code final gets the correct errno value (probably
   * EINTR).
   */

  int saved_errno = rtcb->pterrno;

  board_autoled_on(LED_SIGNAL);

  sinfo("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n",
        rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head);
  ASSERT(rtcb->xcp.sigdeliver != NULL);

  /* Save the real return state on the stack. */

  up_copyfullstate(regs, rtcb->xcp.regs);
  regs[REG_PC]         = rtcb->xcp.saved_pc;
  regs[REG_CPSR]       = rtcb->xcp.saved_cpsr;

  /* Get a local copy of the sigdeliver function pointer. we do this so that
   * we can nullify the sigdeliver function pointer in the TCB and accept
   * more signal deliveries while processing the current pending signals.
   */

  sigdeliver           = rtcb->xcp.sigdeliver;
  rtcb->xcp.sigdeliver = NULL;

  /* Then restore the task interrupt state */

  up_irq_restore(regs[REG_CPSR]);

  /* Deliver the signals */

  sigdeliver(rtcb);

  /* Output any debug messages BEFORE restoring errno (because they may
   * alter errno), then disable interrupts again and restore the original
   * errno that is needed by the user logic (it is probably EINTR).
   */

  sinfo("Resuming\n");
  (void)up_irq_save();
  rtcb->pterrno = saved_errno;

  /* Then restore the correct state for this thread of execution. */

  board_autoled_off(LED_SIGNAL);
  up_fullcontextrestore(regs);
}

上面討論的兩個(gè)函數(shù)输涕,都會(huì)最終調(diào)用到sig_deliver()函數(shù),完成最終實(shí)際上的Handler處理慨畸,sig_deliver()函數(shù)處理那些被鏈接到tcb->sigpendactionq中的sigq_t莱坎,將sigq_t結(jié)構(gòu)轉(zhuǎn)移到tcb->sigpostedq隊(duì)列中,緊接著執(zhí)行信號(hào)處理函數(shù)寸士。處理完當(dāng)前的信號(hào)后型奥,緊接著調(diào)用sig_unmaskpendingsignal()函數(shù),查詢tcb_group->tg_sigpendingq中是否還有pending的信號(hào)需要去處理碉京,并調(diào)用sig_tcbdispatch()繼續(xù)進(jìn)行分發(fā)。最終處理完本次任務(wù)后螟深,將sigq_t結(jié)構(gòu)體從任務(wù)tcb->sigpostedq中移除谐宙,并釋放該結(jié)構(gòu)。
代碼如下:

/****************************************************************************
 * Name: sig_deliver
 *
 * Description:
 *   This function is called on the thread of execution of the signal
 *   receiving task.  It processes all queued signals then returns.
 *
 ****************************************************************************/

void sig_deliver(FAR struct tcb_s *stcb)
{
  FAR sigq_t *sigq;
  FAR sigq_t *next;
  sigset_t    savesigprocmask;
  irqstate_t  flags;
  int         saved_errno;

  sched_lock();

  /* Save the thread errno.  When we finished dispatching the
   * signal actions and resume the task, the errno value must
   * be unchanged by the operation of the signal handling.  In
   * particular, the EINTR indication that says that the task
   * was reawakened by a signal must be retained.
   */

  saved_errno = stcb->pterrno;
  for (sigq = (FAR sigq_t *)stcb->sigpendactionq.head; (sigq); sigq = next)
    {
      next = sigq->flink;
      sinfo("Sending signal sigq=0x%x\n", sigq);

      /* Remove the signal structure from the sigpendactionq and place it
       * in the sigpostedq.  NOTE:  Since signals are processed one at a
       * time, there should never be more than one signal in the sigpostedq
       */

      flags = enter_critical_section();
      sq_rem((FAR sq_entry_t *)sigq, &(stcb->sigpendactionq));
      sq_addlast((FAR sq_entry_t *)sigq, &(stcb->sigpostedq));
      leave_critical_section(flags);

      /* Call the signal handler (unless the signal was cancelled)
       *
       * Save a copy of the old sigprocmask and install the new
       * (temporary) sigprocmask.  The new sigprocmask is the union
       * of the current sigprocmask and the sa_mask for the signal being
       * delivered plus the signal being delivered.
       */

      savesigprocmask = stcb->sigprocmask;
      stcb->sigprocmask = savesigprocmask | sigq->mask | SIGNO2SET(sigq->info.si_signo);

      /* Deliver the signal.  In the kernel build this has to be handled
       * differently if we are dispatching to a signal handler in a user-
       * space task or thread; we have to switch to user-mode before
       * calling the task.
       */

#if defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)
      if ((stcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL)
        {
          /* The sigq_t pointed to by sigq resides in kernel space.  So we
           * cannot pass a reference to sigq->info to the user application.
           * Instead, we will copy the siginfo_t structure onto the stack.
           * We are currently executing on the stack of the user thread
           * (albeit temporarily in kernel mode), so the copy of the
           * siginfo_t structure will be accessible by the user thread.
           */

          siginfo_t info;
          memcpy(&info, &sigq->info, sizeof(siginfo_t));

          up_signal_dispatch(sigq->action.sighandler, sigq->info.si_signo,
                             &info, NULL);
        }
      else
#endif
        {
          /* The kernel thread signal handler is much simpler. */

          (*sigq->action.sighandler)(sigq->info.si_signo, &sigq->info,
                                     NULL);
        }

      /* Restore the original sigprocmask */

      stcb->sigprocmask = savesigprocmask;

      /* Now, handle the (rare?) case where (a) a blocked signal was
       * received while the signal handling executed but (b) restoring the
       * original sigprocmask will unblock the signal.
       */

      sig_unmaskpendingsignal();

      /* Remove the signal from the sigpostedq */

      flags = enter_critical_section();
      sq_rem((FAR sq_entry_t *)sigq, &(stcb->sigpostedq));
      leave_critical_section(flags);

      /* Then deallocate it */

      sig_releasependingsigaction(sigq);
    }

  stcb->pterrno = saved_errno;
  sched_unlock();
}

Nuttx OS的學(xué)習(xí)和相關(guān)文檔更新持續(xù)中...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末界弧,一起剝皮案震驚了整個(gè)濱河市凡蜻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垢箕,老刑警劉巖划栓,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異条获,居然都是意外死亡忠荞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門帅掘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)委煤,“玉大人,你說(shuō)我怎么就攤上這事修档”探剩” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵吱窝,是天一觀的道長(zhǎng)讥邻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)院峡,這世上最難降的妖魔是什么兴使? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮撕予,結(jié)果婚禮上鲫惶,老公的妹妹穿的比我還像新娘。我一直安慰自己实抡,他們只是感情好欠母,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布欢策。 她就那樣靜靜地躺著,像睡著了一般赏淌。 火紅的嫁衣襯著肌膚如雪踩寇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天六水,我揣著相機(jī)與錄音俺孙,去河邊找鬼。 笑死掷贾,一個(gè)胖子當(dāng)著我的面吹牛睛榄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播想帅,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼场靴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼此洲!你這毒婦竟也來(lái)了孽椰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凡泣,失蹤者是張志新(化名)和其女友劉穎浅缸,沒想到半個(gè)月后轨帜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衩椒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年蚌父,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烟具。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梢什,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出朝聋,到底是詐尸還是另有隱情嗡午,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布冀痕,位于F島的核電站荔睹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏言蛇。R本人自食惡果不足惜僻他,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腊尚。 院中可真熱鬧吨拗,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至娇妓,卻和暖如春像鸡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哈恰。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工只估, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人着绷。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓蛔钙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親荠医。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夸楣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 信號(hào)的基本概念 信號(hào)被認(rèn)為是一種軟件中斷(區(qū)別于硬件中斷),信號(hào)機(jī)制提供了一種在單進(jìn)程/線程下處理異步事件的方法。...
    小葉大孟閱讀 1,927評(píng)論 0 1
  • 一子漩、信號(hào)及信號(hào)來(lái)源 信號(hào)本質(zhì) 信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬,在原理上石洗,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一...
    丶Em1tu0F閱讀 1,445評(píng)論 0 1
  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記幢泼,整理的知識(shí)點(diǎn),也是為了防止忘記讲衫,尊重勞動(dòng)成果缕棵,轉(zhuǎn)載注明出處哦!如果你也喜歡涉兽,那...
    波波波先森閱讀 11,273評(píng)論 4 56
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,418評(píng)論 8 265
  • 今天最快樂的事是什么? 跟Heden玩 今天讓你覺得最有趣的事是什么? 媽媽打蚊子時(shí)讓我覺得很搞笑 今天你最大的收...
    宋燕_f862閱讀 151評(píng)論 0 0