Linux学习之悟空派上实现OLED的无线网IP及CPU温度显示【守护进程】

起因

最近各种网购平台似乎都在推送99元的悟空派全志H3的开发板,出于好奇就买了一块来试试水,由于这块板子基本上和orangepi-Zero的硬件结构一模一样,所以设备树、boot这些就用orangepi现成的部件了。
因为本人比较喜欢使用SSH操作,但是在不同环境下使用连接的WiFi不一样,所以对应的IP地址也就不一样,所以我给悟空派弄了一个0.98寸的OLED,用来显示CPU温度和当前IP地址,并5秒会刷新一次数据内容,为我连接SSH提供了一定的帮助,但是由于该任务是开机启动的前台任务,就导致我的串口Shell始终处于显示该线程打印数据内容的状态下,导致我无法在新环境连接新WiFi,所以考虑如何解决该问题。

处理方案

由于该任务我希望从设备启动时运行到设备关机时关闭,不希望其与终端产生关联,故在linux环境中找到较为合适的处理方案便为将守护进程。

守护进程(Daemon)

代蒙(希腊文:δαίμων、拉丁文:Dæmon、英文:Daemon)是希腊神话中的一种介于神与人之间的精灵或妖魔。它们与神祇的区别在于精灵并不具有人的外貌,而是一种善恶并存的超自然存在。在罗马神话中,代蒙称为格尼烏斯(Genius)。

代蒙无处不在,伴随着人的一生。 根据古希腊唯心主义哲学派的解释,人一生下来一直到死亡都有代蒙伴随并支配他的一切行动。

从这个英文单词的意义可以得知,daemon在linux系统中就是伴随linux运行一生并支配其行动的东西,这个东西善恶并存,是否就可以理解成daemon的好坏取决于编写daemon的我们,且无论好坏都将伴随linux的整个运行周期。

下面是标准理解,此处借鉴了大佬 JMW1407文章中 【Linux】守护进程( Daemon)的定义,作用,创建流程_daemon的作用-CSDN博客

1、定义

守护进程是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或循环等待处理某些事件的发生;它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机才随之一起停止运行;
守护进程一般都以root用户权限运行,因为要使用某些特殊的端口(1-1024)或者资源;
守护进程的父进程一般都是init进程,因为它真正的父进程在fork出守护进程后就直接退出了,所以守护进程都是孤儿进程,由init接管;
守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
守护进程的名称通常以d结尾,比如sshd、xinetd、crond等

2、作用

  1. 守护进程是一个生存周期较长的进程,通常独立于控制终端并且周期性的执行某种任务或者等待处理某些待发生的事件
  2. 大多数服务都是通过守护进程实现的
  3. 关闭终端,相应的进程都会被关闭,而守护进程却能够突破这种限制

功能实现

基本源码

首先大家可以看一下我是如何在悟空派使用OLED显示IP地址和温度的,源代码如下

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <syslog.h>#define MAXFILE 65535#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>#include "oled.h"
#include "font.h"#define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp"
#define MAX_SIZE 32#define ETH_NAME "wlan0"double temp_get(void){int fd;double temp = 0;char buf[MAX_SIZE];// 开/sys/class/thermal/thermal_zoneo/tempfd = open(TEMP_PATH,O_RDONLY);if (fd < 0) {fprintf(stderr,"failed to open thermal_zoneo/tempin");return -1;}//读取内容if(read(fd,buf,MAX_SIZE) < 0) {fprintf(stderr,"fatled to read tempin");return -1;}// 转换为浮点数打印temp = atoi(buf);temp /= 1000;// printf("temp: %.2f\n", temp);//关闭文件close(fd);return temp;
}//获取当前IP地址
char* get_local_ip()
{int sock;struct sockaddr_in sin;struct ifreq ifr;sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock == -1) {perror("socket");return NULL;}strncpy(ifr.ifr_name, ETH_NAME, IFNAMSIZ);ifr.ifr_name[IFNAMSIZ - 1] = 0;if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {perror("ioctl");return NULL;}memcpy(&sin, &ifr.ifr_addr, sizeof(sin));return inet_ntoa(sin.sin_addr);
}//OLED显示的基本框架
int oled_demo(struct display_info *disp) {int i = 0;char buf[100];double temp = 0;//从系统获取并在OLED上显示温度temp = temp_get();disp->font = font3;sprintf(buf, "CPU-Temp: %.2f C", temp);oled_putstrto(disp, 0, 0, buf);//从系统中获取并在OLED显示当前IP地址char* local_ip = get_local_ip();sprintf(buf, "WIP:%s", local_ip);oled_putstrto(disp, 0, 10, buf);//显示博主本人IDdisp->font = font1;oled_putstrto(disp, 0, 20, "----ASWaterbenben----");//打印分隔符disp->font = font2;for(i=0;i<128;i++)oled_putpixel(disp, i, 30, 1);oled_putstrto(disp, 0, 32, " ");oled_putstrto(disp, 0, 37, "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ");//打印结束标记disp->font = font1;oled_putstrto(disp, 0, 54, "*********************");oled_send_buffer(disp);return 0;
}//故障输出暂不启用
void show_error(int err, int add) {//const gchar* errmsg;//errmsg = g_strerror(errno);// printf("\nERROR: %i, %i\n\n", err, add);//printf("\nERROR\n");
}//用户输出暂不启用
void show_usage(char *progname) {// printf("\nUsage:\n%s <I2C bus device node >\n", progname);
}int main(int argc, char **argv) {int i;int e;char filename[32];struct display_info disp;if (argc < 2) {show_usage(argv[0]);return -1;}//清空显示句柄memset(&disp, 0, sizeof(disp));sprintf(filename, "%s", argv[1]);disp.address = OLED_I2C_ADDR;disp.font = font2;//打开OLED的设备驱动e = oled_open(&disp, filename);char *buf = "OLED fresh\r\n";int len = strlen(buf);if (e < 0) {show_error(1, e);}else {//OLED设备初始化e = oled_init(&disp);if (e < 0) {show_error(2, e);}else {//循环每隔5秒显示一次DEMOwhile(1){oled_demo(&disp);sleep(5);}}}return 0;
}

显示效果

效果如下图所示
在这里插入图片描述

但是在终端启动该功能后,将终端关闭则该进程也就同时关闭,并未达到预期效果,故考虑使用守护进程的处理方法进行修改;

守护进程实现逻辑和源码

脱离终端控制

由于在终端中启动的进程寿命与终端一致。终端被关闭则进程也被关闭,故首先需要脱离终端控制;

使用umask(0);为当前进程获取最大访问权限
使用fork();函数创建子进程,使用exit(0);退出父进程

完成上述操作后,终端认为父进程已退出,此时可继续进行终端操作。

独立进程

父进程退出后虽然终端可以继续操作,但是新建的子进程依旧归属于父进程所在的进程组、会话期、控制终端。故需要是因setsid();函数将子进程从父进程组中独立出来。

由于此时没有终端作为进程状态输出界面,故需要以其他形式将当前进程反馈的信息打印并保存,便于后续检查线程的运行情况,故使用openlog(“daemon”, LOG_PID, LOG_DAEMON);打开日志服务;
进程中调用 chdir() 函数,让根目录 ”/” 成为当前进程的工作目录 ,防止线程源文件所在位置是可卸载的存储介质,若介质被移除导致进程终端的问题。

进程垃圾处理

清除父进程连带可能产生的已启动文件,由于父进程已经关闭,但是当前进程会继承父进程已经打开的文件,这些文件当前进程并未使用,则将所有继承下来已打开的进程完全关闭。

加入功能代码

/** Copyright (c) 2015, Vladimir Komendantskiy* MIT License** SSD1306 demo of block and font drawing.*///
// fixed for OrangePiZero by HypHop
//#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <syslog.h>#define MAXFILE 65535#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>#include "oled.h"
#include "font.h"#define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp"
#define MAX_SIZE 32#define ETH_NAME "wlan0"double temp_get(void){int fd;double temp = 0;char buf[MAX_SIZE];// 开/sys/class/thermal/thermal_zoneo/tempfd = open(TEMP_PATH,O_RDONLY);if (fd < 0) {fprintf(stderr,"failed to open thermal_zoneo/tempin");return -1;}//读取内容if(read(fd,buf,MAX_SIZE) < 0) {fprintf(stderr,"fatled to read tempin");return -1;}// 转换为浮点数打印temp = atoi(buf);temp /= 1000;// printf("temp: %.2f\n", temp);//关闭文件close(fd);return temp;
}char* get_local_ip()
{int sock;struct sockaddr_in sin;struct ifreq ifr;sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock == -1) {perror("socket");return NULL;}strncpy(ifr.ifr_name, ETH_NAME, IFNAMSIZ);ifr.ifr_name[IFNAMSIZ - 1] = 0;if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {perror("ioctl");return NULL;}memcpy(&sin, &ifr.ifr_addr, sizeof(sin));return inet_ntoa(sin.sin_addr);
}int oled_demo(struct display_info *disp) {int i = 0;char buf[100];double temp = 0;temp = temp_get();disp->font = font3;sprintf(buf, "CPU-Temp: %.2f C", temp);oled_putstrto(disp, 0, 0, buf);//putstrto(disp, 0, 0, "Spnd spd  2468 rpm");//      oled_putstrto(disp, 0, 9+1, "Spnd cur  0.46 A");char* local_ip = get_local_ip();sprintf(buf, "WIP:%s", local_ip);oled_putstrto(disp, 0, 10, buf);disp->font = font1;//      oled_putstrto(disp, 0, 18+2, "Spnd tmp    53 C");oled_putstrto(disp, 0, 20, "----ASWaterbenben----");disp->font = font2;//      oled_putstrto(disp, 0, 27+3, "DrvX tmp    64 C");for(i=0;i<128;i++)oled_putpixel(disp, i, 30, 1);oled_putstrto(disp, 0, 32, " ");oled_putstrto(disp, 0, 37, "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ");disp->font = font1;//      oled_putstrto(disp, 0, 54, "Total cur  2.36 A");oled_putstrto(disp, 0, 54, "*********************");oled_send_buffer(disp);disp->font = font3;// for (i=0; i<100; i++) {//      sprintf(buf, "Spnd spd  %d rpm", i);//      oled_putstrto(disp, 0, 0, buf);//      oled_putstrto(disp, 135-i, 36+4, "===");//      oled_putstrto(disp, 100, 0+i/2, ".");//      oled_send_buffer(disp);// }//oled_putpixel(disp, 60, 45);//oled_putstr(disp, 1, "hello");return 0;
}void show_error(int err, int add) {//const gchar* errmsg;//errmsg = g_strerror(errno);// printf("\nERROR: %i, %i\n\n", err, add);//printf("\nERROR\n");
}void show_usage(char *progname) {// printf("\nUsage:\n%s <I2C bus device node >\n", progname);
}int main(int argc, char **argv) {printf("pid = %d\n", getpid());int i;int fd;pid_t pid;// 第一步umask(0);// 第二步pid = fork();if(pid < 0) {perror("fork error!");exit(1);} else if(pid > 0) {exit(0);}// 打开系统日志服务openlog("daemon", LOG_PID, LOG_DAEMON);// 第三步setsid();// 第四步chdir("/");// 第五步for (i = 0; i < MAXFILE; ++i) {close(i);}signal(SIGCHLD,SIG_IGN);int e;char filename[32];struct display_info disp;if (argc < 2) {show_usage(argv[0]);return -1;}memset(&disp, 0, sizeof(disp));sprintf(filename, "%s", argv[1]);disp.address = OLED_I2C_ADDR;disp.font = font2;e = oled_open(&disp, filename);char *buf = "OLED fresh\r\n";int len = strlen(buf);if (e < 0) {show_error(1, e);}else {e = oled_init(&disp);if (e < 0) {show_error(2, e);}else {while(1){oled_demo(&disp);fd = open("/tmp/deamon.log", O_CREAT|O_WRONLY|O_if (fd < 0) {syslog(LOG_ERR, "open");exit(1);}write(fd, buf, len+1);close(fd);sleep(5);}}}closelog();return 0;
}

编译与执行

使用gcc命令将修改好的c文件编译为out文件,并将out文件加入到开机自启任务中

即在 /etc/rc.local中的exit(0);前添加/home/orangepi/Cprogram/temp/oled.sh(oled.sh为已经授权的sh运行脚本,运行脚本核心是将编译完成的执行文件带入硬件固有的I2C硬件接口),oled.sh内容如下:

#!/bin/bash
cd /home/orangepi/Cprogram/temp
./oled_demo /dev/i2c-0

/home/orangepi/Cprogram/temp为执行文件所在路径

oled_demo就是编译完成的执行文件,/dev/i2c-0为OLED屏幕当前使用的硬件I2C接口。

效果

效果就不太好展示了,大概口头说一下,就是我的悟空派在启动后OLED就被点亮,并在屏幕上显示当前CPU温度、当前无线网IP地址,同时串口终端也成正常终端的状态,每次屏幕内容刷新都会在/tmp/deamon.log日志文件中打印一条消息OLED fresh。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/95246.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java方法案例

设计一个方法用于遍历数组&#xff0c;要求遍历的结果是在一行上的。例如&#xff1a;[11,22,33,44,55] public class index {public static void main(String[] args) {int[] arr {11,22,33,44,55};printArr(arr);}public static void printArr(int[] arr){System.out.print…

C++——list(2)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年9月28日 内容&#xff1a;C——list内容讲解 目录 前言&#xff1a; list的const迭代器&#xff1a; const的iterator&#xff1a; const迭代器&#xff1a; operator->: 拷贝构造&#xff1a; 迭代器接口补充&…

QT实验之闪烁灯

QT实验之闪烁灯 在QT中&#xff0c;使用QPainter来实现闪烁灯效果的一种方法是使用QTimer来周期性地改变灯的状态。 首先&#xff0c;你需要一个QPixmap对象&#xff0c;它包含了灯的图片。然后&#xff0c;使用QTimer来周期性地切换灯的状态。当灯的状态改变时&#xff0c;你需…

(c语言)位操作符

#include<stdio.h> //位操作符(只针对整数进行操作) //& - 按(二进制)位与 (有0则0) //| - 按(二进制)位或 (有1则1) //^ - 按(二进制)位异或 (不同为1,相同为0) int main() { int a 3; int b -5; //00000000000000000000000000000011——3的补码 …

【408复习】在b站开播通知

从今天开始制作408的讲解课程&#xff0c;欢迎大家来围观&#xff1b; 视频讲解的方式&#xff0c;在b站讲解&#xff0c; 账号&#xff1a;JustinAndCindy 参考书目&#xff1a; 操作系统 《计算机操作系统》第四版 汤小丹 数据结构 《无》 计算机网络 谢希仁 计算机组成原理 …

torch.randn()函数

torch.randn函数是PyTorch中用于生成具有正态分布&#xff08;均值为0&#xff0c;标准差为1&#xff09;的随机数的函数。它可以用于创建具有指定形状的张量&#xff0c;并且张量中的每个元素都是独立的随机数&#xff0c;遵循标准正态分布&#xff08;均值为0&#xff0c;标准…

【LittleXi】【MIT6.S081-2020Fall】Lab: locks

【MIT6.S081-2020Fall】Lab: locks 【MIT6.S081-2020Fall】Lab: locks内存分配实验内存分配实验准备实验目的1. 举一个例子说明修改前的**kernel/kalloc.c**中如果没有锁会导致哪些进程间竞争(races)问题2. 说明修改前的kernel/kalloc.c中锁竞争contention问题及其后果3. 解释a…

Elasticsearch安装访问

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore OneMore插件是一款非常强大&#xff0c;多达一百多个扩展功能的OneNote笔记插件&#xff0c;而且免费开源&#xff0c;不断更新的优秀插件—【蘇小沐】 1、实验 【OneMore官网&#xff1a;OneMore - a OneNote add-in (on…

C++——类和对象

文章目录 1.面向过程和面向对象的区别2.类的定义3.类的特点3.1封装性3.2继承性3.3多态性 4.类的访问限定符及封装4.1访问限定符4.2封装 5.类的作用域6.类的实例化7.计算类对象大小8.this指针8.1this指针的引出8.2this指针的特性 9.类的6个默认成员函数9.1默认构造函数9.1.1概念…

Elastic SQL 输入:数据库指标可观测性的通用解决方案

作者&#xff1a;Lalit Satapathy, Ishleen Kaur, Muthukumar Paramasivam Elastic SQL 输入&#xff08;metricbeat 模块和输入包&#xff09;允许用户以灵活的方式对许多支持的数据库执行 SQL 查询&#xff0c;并将结果指标提取到 Elasticsearch。 本博客深入探讨了通用 SQL …

TinyWebServer学习笔记-threadpool

线程池的特点&#xff1a; 空间换时间,浪费服务器的硬件资源,换取运行效率. 池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源. 当服务器进入正式运行阶段,开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配.…

单调队列---数据结构与算法

简介 队列也是一种受限制的线性表和栈相类似&#xff0c;栈是先进后出&#xff0c;而队列是先进先出&#xff0c;就好像一没有底的桶&#xff0c;往里面放东西&#xff0c;如图 在这里也是用数组来实现队列&#xff0c;用数组实现的叫做顺序队列 队列的数组模拟 const int N…

学习笔记|ADC反推电源电压|扫描按键(长按循环触发)|课设级实战练习|STC32G单片机视频开发教程(冲哥)|第十八集:ADC实战

文章目录 1.ADC反推电源电压测出Vref引脚电压的意义?手册示例代码分析复写手册代码Tips&#xff1a;乘除法与移位关系为什么4096后面还有L 2.ADC扫描按键(长按循环触发)长按触发的实现 3.实战小练1.初始状态显示 00 - 00 - 00&#xff0c;分别作为时&#xff0c;分&#xff0c…

buuctf-[GXYCTF2019]禁止套娃 git泄露,无参数rce

用dirsearch扫一下&#xff0c;看到flag.php 访问一下没啥东西&#xff0c;使用githack python2 GitHack.py http://8996e81f-a75c-4180-b0ad-226d97ba61b2.node4.buuoj.cn/.git/查看index.php <?php include "flag.php"; echo "flag在哪里呢&#xff1f;…

Net相关的各类开源项目

Net相关的各类开源项目 WPFHandyControlLive-ChartsWPFDeveloperswpf-uidesignStylet WebScheduleMasterYiShaAdminBlog.CoreNebula.AdminNewLife.CubeOpenAuth UnityuGUIUnityCsReferenceEpitomeMyUnityFrameWorkKSFrameworkTowerDefense-GameFramework-Demo 通用ClientServer…

事件循环机制

eventLoop 事件循环&#xff08;Event Loop&#xff09;是用于管理和调度异步任务执行的一种机制&#xff0c;通常在浏览器中&#xff0c;也在其他 JavaScript 运行环境中存在。事件循环确保 JavaScript 单线程的执行模型下能够处理非阻塞的异步任务&#xff0c;以避免程序阻塞…

数据库安全与保护

数据库安全与保护 文章目录 第一节 数据库完整性一、完整性约束条件的作用对象1.列级约束2.元组约束3.表级约束 二、定义与实现完整性约束1、实体完整性2、参照完整性3、自定义完整性非空约束 三、命名完整性约束四、更新完整性约束1、删除约束2、添加约束 第二节 触发器一、创…

NUWA论文阅读

论文链接&#xff1a;NUWA: Visual Synthesis Pre-training for Neural visUal World creAtion 文章目录 摘要引言相关工作视觉自回归模型视觉稀疏自注意 方法3D数据表征3D Nearby Self-Attention3D编码器-解码器训练目标 实验实现细节与SOTA比较T2I微调T2V微调V2V微调Sketch-t…