1. 程式人生 > 其它 >Linux核心機制之通知鏈

Linux核心機制之通知鏈

linux核心中的事件通知鏈主要用於各模組之間的存在依賴, 用於事件的通知. 通知鏈只能用於核心空間之間的各模組之間, 不能用於核心空間和使用者空間的之間. 事件通知鏈就是在特定的事件發生後, 通過回撥函式去主動通知接受方. 核心程式碼在kernel-4.14/kernel/notifier.c , 標頭檔案kernel-4.14/include/linux/notifier.h

通知鏈的核心資料結構:

struct notifier_block {
   notifier_fn_t notifier_call;          //通知鏈要執行的回撥函式
   struct notifier_block __rcu *next;    //連線其他的通知鏈, 形成連結串列
   int priority;                         //這個通知的優先順序, 預設0, 值越大, 優先順序越高
};
  • 通知鏈型別

    1. 原子通知鏈: 回撥函式在中斷或原子上下文中執行, 不允許阻塞

      struct atomic_notifier_head {
      	spinlock_t lock;
      	struct notifier_block __rcu *head;
      };
      
    2. 可阻塞通知鏈: 回撥函式在程序上下文執行, 允許阻塞

      struct blocking_notifier_head {
      	struct rw_semaphore rwsem;
      	struct notifier_block __rcu *head;
      };
      
    3. 原始通知鏈: 對回撥函式無限制, 所有的鎖和保護機制由呼叫者維護

      struct raw_notifier_head {
      	struct notifier_block __rcu *head;
      };
      
    4. SRCU通知鏈: 可阻塞通知鏈變體

      struct srcu_notifier_head {
      	struct mutex mutex;
      	struct srcu_struct srcu;
      	struct notifier_block __rcu *head;
      };
      
  • 操作函式

    • 定義鏈頭以及初始化

      #define ATOMIC_NOTIFIER_HEAD(name) 原子通知鏈
      #define BLOCKING_NOTIFIER_HEAD(name) 可阻塞通知鏈
      #define RAW_NOTIFIER_HEAD(name) 原始通知鏈
      
    • 註冊/解除安裝 通知鏈

      //註冊
      extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *nb);
      extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
      extern int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb);
      extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);
      
      extern int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
      //解除安裝
      extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *nb);
      extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
      extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *nb);
      extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);
      
    • 通知函式/回撥函式

      extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
      extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
      extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
      extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
      
  • 事例

    馬達和camera的模組是分開的, 現在有一個需求: 在camera open/close, 需要把camera當前的狀態發給馬達的驅動模組. 我們可以通過事件通知鏈來實現, camera是事件通知方, 馬達是事件接受方

    imgsensor.c --- camera

    ...
    extern int camera_notifier_call_chain(unsigned long val, void *v);
    
    MINT32 imgsensor_sensor_open(struct IMGSENSOR_SENSOR *psensor)
    {
    	...
    	if (IMGSENSOR_SENSOR_IDX_MAIN == psensor_inst->sensor_idx)
    		camera_notifier_call_chain(IMGSENSOR_STATE_OPEN, NULL);
    	...
    }
    
    MINT32 imgsensor_sensor_close(struct IMGSENSOR_SENSOR *psensor)
    {
    	...
    	if (IMGSENSOR_SENSOR_IDX_MAIN == psensor_inst->sensor_idx)
    		camera_notifier_call_chain(IMGSENSOR_STATE_CLOSE, NULL);
    	...
    }
    

    main_lens.c ---- 馬達模組

    #include <linux/notifier.h>
    ...
     
    static struct notifier_block af_notifier;
    static BLOCKING_NOTIFIER_HEAD(af_notifier_list);
    static int camera_status_notice_callback(struct notifier_block *nb, unsigned long val, void *data)
    {
    	int ret = NOTIFY_OK;
    
    	back_camera_opened = !!val;
    	switch(back_camera_opened) {
    		case 0: //back camera close
    			//do soming
    			break;
    		case 1: //back camera open
                //do soming
    			break;
    		default:
    			ret = NOTIFY_BAD;
    			printk("[%s], camera status error!!!. \n", __func__);
    			break;
    	}
    	return ret;
    }
    int camera_notifier_call_chain(unsigned long val, void *v)
    {
    	return blocking_notifier_call_chain(&af_notifier_list, val, v);
    }
    EXPORT_SYMBOL(camera_notifier_call_chain);
    
    static inline int Register_AF_CharDrv(void)
    {
        ...
        af_notifier.notifier_call = camera_status_notice_callback;
    	blocking_notifier_chain_register(&af_notifier_list, &af_notifier); 
        ...
    }
    
    static inline void Unregister_AF_CharDrv(void)
    {
        ...
        blocking_notifier_chain_unregister(&af_notifier_list, &af_notifier);
        ...
    }
    
  • 擴充套件

    以上是一個通知方一個接收方的處理, 那多個接收方該如何實現都通知到?

    我們需要在寫一個通知塊, 將之加入定義好的連結串列上

    main_lens.c

    #include <linux/notifier.h>
    ...
     
    
    static BLOCKING_NOTIFIER_HEAD(af_notifier_list);
    /* ------------------------第一個通知塊----------------------------------------------------- */
    static struct notifier_block af_notifier;
    static int camera_status_notice_callback(struct notifier_block *nb, unsigned long val, void *data)
    {
    	int ret = NOTIFY_OK;
    
    	back_camera_opened = !!val;
    	switch(back_camera_opened) {
    		case 0: //back camera close
    			//do soming
    			break;
    		case 1: //back camera open
                //do soming
    			break;
    		default:
    			ret = NOTIFY_BAD;
    			printk("[%s], camera status error!!!. \n", __func__);
    			break;
    	}
    	return ret;
    }
    /*---------------------------------------------------------------------------------------- */
    /* ------------------------第二個通知塊----------------------------------------------------- */
    static struct notifier_block module_notifier = {
    	.notifier_call = module_notice_callback,
    	.priority = 5,
    };
    static int module_notice_callback(struct notifier_block *nb, unsigned long val, void *data)
    {
    	....  //do soming
    }
    /*--------------------------------------------------------------------------------------- */
    int camera_notifier_call_chain(unsigned long val, void *v)
    {
    	return blocking_notifier_call_chain(&af_notifier_list, val, v);
    }
    EXPORT_SYMBOL(camera_notifier_call_chain);
    
    static inline int Register_AF_CharDrv(void)
    {
        ...
        af_notifier.notifier_call = camera_status_notice_callback;
        
    	blocking_notifier_chain_register(&af_notifier_list, &af_notifier); 
        blocking_notifier_chain_register(&af_notifier_list, &module_notifier); //第二個通知塊
        ...
    }
    
    static inline void Unregister_AF_CharDrv(void)
    {
        ...
        blocking_notifier_chain_unregister(&af_notifier_list, &af_notifier);
        blocking_notifier_chain_unregister(&af_notifier_list, &module_notifier);
        ...
    }