网站制作 深圳,嘉兴自助模板建站,网站开发与维护是干什么的,烟台企业做网站文章目录服务器程序规范日志rsyslogd 守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和根目录服务器程序后台化服务器程序规范
Linux 服务器程序一般以后台进程#xff08;守护进程[daemon]#xff09;形式运…
文章目录服务器程序规范日志rsyslogd 守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和根目录服务器程序后台化服务器程序规范
Linux 服务器程序一般以后台进程守护进程[daemon]形式运行。它没有控制终端因此不会意外接受到用户输入守护进程的父进程通常是 init 进程PID为1。服务器的调试和维护都需要一个专业的日志系统Linux 常用一个守护进程 rsyslogdsyslogd的升级版 来处理系统日志。Linux 服务器程序一般以 非root 的身份运行。如mysqld、httpd、syslogd 等后台进程分别拥有自己的运行账户 mysql、apache 和 syslog。Linux 服务器程序通常能处理很多命令行选项如果一次运行的选项太多那么可以用配置文件来管理。配置文件一般放在 /etc 目录下。Linux 服务器程序通常在启动时会生成一个记录该后台进程 PID 的文件并存入 /var/run 目录中。例如 syslogd 的 PID 文件是 /var/run/syslogd.pid 。 日志
rsyslogd 守护进程
既能接受用户进程输出的日志也能接受内核日志。
用户进程调用 syslog函数 将日志输出到一个 UNIX 本地域 socket 类型AF_UNIX的文件 /dev/log 中rsyslogd 则监听该文件以获得用户进程的输出。内核日志由 printk 等函数打印至内核的 环状缓存ring buffer 中环状缓存的内容直接映射到 /proc/kmsg 中rsyslogd 读取该文件以获得内核日志。 syslog函数
应用程序通过 syslog函数 与 rsyslogd 守护进程通信
#includesyslog.h
void syslog( int priority, const char* message, ...);
// 采用可变参数message 和 第三个参数……来结构化输出
// priority设施值与日志级别的按位或设施值的默认值是 LOG_USER。限于 LOG_USER 设施值对应的日志级别有
#includesyslog.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 的默认输出方式进一步结构化日志内容
#includesyslog.h
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函数 就用以设置日志掩码
#includesyslog.h
int setlogmask int maskpri );
// maskpri指定日志掩码值
// 该函数始终会成功返回调用进程旧的日志掩码值closelog函数
用以关闭日志功能
#includesyslog.h
void closelog();用户
服务器中不同的用户有不同的权限因此 获取or设置 当前进程的 真实用户IDUID、有效用户IDEUID、真实组IDGID、有效组EGID 就尤为重要
#include sys/types.h
#include unistd.h
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 UID 和 EUID。EUID 方便了资源访问使得 运行程序的用户 拥有 该程序的有效用户 的权限。如任何用户都可以通过 su程序 修改自己的账户信息这就不得不访问需要 root 权限的 /etc/passwd 文件。那么以普通用户身份启动的 su程序 如何能访问/etc/passwd 文件呢
用 ls 命令可以查看到su程序 的所有者是 root且被设置了 set-user-id标志 即任何普通用户运行 su程序 时su程序 的有效用户为 root。有效用户为 root 的进程被称为 特权进程privileged processes。
类似的EGID 为运行目标程序的组用户提供有效组的权限。 进程间的关系
进程组
Linux 下每个进程都隶属于一个进程组PGID 即为它的 进程组ID。进程组将一直存在知道其中所有进程都退出或者加入到其他线程组。每个进程组都有一个首领进程其 PGID 和 PID 相同。
#include unistd.h
pid_t getpgid( pid_t pid );
// 成功返回 ID失败返回 -1 并设置 errno
int setpgid( pid_t pid, pid_t pgid );
// 将 PID 为 pid 的进程的 PGID 设置为 pgid若 pid 和 pgid 相等则 pid 指定的进程将被设定为进程组首领
// 若 pid0则表示设置 当前进程 的 PGID 为 pgid
// 若 pgid0则使用 pid 作为目标进程的 PGID
// 成功时返回 0失败返回 -1 并设置 errno一个进程只能设置自己或者子进程的 PGID子进程调用 exec 系列函数后父进程不再能设置它的 PGID。 会话
一些有关联的进程组能形成一个会话session下面的函数用以创建一个会话
#includeunistd.h
pid_t setsid( void );
// 成功时返回新的进程组的 PGID失败则返回 -1 并设置 errno该函数不能由进程组的首领进程调用否则产生一个错误。对于非组首领的进程创建新会话的同时且有如下额外效果
调用进程成为会话的首领此时该进程是新会话的唯一成员。新建一个进程组其 PGID 就是调用进程的 PID调用进程成为该组的首领。调用进程将甩开终端如果有的话。
Linux 进程并未提供所谓 会话IDSID 的概念但将会话首领所在的进程组的 PGID 视为 SID并提供了如下函数来读取 SID
#includeunistd.h
pid_t getsid( pid_t pid );系统资源限制
Linux 上运行的程序都会受到资源限制的影响比如物理设备限制CPU数量、内存数量等、系统策略限制CPU时间等以及具体实现的限制文件名的最大长度。这些系统资源限制可以通过下面的函数来读取和设置
#include sys/resource.h
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 启动的所有后续程序有效。还可以通过修改配置文件来改变系统软限制和硬限制这种修改是永久的。 改变工作目录和根目录
#includeunistd.h
/* 获取进程当前工作目录 */
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 之后不能直接访问的文件。
// 只有特权进程才能改变根目录服务器程序后台化
让一个进程以守护进程的方式运行
#includeunistd.h
int daemon(int nochdir, int noclose);
// nochdir 用于指定是否改变工作目录为 0 则工作目录被设置为“/”根目录否则继续使用当前目录
// noclose 为 0 时标准输入、输出、错误输出都被重定向到 /dev/null 文件否则依然使用原来的设备
// 成功返回 0失败返回 -1 并设置 errno