Linux日志体系


rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志。用户进程是通过调用syslog函数生成系统日志的。该函数将日志输出到一个UNIX本地域socket类型(AF_UNIX)的文件/dev/log中,rsyslogd 则监听该文件以获取用户进程的输出。内核日志在老的系统上是通过另外一个守护进程rklogd来管理的,rsyslogd 利用额外的模块实现了相同的功能。内核日志由printk等函数打印至内核的环状缓存(ring buffer) 中。环状缓存的内容直接映射到/proc/kmsg文件中。rsyslogd 则通过读取该文件获得内核日志。

rsyslogd守护进程在接收到用户进程或内核输人的日志后,会把它们输出至某些特定的日志文件。默认情况下,调试信息会保存至/varlog/debug文件,普通信息保存至/var/log/messages文件,内核消息则保存至/var/log/kerm.log文件。不过,日志信息具体如何分发,可以在rsyslogd的配置文件中设置。rsyslogd 的主配置文件是/etc/syslog.conf,其中主要可以设置的项包括:内核日志输人路径,是否接收UDP日志及其监听端口(默认是514,见/etc/services文件),是否接收TCP日志及其监听端口,日志文件的权限,包含哪些子配置文件(比如/etc/rsyslog.d/* .conf)。rsyslogd 的子配置文件则指定各类日志的目标存储文件。

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

#ifdef __va_arg_pack
__fortify_function void
syslog (int __pri, const char *__fmt, ...)
{__syslog_chk (__pri, __USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
}
#elif !defined __cplusplus
# define syslog(pri, ...) \__syslog_chk (pri, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
#endif

该函数采用可变参数(第二个参数message和第三个参数)来结构化输出。priority 参数是所谓的设施值与日志级别的按位或。设施值的默认值是LOG USER,我们下面的讨论也只限于这一种设施值。日志级别有如下几个:

#define  LOG_EMERG   0   /* system is unusable */
#define LOG_ALERT   1   /* action must be taken immediately */
#define LOG_CRIT    2   /* critical conditions */
#define LOG_ERR     3   /* error conditions */
#define LOG_WARNING 4   /* warning conditions */
#define LOG_NOTICE  5   /* normal but significant condition */
#define LOG_INFO    6   /* informational */
#define LOG_DEBUG   7   /* debug-level messages */

下面这个函数可以改变syslog的默认输出方式,进一步结构化日志内容:

/* Open connection to system logger.This function is a possible cancellation point and therefore notmarked with __THROW.  */
extern void openlog (const char *__ident, int __option, int __facility);

ident参数指定的字符串将被添加到日志消息的日期和时间之后,它通常被设置为程序的名字。logopt 参数对后续syslog调用的行为进行配置,它可取下列值的按位或:

/** Option flags for openlog.** LOG_ODELAY no longer does anything.* LOG_NDELAY is the inverse of what it used to be.*/
#define LOG_PID     0x01    /* log the pid with each message */
#define LOG_CONS    0x02    /* log on the console if errors in sending */
#define LOG_ODELAY  0x04    /* delay open until first syslog() (default) */
#define LOG_NDELAY  0x08    /* don't delay open */
#define LOG_NOWAIT  0x10    /* don't wait for console forks: DEPRECATED */
#define LOG_PERROR  0x20    /* log to stderr as well */

facility参数可用来修改syslog函数中的默认设施值。

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

/* Set the log mask level.  */
extern int setlogmask (int __mask) __THROW;

maskpri参数指定日志掩码值。该函数始终会成功,它返回调用进程先前的日志掩码值。
最后,不要忘了使用如下函数关闭日志功能:

/* Close descriptor used to write to system logger.This function is a possible cancellation point and therefore notmarked with __THROW.  */
extern void closelog (void);

用户信息

用户信息对于服务器程序的安全性来说是很重要的,比如大部分服务器就必须以root身份启动,但不能以root身份运行。下面这一组函数可以获取和设置当前进程的真实用户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 */

需要指出的是,个进程拥有两个用户ID:UID和EUID。EUID存在的目的是方便资源访问:它使得运行程序的用户拥有该程序的有效用户的权限。比如su程序,任何用户都可以使用它来修改自己的账户信息,但修改账户时su程序不得不访问/etc/passwd文件,而访问该文件是需要root权限的。那么以普通用户身份启动的su程序如何能访问/etc/passwd文件呢?窍门就在EUID。用Is命令可以查看到,su 程序的所有者是root,并且它被设置了set-user-id标志。这个标志表示,任何普通用户运行su程序时,其有效用户就是该程序的所有者root.那么,根据有效用户的含义,任何运行su程序的普通用户都能够访问/epassd文件。有效用户为root的进程称为特权进程(privileged processes)。EGID 的含义与EUID类似:给运行目标程序的组用户提供有效组的权限。进程的UID和EUID的区别,进程的UID是启动程序的用户的ID,而EUID则是root账户(文件所有者)的ID。

进程间关系

进程组

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

extern __pid_t getpgid (__pid_t __pid) __THROW;

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

下面的函数用于设置PGID:

/* Set the process group ID of the process matching PID to PGID.If PID is zero, the current process's process group ID is set.If PGID is zero, the process ID of the process is used.  */
extern int setpgid (__pid_t __pid, __pid_t __pgid) __THROW;

该函数将PID为pid的进程的PGID设置为pgid。如果pid和pgid相同,则由pid指定的进程将被设置为进程组首领;如果pid为0,则表示设置当前进程的PGID为pgid;如果pgid为0,则使用pid作为目标PGID。setpgid函数成功时返回0,失败则返回-1并设置errno。

一个进程只能设置自己或者其子进程的PGID。并且,当子进程调用exec系列函数后,我们也不能再在父进程中对它设置PGID。

会话

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

/* Create a new session with the calling process as its leader.The process group IDs of the session and the calling processare set to the process ID of the calling process, which is returned.  */
extern __pid_t setsid (void) __THROW;

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

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

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

Linux进程并未提供所谓会话ID(SID)的概念,但Linux系统认为它等于会话首领所在的进程组的PGID,并提供了如下丽数来读取SID:

/* Return the session ID of the given process.  */
extern __pid_t getsid (__pid_t __pid) __THROW;

用ps命令查看进程间关系

   PID    PPID    PGID     SID COMMAND53998   51650   53998   53998 bash54717   53998   54717   53998 ps54718   53998   54717   53998 less

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

系统资源限制

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

#include<sys/resource.h>typedef int __rlimit_resource_t;/* Put the soft and hard limits for RESOURCE in *RLIMITS.Returns 0 if successful, -1 if not (and sets errno).  */
extern int getrlimit (__rlimit_resource_t __resource, struct rlimit *__rlimits) __THROW;

__rlimits参数是rlimit结构体类型的指针,rlimit 结构体的定义如下:

struct rlimit{/* The current (soft) limit.  */rlim_t rlim_cur;/* The hard limit.  */rlim_t rlim_max;};

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

resource参数指定资源限制类型。表7-1列举了部分比较重要的资源限制类型。

setrlimit和getrlimit成功时返回0,失败则返回-1并设置errno。

改变工作目录和根目录

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

/* Get the pathname of the current working directory,and put it in SIZE bytes of BUF.  Returns NULL if thedirectory couldn't be determined or SIZE was too small.If successful, returns BUF.  In GNU, if BUF is NULL,an array is allocated with `malloc'; the array is SIZEbytes long, unless SIZE == 0, in which case it is asbig as necessary.  */
extern char *getcwd (char *__buf, size_t __size) __THROW __wur;/* Change the process's working directory to PATH.  */
extern int chdir (const char *__path) __THROW __nonnull ((1)) __wur;

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

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

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

/* Make PATH be the root directory (the starting point for absolute paths).This call is restricted to the super-user.  */
extern int chroot (const char *__path) __THROW __nonnull ((1)) __wur;

path参数指定要切换到的目标根目录。它成功时返回0,失败时返回-1并设置ermo。chroot并不改变进程的当前工作目录,所以调用chroot之后,我们仍然需要使用chdir(“/”)来将工作目录切换至新的根目录。改变进程的根目录之后,程序可能无法访问类似/dev的文件(和目录),因为这些文件(和目录)并非处于新的根目录之下。不过好在调用chroot之后,进程原先打开的文件描述符依然生效,所以我们可以利用这些早先打开的文件描述符来访问调用chroot之后不能直接访问的文件(和目录),尤其是一些日志文件。此外,只有特权进程才能改变根目录。

服务程序后台化

/* Put the program in the background, and dissociate from the controllingterminal.  If NOCHDIR is zero, do `chdir ("/")'.  If NOCLOSE is zero,redirects stdin, stdout, and stderr to /dev/null.  */
extern int daemon (int __nochdir, int __noclose) __THROW __wur;

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

Linux服务器程序规范化相关推荐

  1. 7 linux服务器程序规范

    1. Linux服务器程序一般以后台进程形式运行.后台进程又称守护进程(daemon),它没有控制终端,因而不会意外接收到用户输入.父进程通常为init(PID为1的进程) 2. Linux服务器程序 ...

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

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

  3. 《Linux高性能服务器编程》读书笔记:linux服务器程序规范

    服务器程序规范,需要强化和遵守的,作者给列了4条规范: 1.Linux服务器程序一般以后台进程形式运行,也就是以daemon守护进程的方式,守护进程的父进程通常是init进程(PID进程为1),作者在 ...

  4. 第7章 Linux服务器程序规范

    7.1 日志 Linux提供一个守护进程来处理系统日志:syslogd,默认配置文件为/etc/syslog.conf.这个服务下面有一系列子服务,如:mail,cron.当其他的程序或服务需要记录日 ...

  5. vs生成linux服务器程序,从Visual Studio到Linux上调试C++代码

    从Visual Studio到Linux上调试C++代码 04/30/2015 5 分钟可看完 本文内容 [原文发表时间] 2015/4/29 10:00 PM 正如您可能已经听说的那样,Visual ...

  6. linux服务器程序乱码,Linux安装GBK/GB2312程序显示乱码的五种解决方法

    不少用户在Linux系统中安装GBK或GB2312的时候遇到了乱码问题,这主要是系统默认语言是uft8所导致,对于该问题可用五种方法进行解决,下面小编就给大家介绍下Linux安装GBK或GB2312程 ...

  7. linux服务器程序开发,怎样搭建一个linux开发服务器

    在开发过程中,必然会出现多人同时工作.协着的情况,在嵌入式开发项目中更为平常,这样可以加快项目周期,为产品上市占得时间先机.目前,使用Linux作为开发产品的操作系统情况越来越多,使用越来越广泛.为了 ...

  8. Linux服务器程序编程的几个坎

    TCP.IP.SOCKET基础知识 服务器模型(参考http://fanqiang.chinaunix.net/a4/b7/20010508/112359.html) 协议数据包的封包.解包 DB读写 ...

  9. Linux服务器开发初步

      服务器开发需要考虑的内容很多,比如服务器的架构.稳定性.性能以及负载能力等等. 事实上,在开发服务器的过程中,需要综合考虑各种因素,比如就客户端连接时间较短却又比较频繁的服务器(例如HTTP服务器 ...

最新文章

  1. 用一行代码高效的求一个列表的平方和
  2. python怎么继承_Python: 如何继承str/string?
  3. Python语言学习:python语言代码调试—异常处理之详细攻略
  4. 阿里云高校君一岁啦!
  5. 特效html布局,一些好玩的css特效
  6. Android 图形架构
  7. leetcode面试题 04.03. 特定深度节点链表(bfs)
  8. 进程之父子进程的关系
  9. js小学生图区_推荐12个最好的 JavaScript 图形绘制库
  10. 6-5-1:STL之stack和queue——stack和queue的快速入门、常用接口以及适配器的概念
  11. mysql 聚簇_mysql索引总结(3)-MySQL聚簇索引和非聚簇索引
  12. oracle 安全备份与rman_Oracle RMAN备份与还原注意事项
  13. 使用python Gstreamer编写mp4视频播放器
  14. ServerStatus 云探针部署
  15. 使用moment.js加减日期时间
  16. 2019计算机专业英语国家线,2019英语一国家线多少
  17. 微信小程序新手教程 1.0
  18. mysql统计出每个姓氏的人数_Python 之 MySql 每日一练 130——同名同性学生名单,并统计人数...
  19. FBX SDK 总结之中文问题
  20. 适用于各浏览器支持图片预览,无刷新异步批量上传js插件(2)

热门文章

  1. lisp 画伯努利双纽线_伯努利双纽线的应用有哪些?
  2. android数据线接口定义,安卓手机数据线接口类型
  3. 解读大内老A的《.NET Core框架本质》
  4. iOS之深入探究动画渲染降帧
  5. Cron表达式学习:每天十二点执行一次:0 0 12 * * ?
  6. 【自然语言处理】【多模态】CLIP:从自然语言监督中学习可迁移视觉模型
  7. python音频 降噪_python降噪_python音频降噪_python图片降噪 - 云+社区 - 腾讯云
  8. Qt编写物联网管理平台13-短信告警
  9. 2020年总结:携梦而行,无怨无悔
  10. Flink问题记录:A raw type backed by type information has no serializable string representation. It ....