Socket编程(四)

Linux服务器程序规范

Posted by Gavin on November 27, 2019

雪里开花却是迟

何如独占上春时

服务器程序规范

  • Linux服务器程序一般以后台进程(daemon)形式运行,没有控制终端,因而没有意外输入,其父进程一般是init进程
  • Linux服务器程序通常有一套日志系统,至少输出日志文件,或者传输到专门日志服务器
  • Linux服务器程序一般以专门的非root身份运行
  • Linux服务器程序通常是可配置的
  • Linux服务器程序通常在启动时生成一个PID文件并存入/var/run目录中,记录该后台进程PID
  • Linux服务器程序要考虑系统资源和限制

日志

Linux提供一个守护进程来处理系统日志——rsyslogd,此进程既能接受用户进程输出的日志,又能接受内核日志,通过系统调用syslog函数生成系统日志,该函数将日志输出到一个UNIX本地socket(AF_UNIX)的文件/dev/log中,rsyslogd则监听该文件获取用户进程输出。内核日志是通过另一个进程rklogd来管理,内核日志由printk等函数打印到内核的环状缓存(ring buffer)中,环状缓存的内容直接映射到/proc/kmsg中,rsyslogd通过读取该文件获得内核日志

应用程序使用syslog函数和rsyslogd通信:

#include <syslog.h>
void syslog(int priority, const char *message, ...)

用户信息

用户信息对服务器程序来说至关重要,比如大部分服务器程序要以root身份启动,但不能以root身份运行,下面是一组获取和设置当前进程真实用户(UID)、有效用户(EUID)、真实组(GID)、有效组(EGID)的函数

#include <sys/types.h>
#include <unistd.h>
uid_t getuid();
uid_t geteuid();
gid_t getgid();
gid_t getegid();
int setuid(uid_t);
int seteuid(uid_t);
int setgid(gid_t);
int setegid(gid_t);

PS:su命令的原理就在于UID、EUID并行,用户执行su命令时,其有效用户就是就是root

进程间关系

  • 进程组

    Linux下每个进程都隶属于一个进程组,因此它们除了PID信息外,还有进程组ID(PGID),相关操作如下:

      #include <unistd.h>
      pid_t getpgid(pid_t pid)
      int setpgid(pid_t pid, pid_t pgid)
    

    每个进程组都有一个首领进程,其PGID和PID相同,进程组将一直存在,直到其中所有进程都退出或者加入其他进程组。一个进程只能设置自己或者自己子进程的PGID,并且当子进程调用exec系列函数后,我们也不能再在父进程中对它设置PGID

  • 会话

      #include <unistd.h>
      pid_t setsid(void)
      pid_t getsid(pid_t pid)
    

    一些有关联的进程组会形成一个会话,创建会话不能由进程组首领进程进行,对于非首领进程创建会话会有如下额外效果:

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

服务器程序后台化

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

//创建子进程,关闭父进程
pid_t pid = fork();
//设置文件权限
umask(0);
//创建新的会话,设置本进程为进程组首领
pid_t sid = setsid();
//切换工作目录
chdir("/");
//关闭标准输入、输出、错误输出
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//重定向输出
open("/dev/null",O_RDONLY);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);

完成相同功能的Linux库函数:

#include <unistd.h>
int daemon(int nochdir, int noclose)