本文主要是介绍Linux的UDEV机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
udev 机制引入:
手机接入Linux热拔插相关
a. 把手机接入开发板
b. 安装adb工具,在终端输入adb安装指令: sudo apt-get install adb
c. dmeg能查看到手机接入的信息,但是输入adb devices会出现提醒
dinsufficient permissions for device: user in plugdev group; are your udev
rules wrong?
d. 配置文件,以支持USB设备的热拔插,支持UDEV的机制
在/etc/udev/rules.d 文件夹下创建规则文件
cd /etc/udev/rules.d/
sudo vim 51-android.rules
在文件中添加内容 SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"
e. 在手机开发者选项中,打开USB调试,重新
udev 概念引入:
udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent来管
理/dev目录下的设备文件。udev在用户空间运行,而不在内核空间 运行。它能够根据系统中的硬
件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。
使用udev后,在/dev目录下就只包含系统中真正存在的设备
------------------------------------------------
图形理解:
下面是一张linux 架构图:
Linux 下面 一切都是文件 -- open 来打开
调用过程:
我们在应用层 调用 open 函数(存放在库函数中) --> 库函数中的open 系统调用 sys_open
--> 系统调用的 sys_read 再调用内核的 kernel_open --> 内核的kernal_open 负责调用硬件的寄存器处理
-------------------------------------
查看进程基础与技巧:
一般形式:
ps -elf | grep a.out
#unix标准风格组合,其中-e 代表列出所有进程,-l 代表长格式,-f 代表完整的格式
消除grep干扰
在平时查看进程得到时候一般都会 多出一个 grep 进程---> 影响判断
比如下图 中的第二个进程 grep:
我们可以这样
忽略 grep进程: ps -elf | grep a.out | grep -v grep
//检索 a.out 的进程,同时忽略 grep 进程
& -- 指定进程后台运行
./a.out & // 后台进程 a.out
// 普通程序 依托 于终端,终端关闭就结束
init -- 进程 pid - 1
显示行号
显示行号:末行模式下输入 **set number** 或 **set nu** 回车
关闭行号:末行模式下输入 **set nonumber** 或 **set nonu** 回车
守护进程:
概念
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行
某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个
系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。守护进程的名称通常以d结尾
UDEV守护进程,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。
基本特点
1)生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。
2)守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出
3)守护进程是在后台运行,不会占着终端,终端可以执行其他命令4)一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程
linux操作系统本身是有很多的守护进程在默默执行,维持着系统的日常活动。大概30-50个
普通守护进程 和 内核适合进程
ppid = 0:内核进程,跟随系统启动而启动,生命周期贯穿整个系统。
cmd列名带[]这种,叫内核守护进程
老祖init:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也负责收养孤儿进程。
cmd列中名字不带[]的普通守护进程(用户集守护进程)
图解如下:
======================================================
守护进程的开发:
daemon()函数
直接借助daemon()函数完成。 daemon -- 守护进程
头文件:
#include <unistd.h>
函数原型:
int daemon(int nochdir, int noclose);
函数参数:
nochdir:为0时表示将当前目录更改至“/”
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
返回值:
成功则返回0,失败返回-1
守护进程 和 后台进程区别
1. 守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);
2. 守护进程关闭终端时不受影响,守护进程不会随着终端的退出而退出;
额外补充:
//C 库函数 char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结
构 struct timeptr 的日期和时间。
//C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。
timer 的值被分解为 tm 结构,并用本地时区表示。
/*
对应的结构体:
struct tm {
int tm_sec; 秒,范围从 0 到 59
int tm_min; 分,范围从 0 到 59
int tm_hour; 小时,范围从 0 到 23
int tm_mday; 一月中的第几天,范围从 1 到 31
int tm_mon; 月份,范围从 0 到 11
int tm_year; 自 1900 起的年数
int tm_wday; 一周中的第几天,范围从 0 到 6
int tm_yday; 一年中的第几天,范围从 0 到 365
int tm_isdst; 夏令时
};
----------------------------------------------------------
注: bool 类型变量linux C不认识,需要包含 一个头文件: <stdbool.h>
exit(0),exit(1) 和 exit(-1)的区别
exit(0)表示程序正常退出;除了0之外,其他参数均代表程序异常退出,如:exit(1),exit(-1)。
exit(1)和exit(-1)是分别返回1和-1到主调程序。
exit(0)则是返回0。exit(0)表示程序正常退出,非0表示非正常退出。
log文件是记录系统活动信息的几个文件,通过它可以帮助快速定位问题,常见的有
----------------------------------------------------------
验证程序:
case1: 创建一个守护进程 , 创建/打开~目录下的daemon.log文件,每10s 写入一次系统时间到文件中
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include<stdbool.h>static bool flag = true;
void handler(int sig) // 收到信号 后就 把 flag =false ---> 进入 main 里面的判断 -- 需要退出了
{
printf("I got a signal %d\nI'm quitting.\n", sig);
flag = false;
}int main()
{
time_t t;
int fd;
//创建守护进程 int daemon(int nochdir, int noclose); -- 返回值 成功0 ,失败-1
// 第一个参数 -- nochdir:为0时表示将当前目录更改至“/”
// 第二个参数 -- noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
if(-1 == daemon(0, 0))
{
printf("daemon error\n");
exit(1); //异常退出
}
//设置信号处理函数
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGQUIT, &act, NULL)) // if 这个进程收到 SIGQUIT 信号,退出进程 exit(0) -正常退出
{
printf("sigaction error.\n");
exit(0);
}//进程工作内容
while(flag) // flag 没收到退出信号的时候一直在执行 以下工作
{
fd = open("/home/orangepi/daemon.log", O_WRONLY | O_CREAT | O_APPEND,0644); // 在我们的~目录下创建daemon.log文件
if(fd == -1)
{
printf("open error\n");
}
t = time(0);
// asctime -- 把系统默认的时间类型 数据转换为 字符串类型的 localtime 拿到本地时间
char *buf = asctime(localtime(&t));
write(fd, buf, strlen(buf)); // 将获得时间数据写入文件
close(fd);
sleep(10); // 每10s写入一次
}
return 0;
}
-------------------------------------------------------
开机启动守护进程:
sudo vi /etc/rc.local
里面添加我们创建的守护进程的 可执行文件 tdaemon , 不要用a.out, 使用 -o 改个名
/home/orangepi/hardwareSoft/udev/tdaemon
注意: 这里一定要用绝对路径,不要用相对路径,因为我们的守护进程会调到根目录,相对路径不一定能访问到
/home/orangepi/douyin/douyin /dev/ttyS5
/home/orangepi/douyin/shouhuDouyin
===================================================
需求:要求语音刷手机的程序一直保持运行,防止应用程序崩溃意外,先实现case2
case2 : 编写判断某进程是否在运行的程序:
1 #include<stdio.h>
2 #include<string.h>
3
4 int main()
5 {
6
7 FILE *file;
8 char buf[128]={'\0'};
9 char *cmd="ps -elf | grep douyin | grep -v grep "; // 通过指令查看
10 file = popen(cmd,"r");
11 fgets(buf,128,file);
12
13 printf("buf: %s\n",buf);
14 if(strstr(buf,"douyin")!=NULL){ // 子串没有进程名字,什么进程不运行
15 puts("douyinPro is running");
16 }
17 else {
18 puts("douyinPro is exited");
19 }
20
21
22 return 0;
23 }
----------------------------------------
以popen的 方式去运行 cmd
同时,能把指令执行的结果拿到手
---------------------------------------------------------------------
case 3 : 守护进程实现 关不掉是 声控刷抖音
声控刷抖音代码参考这篇:串口小项目 - 声控刷抖音-CSDN博客
/home/orangepi/douyin
注意: 守护进程会默认把 路口切换到根目录,执行cmd 的时候要给绝对路径
效果图:
这里kill 程序,模拟程序崩溃
可以看到 我们的抖音进程 一直杀不死, 因为守护进程 一直在后台执行着
只要抖音程序一被关掉就会马上重新创建一个新的的进程
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include<stdbool.h>
int judMent() // 这就是case2的 判断进程是否运行函数
{
FILE *file;
char buf[128]={'\0'};
char *cmd="ps -elf | grep douynUnit | grep -v grep ";
file = popen(cmd,"r");
fgets(buf,128,file);printf("buf: %s\n",buf);
if(strstr(buf,"douyin")!=NULL){
return 0;
}
else {
return -1;
}}
static bool flag = true; // 初始化为true -- 让main 中while一直执行
void handler(int sig) //收到对应信号后 退出
{
printf("I got a signal %d\nI'm quitting.\n", sig);
flag = false;
}
int main()
{
time_t t;
int fd;
//创建守护进程
if(-1 == daemon(0, 0))
{
printf("daemon error\n");
exit(1);
}
//设置信号处理函数
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGQUIT, &act, NULL)) //收到信号SIGQUIT,执行act -这个槽(处理)函数
{
printf("sigaction error.\n");
exit(0);
}
//进程工作内容
while(flag)
{
if(judMent() == -1)//进程被杀死后再打开 -- 杀不死的进程
fd = system("/home/orangepi/douyin/douyinUnit /dev/ttyS5 &");
sleep(2);
}
return 0;
}
--------------------------------------------------------------------------------
UDEV 配置文件:
规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rule.d/ 下。所有的规则文件必须以 ".rules"为后缀名
简单的规则举例:
KERNEL=="sda", NAME="my_root_disk", MODE="0660"
KERNEL 是匹配键,NAME 和 MODE 是赋值键。这条规则的意思是:如果有一个设备的内核名称为sda,则该条件生效,执行后面的赋值:在 /dev 下产生一个名为my_root_disk 的设备文件,并把设备文件的权限设为 0660。
查看设备详细信息
udevadm info --attribute-walk --name=/dev/设备名字
比如我的手机在这里: /dev/bus/usb/001/003 -- 只需要把 = 后的改为这串即可
udevadm info --attribute-walk --name=/dev/bus/usb/001/003
查询到的信息可以写入设备的规则文件中,比如:
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"//多乐两天匹配键 -- 来判断设备
如下:
==================
udev 规则的匹配键:
ACTION:事件(uevent)的行为,例如:add(添加设备)、remove(删除设备);
KERNEL:内核设备名称,例如:sda,cdrom;
DEVPATH:设备的 devpath 路径;
SUBSYSTEM:设备的子系统名称,例如:sda 的系统为 block;
BUS:设备在 devpath 里的总线名称,例如:usb;
DRIVER:设备在 devpath 的设备驱动名称,例如:ide-cdrom;
ID:设备在 devpath 里的识别号;
SYSFS{filename}:设备的 devpath 路径下,设备的属性文件 "filename" 里的内容;ENV{key}:环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键;
PROGRAM:调用外部命令;
RESULT:外部命令 PROGRAM 的返回结果。
===================================================、
U盘的自动挂载:
一般情况:(手动挂载)
一般U盘插入orangepi 需要挂载才能查看里面内容
// 将 u 盘挂载到 mnt 下,才能查看里面内容
sudo mount /dev/sda /mnt/然后 就可以 cd/mnt 去查看我们的 U盘 内容了
要取消挂载: sudo umount /mnt
也可以通过上面的指令来详细查看U盘数据: udevadm info --attribute-walk --name=/dev/设备名字
比如: U盘的匹配条件:KERNELS=="sdb"
SUBSYSTEMS=="block"
配置规则文件, 实现自动挂载U盘
在规则文件下(比如我们在/etc/udev/rule.d/下创建 usbBlock.rules ),加入:
支持挂载多个U盘:
ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir
/media/%k" ,RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode
/media/%k"
然后重启udev 服务即可:
sudo service udev restart
这篇关于Linux的UDEV机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!