【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: 凡是向这个目录写入的内容,自动被丢弃


bool daemon(bool ischdir, bool isclose)
{
    // 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);
    // 一直进行IO
    std::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函数:将进程->守护进程化

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Netty 进阶

文章目录 1. 粘包与半包1.1 粘包现象1.2 半包现象1.3 现象分析1.4 解决方案1) 方法1&#xff0c;短链接2) 方法2&#xff0c;固定长度3) 方法3&#xff0c;固定分隔符4) 方法4&#xff0c;预设长度 2. 协议设计与解析2.1 为什么需要协议&#xff1f;2.2 redis 协议举例2.3 http…

【小白版】最简单的 goland 自定义package 教程

正文 直奔主题&#xff0c;针对小白无法正确使用自定义的package包进行讲解。 在自己的go项目下执行 mod go mod init 项目名创建mod。mod是go管理依赖包的工具&#xff0c;类似Java的pom文件调整goland的配置&#xff0c;具体操作步骤如下面视频 通过视频可以看到原先报红的…

【分配】linear_sum_assignment函数

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 分配问题小结&#xff0c; linear_sum_assignment 函数使用的是Jonker-Volgenant algorithm算法 1. 分配问题 有工人和相应的工作&#xff0c;每个工作…

51单片机中断和定时的结合应用

#include <reg52.h>unsigned int cnt 0;sbit led P1^1;// 初始化定时器 void TimerSetup(){TMOD 0x01; // 定时器的第1个模式TH0 0xB8; // 定时器的初始值-高位TL0 0x00; // 定时器的初始值-低位TR0 1; //启动定时器cnt 0;EA 1; // 开启总中断ET0 1; // 时间中断…

DFS和回溯专题:全排列 II

DFS和回溯专题&#xff1a;全排列 II 题目链接: 全排列 II 参考题解 代码随想录 题目描述 代码纯享版 class Solution {public List<List<Integer>> list_all new ArrayList();public List<Integer> list new ArrayList();public int[] res;public Lis…

NVIDIA CUDA Toolkit

NVIDIA CUDA Toolkit CUDA Toolkit 12.4 Update 1 Downloads | NVIDIA Developer CUDA Toolkit是用于CUDA开发的软件包&#xff0c;主要包括CUDA编译器、运行时库、GPU驱动程序和开发工具等。它允许开发者使用通用编程语言&#xff08;如C、C&#xff09;来利用NVIDIA GPU进行…

echart-better基于最新的echarts5.5标题旋转功能

使用教程以及相关的echarts-better最新的包在这里&#xff1a;https://edu.csdn.net/course/detail/24569 echarts在侧边竖向展示标题&#xff0c;以及次标题 主标题和次标题进行旋转&#xff0c;适用于移动端或其他场景。

如何安装mysl驱动程序jar包

简介&#xff08;为什么要安装mysql驱动jar包&#xff09; MySQL 驱动程序&#xff08;通常以 JAR 文件的形式提供&#xff09;用于在 Java 应用程序中连接和与 MySQL 数据库进行交互。这些驱动程序提供了一组 API&#xff0c;使 Java 应用程序能够执行诸如查询、插入、更新和…

大数据真题讲解系列——拼多多数据分析面试题

拼多多数据分析面试题&#xff1a;连续3次为球队得分的球员名单 问题&#xff1a; 两支篮球队进行了激烈的比赛&#xff0c;比分交替上升。比赛结束后&#xff0c;你有一个两队分数的明细表&#xff08;名称为“分数表”&#xff09;。表中记录了球队、球员号码、球员姓名、得…

parallels desktop 19密钥分享 附PD虚拟机安装教程 支持M/intel

PD19虚拟机安装破解教程 Parallels Desktop 百度网盘下载&#xff1a;https://pan.baidu.com/s/1ezQmJAjIx796NEr9WZbcOg 提取码: 8w61 &#xff08;地址容易失效&#xff0c;来之不易&#xff0c;务必点赞和收藏&#xff0c;如果失效了请到评论区留言反馈&#xff09; 注意&…

猫咪吃主食罐头的好处盘点,附高营养高适口猫罐头推荐清单

关于是否要给猫咪喂食罐头&#xff0c;这可真是个让人头疼的争议话题啊&#xff01;有的猫主人觉得&#xff0c;罐头能让猫咪尝到更多美味&#xff0c;营养也更全面&#xff1b;而有些则觉得&#xff0c;猫粮就足够了&#xff0c;何必多此一举呢&#xff1f;作为一位拥有两只6岁…

LeetCode54. 螺旋矩阵

LeetCode54.螺旋矩阵 题解思路 代码 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> res;int n matrix.size();// 行int m matrix[0].size(); // 列vector<vector<bool>> st(n, v…

C#基础|构造方法相关

哈喽&#xff0c;你好&#xff0c;我是雷工。 以下为C#方法相关的学习笔记。 01 方法的概述 概念&#xff1a;方法表示这个对象能够做什么&#xff0c;也就是封装了这个对象行为。 类型&#xff1a;实例方法—>静态方法&#xff08;抽象方法、虚方法&#xff09;—>特殊…

阿斯达年代记下载注册+短信验证教程分享

阿斯达年代记&#xff1a;三强争霸》预计将于4月24日盛大发布&#xff0c;标志着一款新颖的MMORPG游戏面世&#xff0c;它跨越安卓、苹果和PC三大平台&#xff0c;实现数据互通&#xff0c;满足多元化玩家群体的需求。无论是追求移动便捷的手游爱好者&#xff0c;还是偏爱高性能…

揭秘神器:智能私信破局获客难!

在数字营销的海洋中&#xff0c;每个企业都如同一艘努力航行的船&#xff0c;希望能在广阔的客户蓝海中获得丰收。然而&#xff0c;现实却往往充满挑战&#xff0c;尤其是当面对如何吸引并维系客户这一核心难题时。传统的获客手段逐渐显得力不从心&#xff0c;而智能科技的介入…

OpenHarmony语言基础类库【@ohos.util.Deque (线性容器Deque)】

Deque&#xff08;double ended queue&#xff09;根据循环队列的数据结构实现&#xff0c;符合先进先出以及先进后出的特点&#xff0c;支持两端的元素插入和移除。Deque会根据实际需要动态调整容量&#xff0c;每次进行两倍扩容。 Deque和[Queue]()相比&#xff0c;Queue的特…

Postman获取接口返回值设置为变量

//设置环境变量&#xff0c;提取token // 把responseBody转为json字符串 var jsonData JSON.parse(responseBody); // 设置环境变量&#xff0c;提取token pm.environment.set("Authorization", jsonData.token_type " " jsonData.access_token); //设…

如何分析和优化慢sql语句

前言 sql查询速度比较慢容易成为性能瓶颈,这时我们可以优化我们的sql语句或数据库表 一般sql语句执行很慢的种类分为: 1.聚合查询 2.多表查询 3.表数据量过大查询 4.深度分页查询 这四种的前三种都可以通过优化sql语句来优化sql查询速度 正文 聚合查询 我们可以通过尝…

洗地机什么牌子比较好?4款洗地机品牌型号深度推荐

随着科技的不断发展&#xff0c;清洁工具也在不断进化。手持洗地机作为一种新型的清洁工具&#xff0c;因其便捷、高效的特点受到了消费者的青睐。然而&#xff0c;市场上的洗地机品牌众多&#xff0c;消费者在选择时常常感到困惑。那么&#xff0c;哪些洗地机品牌在口碑上表现…

FinalShell 安装 及使用教程

一 简介&#xff1a; FinalShell 是一款免费的国产的集 SSH 工具、服务器管理、远程桌面加速的良心软件&#xff0c;同时支持 Windows,macOS,Linux&#xff0c;它不单单是一个 SSH 工具&#xff0c;完整的说法应该叫一体化的的服务器&#xff0c;网络管理软件&#xff0c;在很…