linux服务器程序规范

linux系统日志

服务器的调试和维护都需要一个专业的日志系统。linux提供一个守护进程(syslogd)来处理系统日志,现在linux系统上使用的是它的升级版(rsyslogd)。

rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志。

    用户进程是通过调用syslog函数生成系统日志的。该函数将日志输出到一个UNIX本地域socket类型(AF_UNIX)的文件/dev/log中,rsyslogd则监听该文件以获取用户进程的输出。

    内核日志由printk等函数打印至内核的环状缓存(ring buffer)中。环状缓存的内容直接映射到/proc/kmsg文件中。rsyslogd通过读取该文件获得内核日志。

    rsyslogd守护进程在接收到用户进程或内核输入的日志后,会把它们输出至某些特定的日志文件。默认情况下,调试信息会保存至/var/log/debug文件,普通信息保存至/var/log/messages文件,内核信息保存至/var/log/kern.log文件。日志信息具体如何分发,可以在rsyslogd的配置文件(/etc/rsyslog.conf)中设置。

wKioL1PYwTqiW6_WAACFFTzoM9E430.jpg

#include <syslog.h>

void openlog(const char *ident, int option, int facility);

    此函数用于改变syslog的默认输出方式,进一步结构化日志内容。

    调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。调用closelog也是可选择的,它只是关闭被用于与syslog守护进程通信的描述符。

1.ident指定的字符串将被添加到日志消息的日期和时间之后,通常被设置为程序的名称。 

2.option参数对后续syslog调用的行为进行配置,它可取下列值的按位或:

#define LOG_PID     0x01  //在日志消息中包含程序PID

#define LOG_CONS    0x02  //如果消息不能记录到日志文件,则打印至终端

#define LOG_ODELAY  0x04  //延迟打开日志功能,知道第一次调用syslog

#define LOG_NDELAY  0x08  //不延迟打开日志功能

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

LOG_AUTH   //security/authorization messages (DEPRECATED Use LOG_AUTHPRIV instead)

LOG_AUTHPRIV //security/authorization messages (private)

LOG_CRON   //clock daemon (cron and at)

LOG_DAEMON  //system daemons without separate facility value

LOG_FTP     //ftp daemon

LOG_KERN    //kernel messages

LOG_LOCAL0 through LOG_LOCAL7   //reserved for local use

LOG_LPR     //line printer subsystem

LOG_MAIL    //mail subsystem

LOG_NEWS    //USENET news subsystem

LOG_SYSLOG  //messages generated internally by syslogd

LOG_USER (default)   //generic user-level messages

LOG_UUCP    //UUCP subsystem 

void closelog(void);

openlog函数用来打开一个到系统日志记录程序的连接,打开之后就可以用syslog或vsyslog函数向系统日志里添加信息了。closelog函数就是用来关闭此连接的。 

void syslog(int priority, const char *format, ...);

应用程序使用syslog函数与rsyslogd守护进程通信。

priority(级别)参数是设施值(facility)与日志级别的按位或。设施值(facility)的默认值是LOG_USER

日志级别有如下几个:

#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 //调试

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

int setlogmask(int maskpri);

进程间关系

linux下每个进程都隶属于一个进程组,因此除了有PID信息外,还有进程组ID(PGID)。

使用如下函数获取指定进程的PGID:

pid_t getpgid(pid_t pid);

每个进程组都有一个首领进程,其PGID和PID相同。进程组将一直存在,直到其中所有进程都退出,或者加入到其他进程组。

使用如下函数设置PGID:

int setpgid(pid_t pid, pid_t pgid);

一个进程只能设置自己或者其子进程的PGID。

会话

一些有关联的进程组将形成一个会话(session)。

下面的函数用于创建一个会话:

pid_t setsid(void);

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

1、调用进程成为会话的首领,此时该进程是新会话的唯一成员。

2、新建一个进程组,其PGID就是调用进程的PID,调用进程成为该组的首领。

3、调用进程将甩开终端(如果有的话)。

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

pid_t getsid(pid_t pid);

用户信息

UID、EUID、GID、EGID

用户信息对于服务器程序的安全性来说是很重要的。下面这一组函数可以获取和设置当前进程的真实用户ID、有效用户ID、真实组ID、有效组ID。

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上运行的程序都会受到资源限制的影响。可通过如下一对函数读取和设置:

#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;

    rlim_t rlim_max;

}

rlim_t是一个整数类型,它描述资源级别。rlim_cur成员指定资源的软限制,rlim_max成员指定资源的硬限制。

wKiom1PZ9g_B40LcAAMQPqkgNu4853.jpg

改变工作目录和根目录

有些服务器程序需要改变工作目录和根目录。

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

#include <unistd.h>

char* getcwd(char *buf, size_t size);

int chdir(const char *path);

改变进程根目录的函数:

int chroot(const char *path);

chroot并不改变进程的当前工作目录,所以调用chroot之后,仍需要使用chdir("/")将工作目录切换至新的根目录。改变进程的根目录之后,程序可能无法访问类似/dev的文件(和目录),因为这些文件并非处于新的根目录之下。不过好在调用chroot之后,进程原先打开的文件描述符依然生效。

服务器程序后台化

bool daemonize()

{

    int fd;

    pid_t sid;


    //创建子进程,关闭父进程,这样可以使程序在后台运行

    pid_t pid = fork();

    if(pid < 0)

    {

        return false;

    }else if(pid > 0)

    {

        exit(0);

    }

    //将标准输入、标准输出和标准错误输出都定向到/dev/null文件

    fd = open("/dev/null", O_RDWR);

    if (fd >= 0) 

    {

        if (fd != STDIN_FILENO)

    dup2(fd, STDIN_FILENO);

        if (fd != STDOUT_FILENO)

    dup2(fd, STDOUT_FILENO);

        if (fd != STDERR_FILENO)

    dup2(fd, STDERR_FILENO);

if (fd > STDERR_FILENO)

    close(fd);

    }

    

    umask(022); //设置文件权限掩码

    chdir("/"); //切换工作目录


    //创建新的会话,设置本进程为进程组的首领

    sid = setsid();

    if(sid < 0)

    {

        return false;

    }

    return true;

}

linux提供了完成同样功能的库函数:

int daemon(int nochdir, int noclose);

你可能感兴趣的:(linux,server)