【Linux】网络与守护进程

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:进程状态、类型、优先级、命令行参数概念、环境变量(重要)、程序地址空间

在这里插入图片描述


目录

  • 👉🏻守护进程
  • 👉🏻写一个守护进程
    • daemon函数
    • setsid函数
    • Daemon.hpp
    • main.cc
  • 👉🏻将tcp通信进程变为守护进程
    • Main.cc

👉🏻守护进程

🌈 概念
Linux 的守护进程(Daemon Process)是一种在后台运行的特殊类型的进程,它们与用户没有交互界面,通常用于执行系统任务、服务或守护程序。

守护进程在 Linux 系统中被设计为长期运行的进程,独立于任何控制终端,并在系统启动时自动启动。它们通常在系统启动过程中由初始化进程(init process)启动,并在系统关闭时由系统管理器来终止。


而我们的网络服务,不能在bash中以前台进程的方式运行,真正的服务器,必须在Linux后台,以守护进程的方式运行

我们平常登录Linux,操作系统都会给我提供一个bash(命令行解释器),在此页面中我们也称为会话,在同一个会话中创建的进程,无非分为前台和后台进程,但是,后台进程可以有很多个,而前台进程只能有一个

同时启动的多个进程,可以是属于同一个进程组的,组ID一般是多个进程中的第一个进程
在这里插入图片描述


在这里插入图片描述
如图,我们启动一个睡眠进程,此时睡眠进程在前台,bash就退到后台去了,所以我们此时发的任何命令都无法被解析。
此时我们可以ctrl+Z终止当前进程,睡眠进程就会停止了。
在这里插入图片描述

fg 进程号数放前台,bg 进程号数放后台

👉🏻写一个守护进程

daemon函数

daemon()函数是一个Linux/Unix系统中用来创建守护进程(daemon)的函数,它通过一系列步骤将当前进程转变为一个守护进程。在标准的C库中没有daemon()函数,但是在一些系统中(如GNU libc)提供了这个函数。

以下是daemon()函数的一般形式:

int daemon(int nochdir, int noclose);
  • nochdir:如果该参数非零,表示在调用daemon()函数之后,守护进程的工作目录将不会被修改,即不会改变当前工作目录为根目录。
  • noclose:如果该参数非零,表示在调用daemon()函数之后,守护进程的标准输入、输出和错误输出将不会被关闭,即不会关闭文件描述符0、1和2。

daemon()函数的主要作用是将当前进程转变为一个守护进程。守护进程是在后台运行的系统服务,通常独立于终端会话,以提供某种服务或执行某些系统任务。通常,守护进程需要满足以下几个特性:

  1. 与终端分离:守护进程通常不应该与任何终端相关联,因此在创建守护进程时,需要将其与任何终端分离。

  2. 独立于父进程:守护进程应该是一个独立的进程,不受父进程的影响,即使父进程退出,守护进程也能够继续运行。

  3. 关闭文件描述符:通常,守护进程需要关闭与标准输入、输出和错误输出相关联的文件描述符,以确保不会受到终端会话的影响。

daemon()函数在实现这些特性时会完成一系列的操作,包括创建子进程、关闭父进程、改变工作目录、设置文件掩码等。它简化了创建守护进程的过程,使得开发者能够更容易地编写具有守护进程特性的程序。

setsid函数

setsid函数是一个Unix系统调用,用于创建一个新的会话并设置当前进程的会话ID(Session ID)为新会话的ID。它通常在守护进程(daemon)中使用,以确保进程独立于其父进程运行,并且不受终端的影响。

在调用setsid函数之后,当前进程将成为一个新的会话的领导者(session leader),同时成为一个新的进程组的领导者,并且没有控制终端。这样做有几个作用:

  1. 摆脱控制终端:会话领导者不再有控制终端,这意味着即使用户退出登录或关闭终端,该进程仍然可以继续运行。
  2. 独立于父进程:新的会话和进程组意味着进程不再受父进程的影响,即使父进程退出,进程也可以继续运行。
  3. 改变工作目录:新的会话通常会将进程的工作目录更改为根目录,以确保不受当前工作目录的影响。

setsid函数的原型如下:

pid_t setsid(void);

其中,setsid不接受任何参数,它返回一个pid_t类型的值,表示新的会话ID。如果调用成功,返回值为新的会话ID,如果失败则返回-1,并设置errno来指示错误的原因。

要使用setsid函数,通常需要在程序中先调用fork创建一个子进程,然后在子进程中调用setsid函数。这样可以确保新的会话和进程组不会受到父进程的影响。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");exit(EXIT_FAILURE);} else if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 在子进程中调用setsid函数pid_t sid = setsid();if (sid < 0) {perror("setsid");exit(EXIT_FAILURE);}// 这里是新的会话领导者// 关闭标准输入、输出和错误输出close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 守护进程的主体逻辑return 0;
}

以上是一个简单的守护进程的示例,其中在子进程中调用了setsid函数,创建了一个新的会话,并关闭了标准输入、输出和错误输出,最后进入守护进程的主体逻辑。


为什么要先调用fork创建一个子进程? 🤔
父进程在创建子进程后立即退出,这样子进程就不会成为孤儿进程,而是被init进程接管。这一步确保了守护进程不会有父进程,从而独立于任何终端会话。

父进程在创建子进程后立即退出,这样子进程就不会成为孤儿进程,而是被init进程接管。这一步确保了守护进程不会有父进程,从而独立于任何终端会话。

总的来说,就是如果不创建子进程,父进程成为当前会话的唯一进程组,也就是进程组的组长,父进程如果一旦退出,整个会话也就退出玩完了。

Daemon.hpp

#pragma once
#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>const char *root = "/";
const char *dev_null = "/dev/null";//  /dev/null: 凡是向这个目录写入的内容,自动被丢弃void daemon()
{// 1. 忽略可能引起程序异常退出的信号signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);// 2. 让自己不要成为组长if (fork() > 0)exit(0);// 3. 设置让自己成为一个新的会话, 后面的代码其实是子进程在走setsid();// 4. 每一个进程都有自己的CWD(当前目录),是否将当前进程的CWD更改成为 / 根目录if (ischdir)chdir(root);// 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了if (isclose){close(0);close(1);close(2);}else{// 这里一般建议就用这种int fd = open(dev_null, O_RDWR);if (fd > 0){dup2(fd, 0);//重定向,原本向标准输入中读的,现在从fd中读dup2(fd, 1);dup2(fd, 2);close(fd);}}
}

main.cc

#include "Daemon.hpp"
#include <unistd.h>int main()
{// 变成守护进程Daemon(true, false);// 是我要执行的核心代码while(true){sleep(1);}return 0;
}

👉🏻将tcp通信进程变为守护进程

代码参考:【Linux】socket编程3

代码目录:
在这里插入图片描述

Main.cc

#include <memory>#include "TcpServer.hpp"
#include "CommErr.hpp"
#include "Translate.hpp"
#include "Daemon.hpp"void Usage(std::string proc)
{std::cout << "Usage : \n\t" << proc << " local_port\n" << std::endl;
}
void Interact(int sockfd)
{while(true){char buffer[1024];ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);//读取客户端信息if (n > 0){buffer[n] = 0;cout<<"clinet say:"<<buffer<<endl;string message = buffer;if(write(sockfd, message.c_str(), message.size())<0)//发送回客户端cout<<"send fail"<<endl;}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));break;}}
}
void Ping(int sockfd, InetAddrtoLocal addr)
{lg.LogMessage(Debug, "%s select %s success, fd : %d\n", addr.Info().c_str(), "ping", sockfd);// 一直进行IOstd::string message = "Ping Service Start...";write(sockfd, message.c_str(), message.size());//先回应来自客户端的消息Interact(sockfd);}void Translate(int sockfd,InetAddrtoLocal addr)
{lg.LogMessage(Debug, "%s select %s success, fd : %d\n", addr.Info().c_str(), "Translate", sockfd);std::string message = "Translate Service Start...";write(sockfd, message.c_str(), message.size());//先回应来自客户端的消息Translater trans;while(true){char buffer[1024];ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);//读取客户端信息if (n > 0){buffer[n] = 0;cout<<"clinet say:"<<buffer<<endl;string message = trans.Excute(buffer);if(write(sockfd, message.c_str(), message.size())<0)//发送回客户端cout<<"send fail"<<endl;}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));break;}}
}// 改成大写,字符串改成大写
void Transform(int sockfd, InetAddrtoLocal addr)
{lg.LogMessage(Debug, "%s select %s success, fd : %d\n", addr.Info().c_str(), "Transform", sockfd);}
// ./tcp_server 8888
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);return Usage_Err;}uint16_t port = stoi(argv[1]);//进程变为守护进程Daemon(false, false);lg.Enable(ClassFile);//使日志可以向显示屏打印std::unique_ptr<TcpServer> tsvr = make_unique<TcpServer>(port);//在内核中注册服务类型tsvr->RegisterFunc("ping", Ping);tsvr->RegisterFunc("translate", Translate);tsvr->RegisterFunc("transform", Transform);//初始化和启动服务端tsvr->Init();tsvr->Start();return 0;
}
  • /dev/null: 凡是向这个目录写入的内容,自动被丢弃
  • kill -9 PID 删除进程
  • daemon函数:将进程->守护进程化

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

面试八股——集合——List

主要问题 数组 如果数组索引从0开始时&#xff0c;数组的寻址方式为&#xff1a; 如果数组索引从1开始时&#xff0c;数组的寻址方式为&#xff1a; 此时对于CPU来说增加了一个减法指令&#xff0c;降低寻址效率。 ArrayList⭐ ArrayList构造函数 尤其说一下第三个构造函数流…

【复习笔记】FreeRTOS(五)时间片调度

本文是FreeRTOS复习笔记的第五节&#xff0c;时间片调度。 上一篇文章&#xff1a; 【复习笔记】reeRTOS(四) 列表项的插入和删除 文章目录 1.时间片调度简介1.1. 运行过程 二、实验设计三、测试例程四、实验效果 1.时间片调度简介 FreeRTOS支持多个任务同时拥有一个优先级&am…

设计千万级并发系统架构需要考虑的各方面因素

设计千万级并发系统架构需要考虑多方面因素&#xff0c;包括系统的可伸缩性、高可用性、性能、安全性等。 1、分布式架构&#xff1a; 使用微服务架构&#xff1a;将系统拆分成多个独立的服务&#xff0c;每个服务都可以独立部署和扩展。 使用分布式服务框架&#xff1a;如S…

顺丰同城急送API的坑(附源码)

一、背景 最近公司让我对接顺丰同城急送的API&#xff0c;讲讲里面我遇到的坑 官方的API文档给我的感觉是不怎么规范的&#xff0c;很多细节要靠猜&#xff0c;示例代码也不全&#xff0c;具体细节不多说&#xff0c;如果你现在也需要对接他们API&#xff0c;可以参考本篇博客…

爬虫 | 基于 requests 实现加密 POST 请求发送与身份验证

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目旨在实现一个简单的 Python 脚本&#xff0c;用于向指定的 URL 发送 POST 请求&#xff0c;并通过特定的加密算法生成请求头中的签名信息。这个脚本的背后是与某个特定的网络服务交互&#xff0c;发送特定格式的 JSON 数据…

LeetCode in Python 1338. Reduce Array Size to The Half (数组大小减半)

数组大小减半思路简单&#xff0c;主要是熟悉python中collections.Counter的用法&#xff0c;采用贪心策略即可。 示例&#xff1a; 图1 数组大小减半输入输出示例 代码&#xff1a; class Solution:def minSetSize(self, arr):count Counter(arr)n, ans 0, 0for i, valu…

北大字节联合发布视觉自动回归建模(VAR):通过下一代预测生成可扩展的图像

北大和字节发布一个新的图像生成框架VAR。首次使GPT风格的AR模型在图像生成上超越了Diffusion transformer。 同时展现出了与大语言模型观察到的类似Scaling laws的规律。在ImageNet 256x256基准上,VAR将FID从18.65大幅提升到1.80,IS从80.4提升到356.4,推理速度提高了20倍。 相…

关于Jetson空间不足的解决问题(sd卡挂载和conda更改环境安装路径)

文章目录 问题描述挂载sd卡到指定目录查看conda路径更改环境路径指定路径安装conda虚拟环境 问题描述 因为在做毕设的时候&#xff0c;用到了Jetson&#xff0c;发现这个空间太小了&#xff0c;如果下conda的包根本不够用&#xff0c;所以就想挂载sd卡&#xff0c;然后把环境安…

国外GIS软件排名简介<30个>

简介 国外gisgeography网站进行了一次GIS软件排名&#xff0c;通过分析、制图、编辑等因素进行测试&#xff0c;具体规则如下&#xff1a; 分析&#xff1a;矢量/栅格工具、时态、地统计、网络分析和脚本。 制图&#xff1a;地图类型、坐标系、地图布局/元素、标注/注记、3D …

C#到底属于编译型语言还是解释型语言?

C#是一种编译型语言&#xff0c;也称为静态类型语言&#xff0c;这意味着C#代码在运行之前需要经过编译器的编译处理&#xff0c;并生成一个可执行的本地代码文件&#xff08;通常是.exe或.dll文件&#xff09;。相反&#xff0c;解释型语言将代码转换为低级代码后直接执行&…

计算机视觉——手机目标检测数据集

这是一个手机目标检测的数据集&#xff0c;数据集的标注工具是labelimg,数据格式是voc格式&#xff0c;要训练yolo模型的话&#xff0c;可以使用脚本改成txt格式&#xff0c;数据集标注了手机&#xff0c;标签名&#xff1a;telephone,数据集总共有1960张&#xff0c;有一部分是…

软件无线电安全之GNU Radio基础 -上

GNU Radio介绍 GNU Radio是一款开源的软件工具集&#xff0c;专注于软件定义无线电&#xff08;SDR&#xff09;系统的设计和实现。该工具集支持多种SDR硬件平台&#xff0c;包括USRP、HackRF One和RTL-SDR等。用户可以通过GNU Radio Companion构建流程图&#xff0c;使用不同…

BackTrader 中文文档(二十七)

原文&#xff1a;www.backtrader.com/ 数据 - 多个时间框架 原文&#xff1a;www.backtrader.com/blog/posts/2015-08-24-data-multitimeframe/data-multitimeframe/ 有时&#xff0c;使用不同的时间框架进行投资决策&#xff1a; 周线用于评估趋势 每日执行进入 或者 5 分钟…

软考132-上午题-【软件工程】-沟通路径

一、定义 1-1、沟通路径1 沟通路径 1-2、沟通路径2 沟通路径 n-1 二、真题 真题1&#xff1a; 真题2&#xff1a; 真题3&#xff1a;

发布 Chrome/Edge浏览器extension扩展到应用商店

Chrom Extension发布流程 创建和发布自定义 Chrome 应用和扩展程序&#xff1a;https://support.google.com/chrome/a/answer/2714278?hlzh-Hans 在 Chrome 应用商店中发布&#xff1a;https://developer.chrome.com/docs/webstore/publish?hlzh-cn 注册开发者帐号&#…

图解CPU的实模式与保护模式

哈喽&#xff0c;大家好&#xff0c;我是呼噜噜&#xff0c;好久没有更新old linux了&#xff0c;在上一篇文章Linux0.12内核源码解读(7)-陷阱门初始化中&#xff0c;我们简要地提及了中断&#xff0c;但是中断机制在计算机世界里非常重要&#xff0c;处处都离不开中断&#xf…

Element——组件

element官网 https://element.eleme.cn/#/zh-CN/component/layout vscode格式化快捷键&#xff1a;shiftaltf table表格 <template><el-table:data"tableData"style"width: 100%"><el-table-columnprop"date"label"日期…

Git使用总结(不断更新中)

branch 本地分支操作 删除本地分支 git branch -d <local-branch-name>远端分支操作 从远端分支创建本地分支 git checkout -b <local-branch-name> origin/<remote-branch-name>git ignore 如果工程的代码文件中有不希望上传到远端的文件&#xff0c;…

排列特征重要性(Permutation Feature Importance)

5个条件判断一件事情是否发生&#xff0c;每个条件可能性只有2种&#xff08;发生或者不发生&#xff09;&#xff0c;计算每个条件对这件事情发生的影响力。排列特征重要性模型的程序。 例一 在机器学习领域&#xff0c;排列特征重要性&#xff08;Permutation Feature Impor…

【honggfuzz学习笔记】honggfuzz的基本特性

本文架构 1.动机2.honggfuzz的基本概念官网描述解读 3. honggfuzz的反馈驱动(Feedback-Driven)软件驱动反馈&#xff08;software-based coverage-guided fuzzing&#xff09;代码覆盖率代码覆盖率的计量单位 代码覆盖率的统计方式 硬件驱动反馈&#xff08; hardware-based co…