Linux网络编程—Day10

news/2024/10/18 5:53:43/

Linux服务器程序规范

  • Linux服务器程序一般以后台进程形式运行。后台进程又称守护进程。它没有控制终端,因而也不会意外接收到用户输入。 守护进程的父进程通常是init进程(PID为1的进程);
  • Linux服务器程序通常有一套日志系统,它至少能输出日志到文件,有的高级服务器还能输出日志到专门的UDP服务器。大部分后台进程都在/var/log目录下拥有自己的日志目录;
  • Linux服务器程序一般以某个专门的非root身份运行。比如mysqld、httpd、syslogd等后台进程,分别拥有自己的运行账户mysql、apache和syslog;
  • Linux服务器程序通常是可配置的。服务器程序通常能处理很多命令行选项,如果一次运行的选项太多,则可以用配置文件来管理。绝大多数服务器程序都有配置文件,并存放在/etc目录下;
  • Linux服务器进程通常会在启动的时候生成一个PID文件并存入/var/run目录中,以记录该后台进程的PID;
  • Linux服务器程序通常需要考虑系统资源和限制,以预测自身能 承受多大负荷,比如进程可用文件描述符总数和内存总量等。

日志

服务器的调试和维护都需要一个专业的日志系统。Linux提供一个守护进程来处理系统日志——rsyslogd。rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志。用户进程是通过调用syslog函数生成系统日志的。该函数将日志输出到一个UNIX本地域socket类型的文件/dev/log中, rsyslogd则监听该文件以获取用户进程的输出。rsyslogd守护进程在接收到用户进程或内核输入的日志后,会把它 们输出至某些特定的日志文件。默认情况下,调试信息会保存至var/log/debug文件,普通信息保存至/var/log/messages文件,内核消息则保存至/var/log/kern.log文件。

应用程序使用syslog函数与rsyslogd守护进程通信。syslog函数的定义如下:

#include<syslog.h>
void syslog(int priority,const char*message,...);

该函数采用可变参数来结构化输出,priority参数是所谓的设施值与日志级别的按位或,设施值的 默认值是LOG_USER,日志级别有如下几个:

#include<syslog.h>
#define LOG_EMERG 0/*系统不可用*/
#define LOG_ALERT 1/*报警,需要立即采取动作*/
#define LOG_CRIT 2/*非常严重的情况*/
#define LOG_ERR 3/*错误*/
#define LOG_WARNING 4/*警告*/
#define LOG_NOTICE 5/*通知*/
#define LOG_INFO 6/*信息*/
#define LOG_DEBUG 7/*调试*/

openlog可以改变syslog的默认输出方式:

#include<syslog.h>
void openlog(const char*ident,int logopt,int facility);

ident参数指定的字符串将被添加到日志消息的日期和时间之后,它通常被设置为程序的名字。logopt参数对后续syslog调用的行为进行配置。facility参数可用来修改syslog函数中的默认设施值。

日志的过滤也很重要。程序在开发阶段可能需要输出很多调试信息,而发布之后我们又需要将这些调试信息关闭。解决这个问题的方法并不是在程序发布之后删除调试代码(因为日后可能还需要用 到),而是简单地设置日志掩码,使日志级别大于日志掩码的日志信息被系统忽略。下面这个函数用于设置syslog的日志掩码:

#include<syslog.h>
int setlogmask(int maskpri);

maskpri参数指定日志掩码值。该函数始终会成功,它返回调用进程先前的日志掩码值。关闭日志功能:

#include<syslog.h>
void closelog();

用户信息

UID、EUID、GID和EGID

下面这一组函数可以获取和设置当前进程的真实用户ID(UID)、有效用户ID(EUID)、真实组ID(GID)和有效组ID(EGID):

#include<sys/types.h>
#include<unistd.h>
uid_t getuid();/*获取真实用户ID*/
uid_t geteuid();/*获取有效用户ID*/
gid_t getgid();/*获取真实组ID*/
gid_t getegid();/*获取有效组ID*/
int setuid(uid_t uid);/*设置真实用户ID*/
int seteuid(uid_t uid);/*设置有效用户ID*/
int setgid(gid_t gid);/*设置真实组ID*/
int setegid(gid_t gid);/*设置有效组ID*/

切换用户

下面的代码清单展示了如何将以root身份启动的进程切换为以一个普通用户身份运行:

static bool switch_to_user(uid_t user_id,gid_t gp_id)
{/*先确保目标用户不是root*/
if((user_id==0)&&(gp_id==0))
{
return false;
}
/*确保当前用户是合法用户:root或者目标用户*/
gid_t gid=getgid();
uid_t uid=getuid();
if(((gid!=0)||(uid!=0))&&((gid!=gp_id)||(uid!=user_id)))
{
return false;
}
/*如果不是root,则已经是目标用户*/
if(uid!=0)
{
return true;
}
/*切换到目标用户*/
if((setgid(gp_id)<0)||(setuid(user_id)<0))
{
return false;
}
return true;
}

进程间关系

进程组

Linux下每个进程都隶属于一个进程组,因此它们除了PID信息外,还有进程组ID(PGID)。我们可以用如下函数来获取指定进程的PGID:

#include<unistd.h>
pid_t getpgid(pid_t pid);

该函数成功时返回进程pid所属进程组的PGID,失败则返回-1并设置errno。每个进程组都有一个首领进程,其PGID和PID相同。进程组将一直存在,直到其中所有进程都退出,或者加入到其他进程组。设置PGID:

#include<unistd.h>
int setpgid(pid_t pid,pid_t pgid);

setpgid 函数成功时返回0,失败则返回-1并设置errno。一个进程只能设置自己或者其子进程的PGID。并且,当子进程调用exec系列函数后,我们也不能再在父进程中对它设置PGID。

会话

一些有关联的进程组将形成一个会话(session)。下面的函数用于创建一个会话:

#include<unistd.h>
pid_t setsid(void);

该函数不能由进程组的首领进程调用,否则将产生一个错误。对于非组首领的进程,调用该函数不仅创建新会话,而且有如下额外效果:

  • 调用进程成为会话的首领,此时该进程是新会话的唯一成员
  • 新建一个进程组,其PGID就是调用进程的PID,调用进程成为该组的首领。
  • 调用进程将甩开终端

该函数成功时返回新的进程组的PGID,失败则返回-1并设置errno。

用ps命令查看进程关系

执行ps命令可查看进程、进程组和会话之间的关系:

我们是在bash shell下执行ps和less命令的,所以ps和less命令的父进程是bash命令,这可以从PPID(父进程PID)一列看出。这3条命令创建 了1个会话(SID是2375)和2个进程组(PGID分别是2735和71265)。bash命令的PID、PGID和SID都相同,很明显它既是会话的首领,也是组2375的首领。ps命令则是组71256的首领,因为其PID也是71256;

 系统资源限制

 Linux上运行的程序都会受到资源限制的影响,比如物理设备限制 (CPU数量、内存数量等)、系统策略限制(CPU时间等),以及具体实现的限制(比如文件名的最大长度)。Linux系统资源限制可以通过如下一对函数来读取和设置:

#include<sys/resource.h>
int getrlimit(int resource,struct rlimit*rlim);
int setrlimit(int resource,const struct rlimit*rlim);

setrlimit和getrlimit成功时返回0,失败则返回-1并设置errno。rlim参数是rlimit结构体类型的指针,rlimit结构体的定义如下:

struct rlimit
{rlim_t rlim_cur;rlim_t rlim_max;
};

rlim_t是一个整数类型,它描述资源级别。rlim_cur成员指定资源的软限制,rlim_max成员指定资源的硬限制。软限制是一个建议性的、最好不要超越的限制,如果超越的话,系统可能向进程发送信号以终止其运行。例如,当进程CPU时间超过其软限制时,系统将向进程发送 SIGXCPU信号;当文件尺寸超过其软限制时,系统将向进程发送 SIGXFSZ信号。硬限制一般是软限制的上限。普通程序可以减小硬限制,而只有以root身份运行的程序才能增加硬限制。此外, 我们可以使用ulimit命令修改当前shell环境下的资源限制(软限制或/和硬限制),这种修改将对该shell启动的所有后续程序有效。我们也可以通过修改配置文件来改变系统软限制和硬限制,而且这种修改是永久。

resource参数指定资源限制类型。下列举了部分比较重要的资源限制类型:

 改变工作目录和根目录

有些服务器程序还需要改变工作目录和根目录,例如Web服务器,一般来说,Web服务器的逻辑根目录并非文件系统的根目录“/”,而是站点的根目录。

获取进程当前工作目录和改变进程工作目录的函数分别是:

#include<unistd.h>
char*getcwd(char*buf,size_t size);
int chdir(const char*path);

buf参数指向的内存用于存储进程当前工作目录的绝对路径名,其大小由size参数指定。如果当前工作目录的绝对路径的长度超过了size,则getcwd将返回NULL,并设置errno为 ERANGE。如果buf为NULL并且size非0,则getcwd可能在内部使用 malloc动态分配内存,并将进程的当前工作目录存储在其中。如果是这种情况,则我们必须自己来释放getcwd在内部创建的这块内存。getcwd 函数成功时返回一个指向目标存储区的指针,失败则返回NULL并设置errno。

chdir函数的path参数指定要切换到的目标目录。它成功时返回0, 失败时返回-1并设置errno。

改变进程根目录的函数是chroot,其定义如下:

#include<unistd.h>
int chroot(const char*path);

path参数指定要切换到的目标根目录。它成功时返回0,失败时返 回-1并设置errno。chroot并不改变进程的当前工作目录,所以调用chroot之后,我们仍然需要使用chdir(“/”)来将工作目录切换至新的根目录。只有特权进程才能改变根目录。

服务器程序后台化

如何在代码中让一个进程以守护进程的方式运行? 守护进程的编写遵循一定的步骤,下面代码将服务器程序以守护进程的方式运行:

bool daemonize()
{pid_t pid = fork();if ( pid < 0 )return false;else if ( pid > 0 )exit( 0 );umask( 0 );pid_t sid = setsid();if ( sid < 0 )return false;if ( ( chdir( "/" ) ) < 0 ){/* Log the failure */return false;}close( STDIN_FILENO );close( STDOUT_FILENO );close( STDERR_FILENO );open( "/dev/null", O_RDONLY );open( "/dev/null", O_RDWR );open( "/dev/null", O_RDWR );return true;
}

实际上,Linux提供了完成同样功能的库函数:

#include<unistd.h>
int daemon(int nochdir,int noclose);

其中,nochdir参数用于指定是否改变工作目录,如果给它传递0,则工作目录将被设置为“/”(根目录),否则继续使用当前工作目录。noclose参数为0时,标准输入、标准输出和标准错误输出都被重定向到/dev/null文件,否则依然使用原来的设备。该函数成功时返回0,失败则返回-1并设置errno。


http://www.ppmy.cn/news/80313.html

相关文章

黑客如何从零学起?

一、MYSQL5.7 MySQL是如今使用最多的数据库&#xff0c;是众多企业的首选&#xff0c;在未来几年都将被持续推动发展。 学习MySQL需注重实战操作&#xff0c;循序渐进地了解MySQL中的各项技术&#xff0c;这样才能在实际工作中的关键应用。 想进入网络安全行业&#xff0c; …

Systrace系列2 —— 预备知识

本文主要是讲解一些分析 Systrace 的预备知识, 主要baoku如何查看 Systrace 中的线程状态 , 如何对线程的唤醒信息进行分析, 如何解读信息区的数据, 以及介绍了常用的快捷键. 通过本篇文章的学习, 相信你可以掌握进程和线程相关的一些信息, 也知道如何查看复杂的 Systrace 中包…

从0到1无比流畅的React入门教程

无比流畅的React入门教程TOC React 是什么 简介 用于构建 Web 和原生交互界面的库React 用组件创建用户界面通俗来讲&#xff1a;是一个将数据渲染为HTML视图的开源JS库 其他信息 Facebook 开发&#xff0c;并且开源 为什么使用React? 原生JS使用DOM-API修改UI代码很繁…

文生图关键问题探索:个性化定制和效果评价

文生图&#xff08;Text-to-Image Generation&#xff09;是AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;的一个主要方向。近年来&#xff0c;文生图模型的效果和质量得到飞速提升&#xff0c;投资界和研究界都在密切关注文生图模型的进展。…

图的创建——C语言描述

图的创建——C语言描述 文章目录 图的创建——C语言描述0 测试用例框架1 定义2 邻接矩阵法2.1 数据结构 2.2 构建图代码&#xff08;2&#xff09;测试用例&#xff08;3&#xff09;**打印结果** 0 测试用例框架 https://blog.csdn.net/m0_59469991/article/details/12713711…

关于HTTP请求GET和POST的区别

关于HTTP请求GET和POST的区别 1.GET提交&#xff0c;请求的数据会附在URL之后&#xff08;就是把数据放置在HTTP协议头&#xff1c;request-line&#xff1e;中&#xff09; GET:特定浏览器和服务器对URL长度有限制&#xff0c;例如IE对URL长度的限制是2083字节(2K35)。对于…

【Python sqlite3】零基础也能轻松掌握的学习路线与参考资料

Python sqlite3是Python语言自带的轻量级关系数据库管理系统&#xff0c;它可以让我们在不需要额外的安装和配置下&#xff0c;使用SQLite数据库进行操作和管理。SQLite是一个功能强大的嵌入式数据库&#xff0c;它非常适合在轻量级应用程序中使用&#xff0c;如桌面应用程序、…

数据结构—排序算法交换排序(冒泡快排)

目录 1.交换排序—冒泡排序 1.1冒泡排序基本思想 1.2冒泡排序的实现 2.交换排序—快速排序 1.1快速排序基本思想 1.2基准值划分—分析 1. hoare版&#xff1a; 2. 挖坑法&#xff1a; 3. 前后指针版本 1.3 hoare快排的具体实现 1.4 挖坑法快排的具体实现 1.5 前后指…