网络编程基础(4):Linux服务器程序框架
包含服务器的重要组成部分介绍,日志系统、守护进程设置、进程间关系等
Linux服务器主体框架
- 网络通信部分
- 以守护进程运行服务器程序
- 日志系统
- 设立单独的运行账号
- 拥有相应的配置文件
日志
相关定义
Linux自身提供一个守护进程来处理系统日志。最初的是syslogd,目前大部分采用的是升级版rsyslogd。
在具体使用时,是调用syslog函数来生成系统日志,该函数将日志输出到一个UNIX本地域socket类型的文件/dev/log中,rsyslogd则监听该文件以获取用户进程的输出。
syslog函数
借助该函数与rsyslogd守护进程通信,其定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <syslog.h> void syslog(int priority, const char* message, ....);
|
若希望更进一步结构化日志内容,可以用openlog函数,进一步设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <syslog.h> void openlog(const char* ident, int logopt, int facility);
|
最后,调用closelog函数,关闭日志功能
1 2
| #include <syslog.h> void closelog();
|
用户信息
一个进程拥有两个用户ID:UID(真实用户)& EUID(有效用户)。有效用户为真实用户提供自身所具有的权限。
需要核心处理的业务逻辑:如何将以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
| static bool switch_to_user(uid_t user_id, gid_t gp_id) { if((user_id == 0) && (gp_id == 0)) { return false; } gid_t gid = getgid(); uid_t uid = getuid(); if(((gid != 0)||(uid != 0)) && ((gid != gp_id) || (uid != user_id))) { return false; } if(uid != 0) { return true; } if((setgid(gp_id) < 0) || (setuid(user_id) < 0)) { return false; } return true; }
|
进程间关系
进程组
Linux下每一个进程都隶属一个进程组,因此他们既有PID信息,还有进程组ID(PGID)
1 2 3 4 5 6 7
| #include <unistd.h> pid_t getpgid(pid_t pid);
int setpgid(pid_t pid, pid_t pgid);
|
会话
一些有关联的进程将形成一个会话。可采用setsid函数创建一个会话。
1 2
| #include <unistd.h> pid_t setsid(void);
|
某一进程创建会话后,成为会话的首领,此时该进程是会话的唯一成员。
新建一个进程组,其PGID就是调用进程的PID,调用进程是该组的首领。
Linux进程并未提供单独的会话ID的概念,它会将会话ID默认为会话首领所在的进程组的PGID。可利用如下函数来读取SID
1 2
| #include <unistd.h> pid_t getsid(pid_t pid);
|
ps命令查看进程关系
1 2 3 4 5 6
| gao@gao-VirtualBox:~/桌面/net_code/第七章$ ps -o pid,ppid,pgid,sid,comm | less PID PPID PGID SID COMMAND 6182 6173 6182 6182 bash 8239 6182 8239 6182 ps 8240 6182 8239 6182 less
|
可以看到,ps和less指令的父进程是6182(bash)。同时ps和less是一组,ps是该组的首领。
服务器程序后台化
即将其设定为守护进程,利用代码讲解。
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
| bool daemonize() { pid_t pid = fork(); if(pid < 0) return false; else if(pid > 0) exit(0); umask(0); pid_t sid = setsid(); if(sid < 0) return false; if((chdir("/")) < 0) return false; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); return true; }
|
Linux中也提供了标准的库函数来实现守护进程。
1 2 3 4 5 6 7
| #include <unistd.h> int daemon(int nochdir, int noclose);
|
这个函数的使用
1 2 3 4 5 6 7 8 9 10 11 12
| #include <unistd.h> #include <stdio.h>
int main() { daemon(0, 0); while(1) { sleep(1000); } return 0; }
|
执行此函数后,其pid为1,形成守护进程。