Linux 服务器程序有一些基本的规范:
- Linux 服务器程序以守护进程的方式运行
- Linux 服务器程序通常有一套日志系统,一般在
var/log
目录下有自己的日志目录- 如果是使用 c++ 的话,那记录日志使用 spdlog 是个很好的选择
- Linux 服务器程序一般会创建一个用户来运行自己的程序
- Linux 服务器程序的配置文件存放在
/etc
目录下 - Linux 服务器程序在启动后会在
/var/run
目录中生成一个 PID 文件,以记录进程 PID - Linux 服务器进程需要考虑系统资源和限制,以预测自身能够承受多大负荷
日志
Linux 系统日志
Linux 提供了 rsyslogd
守护进程来处理系统日志,它既能够接收用户进程输出日志,又可以接收内核日志。
- 用户进程调用
syslog()
函数生成日志,该函数将日志输出到AF_UNIX
类型的文件/dev/log
中,rsyslogd
监听/dev/log
将内存输出至/var/log/
- 进程日志的存放位置由
/etc/rsyslog.d/*.conf
文件指定
- 进程日志的存放位置由
- 内核日志由
printk
等函数打印到内核环形缓存,其内容被映射到/proc/kmsg
,rsyslogd
读取该文件后存储日志- 内核日志被存放于
/var/log/kern.log
- 内核日志被存放于
syslog
1 |
|
priority
指定日志级别,默认值是 LOG_USER
,可选值有:
LOG_EMERG
: 系统不可用LOG_ALERT
:报警,需要立即采取动作LOG_CRIT
:严重情况LOG_ERR
:错误LOG_WARNING
:警告LOG_NOTICE
:通知LOG_INFO
:信息LOG_DEBUG
:调试
openlog()
可以在日志中添加附加信息:
ident
: 指定的字符串将被附加到日志之后,当设置为 NULL,则为程序名称option
: 指定打印行为,比如是否包含 PID,是否延迟打开日志等facility
: 说明输出日志的程序属于哪种类别
setlogmask()
设置日志的打印级别,这样在不用的应用场景可以屏蔽一些冗余输出。
用户信息
UID,EUID,GID,EGID
UID : 运行该进程的用户 ID
EUID : 该程序所有者的真实用户 ID,当该程序权限设置了 set-user-id
标志后,运行该程序的用户就可以以文件所有者的权限来执行
- 比如
su
程序就具有set-user-id
标志,这样才能够修改自身密码
GID(运行该进程的组 ID),EGID(程序所有者的组 ID)同理
1 |
|
切换用户
以下代码将以 root
身份启动的进程切换为以普通用户身份运行:
- 以
root
身份运行该代码即可查看效果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static bool switch_to_user(uid_t user_id, gid_t gp_id) {
//确保目标用户不是 root
if ((user_id == 0) && (gp_id == 0)) {
printf("error:Can't change to root!\n");
return false;
}
uid_t uid = getuid();
//如果当前用户是普通用户,则不用切换
if (uid != 0) {
return true;
}
if ((setgid(gp_id) < 0) || (setuid(user_id) < 0)) {
perror("Change id error:");
return false;
}
return true;
}
int main(int argc, char *argv[]) {
printf("before switch, uid = %d, gid = %d\n", getuid(), getgid());
switch_to_user(1000, 1000);
printf("after switch, uid = %d, gid = %d\n", getuid(), getgid());
return 0;
}
进程关系
进程组
每个进程都归属于一个组,所以进程包含进程 ID(PID)和进程组 ID(PGID)。
当一个进程的 PID 等于 PGID 时,此进程便是该进程组的首领进程。
1 |
|
会话
一些有关联的进程组将形成一个会话(session):
1 |
|
调用此函数后:
- 调用进程成为会话的首领,此时该进程是新会话的唯一成员
- 新建一个进程组,其 PGID 与调用进程的 PID 一致,也就是调用进程也是该组的首领
- 调用进程将脱离终端
可以使用命令 ps -o pid,ppid,pgid,sid,comm | less
来查看几个 ID 的值。
系统资源限制
linux 资源限制使用如下函数读取和设置:
1 |
|
resource
有些重要设置:
RLIMIT_AS
: 虚拟内存总限制,超过将产生ENOMEM
错误RLIMIT_CORE
: 核心转储文件大小限制RLIMIT_CPU
: CPU 时间限制RLIMIT_DATA
: 数据段限制(data,bss,堆)RLIMIT_NOFILE
: 文件描述符数量限制,超过将产生EMFILE
错误RLIMIT_FSIZE
: 文件大小限制,超过将产生EFBIG
错误RLIMIT_NPROC
: 用户能创建的进程数限制,超过将产生EAGAIN
错误RLIMIT_SIGPENDING
: 用户能够挂起的信号数量限制RLIMIT_STACK
: 进程栈内存限制,超过将产生SIGSEGV
信号
改变工作目录和根目录
一些服务程序需要修改工作目录和逻辑根目录:
1 |
|
服务器程序后台化
linux 提供了 daemon
函数完成将当前进程切换为守护进程的方式:
1 |
|
其目的就是为了让当前进程脱离 shell 并且其输出不会出现在终端之上。
下面的代码演示如何将一个进程以守护进程的方式运行:
1 |
|