内核hotplug事件---利用Netlink处理hotplug实现热插拔监控
来源:互联网 发布:如何安装网络摄像头 编辑:程序博客网 时间:2024/05/21 15:46
先上monitor的demo:
sd_monitor.c
#include <stdio.h>#include <stdlib.h> #include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <signal.h>#include <fcntl.h>#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <pthread.h>#include <linux/netlink.h>#include <linux/version.h>#include <linux/input.h>#include <syslog.h>#include <stdarg.h>//#include <execinfo.h>#include <errno.h>#include <sys/vfs.h>/************** for tcp ********************/#include <sys/types.h>/* See NOTES */ #include <sys/socket.h>#include <netdb.h>#include <arpa/inet.h>#include <netinet/in.h>#include <unistd.h>#include <fcntl.h>#include <ctype.h>#defineRESTART_AK_IPC"/mnt/mtd/start_ipc.sh"#define SDP1_DEV_NAME "/dev/mmcblk0p1"#define SD_DEV_NAME "/dev/mmcblk0"#define SD_TOOL"dsk_repair"#define MOUNT_SDP1"mount -rw /dev/mmcblk0p1 /mnt/sd_card" #define MOUNT_SD"mount -rw /dev/mmcblk0 /mnt/sd_card" #define UMOUNT_SD"umount /mnt/sd_card -l"/** * * @brief do_syscmd 执行系统命令 * * @author aj * * @date 2014-10-28 * * @param[in] char * cmd, 要执行的命令 char *result, 执行结果 * * @return int * * @retvallengh of result buf --> success, less than zero --> failed * */int do_syscmd(char *cmd, char *result){char buf[4096];FILE *filp;filp = popen(cmd, "r");if (NULL == filp){ipc_print("[%s:%d] popen %s, cmd:%s!\n", __func__, __LINE__, strerror(errno), cmd);return -2;}//fgets(buf, sizeof(buf)-1, filp);memset(buf, '\0', sizeof(buf));fread(buf, sizeof(char), sizeof(buf)-1, filp);sprintf(result, "%s", buf);pclose(filp);return strlen(result);}/** * * @brief daemon_check_sd_prepare write something to the TF card * * @author * * @date 2015-5-18 * * @param[in] void * * @return * */void daemon_test_sd_ro(void){system("dd if=/dev/zero of=/mnt/sd_card/test_tf bs=1024 count=1");system("rm -rf /mnt/sd_card/test_tf");}/** * * @brief daemon_check_sd_ro check the sd card * * @author * * @date 2015-5-18 * * @param[in] int flag, 1->mmcblkp0, 0->mmcblk * * @return * */void daemon_check_sd_ro(int mmcblk0_p1){char cmd[128] = {0}, result[256], badfile[100] ={0}, dev_name[20] = {0};daemon_test_sd_ro();if(mmcblk0_p1)sprintf(dev_name, "%s", SDP1_DEV_NAME);//"/dev/mmcblk0p1"elsesprintf(dev_name, "%s", SD_DEV_NAME);//"/dev/mmcblk0"sprintf(cmd, "mount | grep %s", dev_name); //do "mount | grep dev"do_syscmd(cmd, result);if(strlen(result) > 0){if(strstr(result, "rw,relatime") == NULL){//if not read writeif(strstr(result, "ro,relatime") != NULL){//find ro lable, the card was read onlyipc_print("[%s:%d] The SD Card is Read Only, repair it\n", __func__, __LINE__);}}else{ipc_print("[%s:%d] The SD Card is OK\n", __func__, __LINE__);return;}}}/** * * @brief daemon_umount_sd 卸载SD 卡 * * @author * * @date * * @param[in] void * * @return void * *//* umount the sd card and delete the sd_test dir */void daemon_umount_sd(void){char cmd[128] = {0};/** notice the anyka_ipc that **/sprintf(cmd, "echo %d > %s; killall -12 aoni_ipc", 0, "/tmp/sd_status");system(cmd); system(UMOUNT_SD);ipc_print("[%s:%d] *** umount the sd ***\n", __func__, __LINE__);}/** * * @brief daemon_mount_sd * * @author * * @date * * @param[in] int flag, 1->mmcblkp0, 0->mmcblk * * @return void * *//* create a sd_test dir and mount the sd card in it */void daemon_mount_sd(int flag){char cmd[128], status[20] = {0};int mmcblk0_p1; if(flag == 0) { if(access(SDP1_DEV_NAME, R_OK) >= 0) { ipc_print("**********we will skip mount /dev/mmcblk0*******\n"); return; } }if (flag){mmcblk0_p1 = 1;sprintf(cmd, "%s", MOUNT_SDP1);}else{mmcblk0_p1 = 0;sprintf(cmd, "%s", MOUNT_SD);} system(cmd);//check sdcard readonly or not, and repair it.daemon_check_sd_ro(mmcblk0_p1);/** when the sd card message is not come first time, * it means hot plug and we will send sig to anyka_ipc */ if(system_start_flag == 0) { //send message to anyka_ipc that card is readysprintf(status, "echo %d > %s", 1, "/tmp/sd_status");system(status); system("killall -12 aoni_ipc"); //send ipc_print to ipc }ipc_print("[%s:%d] *** mount the sd to /mnt ***\n", __func__, __LINE__);}/** * * @brief daemon_init_hotplug_sock 热插拔检测 * * @author * * @date * * @param[in] void * * @return int, -1, 失败;else 成功 * *//* create the socket to recevie the uevent */static int daemon_init_hotplug_sock(void){struct sockaddr_nl snl; const int buffersize = 2048; int retval; memset(&snl, 0x00, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 1;int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);if (hotplug_sock == -1){ ipc_print("[%s:%d] socket: %s\n", __func__, __LINE__, strerror(errno)); return -1; } /* set receive buffersize */ setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); retval = bind(hotplug_sock, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); if (retval < 0) { ipc_print("[%s:%d] bind failed: %s\n", __func__, __LINE__, strerror(errno)); close(hotplug_sock); hotplug_sock = -1; } return hotplug_sock;}pid_t gettid(){return syscall(SYS_gettid);}/* waitting the uevent and do something */void *daemon_pth_func(void *data){ char buf[UEVENT_BUFFER_SIZE * 2] = {0}; char temp_buf[20];//find action char *p;//find blk int i; int hotplug_sock;//hotplug socket handle int p1_removed = 0;ipc_print("[%s:%d] This thread id: %ld\n", __func__, __LINE__, (long int)gettid());hotplug_sock = daemon_init_hotplug_sock(); if (hotplug_sock < 0)return NULL; system_start_flag = 1;sleep(1); if (access (SDP1_DEV_NAME, F_OK) == 0)daemon_mount_sd(1);else if (access (SD_DEV_NAME, F_OK) == 0)daemon_mount_sd(0); system_start_flag = 0; while(1) {//clear buf memset(buf, 0, sizeof(buf));//block here to wait ipc_print recv(hotplug_sock, &buf, sizeof(buf), 0); p = strrchr(buf, '/');//get block name//get action for (i = 0; buf[i] != '@' && buf[i] != 0; i++) temp_buf[i] = buf[i]; temp_buf[i] = 0;if (strcmp(temp_buf, "change"))ipc_print("%s\n", buf);//card insertif (!strcmp(temp_buf, "add")) {if (!strcmp(p, "/mmcblk0p1")) {//sleep(1);daemon_mount_sd(1);} else if (!strcmp(p, "/mmcblk0")) {//sleep(1);daemon_mount_sd(0);}p1_removed = 0;continue;}//card extractif (!strcmp(temp_buf, "remove")) {//if p1 removed, than we do need to umount k0if(!strcmp(p, "/mmcblk0p1")) {daemon_umount_sd();p1_removed = 1;} else if((!strcmp(p, "/mmcblk0")) && (!p1_removed))daemon_umount_sd();} }ipc_print("[%s:%d] Exit thread, id : %ld\n", __func__, __LINE__, (long int)gettid());return NULL;}
1. 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/outw/outl等函数,避开设备驱动程序,直接去操作IO端口。(没有用过)
2. 用ioctl函数去操作/dev目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。
3. 用write/read/mmap去操作/dev目录下对应的设备,这也是设备驱动程序提供的接口。像framebuffer等都是这样做的。
上面的方法在大多数情况下,都可以正常工作,但是对于热插拨(hotplug)的设备,比如像U盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的hotplug问题了。
处理hotplug传统的方法是,在内核中执行一个称为hotplug的程序,相关参数通过环境变量传递过来,再由hotplug通知其它关注hotplug事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用NETLINK实现的,这是一种特殊类型的socket,专门用于内核空间与用户空间的异步通信。上面的这个简单的例子,可以监听来自内核hotplug的事件。
udev的主体部分在udevd.c文件中,它主要监控来自4个文件描述符的事件/消息,并做出处理:
1. 来自客户端的控制消息。这通常由udevcontrol命令通过地址为/org/kernel/udev/udevd的本地socket,向udevd发送的控制消息。其中消息类型有:
l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。
l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。
l UDEVD_CTRL_SET_LOG_LEVEL 设置LOG的级别。
l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。
l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制(遍历proc目录下所有进程,根据session的值判断)。
l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。
2. 来自内核的hotplug事件。如果有事件来源于hotplug,它读取该事件,创建一个udevd_uevent_msg对象,记录当前的消息序列号,设置消息的状态为EVENT_QUEUED,然后并放入running_list和exec_list两个队列中,稍后再进行处理。
3. 来自signal handler中的事件。signal handler是异步执行的,即使有signal产生,主进程的select并不会唤醒,为了唤醒主进程的select,它建立了一个管道,在signal handler中,向该管道写入长度为1个子节的数据,这样就可以唤醒主进程的select了。
4. 来自配置文件变化的事件。udev通过文件系统inotify功能,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有变化,它就重新加载配置文件。
其中最主要的事件,当然是来自内核的hotplug事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rule。
关于rule的编写方法可以参考《writing_udev_rules》,udev_rules.c实现了对规则的解析。
在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。
在udevd.c:udev_event_process函数中,我们可以看到,如果RUN参数以”socket:”开头则认为是发到socket,否则认为是执行指定的程序。
下面的规则是执行指定程序:
60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
下面的规则是通过socket发送消息:
90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"
- 内核hotplug事件---利用Netlink处理hotplug实现热插拔监控
- hotplug --- netlink
- hotplug
- hotplug
- hotplug
- 监听来自内核hotplug的事件(代码)
- 监听来自内核hotplug的事件(代码)
- netlink,hotplug的工作笔记
- 利用udev实现设备hotplug通知
- openwrt 实现hotplug-button
- openwrt 实现hotplug-button
- linux中的hotplug技术实现监控u盘插拔
- udev和mdev hotplug事件
- hotplug firmware
- CPU-hotplug
- hotplug 脚本
- memory hotplug
- CPU Hotplug
- eclipse环境下无法创建android virtual Devices(AVD)问题解决办法汇总
- the Maxnumber (对rmq问题的初步了解)
- QT迭代查找文件夹下的所有文件,并根据文件后缀筛选
- 类加载机制和反射
- [网络]计算机网络重点知识
- 内核hotplug事件---利用Netlink处理hotplug实现热插拔监控
- js实现:仿京东搜索栏随滑动透明度渐变
- linux tar命令及相关压缩解压缩命令
- tp-link路由器Portal功能典型配置案例
- shell脚本入门篇---shell基础
- Android切词工具——BreakIterator(2)
- 回溯法
- java 8(一)--Lambda表达式
- 酒店OTT小区IPTV完整解决方案