Linux服务器程序规范
日志
Linux系统日志
服务器的调试和维护都需要一个专业的日志系统
Linux提供一个守护进程syslogd
来处理系统日志
现在的Linux系统上使用的都是其升级版rsyslogd
rsyslogd
守护进程既可以接受用户进程输出的日志,又能接收内核日志
用户进程通过调用syslog
函数生成系统日志,该函数将日志输出到一个UNIX本地域socket类型(AF_UNIX)的文件/dev/log
中。rsyslogd
则监听该文件以获取用户进程的输出
内核日志在老系统上是通过守护进程rklogd
管理,rsyslogd
利用额外的模块实现了相同的功能。内核日志由printk等函数打印至内核的环状缓存中。环状缓存的内容直接映射到/proc/kmsg
文件中,rsyslogd
通过读取该文件获得内核日志
rsyslogd
守护进程在接收到用户进程或内核输出的日志后,会将它们输出到某些特定的日志文件中
默认情况下,调试信息保存至/var/log/debug
文件中,普通信息保存至/var/log/messages
文件,内核消息保存至/var/log/kern.log
文件
具体如何存放可以在rsyslogd
的配置文件中设置,rsyslogd
的主配置文件/etc/rsyslog.conf
syslog函数
#include<syslog.h>
void syslog(int priority,const char* message,...);
//priority设施值域日志级别的按位或
//设施值默认值为LOG_USER1
//日志级别
#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 //调试
//该函数可以改变syslog的默认输出方式,进一步结构化日志结构
#include<syslog.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函数中默认设施值
//程序在开发阶段可能需要输出很多调试信息,发布后又需要将调试信息关闭,可以设置日志掩码,使日志级别大于日志掩码的日志信息被系统忽略
#include<syslog.h>
int setlogmask(int maskpri);
//maskpri指定日志掩码值,该函数始终会成功,返回调用进程先前的日志掩码值
//关闭日志功能
#include<syslog.h>
void closelog();
用户信息
UID、EUID、GID、EGID
- UID:真实用户ID
- EUID:有效用户ID
方便资源访问,使得运行程序的用户拥有该程序的有效用户的权限
- GID:真实组ID
- EGID:有效组ID
#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
进程间关系
进程组
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);
//将PID为pid的进程的PGID设置为pgid
//如果pid和pgid相同,则有pid指定的进程将被设置为进程组首领
//如果pid为0,则表示设置当前进程的PGID为pgid
//如果pigd为0,则使用pid作为目标PGID
//成功时返回0,失败返回-1并设置errno
//一个进程只能设置自己或其子进程的PGID,当子进程调用exec系列函数后,不能再在父进程中设置PGID
会话
一些有关联的进程组将形成一个会话
#include<unistd.h>
pid_t setsid(void);
//该函数不能有进程组的首轮进程调用,否则将产生错误
//对于调用该函数的进程
//调用进程成为会话的首领,此时该进程是新会话的唯一成员
//新建一个进程组,其PGID就是调用进程的PID,调用进程成为该组的首领
//调用进程将甩开终端(如果有的话)
//成功是返回新进程组的PGID,失败返回-1并设置errno
//Linux进程未提供会话ID的概念,但Linux系统认为其等于会话首领所在的进程组的PGID
//读取SID
#include<unistd.h>
pid_t getsid(pid_t pid);
用ps命令查看进程关系
系统资源限制
//Linux系统资源限制可通过以下函数读取和设置
#include<sys/resource.h>
int getrlimit(int resource,struct rlimit* rlim);
int setrlimit(int resource,const struct rlimit* rlim);
struct rlimit
{
rlim_t rlim_cur;
rilm_t rlim_max;
}
改变工作目录和根目录
#include<unistd.h>
char* getcwd(chat* buf,size_t size);
//获取进程当前工作目录
//buf指向的内存用于存储进程当前工作目录的绝对路径,大小由size参数指定
//如果当前工作目录的绝对路径长度超过了size,则getcwd返回NULL,并设置errno未ERANGE
//如果buf未NULL并且size非0,则getcwd可能在内部使用malloc动态分配内存,并将进程的当前工作目录存储在其中
//如果是该情况,则需要自己释放getcwd在内部创建的这块内存
//getcwd成功是返回一个指向目标存储区(buf指向的缓存区或getcwd在内部动态创建的缓存区)的指针,失败返回NULL并设置errno
int chdir(consy char* path);
//改变进程工作目录
//成功返回0,失败返回-1并设置errno
int chroot(const char* path);
//改变进程根目录
//成功返回0,失败返回-1并设置errno