除了网络通信外, 服务器程序还必须考虑许多其他细节问题, 零碎, 但基本上都是模板式的.
Linux 服务器程序一般以后台形式运行. 后台程序又称守护进程. 它没有控制终端, 因而也不会意外接受用户输入. 守护进程的父进程一般是 init 进程(pid=1).
Linux 服务器程序通常有一套日志系统, 它至少能输出日志到文件, 有的高级服务器可以输出日志到专门的 UDP 服务器. 大部分后台进程都在 / var/log 下有自己的日志目录.
Linux 服务器程序一般以某个专门的非 root 身份运行. mysqld, httpd, syslogd 等后台进程, 并分别有自己的运行账户 mysql, apache, syslog.'
Linux 服务器通常时可配置的. 服务器程序通常处理很多命令选项, 如果一次运行的选项太多, 则克拉一用配置文件来管理. 绝大多数服务器程序都有配置文件并存放在 / etc 下.
Linux 服务器程序通常在启动时生成一个 PID 文件并存入 / var/run 目录中, 以记录该后台进程的 PID.
Linux 服务器程序通常需要考虑系统资源和限制, 以预测自身能承受多大负荷, 比如进程可用文件描述符总数和内存总量等.
01 日志
1.Linux 系统日志:
Linux 提供一个守护进程来处理系统日志 - syslogd, 升级版 - rsyslogd.
rsyslogd 守护进程可以接收用户进程输出日志, 可以接受内核日志.
用户进程时通过调用 syslog 函数生成系统日志的.
该函数将日志输出到一个 unix 本地域 socket 类型 (AF_UNIX) 的文件 / dev/log 中, rsyslogd 则监听该文件以获取用户进程的输出.
内核日志在以前的系统上时通过另一个守护进程 rklogd 来管理的, rsyslogd 利用额外的模块实现了相同的功能. 内核日志由 printk 等换树打印至内核环状缓存中. 环状缓存的内容直接映射到 / proc/kmsg.
rsyslogd 通过读取该文件获得内核日志, 默认调试信息保存在 / var/log/debug, 普通信息保存至 / var/log/messages, 内核信息:/var/log/kern.log. 配置文件:/etc/rsyslog.conf, 主要设置内核日志输入路径, 是否接受 UDP 日志, 及其监听端口 (默认 514 /etc/services) 是否接受 TCP 日志及其监听端口, 日志文件权限, 包含哪些配置文件.
2.syslog()
应用程序使用 syslog()与守护进程 rsyslogd 通信.
该函数采用可变参数 (第二个参数 message 和第三个参数...) 来结构化输出.
priority: 设施值 (按位异或) 日志级别. 设施值默认: LOG_USER, 下面针对默认设施值, 讨论日志级别.
2.1 下面这个函数可以改变 syslog 的默认输出方式, 进一步结构化日志内容
(1)ident: 指定字符串将被添加到日志消息的日期和时间之后, 通常设为程序的名字.
(2)logopt: 对后续 syslog 调用的行为进行配置, 它可取下列值的按位异或
(3)facility: 用来修改 ysyslog 默认设施值
此外, 日志过滤也很重要, 程序再开发阶段可能需要输出很多调试信息, 而发布之后, 我们又要将这些调试信息关闭, 解决这个问题的方法并不是再程序发布之后, 删除调试代码(日后可能还会用到), 而是缉拿但地设置日志掩码, 使日志级别大于日志掩码的日志被系统忽略.
2.2 下面这个函数用于设置 syslog 的日志掩码.
maskpri: 指定日志掩码值, 该函数始终回成功, 它返回调用进程先前的日志掩码值.
2.3 关闭日志功能:
02 用户信息
1.UID, EUID, GID, EGID
用户信息对于服务器安全很重要, 大多说服务器以 root 启动, 非 root 运行
基础知识:
一个进程拥有两个用户 ID, UID, EUID, EUID 存在的目的是为了方便资源的访问, 它使得运行程序的用户拥有该程序的有效用户权限, 比如, su 用来更改账户信息, 但修改账户时 su 程序的所有者是 root, 在普通用户运行 su 程序时, 其有效用户就是该程序的所有者 root, 有效用户为 root 的进程称为特权进程, EGID 与 EUID 类似, 下面演示 uid, euid 区别:
将生成的可执行文件, 所有者设置为 root, 并设置该文件 set-user-id 标志, 然后运行.
从测试输出结果看, 进程的 uid 是启动程序的用户 id, 而 euid 是 root.
2. 切换用户
03 进程间关系
1. 进程组:
Linux 下每一个进程都属于一个进程组, 因此他们除了 pid 之外, 还有进程组 ID(PGID). 我们用如下函数获取指定进程组 PGID.
成功返回 pid, 失败 - 1, 设置 errno.
如果 pid 与 pgid 相同, 则由 pid 指定的进程别设置为进程组首领: 如果 pid 为 0, 表示当前进程的 PGID 为 pgid; 如果 pgid 为 0, 则使用 pid 作为目标 pgid.setpid 函数成功时返回 0, 失败 - 1, 设置 errno.
一个进程只能设置自己或者其子进程的 PGID. 并且, 当子进程调用 exec 系列函数后, 我们也不能再在父进程中对他设置 PGID.
2. 会话
(1)一些有关联的进程将组成一个会话, 下面的函数用于创建一个会话:
该函数不能由进程组的首领进程调用, 否则将产生一个错误. 对于非首领的进程, 调用该函数不仅创建新会话, 而且有如下额外效果.
调用进程成为会话的首领, 此时该进程时新会话的唯一成员.
新建一个进程组, 其 PGID 就是调用进程的 PID, 调用进程成为该组的首领.
调用进程将甩开终端(如果有)
该函数成功时返回新的进程组 PGID, 失败 - 1, errno.
Linux 进程并未提供所谓会话 ID 的概念, 但 Linux 系统认为它等于会话首领所在的进程组的 PGID,
(2)并提供了如下函数读取 SID
3. 用 ps 命令查看进程关系
执行 ps 命令可查看进程, 进程组和会话之间的关系.
在 bash_shell 下执行 ps 和 less 命令, 所以 ps 和 less 命令的父进程时 bash 命令, 这个可以从 PPID(父进程 PID)一列看出.
这三条命令创建了一个会话 (SID 是 2962) 和两个进程组(PGID:2962, 3102)bash 命令的 PID,PGID 和 SID 都相同, 显然它时会话的首领, 也就是组 2962 的首领. ps 时 3102 的首领,
04 系统资源限制
Linux 上运行的程序都会受到资源限制的影响, 比如物理设备限制(cpu 数量, 内存数量等), 系统策略限制(cup 时间等), 以及具体实现的限制(文件名最大长度)Linux 系统资源限制可以通过如下一对函数来读取和设置:
getrlimit , setrlimit
rlimit 结构体定义如下:
成功返回 0, 失败 - 1, 置 errno
rlim_t 是一个整数类型, 它描述资源级别
rlim_cur 成员指定资源的软限制, 建议性的, 最好不要超越的限制, 如果超越, 系统可能向进程发送信号, 并终止运行, 如果当前进程 CPU 时间超过软限制, 系统将向进程发送 SIGXCPU 信号; 当文件尺寸超过其软限制时, 系统将向进程发送 SIZEXFSZ 信号.
rlim_max 成员指定资源的硬限制. 硬限制一般是软限制的上限, 普通程序可以减小应限制, 而只有以 root 身份运行的程序才能增加硬限制, 此外我们可以使用 ulimit 命令修改当前 shell 环境下的资源限制 (软 / 硬) 这种修改对该 shell 启动的所有后续程序都有效, 我们也可以通过修改配置文件来改变系统软限制和应限制, 而这种修改时永久的.
05 改变工作目录和根目录
有些服务器程序好需要改变工作目录和根目录(web /var/www)
获取当前进程工作目录和改变进程的工作目录的函数:
buf 参数指向的内存用于存储当前工作目录的绝对路径, size 指定其大小
如果当前目录的绝对路径超度 (+1 ('')) 超过了 size, 则 getcwd 返回 NULL,errno:ERANG.
chdir 中 path 指向要切换到的目录. 成功 0, 失败 - 1 置 errno.
改变进程根目录: chroot
chroot 并不改变进程的当前工作目录, 调用 chroot 之后, 仍需要调用 chdir("/")来将工作转至新的工作目录, 之后原来的文件描述符依然生效. 所以可以利用早先打开的文件描述符来访问调用 chroot 之后不能直接访问的文件(目录).
06 服务器程序后台化
最后, 如何在代码中让一个进程以守护进程的防止运行, 守护进程的编写遵循一定的步骤, 下面一个实例.
实际上, linux 提供了完成同样功能的库函数:
nochdir: 传 0 则工作目录将被设置为 "/", 否则继续使用当前工作目录.
noclose: 传 0 标准输入输出, 标准错误输出都被重定向到, dev/null, 否则继续使用原来的设备, 成功 0, 失败 - 1 置 error.
来源: http://server.51cto.com/sOS-579143.htm