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.rhkb.cn/news/149007.html

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

相关文章

回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测

回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测 目录 回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测预测效果基本介绍模型描述程序设计预测效果 <

【MyBatis-Plus】快速精通Mybatis-plus框架—快速入门

大家在日常开发中应该能发现&#xff0c;单表的CRUD功能代码重复度很高&#xff0c;也没有什么难度。而这部分代码量往往比较大&#xff0c;开发起来比较费时。 因此&#xff0c;目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是…

华为云云耀云服务器L实例评测|安装搭建学生成绩管理系统

1.前言概述 华为云耀云服务器L实例是新一代开箱即用、面向中小企业和开发者打造的全新轻量应用云服务器。多种产品规格&#xff0c;满足您对成本、性能及技术创新的诉求。云耀云服务器L实例提供丰富严选的应用镜像&#xff0c;实现应用一键部署&#xff0c;助力客户便捷高效的在…

全排列[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个不含重复数字的数组nums&#xff0c;返回其所有可能的全排列。你可以按任意顺序返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例…

JAVA面经整理(8)

一)为什么要有区&#xff0c;段&#xff0c;页&#xff1f; 1)页是内存和磁盘之间交互的基本单位内存中的值修改之后刷到磁盘的时候还是以页为单位的索引结构给程序员提供了高效的索引实现方式&#xff0c;不过索引信息以及数据记录都是记录在文件上面的&#xff0c;确切来说是…

【牛客网-面试必刷TOP 101】01链表

BM1 反转链表 解题思路 第一种方法&#xff1a;借助栈 1. 栈的特点是先进后出&#xff0c;用stack来存储链表&#xff0c;之后新建一个头节点&#xff0c;按出栈顺序拼接形成新的链表。 2. 注意&#xff0c;最后一个节点的next要赋值null 3. 空间复杂度O(N), 时间复杂度O(N)J…

ThemeForest – Canvas 7.2.0 – 多用途 HTML5 模板

ThemeForest 上的 HTML 网站模板受到全球数百万客户的喜爱。与包含网站所有页面并允许您在 WP 仪表板中自定义字体和样式的 WordPress 主题不同&#xff0c;这些设计模板是用 HTML 构建的。您可以在 HTML 编辑器中编辑模板&#xff0c;但不能在 WordPress 上编辑模板&#xff0…

医疗器械标准目录汇编2022版共178页(文中附下载链接!)

为便于更好地应用医疗器械标准&#xff0c;国家药监局医疗器械标准管理中心组织对现行1851项医疗器械国家和行业标准按技术领域&#xff0c;编排形成《医疗器械标准目录汇编&#xff08;2022版&#xff09;》 该目录汇编分为通用技术领域和专业技术领域两大类&#xff0c;通用…

ChatGPT付费创作系统V2.3.4独立版 +WEB端+ H5端 + 小程序最新前端

人类小徐提供的GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序&#xff0c;是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。当前全民热议ChatGPT&#xff0c;流量超级大&#xff0c;引流不要太简单&#xff01;一键下单即可拥有自己的GPT&#xff0…

14链表-环形链表、龟兔赛跑算法

目录 LeetCode之路——141. 环形链表 分析&#xff1a; 解法一&#xff1a;哈希表 解法二&#xff1a;龟兔赛跑 LeetCode之路——141. 环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针…

优化方法的应用(optimtool.example)

import optimtool as oo from optimtool.base import np, sp, pltpip install optimtool>2.4.2优化方法的应用&#xff08;optimtool.example&#xff09; import optimtool.example as oeLasso问题&#xff08;Lasso&#xff09; oe.Lasso.[函数名]([矩阵A], [矩阵b], [因…

矩阵的c++实现(2)

上一次我们了解了矩阵的运算和如何使用矩阵解决斐波那契数列&#xff0c;这一次我们多看看例题&#xff0c;了解什么情况下用矩阵比较合适。 先看例题 1.洛谷P1939 【模板】矩阵加速&#xff08;数列&#xff09; 模板题应该很简单。 补&#xff1a;1<n<10^9 10^9肯定…

QGIS文章四——对遥感影像进行土地类型分类

关于土地类型分类&#xff0c;按照性质、用途、利用现状有不同的分类标准。 一、按照国家土地性质分类标准&#xff0c;一般分五类:商业用地、综合用地、住宅用地、工业用地和其他用地。 二、按照用途进行土地分类&#xff1a;可以分为农用地、建设用地和未利用土地&#xff0c…

专题一:双指针【优选算法】

双指针应用场景&#xff1a; 数组划分、数组分块 目录 一、移动0 二、复写0 从后向前 三、快乐数 链表带环 四、盛水最多的容器 单调性双指针 五、有效三角形个数 单调性双指针 六、和为s的两个数字 七、三数之和 细节多 需再练 一、移动0 class Solution { public:void move…

【iptables 实战】9 docker网络原理分析

在开始本章阅读之前&#xff0c;需要提前了解以下的知识 阅读本节需要一些docker的基础知识&#xff0c;最好是在linux上安装好docker环境。提前掌握iptables的基础知识&#xff0c;前文参考【iptables 实战】 一、docker网络模型 docker网络模型如下图所示 说明&#xff1…

如何禁用Windows 10快速启动(以及为什么要这样做)

如果您不想启用Windows 10快速启动&#xff0c;则可以相对轻松地禁用它。 快速启动是一项功能&#xff0c;首先在 Windows 8 中作为快速启动实现&#xff0c;并延续到 Windows 10&#xff0c;让您的 PC 更快地启动&#xff0c;因此得名。虽然这个方便的功能可以通过将操作系统…

MySQL 事务隔离级别与锁机制详解

目录 一、前言二、事务及其ACID属性三、并发事务处理带来的问题四、事务隔离级别4.1、隔离级别分类4.2、查看当前数据库的事务隔离级别:4.3、临时修改数据库隔离级别&#xff08;重启MySQL后恢复到配置中的级别&#xff09; 五、表数据准备六、MySQL常见锁介绍5.1、锁分类5.2、…

软考高级之系统架构师之设计模式

概述 设计模式是一种通用的设计方法&#xff0c;实际开发中可能不止23种。为方便理解和应用&#xff0c;一般分为3类&#xff1a; 创建型&#xff0c;通过采用抽象类所定义的接口&#xff0c;封装系统中对象如何创建、组合等信息。工厂方法模式、抽象工厂模式、单例模式、建造…

PsychoPy Coder 心理学实验 斯特鲁普效应

选题&#xff1a;斯特鲁普效应实验 选题来源&#xff1a;你知道的「有趣的心理学实验」有哪些&#xff1f; - 知乎 (zhihu.com) 测试目标&#xff1a;探索斯特鲁普效应&#xff0c;即被试在判断文字颜色时&#xff0c;当文字的颜色与其所表示的颜色名称不一致时&#xff0c;是…

0基础学习VR全景平台篇 第103篇:使用英文、法文、德文等其他语言

大家好&#xff0c;欢迎观看蛙色VR官方系列——后台使用课程&#xff01; 蛙色VR平台目前已支持中英文语言进行切换&#xff0c;本期教程为大家带来&#xff0c;如何实现日文、法文、德文、俄文乃至其他小语种离线包里语言切换教程&#xff01; 语言切换样例展示 一、使用本功…