《Linux高性能服务器编程》学习总结(七)——Linux服务器程序规范

第七章      Linux服务器程序规范

  服务器程序除了需要网络通信外,还应该考虑很多其他的细节,而这些细节很多很杂,但又基本是模板式的。1)服务器程序基本都是以后台形式运行的,没有控制终端,不能接受用户输入,其父进程通常是init。2)服务器程序有一套日志系统。3)服务器程序以某个专门的非root身份运行。4)服务器通常是可配置的。5)服务器进程启动时通常会生成一个PID文件以记录后台进程的PID。6)服务器程序同城需要考虑系统资源和限制。

  服务器一般使用syslog函数与rsyslogd守护进程通信,其参数需要指示日志级别和格式化输出,openlog函数可以改变默认输出方式,setlogmask函数可以设置日志掩码,如果日志级别大于掩码的日志信息被忽略。

  一个进程有两个用户ID:UID和EUID,UID一般是进程创建者的ID,而EUID则是进程对文件和资源访问的权限,举个例子:/etc/passwd文件需要root权限才能访问,但是当普通用户想要访问时需要在命令前加上sudo,其原理就是sudo改变了EUID,使其能够访问passwd文件。

  我们使用一个能更改当前进程的运行权限的例子来说明一下,下面的程序可以将以root身份启动的进程变成以普通用户身份运行。

 1 /*************************************************************************
 2     > File Name: 7-2.cpp
 3     > Author: Torrance_ZHANG
 4     > Mail: [email protected]
 5     > Created Time: Fri 02 Feb 2018 07:17:19 PM PST
 6  ************************************************************************/
 7
 8 #include"head.h"
 9 using namespace std;
10
11 static bool switch_to_user(uid_t user_id, gid_t gp_id) {
12     //确保目标用户不是root
13     if((user_id == 0) && (gp_id == 0)) return false;
14     gid_t gid = getgid();
15     uid_t uid = getuid();
16     //确保当前用户是合法用户,或者是root或者已经是目标用户
17     if(((gid != 0) || (uid != 0)) && ((gid != gp_id) || (uid != user_id))) return false;
18     //如果不是root用户则已经是目标用户
19     if(uid != 0) return true;
20     //设置为目标用户
21     if((setgid(gp_id) < 0) || (setuid(user_id) < 0)) return false;
22     return true;
23 }
24
25 int main() {
26     uid_t user_id;
27     gid_t gp_id;
28     user_id = getuid();
29     gp_id = getgid();
30     printf("uid = %d, gid = %d\n", user_id, gp_id);
31     if(switch_to_user(1000, 1000) == false) {
32         perror("switch_to_user");
33         return 1;
34     }
35     user_id = getuid();
36     gp_id = getgid();
37     printf("uid = %d, gid = %d\n", user_id, gp_id);
38 }

 

  root用户的uid和gid都是0,我们成功将其改为了非0的数。

  可以说进程的uid和gid都是其所属用户的相关信息,接下来我们来看一下进程本身都有什么属性信息及进程之间都有什么关系。在Linux下每个进程都隶属于一个进程组,所以进程除了pid信息外还有pgid,而每个进程组都有一个首领进程,其pgid和pid相同,值得一提的是,一个进程只能设置自己和其子进程的pgid,并且当子进程进行exec函数族调用后就不能在父进程中改变其pgid。而多个进程组可以形成一个会话,但是创建会话的进程不能是某个进程组的首领进程,否则将产生错误。对于其余进程创建会话,则有如下效果:1)调用进程成为会话的首领,此时该进程是新会话的唯一成员。2)新建一个进程组,其pgid就是调用进程的pid,调用进程成为该组首领。3)调用进程将甩开终端。

  最后我看一下如何将进程以守护进程的形式运行,代码如下:

 1 /*************************************************************************
 2     > File Name: 7-3.cpp
 3     > Author: Torrance_ZHANG
 4     > Mail: [email protected]
 5     > Created Time: Fri 02 Feb 2018 07:59:21 PM PST
 6  ************************************************************************/
 7
 8 #include"head.h"
 9 using namespace std;
10
11 bool daemonize() {
12     //创建新进程并将父进程退出可以使子进程后台运行
13     pid_t pid = fork();
14     if(pid < 0) return false;
15     else if(pid > 0) exit(0);
16
17     //清空文件掩码,这样新建的文件权限为mode&0777
18     umask(0);
19
20     //创建新会话并设置本进程为进程组首领
21     pid_t sid = setsid();
22     if(sid < 0) return false;
23
24     //改变工作目录
25     if((chdir("/")) < 0) return false;
26
27     //关闭标准设备
28     close(STDIN_FILENO);
29     close(STDOUT_FILENO);
30     close(STDERR_FILENO);
31
32     //将其重定向
33     open("/dev/null", O_RDONLY);
34     open("/dev/null", O_RDWR);
35     open("/dev/null", O_RDWR);
36 }
37
38 int main() {
39     daemonize();
40     while(1) sleep(1);
41 }

  我们发现程序确实以守护进程形式运行,有意思的是,第一次运行时发现其父进程不是我们熟知的托管孤儿进程的init,而是另外一个/sbin/upstart --user,通过查询资料发现这个进程是Ubuntu图形化界面的一个守护进程,完成了init进程的一部分功能,当切换到命令行界面执行程序后,父进程就变成了init。此外,Linux系统还提供了一个daemon函数完成这个功能。

原文地址:https://www.cnblogs.com/Torrance/p/8409112.html

时间: 2024-11-05 21:36:45

《Linux高性能服务器编程》学习总结(七)——Linux服务器程序规范的相关文章

Linux Shell脚本编程学习笔记和实战

http://www.1987.name/141.html shell基础 终端打印.算术运算.常用变量 Linux下搜索指定目录下特定字符串并高亮显示匹配关键词 从键盘或文件中获取标准输入 [read命令] 文件的描述符和重定向 数组.关联数组和别名使用 函数的定义.执行.传参和递归函数 条件测试操作与流程控制语句 获取时间日期格式和延时 [date.sleep命令] 内部字段分隔符IFS和脚本的调试DEBUG 显示.读取或拼接文件内容 [cat命令] 文件查找与打印文件列表 [find命令]

Linux高性能server编程——高级I/O函数

 高级I/O函数 pipe函数 pipe函数用于创建一个管道,实现进程间的通信. #include <unistd.h> int pipe(int pipefd[2]); 通过pipe函数创建的文件描写叙述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写入的数据能够从fd[0]读出,不能反过来.管道内部传输的数据时字节流,和TCP字节流概念同样,但有差别,管道本身拥有一个容量限制,它规定假设应用程序不将数据从管道读走的话,该管道最多能被写入多少字节的数据.管道容量阿东小默认是65

Python编程学习,高效求解素数程序实例

素数是编程中经常需要用到的. 作为学习Python的示例,下面是一个高效求解一个范围内的素数的程序,不需要使用除法或者求模运算. 1 #coding:utf-8 #设置python文件的编码为utf-8,这样就可以写入中文注释 2 def primeRange(n): 3 myArray=[1 for x in range(n+1)] ##列表解析,生成长度为(n+1)的列表,每个数值都为1 4 myArray[0]=0 5 myArray[1]=0 6 startPos=2 7 while s

c++游戏服务器编程学习笔记(一)

c++游戏服务器编程c++运行效率非常高TCP传输控制协议IP网际协议SocketLinux 乌班图开源第三方库BOOST80%游戏服务器端用C++工作量最大的地方是具体的游戏逻辑常见的游戏服务器框架和重点部分的实现IP网际协议详解OSI模型 开放系统的通信交互模型 学术性产物的应用层.表示层.会话层.传输层.网络层.链路层.物理层 IP模型产生于实践用于实践 成为标准 数据进入协议栈的封装以太网封装最小46字节,不足的用0最大1500个字节 IP协议特点:不可靠.无连接不能保证IP的数据报成功

Hasen的linux设备驱动开发学习之旅--linux设备驱动中的并发与竞态

/** * Author:hasen * 参考 :<linux设备驱动开发详解> * 简介:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:linux设备驱动中的并发与竞态 * Date:2014-11-04 */ 1.并发与竞态 并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(软件上的全 局变量,静态变量等)的访问则很容易导致竞态(race conditions). 主要的竞态发生在以下几种情况: (1)对称多处理(SMP)

编程学习的七个阶段

在学习编程的时候,总会遇到这样那样的困难,笔者整理了编程学习各阶段的问题及心态分析.希望对广大编程爱好者有帮助. 第1阶段:机会 你很开心,你一直想学这个新的内容,比如说某编程语言,无论是规划还是环境所迫,总之你终于有这个机会学习了. 第2阶段:没有根据地乐观 谷歌搜索--挑选一两个自己感兴趣的教程,买资料和书,然后开始MOOC(大规模在线开放教程).教程很有趣,甚至还有在线REPL,可以让你玩耍,你觉得你正在进步中.当然也有烦恼--手指还不熟练,但你坚持,不肯放弃,并准备向你的第一个项目进军.

arp学习笔记(linux高性能服务编程)

先看看arp的定义吧 现在linux运行这条命令 tcpdump -i eth0:1 -ent '(dst 192.168.5.190 and src 192.168.5.109)or( dst 192.168.5.109 and src 192.168.5.190)' -x bc:ee:7b:9d:ee:02 > Broadcast, ethertype ARP (0x0806), length 60: Request who-has 192.168.5.190 tell 192.168.5.

【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [实验目的] 1.熟练掌握线程的创建与终止方法: 2.熟练掌握线程间通信同步方法: 3.应用套接字函数完成多线程服务器,实现服务器与客户端的信息交互. [实验内容] 通过一个服务器实现最多5个客户之间的信息群发. 服务器显示客户的登录与退出: 客户连接后首先发送客户名称,之后发送群聊信息: 客户输入bye代表退

Linux高性能server编程——信号及应用

?? 信号 信号是由用户.系统或者进程发送给目标进程的信息.以通知目标进程某个状态的改变或系统异常. Linux信号可由例如以下条件产生: 对于前台进程.用户能够通过输入特殊的终端字符来给它发送信号.比方输入Ctrl+C一般会给进程发送一个终端信号. 2.系统异常 系统状态变化 执行kill命令或调用kill函数 Linux信号概述 发送信号 Linux下,一个进程给其它进程发送信号的API是kill函数.其定义例如以下: #include <sys/types.h> #include <

Linux高性能server编程——I/O复用

?? IO复用 I/O复用使得程序能同一时候监听多个文件描写叙述符.通常网络程序在下列情况下须要使用I/O复用技术: client程序要同一时候处理多个socket client程序要同一时候处理用户输入和网络连接 TCPserver要同一时候处理监听socket和连接socket,这是I/O复用使用最多的场合 server要同一时候处理TCP请求和UDP请求.比方本章将要讨论的会社server server要同一时候监听多个port.或者处理多种服务. I/O复用尽管能同一时候监听多个文件描写