Linux的热拔插Udev机制及守护进程

1.Udev机制
Udev是一个设备管理工具,以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件。udev在用户空间运行,而不在内核空间运行,他能够根据系统中硬件设备的状态更新文件,包括设备文件的创建,删除等。设备文件一般都在/dev目录下,使用udev后,在/dev目录下只包含系统中真正存在的设备。
2.守护进程
2.1 守护进程概述
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行且提供某种任务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd,web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。守护进程通常以d结尾
UDEV守护进程,能够根据系统中硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等
基本特点
(1)生存周期长,一般操作系统启动时就启动,关闭的时候关闭
(2)守护进程与终端无关联,即他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出
(3)守护进程是在后台运行,不会占着终端,终端可以执行其他命令
验证:(2)(3)点:
启动一个终端执行一个每秒输出字符的进程:在这里插入图片描述
关闭显示这个程序运行的终端后,通过ps -elf 指令发现此进程已经关闭
若以后台运行的方式运行程序(./a.out &):
在这里插入图片描述
关闭终端后,相应进程也随之关闭

而守护进程,以udev为例:
在这里插入图片描述
不依托于任何终端运行!

(4)一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程退出了,所以它是一个由init接收的孤儿进程
在这里插入图片描述
(第一处箭头表示过滤grep进程
第二处箭头表示该进程的父进程)显示为1而1表示init进程
在这里插入图片描述
PID = 0:内核进程,跟随系统启动而启动,生命周期贯穿整个系统
cmd列名带[]时,为内核守护进程:

init进程:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也负责孤儿进程的回收

cmd列中名字不带[]的为普通用户进程(用户集守护进程)
2.2 守护进程开发实战并添加开机启动
2.2.1 准备工作
(1) 这里的例子是设置一个自动打印当前系统时间的的守护进程。
创建守护进程,这里使用daemon函数创建守护进程:
函数原型:

#include <unistd.h> 
int daemon(int nochdir, int noclose); 
函数参数:
nochdir:为0时表示将当前目录更改至“/” 
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null” 返回值: 成功则返回0,失败返回-1

(2)需要用到struct tm 和 time_t 时间和日期的使用方法:
简单介绍:()

  • struct tm 结构体介绍:
    在标准C/C++中,我们可通过tm结构来获得日期和时间,tm结构在time.h中的定义如下:
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代表一月) - 取值区间为[0,11] /
      int tm_year; / 年份,其值等于实际年份减去1900 /
      int tm_wday; / 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 /
      int tm_yday; / 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 /
      int tm_isdst; / 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
};

  • time_t 类型说明
    日历时间(Calendar Time)是通过time_t数据类型来表示的,用time_t表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数。在time.h中,我们也可以看到time_t是一个长整型数:
#ifndef _TIME_T_DEFINED
typedef long time_t; /* 时间值 /
#define _TIME_T_DEFINED / 避免重复定义 time_t */
#endif

大家可能会产生疑问:既然time_t实际上是长整型,到未来的某一天,从一个时间点(一般是1970年1月1日0时0分0秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对time_t数据类型的值来说,它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间,并通过_time64()函数来获得日历时间(而不是通过使用32位字的time()函数),这样就可以通过该数据类型保存3001年1月1日0时0分0秒(不包括该时间点)之前的时间。

  • 日期使用方法

    1. 或得日历时间:
      可以通过time()函数来获得日历时间(Calendar Time),其原型为:
time_t time(time_t * timer);

如果你已经声明了参数timer,你可以从参数timer返回现在的日历时间,同时也可以通过返回值返回现在的日历时间,即从一个时间点(例如:1970年1月1日0时0分0秒)到现在此时的秒数。如果参数为空(NULL),函数将只通过返回值返回现在的日历时间。

#include “time.h”
#include “stdio.h”
int main(void)
{
      struct tm *ptr;
      time_t lt;
      lt =time(NULL);
      printf(“The Calendar Time now is %d\n”,lt);
      return 0;
}
运行的结果与当时的时间有关,结果:

The Calendar Time now is 1122707619
  1. 获取日期时间:

可以使用的函数是gmtime()和localtime(),来获取日期和时间,这两个函数的原型为:

struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);

gmtime()函数是将日历时间转化为世界标准时间(即格林尼治时间),并返回一个tm结构体来保存这个时间。
localtime()函数是将日历时间转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是2005年7月30日7点18分20秒,那么我用localtime()函数在中国地区获得的本地时间会比时间标准时间晚8个小时,即2005年7月30日15点18分20秒。

  • 固定的时间格式
    可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来,两者的返回值都是char*型的字符串。返回的时间格式为:
    星期几 月份 日期 时:分:秒 年\n\0
    例如:Wed Jan 02 02:03:55 1980\n\0
    其中\n是一个换行符,\0是一个空字符,表示字符串结束。下面是两个函数的原型:
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);

asctime()函数:是通过tm结构来生成具有固定格式的保存时间信息的字符串,
ctime()函数:是通过日历时间来生成时间字符串。
这样的话,asctime()函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了,而ctime()函数需要先参照本地的时间设置,把日历时间转化为本地时间,然后再生成格式化后的字符串。在下面,如果lt是一个非空的time_t变量的话,那么:

#include “time.h”
#include “stdio.h”
int main(void)
{
      struct tm *ptr;
      time_t lt;
      lt =time(NULL);//获取时间函数,从标准时间到此刻的ms数
      ptr=gmtime(&It);
      printf(asctime(ptr));
      printf(ctime(&It));
      return 0;
}

运行结果:
Sat Jul 30 08:43:03 2005 //标准时间
Sat Jul 30 16:43:03 2005 //当地时间,中国地区便宜+8小时

2.2.2 创建守护进程代码:

#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>


static int flag = 1;


void handler(int signum)
{

	printf("I got sinum %d,and quit\n",signum);
	flag = 0;

}



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)) 
	{ 	
		printf("sigaction error.\n");
	   	exit(0);
   	}
	//杩涚▼宸ヤ綔鍐呭 
	while(flag) { 
		fd = open("/home/pi/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644); 
		if(fd == -1) 
		{ 
			printf("open error\n");}
		t = time(0); 
		char *buf = asctime(localtime(&t));
	   	write(fd, buf, strlen(buf));
	   	close(fd);
	   	sleep(10); }
	return 0; 
}

开机启动设置:
sudo vi /etc/rc.local
再次文件中添加需要开机自启动的文件,例如
/home/pi/WiringPi/daemonTest/tdaemon
在这里插入图片描述
实现开机自启动!
并生成daemon.log 文件:
在这里插入图片描述
将系统日期和时间每隔10s写入daemon.log文件

小应用:
自动启动语言识别刷都有程序,并检测程序是否退出,退出时重新启动
语音控制刷抖音

#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>


static int flag = 1;


void handler(int signum)
{

	printf("I got sinum %d,and quit\n",signum);
	flag = 0;

}
int judge()
{
	FILE *fd;
	char buf[128] ={'\0'}; 
	char *cmd = "ps -elf|grep douyinControl|grep -v grep";
	fd  = popen(cmd,"r");

	fgets(buf,sizeof(buf),fd);//从FILE流中读取内容到buf中
	//		//字符串检索
	if(strstr(buf,"douyinControl")!=NULL){
		printf("douyinControl is running\n");
		return 0;
	}else{
		printf("douyinControl is not running\n");
		return -1;
	}

}
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)) 
	{ 	
		printf("sigaction error.\n");
		exit(0);
	}
	//进程工作内容 
	while(flag) { 
		if(judge()==-1){
			system("/home/pi/WiringPi/serial/douyinControl /dev/ttyAMA0 &");

		}
		sleep(2);
	}

	return 0; 
}

并添加开机自启动:

sudo vi /etc/rc.local

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值