Linux内核中通知块操作[zz]_yskcg的空间_百度空间
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
// 基本的通知块结构
struct notifier_block {
// 回调函数
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
// 链表中的下一个结构, 这个一个单向链表
struct notifier_block *next;
// 该块的优先级, 在链表中各个块是按此优先级值进行排序的, 值大的在链表前, 表明
// 相应回调函数执行的顺序
int priority;
};
#define ATOMIC_NOTIFIER_INIT(name) {     \
   .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
   .head = NULL }
#define BLOCKING_NOTIFIER_INIT(name) {     \
   .rwsem = __RWSEM_INITIALIZER((name).rwsem), \
   .head = NULL }
#define RAW_NOTIFIER_INIT(name) {     \
   .head = NULL }
#define ATOMIC_NOTIFIER_HEAD(name)     \
struct atomic_notifier_head name =    \
   ATOMIC_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_HEAD(name)     \
struct blocking_notifier_head name =    \
   BLOCKING_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_HEAD(name)      \
struct raw_notifier_head name =     \
   RAW_NOTIFIER_INIT(name)
// nl是链表头块的地址, n是要添加到该链表的通知块
static int notifier_chain_register(struct notifier_block **nl,
   struct notifier_block *n)
{
// 使用的是dummy header算法, 即使刚开始时链表为空也不用显示判断区分
while ((*nl) != NULL) {
// 判断优先权值, 优先权值越大位置越靠前
   if (n->priority > (*nl)->priority)
    break;
   nl = &((*nl)->next);
}
// 将节点n链接到链表nl中的合适位置
n->next = *nl;
// 使用rcu处理函数保证SMP下的安全性, 相当于加上锁再赋值
rcu_assign_pointer(*nl, n);
return 0;
}
// nl是链表头块的地址, n是要删除的通知块
static int notifier_chain_unregister(struct notifier_block **nl,
   struct notifier_block *n)
{
while ((*nl) != NULL) {
// 地址匹配, 进行拆除操作
   if ((*nl) == n) {
// *nl=n->next的安全赋值操作,相当于将节点从链表断开
    rcu_assign_pointer(*nl, n->next);
    return 0;
   }
   nl = &((*nl)->next);
}
return -ENOENT;
}
// 安全地获取通知块指针
nb = rcu_dereference(*nl);
// 链表循环
while (nb) {
// 找下一个块
   next_nb = rcu_dereference(nb->next);
// 调用回调函数
   ret = nb->notifier_call(nb, val, v);
// 如果返回停止标志, 不执行后续结构
   if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
    break;
// 循环到下一节点
   nb = next_nb;
}
return ret;
}
/**
* atomic_notifier_chain_register - Add notifier to an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to an atomic notifier chain.
*
* Currently always returns zero.
*/
/**
* atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain
* @n: Entry to remove from notifier chain
*
* Removes a notifier from an atomic notifier chain.
*
* Returns zero on success or %-ENOENT on failure.
*/
// 只是在基本通知块撤销操作前后加锁解锁进行保护
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
   struct notifier_block *n)
{
unsigned long flags;
int ret;
// 加锁
spin_lock_irqsave(&nh->lock, flags);
ret = notifier_chain_unregister(&nh->head, n);
// 解锁
spin_unlock_irqrestore(&nh->lock, flags);
// 同步rcu, 等待一个grace period
synchronize_rcu();
return ret;
}
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call down_write().
*/
// 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本登记函数
// 不用处理信号灯, 因为此时是不能阻塞
if (unlikely(system_state == SYSTEM_BOOTING))
   return notifier_chain_register(&nh->head, n);
// 使用信号灯进行同步, 可能阻塞
down_write(&nh->rwsem);
// 基本登记函数
ret = notifier_chain_register(&nh->head, n);
up_write(&nh->rwsem);
return ret;
}
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call down_write().
*/
// 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本撤销函数
// 不用处理信号灯, 因为此时是不能阻塞
if (unlikely(system_state == SYSTEM_BOOTING))
   return notifier_chain_unregister(&nh->head, n);

int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
   unsigned long val, void *v)
{
int ret;
// 信号灯同步
down_read(&nh->rwsem);
// 进行基本回调处理
ret = notifier_call_chain(&nh->head, val, v);
up_read(&nh->rwsem);
return ret;
}
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call mutex_lock().
*/
if (unlikely(system_state == SYSTEM_BOOTING))
   return notifier_chain_register(&nh->head, n);
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call mutex_lock().
*/
if (unlikely(system_state == SYSTEM_BOOTING))
   return notifier_chain_unregister(&nh->head, n);
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
   unsigned long val, void *v)
{
int ret;
int idx;
// 使用srcu读锁来加锁
idx = srcu_read_lock(&nh->srcu);
ret = notifier_call_chain(&nh->head, val, v);
srcu_read_unlock(&nh->srcu, idx);
return ret;
}
/* include/linux/netfilter_ipv4/ip_conntrack.h */
// 连接事件处理
static inline void ip_conntrack_event(enum ip_conntrack_events event,
          struct ip_conntrack *ct)
{
// 判断连接是否合法
if (is_confirmed(ct) && !is_dying(ct))
// 调用原子通知回调函数, 执行登记的回调函数
   atomic_notifier_call_chain(&ip_conntrack_chain, event, ct);
}
包装函数就可以实现回调。


郑重声明:资讯 【Linux内核中通知块操作[zz]_yskcg的空间_百度空间】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——