Linux服务器程序规范包括:
- Linux服务器程序一般以后台进程(守护进程)运行,没有控制终端,因而也不会意外接收到用户输入。守护进程的父进程通常是init进程(PID为1的进程)。
- 日志系统,至少能输出日志到文件,有的高级服务器还能输出日志到专门的UDP服务器。大部分后台进程都在/var/log目录下拥有自己的日志目录。
- Linux服务器程序一般以某个专门的非root身份运行,比如mysqld、httpd、syslogd等后台进程,分别拥有自己的运行账户mysql、apache和syslog。
- Linux服务器程序通常是可配置的。服务器程序通常能处理很多命令行选项,如果一次运行的选项太多,则可以用配置文件来管理。绝大多数服务器程序都有配置文件,并存放在/etc目录下。
- Linux服务器进程通常会在启动的时候生成一个PID文件并存入/var/run目录中,以记录该后台进程的PID。比如syslogd的PID文件是/var/run/syslogd.pid。
- Linux服务器程序通常需要考虑系统资源和限制,以预测自身能承受多大负荷,比如进程可用文件描述符总数和内存总量等。
日志
Linux系统日志
守护进程syslogd,处理系统日志。现在的Linux系统上使用的都是它的升级版——rsyslogd。 rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志。
用户进程是通过调用syslog函数生成系统日志的。该函数将日志输出到一个UNIX本地域socket类型(AF_UNIX)的文件/dev/log中,rsyslogd则监听该文件以获取用户进程的输出。
内核日志由printk等函数打印至内核的环状缓存(ring buffer)中。环状缓存的内容直接映射到/proc/kmsg文件中。rsyslogd则通过读取该文件获得内核日志。
rsyslogd的主配置文件是/etc/rsyslog.conf,其中主要可以设置的项包括:内核日志输入路径,是否接收UDP日志及其监听端口(默认是514,见/etc/services文件),是否接收TCP日志及其监听端口,日志文件的权限,包含哪些子配置文件(比如/etc/rsyslog.d/*.conf)。rsyslogd的子配置文件则指定各类日志的目标存储文件。
Linux的系统日志体系:
syslog函数
应用程序使用syslog函数与rsyslogd守护进程通信。
1 |
|
改变syslog的默认输出方式,进一步结构化日志内容:
1 |
|
日志的过滤,设置日志掩码,使日志级别大于日志掩码的日志信息被系统忽略。
1 |
|
关闭日志功能:
1 |
|
用户信息
UID、EUID、GID和EGID
UID:真实用户ID
EUID:有效用户ID
GID:真实组
EGID:有效组ID
1 |
|
一个进程拥有两个用户ID:UID和EUID。
EUID存在的目的是方便资源访问:它使得运行程序的用户拥有该程序的有效用户的权限。
任何普通用户运行su程序时,其有效用户就是该程序的所有者root。任何运行su程序的普通用户都能够访问/etc/passwd文件。
有效用户为root的进程称为特权进程(privileged processes)。EGID的含义与EUID类似:给运行目标程序的组用户提供有效组的权限。
测试进程的UID和EUID的区别:
1 |
|
编译该文件,将生成的可执行文件(名为test_uid)的所有者设置为root,并设置该文件的set-user-id标志,然后运行该程序以查看UID和EUID。具体操作如下:
1 | sudo chown root:root test_uid#修改目标文件的所有者为root |
从测试程序的输出来看,进程的UID是启动程序的用户的ID,而 EUID则是root账户(文件所有者)的ID。
切换用户
将以root身份启动的进程切换为以一个普通用户身份运行:
1 | static bool switch_to_user( uid_t user_id, gid_t gp_id ) |
进程间关系
进程组
Linux下每个进程都隶属于一个进程组,除了PID信息外,还有进程组ID(PGID)。
1 |
|
每个进程组都有一个首领进程,其PGID和PID相同。进程组将一直存在,直到其中所有进程都退出,或者加入到其他进程组。
1 |
|
如果pid和pgid相同,则由pid指定的进程将被设置为进程组首领;如果pid为0,则表示设置当前进程的PGID为pgid;如果pgid为0,则使用pid作为目标PGID。
一个进程只能设置自己或者其子进程的PGID。并且,当子进程调用exec系列函数后,我们也不能再在父进程中对它设置PGID。
会话
一些有关联的进程组将形成一个会话(session)。
1 |
|
该函数不能由进程组的首领进程调用,否则将产生一个错误。对于非组首领的进程,调用该函数不仅创建新会话,而且有如下额外效果:
- 调用进程成为会话的首领,此时该进程是新会话的唯一成员。
- 新建一个进程组,其PGID就是调用进程的PID,调用进程成为该组的首领。
- 调用进程将甩开终端(如果有的话)。
Linux进程并未提供所谓会话ID(SID)的概念,但Linux系统认为它等于会话首领所在的进程组的PGID,并提供了如下函数来读取SID:
1 |
|
用ps命令查看进程关系
执行ps命令可查看进程、进程组和会话之间的关系:
1 | ps-o pid,ppid,pgid,sid,comm|less |
bash shell下执行ps和less命令,因此以ps和less命令的父进程是bash命令,这可以从PPID(父进程PID)一列看出。这3条命令创建了1个会话(SID是1943)和2个进程组(PGID分别是1943和2298)。bash命令的PID、PGID和SID都相同,很明显它既是会话的首领,也是组1943的首领。ps命令则是组2298的首领,因为其PID也是2298。
进程间关系:
系统资源限制
读取和设置Linux系统资源限制的函数:
1 |
|
改变工作目录和根目录
一般来说,Web服务器的逻辑根目录并非文件系统的根目录“/”,而是站点的根目录(对于Linux的Web服务来说,该目录一般是/var/www/)。
获取进程当前工作目录的改变进程工作目录的函数:
1 |
|
chroot并不改变进程的当前工作目录,所以调用chroot之后,仍然需要使用chdir(“/”)来将工作目录切换至新的根目录。
改变进程的根目录之后,程序可能无法访问类似/dev的文件(和目录),因为这些文件(和目录)并非处于新的根目录之下。不过好在调用chroot之后,进程原先打开的文件描述符依然生效,所以可以利用这些早先打开的文件描述符,来访问调用chroot之后不能直接访问的文件(和目录),尤其是一些日志文件。此外,只有特权进程才能改变根目录。
服务器程序后台化
在代码中让一个服务器进程以守护进程的方式运行:
1 | bool daemonize() |
实际上,Linux提供了完成同样功能的库函数:
1 |
|