《UNUX环境高级编程》(13)守护进程

这篇具有很好参考价值的文章主要介绍了《UNUX环境高级编程》(13)守护进程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、引言

  • 守护进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的

2、守护进程的特征

  • Linux的大多数服务就是用守护进程实现的。这些守护进程名通常以d结尾,如inetd提供网络服务,sshd提供ssh登录服务,httpd提供web服务等待。
    • 大多数守护进程都以超级用户权限运行
    • 所有守护进程都没有控制终端。用户层守护进程缺少控制终端可能是守护进程调用了setsid的结果(setsid会断开与控制终端的联系)。
    • 大多数用户层守护进程都是进程组的组长进程以及会话的首进程,而且是这些进程组和会话中的唯一进程
    • 用户层进程的父进程是init(1)进程。

3、编程规则

  • 编写守护进程程序时需要遵循一些基本规则,以防止产生不必要的交互作用。
  • 实例:下面函数可由一个想要初始化为守护进程的程序调用。
    #include "apue.h"
    #include <syslog.h>
    #include <fcntl.h>
    #include <sys/resource.h>
    
    void
    daemonize(const char *cmd)
    {
    	int					i, fd0, fd1, fd2;
    	pid_t				pid;
    	struct rlimit		rl;
    	struct sigaction	sa;
    
    	/*(1)清空文件模式创建屏蔽字*/
    	umask(0);
    
    	/*
    	 * 获取最大文件描述符
    	 */
    	if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    		err_quit("%s: can't get file limit", cmd);
    
    	/*(2)调用fork,然后使父进程exit退出*/
    	if ((pid = fork()) < 0)
    		err_quit("%s: can't fork", cmd);
    	else if (pid != 0) /* parent */
    		exit(0);
    	/*(3)调用setsid创建一个新会话*/
    	setsid();
    
    	/*(3`)再次调用fork,终止父进程,继续使用子进程中的守护进程。
    	  这就保证了该守护进程不是会话首进程,可以防止它取得控制终端*/
    	sa.sa_handler = SIG_IGN;
    	sigemptyset(&sa.sa_mask);
    	sa.sa_flags = 0;
    	if (sigaction(SIGHUP, &sa, NULL) < 0)//忽略SIGHUP信号,见9.10节孤儿进程组
    		err_quit("%s: can't ignore SIGHUP", cmd);
    	if ((pid = fork()) < 0)
    		err_quit("%s: can't fork", cmd);
    	else if (pid != 0) /* parent */
    		exit(0);
    
    	/*(4)将当前工作目录更改为根目录*/
    	if (chdir("/") < 0)
    		err_quit("%s: can't change directory to /", cmd);
    
    	/*(5)关闭所有不再需要的文件描述符*/
    	if (rl.rlim_max == RLIM_INFINITY)
    		rl.rlim_max = 1024;
    	for (i = 0; i < rl.rlim_max; i++)
    		close(i);
    
    	/*(6)将文件描述符0/1/2指向/dev/null*/
    	fd0 = open("/dev/null", O_RDWR);
    	fd1 = dup(0);
    	fd2 = dup(0);
    
    	/*初始化log文件*/
    	openlog(cmd, LOG_CONS, LOG_DAEMON);
    	if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
    		syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
    		  fd0, fd1, fd2);
    		exit(1);
    	}
    }
    
    • 下面是关于守护进程的编程规则
    • (1)调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0),见4.8节相关内容。由继承(如fork)得来的文件模式创建屏蔽字可能会被设置为拒绝某些权限。如果守护进程要创建文件,那么它可能要设置特定的权限。
    • (2)调用fork,然后使父进程exit退出,这样会实现以下几点:
      • 如果该守护进程是shell命令启动的,那么父进程终止会让shell认为这条命令已经执行完毕。
      • 虽然子进程继承了父进程的进程组ID,但是获得了一个新的进程ID,因此子进程不是该进程组的组长进程,这是接下来进行setsid调用的先决条件。
    • (3)调用setsid创建一个新会话,见9.5节相关内容,这样会使调用进程:
      • 成为新会话的首进程
      • 成为新进程组的组长进程
      • 没有控制终端
        >> 有些建议此时再次调用fork,终止父进程,继续使用子进程中的守护进程,这就保证了该守护进程不是会话首进程,可以防止它取得控制终端
        >> 为了避免取得控制终端的另一种方法是:当用open函数打开终端设备时,设置O_NOCTTY标志。
    • (4)将当前工作目录更改为根目录
      • 从父进程处继承过来的当前工作目录可能在一个挂载的文件系统处。因为是守护进程通常在系统再引导前一直存在,所以如果守护进程的当前工作目录在一个挂载文件系统中,那么该文件系统就不能被卸载。
    • (5)关闭不再需要的文件描述符。这使得守护进程不再持有从其父进程继承来的任何文件描述符:可以通过getrlimit函数判定最高文件描述符值,并关闭直到该值的所有描述符。
    • (6)某些守护进程打开/dev/null使文件描述符0/1/2指向该文件。这样使得任何一个试图读标准输入、写标准输出或标准错误的例程都不会产生任何效果。因为守护进程不与终端设备关联,因此其输出无处显式,也无处从交互式用户那里接收输入。
      • /dev/null文件:
        • 一个字符设备文件。称为空设备,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF
        • /dev/null 被称为位桶(bit bucket)或者黑洞(black hole)。空设备通常被用于丢弃不需要的输出流,或作为用于输入流的空文件。这些操作通常由重定向完成。
      • /dev/zero文件:
        • 一个字符设备文件。当你读它的时候,它会提供无限的空字符(NULL, 即0x00)。写入/dev/zero的内容会丢失不见。
      • /dev/random/dev/urandom文件:
        • 字符设备文件。随机数设备,提供不间断的随机字节流。二者的区别是/dev/random产生随机数据依赖系统中断,当系统中断不足时,/dev/random设备会“挂起”,因而产生数据速度较慢,但随机性好;/dev/urandom不依赖系统中断,数据产生速度快,但随机性较低。

4、出错记录

  • syslog设施
    • 因为守护进程不应该有控制终端,所以不能只是将出错消息写到标准错误上。我们不希望所有守护进程都写到控制台设备上,也不希望每个守护进程将它自己的出错消息写到一个单独的文件中。
    • 因此需要关心哪一个守护进程写到哪一个记录文件中,可以通过一个集中的 守护进程出错记录设施 来进行这种管理操作
    • 大多数守护进程使用syslog设施,其组织结构如下:
      《UNUX环境高级编程》(13)守护进程,服务器,linux,运维
    • 有以下3种产生日志消息的方法:
      • 大多数用户守护进程调用syslog函数产生日志消息,该函数将消息发送至UNIX域数据报套接字/dev/log/dev/log是一个套接字类型文件
      • 无论一个用户进程在此主机上,还是在通过TCP/IP网络连接到此主机的其他主机上,都可以将日志消息发送到UDP端口514
      • 内核例程调用log函数产生日志消息
    • 其中syslogd是一个守护进程。不同的进程(client)都可以将log 输送给syslogdserver),由syslogd 集中收集。syslogd可以将log保存到本地,也可以发送到共享内存或远程服务器
    • syslogd守护进程读取所有3种格式的日志消息。syslogd在启动时读一个配置文件/etc/syslog.conf,该文件决定了不同种类消息应该送往何处。如一个紧急消息可在控制台上打印,而警告信息记录到一个文件中。
  • syslog设施的接口函数
    void openlog(const char *ident, int option, int facility);
    void syslog(int priority, const char *format, ...);
    void closelog(void);
    int setlogmask(int mask);
    
    • 调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog

    • closelog也是可选的,因为它只是关闭曾被用于与syslogd守护进程进行通信的描述符

    • openlog函数

      • ident参数:此参数是一个字符串,将被加至每一则日志消息中。(类比perror函数)
      • option参数:指定的标志用来控制openlog()操作和syslog()的后续调用。他的值为下列值或运算的结果:
        • LOG_CONS:若日志消息不能通过UNIX域数据报套接字送至syslogd守护进程,则将该消息写至控制台
        • LOG_NDELAY:立即打开至syslogd守护进程的UNIX域数据包套接字,不要等到第一条消息已经被记录时再打开。(通常在记录第一条消息前不打开该套接字文件)
        • LOG_NOWAIT:在记录日志信息时,不等待可能的子进程的创建
        • LOG_ODELAY:在第一条消息被记录之前延迟打开至syslogd守护进程的连接
        • LOG_PERROR:除了将日志消息发送给syslogd以外,还将它写至标准错误stderr
        • LOG_PID:每条消息都包含进程PID
      • facility参数:指定记录消息程序的类型。syslogd通过指定的配置文件,将以不同的方式来处理来自不同设施的消息(即这个要与syslogd守护进程的配置文件对应,日志信息会写入syslog.conf文件的指定位置)。
      • 如果不调用openlog或者该参数值为0,则在调用syslog时,可以将facility参数作为syslogpriority参数的一部分。
        《UNUX环境高级编程》(13)守护进程,服务器,linux,运维
    • syslog函数:产生一个日志消息

      • priority参数:其priority参数是openlogfacility参数和level的组合
        《UNUX环境高级编程》(13)守护进程,服务器,linux,运维
      • format参数:其中%m字符被替换成errno值对应的出错消息字符串(strerror
    • setlogmask函数

      • 设置进程的记录优先级屏蔽字,并返回之前的屏蔽字
      • mask参数:日志优先级掩码,在该掩码中的消息才会被真正记录。该掩码是level中各个常量的按位或
  • 实例:在一个行式打印机假脱机守护进程中,可能有下列调用序列
    openlog("lpd",LOG_PID,LOG_LPR);
    syslog(LOG_ERR,"open error for %s:%m",filename);
    
    • 该程序将ident字符串设置为程序名"lpd"LOG_PID参数指定该进程ID要始终被打印,并且将系统默认的facility设定为行式打印机系统(LOG_LPR参数)。对syslog制定一个出错条件(LOG_ERR)和一个消息字符串。
    • 不调用openlog,该程序可以有第二个调用形式
      syslog(LOG_LPR|LOG_ERR,"open error for %s:%m",filename);
      
      • 其中,将priority参数指定为levelfacility的组合。

5、单实例守护进程

  • 有时候需要在任意时刻只运行该守护进程的一个副本。如果同时运行该守护进程的多个实例,则会出现错误。

  • 可以通过文件和记录锁机制,该方法保证一个守护进程只有一个副本在运行(见14.3节)。如果每一个守护进程创建一个有固定名字的文件,并在该文件的整体上加一个写锁,那么只允许创建一把这样的写锁。在此之后创建写锁的尝试都会失败,这向后续守护进程副本指明已有一个副本正在运行。

  • 文件和记录锁提供了一种方便的互斥机制。如果守护进程在一个文件的整体上得到一把写锁,那么在该是守护进程终止时,这把锁将被自动删除。这就简化了复原所需的操作。

  • 实例:以下程序说明了如何使用文件和记录锁来保证只运行守护进程的一个副本

    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <syslog.h>
    #include <string.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/stat.h>
    
    #define LOCKFILE "/var/run/daemon.pid"
    #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
    
    extern int lockfile(int);
    
    int
    already_running(void)
    {
    	int		fd;
    	char	buf[16];
    
    	fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
    	if (fd < 0) {
    		syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
    		exit(1);
    	}
    	if (lockfile(fd) < 0) {
    		if (errno == EACCES || errno == EAGAIN) {
    			/*守护进程实例已经存在*/
    			close(fd);
    			return(1);
    		}
    		syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
    		exit(1);
    	}
    	/*能运行到此处,说明该进程是守护进程的唯一副本*/
    	ftruncate(fd, 0);
    	/*得到进程id*/
    	sprintf(buf, "%ld", (long)getpid());
    	/*将进程ID写入该文件*/
    	write(fd, buf, strlen(buf)+1);
    	return(0);
    }
    
    • 该函数会使得守护进程将自己PID写入到指定文件中。如果该文件已经加了锁,那么lockfile函数将会返回失败,errno设为EACCESEAGAINlockfile函数的具体实现见见14.3节)。函数返回1,表明该守护进程已在运行。
    • 需要将文件长度截断为0,其原因是之前的守护进程ID字符串可能长于调用此函数的当前进程的进程ID字符串。比如之前是12345,现在是9999,那么在文件中留下的就是99995,将文件截断为0就可解决该问题。

6、守护进程的惯例

  • 若守护进程使用锁文件,那么该文件通常存储在/var/run目录中/var 包括系统运行时要改变的数据)。守护进程可能需要超级用户权限才能在此目录下创建文件。锁文件的名字通常是name.pid,其中name是该守护进程或服务的名字。如cron守护进程锁文件的名字就是/var/run/crond.pid

  • 若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。配置文件的名字通常是name.conf,其中name是该守护进程或服务的名字。例如syslogd守护进程的配置文件通常是/etc/syslog.conf

  • 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc*/etc/init.d/*)启动的。如果在守护进程终止时,应当自动地重新启动它,则我们可以在/etc/inittab中为该守护进程包括respawn记录项,这样init就重新启动该守护进程。

  • 若一个守护进程有一个配置文件,那么当该守护进程启动时会读该文件,但在之后一般就不会再查看它。若某个管理员更改了配置文件,那么该守护进程可能需要被停止,然后再启动,以使配置文件生效。为避免这种麻烦,某些守护进程将捕捉SIGHUP信号,当它们收到该信号时重新读配置文件

    • 比如通过设置SIGHUP的信号捕捉函数,当收到SIGHUP时在信号捕捉函数内进行配置文件重读;
    • 或者在一个专用线程内通过sigwait函数同步的等待SIGHUP信号阻塞,当有阻塞的SIGHUP时,sigwait函数返回,然后执行配置文件重读。
  • 实例:说明守护进程可以重读其配置文件的一种方法,该程序使用sigwait以及多线程,具体用法见12.8节。

    #include "apue.h"
    #include <pthread.h>
    #include <syslog.h>
    
    sigset_t	mask;
    
    extern int already_running(void);
    
    void
    reread(void)
    {
    	/* ... */
    }
    
    void *
    thr_fn(void *arg)
    {
    	int err, signo;
    
    	for (;;) {
    		err = sigwait(&mask, &signo);
    		if (err != 0) {
    			syslog(LOG_ERR, "sigwait failed");
    			exit(1);
    		}
    
    		switch (signo) {
    		/*当收到SIGHUP信号,该线程调用reread函数重读它的配置文件*/
    		case SIGHUP:
    			syslog(LOG_INFO, "Re-reading configuration file");
    			reread();
    			break;
    		/*当收到SIGTERM信号,会记录消息并退出*/
    		case SIGTERM:
    			syslog(LOG_INFO, "got SIGTERM; exiting");
    			exit(0);
    
    		default:
    			syslog(LOG_INFO, "unexpected signal %d\n", signo);
    		}
    	}
    	return(0);
    }
    
    int
    main(int argc, char *argv[])
    {
    	int					err;
    	pthread_t			tid;
    	char				*cmd;
    	struct sigaction	sa;
    
    	if ((cmd = strrchr(argv[0], '/')) == NULL)
    		cmd = argv[0];
    	else
    		cmd++;
    
    	/*调用13.4节中的daemonize函数来初始化守护进程*/
    	daemonize(cmd);
    
    	/*调用13.5节中的already_running函数以确保该守护进程只有一个副本在运行*/
    	if (already_running()) {
    		syslog(LOG_ERR, "daemon already running");
    		exit(1);
    	}
    
    	/*
    	 * Restore SIGHUP default and block all signals.
    	 */
    	sa.sa_handler = SIG_DFL;
    	sigemptyset(&sa.sa_mask);
    	sa.sa_flags = 0;
    	/*此时SIGHUP信号仍被忽略,所以需要恢复对信号的默认处理方式,
    	否则调用sigwait的线程绝不会见到该信号*/
    	if (sigaction(SIGHUP, &sa, NULL) < 0)
    		err_quit("%s: can't restore SIGHUP default");
    	sigfillset(&mask);
    	/*阻塞所有信号,为何要这样做?防止主线程响应信号,允许其他线程响应*/
    	if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)
    		err_exit(err, "SIG_BLOCK error");
    
    	/*创建一个线程去处理信号,该线程唯一工作是等待SIGHUP和SIGTERM。*/
    	err = pthread_create(&tid, NULL, thr_fn, 0);
    	if (err != 0)
    		err_exit(err, "can't create thread");
    
    	/*
    	 * Proceed with the rest of the daemon.
    	 */
    	/* ... */
    	exit(0);
    }
    
  • 实例:单线程守护进程捕捉SIGHUP并重读其配置文件文章来源地址https://www.toymoban.com/news/detail-631511.html

    #include "apue.h"
    #include <syslog.h>
    #include <errno.h>
    
    extern int lockfile(int);
    extern int already_running(void);
    
    void
    reread(void)
    {
    	/* ... */
    }
    
    void
    sigterm(int signo)
    {
    	syslog(LOG_INFO, "got SIGTERM; exiting");
    	exit(0);
    }
    
    void
    sighup(int signo)
    {
    	syslog(LOG_INFO, "Re-reading configuration file");
    	reread();
    }
    
    int
    main(int argc, char *argv[])
    {
    	char				*cmd;
    	struct sigaction	sa;
    
    	if ((cmd = strrchr(argv[0], '/')) == NULL)
    		cmd = argv[0];
    	else
    		cmd++;
    
    	/*
    	 * Become a daemon.
    	 */
    	daemonize(cmd);
    
    	/*
    	 * Make sure only one copy of the daemon is running.
    	 */
    	if (already_running()) {
    		syslog(LOG_ERR, "daemon already running");
    		exit(1);
    	}
    
    	/*
    	 * Handle signals of interest.
    	 */
    	sa.sa_handler = sigterm;
    	sigemptyset(&sa.sa_mask);
    	sigaddset(&sa.sa_mask, SIGHUP);
    	sa.sa_flags = 0;
    	if (sigaction(SIGTERM, &sa, NULL) < 0) {
    		syslog(LOG_ERR, "can't catch SIGTERM: %s", strerror(errno));
    		exit(1);
    	}
    	sa.sa_handler = sighup;
    	sigemptyset(&sa.sa_mask);
    	sigaddset(&sa.sa_mask, SIGTERM);
    	sa.sa_flags = 0;
    	if (sigaction(SIGHUP, &sa, NULL) < 0) {
    		syslog(LOG_ERR, "can't catch SIGHUP: %s", strerror(errno));
    		exit(1);
    	}
    
    	/*
    	 * Proceed with the rest of the daemon.
    	 */
    	/* ... */
    	exit(0);
    }
    
    • 初始化守护进程后,我们为SIGHUPSIGTERM配置了信号处理程序。可以将重读逻辑放在信号处理程序中,也可以只在信号处理程序中设置一个标志,并由守护进程的主线程完成所有的工作。

7、客户进程-服务器进程模型

  • 守护进程通常被用作服务器进程。例如syslogd进程就是服务器进程,而用户进程(客户进程)用UNIX域数据报套接字向其发送消息。syslogd服务器进程提供的服务就是将一条出错消息记录到日志文件中。
  • 上面介绍的例子中,客户进程和服务器进程之间的通信是单向的。客户进程向服务器进程发送服务请求,服务器进程则不向客户回送任何消息。
  • 后面的章节会有很多客户进程和服务器进程之间双向通信的实例。

到了这里,关于《UNUX环境高级编程》(13)守护进程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux | 网络编程】TCP的服务端(守护进程) + 客户端

    上一节,我们用了udp写了一个服务端和客户端之间通信的代码,只要函数了解认识到位,上手编写是很容易的。 本章我们开始编写tcp的服务端和客户端之前通信的代码,要认识一批新的接口,并将我们之前学习的系统知识加进来,做到融会贯通… 代码详情:👉 Gitee 对于TC

    2024年01月16日
    浏览(46)
  • C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)

    进程 定义:每一个正在运行的应用程序,都是一个进程  进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境 多线程 这段代码在执行完成之前,程序会被卡死(不能操作程序,包括关闭窗口)。因为我们程序在做一些耗时操作的时候,如果主线程去执

    2024年02月22日
    浏览(95)
  • 网络通信(13)-C#TCP服务器和客户端同时在一个进程实现的实例

    有时项目需求中需要服务器和客户端同时在一个进程实现,一边需要现场接收多个客户端的数据,一边需要将数据汇总后发送给远程服务器。下面通过实例演示此项需求。 C#TCP服务器和客户端同时在一个进程实现的实例如下: 界面设计 UI文件代码

    2024年01月22日
    浏览(66)
  • 网络编程(一)TCP单进程服务器编程详解

    想要学习socket网络编程的读者一定要首先学好计算机网络的理论知识,包括 1)osi网络七层模型与ip四层模型 2)套接字含义 3)局域网通信过程 4)广域网通信过程 5)tcp,udp通信协议,在这两个协议中的连接建立,数据封装,传输过程,传输中可能遇到的问题的处理(差错控

    2024年02月15日
    浏览(47)
  • 控制服务和守护进程

    1.1 systemd简介 systemd是用户空间的第一个应用程序,即/sbin/init init程序的类型: SysV风格:init(centos5),实现系统初始化时,随后的初始化操作都是借助于脚本来实现的 特点: 脚本中含有大量的命令,每个命令都要启动一个进程,命令执行完以后就要终止这个进程。如此一来

    2024年02月09日
    浏览(34)
  • Linux网络编程:多进程 多线程_并发服务器

    文章目录: 一:wrap常用函数封装 wrap.h  wrap.c server.c封装实现 client.c封装实现 二:多进程process并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 三:多线程thread并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 ​​​​   read 函数的返回值 wrap.h  wrap

    2024年02月12日
    浏览(56)
  • 【网络编程】详解UDP/TCP套接字的创建流程+守护进程

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、网络编程套接字 1、一些概念 1.1源IP地址和目的IP地址 1.2端口号port 1.3TCP和UDP的性质 1.4网络字节序、IP地址类型转换

    2024年02月05日
    浏览(54)
  • [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单进程、多进程、多线程、线程池tcp服务器

    网络的上一篇文章, 我们介绍了网络变成的一些重要的概念, 以及 UDP套接字的编程演示. 还实现了一个简单更简陋的UDP公共聊天室. [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现…

    2024年02月16日
    浏览(66)
  • 计算机网络编程 | 并发服务器代码实现(多进程/多线程)

    欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。 专栏:《网络编程》 当涉及到构建高性能的服务

    2024年02月08日
    浏览(81)
  • 如何服务器用守护进程保证程序稳定运行

    平常在使用服务器的时候,服务一直不稳定,遂从nohup改为创建一个systemd服务来管理Python程序。 要求:有root权限 创建一个新的systemd服务文件,例如/ etc/systemd/system/your-service.service : 我的是app.py 那么就创建:/etc/systemd/system/app.service 内容模版如下: 模版中必须修改的地方有

    2024年01月20日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包