热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux服务器程序规范、服务器日志、用户、进程间的关系

文章目录服务器程序规范日志rsyslogd守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和


文章目录

  • 服务器程序规范
  • 日志
    • rsyslogd 守护进程
    • syslog函数
    • openlog函数
    • setlogmask函数
    • closelog函数
  • 用户
  • 进程间的关系
    • 进程组
    • 会话
    • 系统资源限制
    • 改变工作目录和根目录
    • 服务器程序后台化




服务器程序规范


  1. Linux 服务器程序一般以后台进程(守护进程[daemon])形式运行。它没有控制终端,因此不会意外接受到用户输入,守护进程的父进程通常是 init 进程(PID为1)。
  2. 服务器的调试和维护都需要一个专业的日志系统,Linux 常用一个守护进程 rsyslogd(syslogd的升级版) 来处理系统日志。
  3. Linux 服务器程序一般以 非root 的身份运行。如:mysqldhttpdsyslogd 等后台进程分别拥有自己的运行账户 mysqlapachesyslog
  4. Linux 服务器程序通常能处理很多命令行选项,如果一次运行的选项太多,那么可以用配置文件来管理。配置文件一般放在 /etc 目录下。
  5. Linux 服务器程序通常在启动时会生成一个记录该后台进程 PID 的文件并存入 /var/run 目录中。例如: syslogdPID 文件是 /var/run/syslogd.pid



日志


rsyslogd 守护进程

既能接受用户进程输出的日志,也能接受内核日志。


  • 用户进程调用 syslog函数 将日志输出到一个 UNIX 本地域 socket 类型(AF_UNIX)的文件 /dev/log 中,rsyslogd 则监听该文件以获得用户进程的输出。
  • 内核日志由 printk 等函数打印至内核的 环状缓存(ring buffer) 中,环状缓存的内容直接映射到 /proc/kmsg 中,rsyslogd 读取该文件以获得内核日志。

在这里插入图片描述




syslog函数

应用程序通过 syslog函数rsyslogd 守护进程通信:

#include
void syslog( int priority, const char* message, ...);
// 采用可变参数(message 和 第三个参数……)来结构化输出
// priority:设施值与日志级别的按位或,设施值的默认值是 LOG_USER。

限于 LOG_USER 设施值对应的日志级别有:

#include
#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
void openlog( const char* ident, int logopt, int facility );
// ident指定的字符串被添加到日志消息的日期和时间之后,通常被设置为程序的名字
// logopt对 syslog 调用的行为进行配置,为下列值的按位或:
#define LOG_PID 0x01 // 在日志消息中博阿寒程序 PID
#define LOG_CONS 0x02 // 如果消息不能记录到日志文件,则打印至终端
#define LOG_ODELAY 0X04 // 延迟打开日志功能知道第一次调用 syslog
#define LOG_NDELAY 0x08 // 不延迟打开日志功能
// facility用来修改 syslog 函数中的默认设施值



setlogmask函数

程序在开发阶段可能需要输出很多调试信息,而发布之后又需要将这些调试信息关闭。

解决这个问题的方法并非是在发布后删除调试代码(日后可能还需要用到),而有更简单的做法——设置日志掩码,使日志级别大于掩码日志信息被系统忽略。

setlogmask函数 就用以设置日志掩码:

#include
int setlogmask( int maskpri );
// maskpri指定日志掩码值
// 该函数始终会成功,返回调用进程旧的日志掩码值



closelog函数

用以关闭日志功能:

#include
void closelog();



用户

服务器中不同的用户有不同的权限,因此 获取or设置 当前进程的 真实用户ID(UID)、有效用户ID(EUID)、真实组ID(GID)、有效组(EGID) 就尤为重要:

#include
#include
uid_t getuid();
uid_t geteuid();
gid_t getgid();
gid_t getegid();
int setuid( uid_t uid );
int seteuid( uid_t uid );
int setgid( gid_t gid );
int setegid( gid_t gid );

一个进程有两个 用户ID: UIDEUIDEUID 方便了资源访问:使得 运行程序的用户 拥有 该程序的有效用户 的权限。如:任何用户都可以通过 su程序 修改自己的账户信息,这就不得不访问需要 root 权限的 /etc/passwd 文件。那么以普通用户身份启动的 su程序 如何能访问/etc/passwd 文件呢?

ls 命令可以查看到,su程序 的所有者是 root,且被设置了 set-user-id标志 ,即任何普通用户运行 su程序 时,su程序 的有效用户为 root。有效用户为 root 的进程被称为 特权进程(privileged processes)

类似的,EGID 为运行目标程序的组用户提供有效组的权限。




进程间的关系


进程组

Linux 下每个进程都隶属于一个进程组,PGID 即为它的 进程组ID。进程组将一直存在,知道其中所有进程都退出或者加入到其他线程组。每个进程组都有一个首领进程,其 PGIDPID 相同。

#include
pid_t getpgid( pid_t pid );
// 成功返回 ID,失败返回 -1 并设置 errno
int setpgid( pid_t pid, pid_t pgid );
// 将 PID 为 pid 的进程的 PGID 设置为 pgid,若 pid 和 pgid 相等,则 pid 指定的进程将被设定为进程组首领
// 若 pid=0,则表示设置 当前进程 的 PGID 为 pgid
// 若 pgid=0,则使用 pid 作为目标进程的 PGID
// 成功时返回 0,失败返回 -1 并设置 errno

一个进程只能设置自己或者子进程的 PGID,子进程调用 exec 系列函数后,父进程不再能设置它的 PGID




会话

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

#include
pid_t setsid( void );
// 成功时返回新的进程组的 PGID,失败则返回 -1 并设置 errno

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


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

Linux 进程并未提供所谓 会话ID(SID) 的概念,但将会话首领所在的进程组的 PGID 视为 SID,并提供了如下函数来读取 SID:

#include
pid_t getsid( pid_t pid );



系统资源限制

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

#include
int getrlimit( int resource, struct rlimit* rlim );
int setrlimit( int resource, const struct rlimit* rlim );
// 成功时返回 0,失败时返回 -1 并设置 errno。
struct rlimit{rlim_t rlim_cur; // 指定资源的软限制,是一个建议性的,最好不要超越的限制,若超越限制,则系统可能向进程发送信号以终止其运行。rlim_t rlim_max; // 指定资源的硬限制,一般是软限制的上限。普通程序可以减小硬限制,只有以 root 身份运行的程序才能增加硬限制。// rlim_t是一个整数类型,描述资源级别。
};

除上述外:


  • 还可以使用 ulimit 命令修改当前 shell 环境下的资源限制(软限制或/和硬限制),这种修改将对该 shell 启动的所有后续程序有效。
  • 还可以通过修改配置文件来改变系统软限制和硬限制,这种修改是永久的。



改变工作目录和根目录

#include
/* 获取进程当前工作目录 */
char* getcwd( char* buf, size_t size );
// buf指向的内存用于存储进程当前工作目录的绝对路径名,大小由 size 参数指定
// 如果当前工作的绝对路径长度(加上末尾的“\0”)超过了 size,则返回 NULL 并设置 errno 为 ERANGE。
// 若 buf 为 NULL 并且 size 非 0,则 getcwd 可能在内部使用 malloc 动态分配内存,并将进程的当前工作目录存储在其中,
// 此时我们需要自己释放 getcwd 在内部创建的这块内存。
// 成功返回一个指向目标存储区(buf指向的缓存区或者getcwd在内部动态创建的缓存区)的指针,失败返回 NULL 并设置 errno。/* 改变进程的工作目录 */
int chdir( const char* path );
// path 指定要切换到的目标目录
// 成功返回 0,失败返回 -1 并设置 errno/* 改变进程根目录 */
int chroot(const char* path);
// 参数和返回值同上,调用本函数后,仍需使用 chdir("/") 来将工作目录切换至新的根目录。
// 改变进程的根目录后,程序可能无法访问处于旧路径的文件or目录,
// 不过可以利用进程已经打开的文件描述符来访问调用 chroot 之后不能直接访问的文件。
// 只有特权进程才能改变根目录



服务器程序后台化

让一个进程以守护进程的方式运行:

#include
int daemon(int nochdir, int noclose);
// nochdir 用于指定是否改变工作目录,为 0 则工作目录被设置为“/”(根目录),否则继续使用当前目录
// noclose 为 0 时,标准输入、输出、错误输出都被重定向到 /dev/null 文件,否则依然使用原来的设备
// 成功返回 0,失败返回 -1 并设置 errno

推荐阅读
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
author-avatar
hi347
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有