在分析PM时看到了notifier 相关的函数,这里记录一下学习内容.
notifier_block的定义
定义 了回调函数notifier_call , 当订阅者接受到消息后执行回调函数进行处理.
回调函数可以阻塞,并且运行在进程上下文,atomic 类型的chain 可以运行在中断上下文.
struct notifier_block;
定义了一种数据结构类型, notifier_fn_t 包含回调函数,对应的action,和携带的额外数据.
typedef int (*notifier_fn_t)(struct notifier_block *nb,
unsigned long action, void *data);
struct notifier_block {
notifier_fn_t notifier_call;
struct notifier_block __rcu *next;
int priority;
};
这里我们可以看到down_write 和up_write. 这就是读写信号量的获取和释放函数.
读写信号量与信号量之间的关系,类似于读写自旋锁与自旋锁之间的关系;读写信号量可能会引起进程阻塞,但是它允许N个读执行单元同时访问共享资源,而最多只允许有一个写执行单元访问共享资.
/*
* Blocking notifier chain routines. All access to the chain is
* synchronized by an rwsem.
*/
/**
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to a blocking notifier chain.
* Must be called in process context.
*
* Currently always returns zero.
*/
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
int 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);// 可能有多个访问者尝试去获取 rwsem ,但是只会有一个获取锁然后执行后面的函数.
ret = notifier_chain_register(&nh->head, n);
up_write(&nh->rwsem);
return ret;
}
EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);
fb_notify.c 里面实现了framebuffer 的notifier
来看看怎么实现的.
1.fb_register_client 注册
2.fb_unregister_client 注销
3.fb_notifier_call_chain 调用
#include <linux/fb.h>
#include <linux/notifier.h>
#include <linux/export.h>
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
/**
* fb_register_client - register a client notifier
* @nb: notifier block to callback on events
*/
int fb_register_client(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&fb_notifier_list, nb);
}
EXPORT_SYMBOL(fb_register_client);
/**
* fb_unregister_client - unregister a client notifier
* @nb: notifier block to callback on events
*/
int fb_unregister_client(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
}
EXPORT_SYMBOL(fb_unregister_client);
/**
* fb_notifier_call_chain - notify clients of fb_events
*
*/
int fb_notifier_call_chain(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&fb_notifier_list, val, v);
}
EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
H:\kernel-codes\linux-4.18.tar\linux-4.18\include\linux\backlight.h
中定义了背光的设备结构体,这是内核的一贯作风,先定义一个该设备的结构体,报需要用的内核变量成员都放进去,比如 他会注册framebuffer 的 notifier block,那就定义一个自己要注册的结构体在这里.
struct backlight_device {
/* Backlight properties */
struct backlight_properties props;
/* Serialise access to update_status method */
struct mutex update_lock;
/* This protects the 'ops' field. If 'ops' is NULL, the driver that
registered this device has been unloaded, and if class_get_devdata()
points to something in the body of that driver, it is also invalid. */
struct mutex ops_lock;
const struct backlight_ops *ops;
/* The framebuffer notifier block */
struct notifier_block fb_notif;
/* list entry of all registered backlight devices */
struct list_head entry;
struct device dev;
/* Multiple framebuffers may share one backlight device */
bool fb_bl_on[FB_MAX];
int use_count;
};
然后背光的实现中把自己的回调函数 bd->fb_notif.notifier_call = fb_notifier_callback; 通过公用接口 fb_register_client 进行注册! 看到了吗? 这里是调用fb_notify.c 里的函数进行注册, 那当然是注册到 fb_notifier_list 这个notifier 的链表上.
因为在fb_notify.c 里面定义了全局的fb notifier 链表头.
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
static int backlight_register_fb(struct backlight_device *bd)
{
memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
bd->fb_notif.notifier_call = fb_notifier_callback;
return fb_register_client(&bd->fb_notif);
}
那是如何调用backlight 里面的回调函数的?
fb_notify.c 会通过 fb_notifier_call_chain 按照优先级调用 注册到自己的fb_notifier_list 上面的回调函数.
所以这里就可以明白,为什么其他的driver 会注册自己的回调函数到公共的notifier chain 上面了吗?
`
**
* fb_notifier_call_chain - notify clients of fb_events
*
*/
int fb_notifier_call_chain(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&fb_notifier_list, val, v);
}
EXPORT_SYMBOL_GPL(fb_notifier_call_chain);