(七)linux服务器程序规范

七 linux服务器程序规范

7.1 日志

linux提供了一个守护进程来处理系统日志–syslogd,不过现在的linux系统使用的都是他的升级版rsyslogd。

rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核的日志。

用户进程通过syslog函数生成系统日志。syslog函数将日志输出到一个UNIX本地域socket类型的文件/dev/log中。rsyslogd则监听该文件来获取用户进程的输出。

内核日志由printk等函数打印至内核的环装缓存中,环装缓存中的内容直接映射到/proc/kmsg文件中。rsyslogd通过读取该文件安获取内核日志。

rsyslogd守护进程在接收到用户进程或者内核输入的日志后,会将他们输出至特定特定的文件中(…),日志信息具体如何分发,可以在rsyslogd的配置文件中设置(etc/rsyslog.conf).

在这里插入图片描述

dmesg函数:可以显示开机信息,kernel 会将开机信息存储在 ring buffer 中。您若是开机时来不及查看信息,可利用 dmesg 来查看。开机信息亦保存在 /var/log 目录中,名称为 dmesg 的文件里。

syslog函数💴应用进程使用syslog函数与rsyslogd守护进程通信。

包含头文件: <syslog.h>

函数原型:

void syslog(int prority, const char* message,...)

priority为所谓的设施值与日志级别的按位或,设施值得默认值为LOG_USER。而日志级别有8类,严重程度逐渐降低。具体查看手册。

函数2: void openlog(const char* ident, int logopt, int facility);

ident👊指定的字符串将被添加到日志消息的日期和时间后,常被设置为程序的名字

logopt🎃对后续的syslog调用的行为进行配置,具体请看书或者查看man手册

facility🐎可用来修改syslog函数的默认设施值

日志的过滤: 调试信息不能简单的删除,因为以后还可能用到,解决这个问题方法是设置掩码,使得日志级别大于掩码的日志直接被忽略:设置日志掩码的函数:

int setlogmask(int maskpri)

maskpri指定日志掩码值,该函数不会失败,返回值为先前设定的日志掩码值

关闭日志功能的函数:

void closelog();

7.2 用户信息

大部分服务器必须以root身份启动,但是不能以root身份运行。一个进程拥有两个用户ID,一个是真实用户,一个是有效用户,有效用户ID的存在是方便资源访问,使得运行程序的用户拥有该程序的有效用户的权限,su程序就是一个例子。对于/etc/passwd的访问,su程序使得该用户拥有了有效用户为root用户的权限,所以可以访问一般用户访问不了的/etc/passwd文件。

static bool switch_to_user(uid_t user_id, gid_t gp_id){
	if((user_id == 0) && (gp_id == 0)){
		return false;//确保目标用户不是root用户,root用户是不能作为目标用户的
	}

	gid_t gid = getgid();
	uid_t uid = getuid();

	//确保当前用户是合法用户:root用户或者目标用户
	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;//转换失败
	reurn true;
}

7.3 进程间关系

首先明确几个进程概念🎲

符号含义
PID进程ID
PGID进程组ID
PPID父进程ID
SID会话ID

进程组

在linux系统中,每个进程除了有PID信息外,还会有一个PGID,如下函数可以获得PGID:

#include<unistd.h>

pid_t getpgid(pid_t pid);

成功📄返回进程组ID;失败🅾️返回-1,并设置errno

首领进程

PGID=PID, 进程组一直存在,直到其中所有进程退出或者加入其他进程组

下列函数可以设置PGID:

#include<unistd.h>

int setpgid(pid_t pid, pid_t pgid);

该函数将PID为pid的进程的PGID设置为pgid。

情况一: pid=pgid, 由pid指定的进程被设置为首领进程;

情况二: pid = 0,表示设置当前进程的PGID为pgid;

情况三: pgid=0,使用pid作为目标PGID。

成功返回0,失败返回-1, 并设置errno

会话

一些有关联的进程组形成一个会话,下列函数用于创建一个会话:

#include<unistd.h>

pid_t = setsid(void);

特别注意 该函数不能由该进程组的首领进程调用,否则会产生错误,对于非组首领的进程,调用该函数会产生额外的效果:

No.1 调用进程会成为会话上的首领,此时该进程是唯一的会话成员

No.2 新建一个进程组,其PGID就是调用进程的PID,

No.3 调用进程将甩开终端

成功💅 返回新进程组的PGID, 失败返回-1并设置errno

Linux并未提供SID的概念,但是认为他等于会话首领所在的进程组的PGID, 以下函数可以读取SID:

#include<unistd.h>

pid_t getsid(pid_t pid);

使用ps命令查看进程、进程组合会话之间的关系

在bash shell中执行ps和less命令,因此ps和less命令的父进程都是bash命令

进程间关系示意图:

在这里插入图片描述

7.4系统资源限制

linux上运行的程序会收到资源程序的限制,比如物理设备的限制,或者实现策略的限制,linux上的资源可以通过以下函数来读取:

#include<sys/resource.h>

int getrlimit(int resource, struct rlimit *rlim);

int setrlimit(int resource, const sruct rlimit *rlim);

其中, rlim参数是rlimit类型的指针。

struct rlimit{
	rlim_t rlim_cur;
	rlim_t rlim_max;
}

rlim_t为一个整数类型,描述资源的级别rlim_cur和rlim_max分别制定资源的软限制和硬限制,进程超过软限制时间,系统会给进程发送相应的信号; 普通程序可以减小硬限制,但是只有root用户可以增加硬限制(可以使用ulimit命令修改当前shell环境下的资源限制,此外修改配置文件也可以)。对于各种资源类型可以查看man手册。

7.5 改变工作目录和根目录🈂️

Web服务器的逻辑根目录并非文件系统的根目录,而是站点的根目录,对于linux的web服务来说,该目录一般是/var/www/

(note) 在linux下有一个目录: /var/www/html ,把文件放到这个目下就会很方便的通过ip访问到, 不过需要启动http服务才行。

获取当前工作目录和改变进程工作目录的函数分别是💎

#include<unistd.h>

char* getcwd(char* buf, size_t size);

int chdir(const char* path);

参数: buf,buf参数指向的内存用于存储进程当前工作目录的绝对路径名,其大小由后边的size参数决定,可能遇到的情况.

情况1. 绝对路径长度+1超过了size, 函数返回NULL, 并设置errno为ERANGE

情况2. buf为NULL, 但是size非0, 则函数使用malloc动态分配内存,并将进程的当前的工作目录存储其中(这就要求使用者手动释放这块内存)

getcwd函数成功是返回一个指向目标存储区(getpwd在内部动态创建的缓存区)的指针,失败返回NULL, 并设置errno.

chdir函数的path参数指定要切换到的目标目录, 成功时返回0, 失败时候返回-1, 并设置errno.

改变进程根目录的函数为chroot

#include<unistd.h>

int chroot(const char* path);

path指定要切换到的目录, 成功是返回0, 失败时返回-1, 并设置errno。值得注意的是, 该函数并不改变进程的当前的工作目录, 也就是说,改变了进程根目录后还需要使用chdir函数改变当前工作目录来切换到此根目录.

使用了chroot目录后, 某些目录和文件就不能访问了,但是在使用chroot函数之前已经打开的文件描述符依旧可以使用, 尤其是一些日志文件. 只有一些特权进程才能改变根目录。

7.6 服务器程序后台化

如何让一个进程以守护进程的方式运行, 守护进程的编写遵循一个步骤,如下:

bool daemonize(){
	//创建子进程,关闭父进程,这样可使子程序在后台运行
	pid_t pid = fork();
	if(pid < 0){
		return false;
	}
	else if(pid > 0){
		exit(0);
	}
	//设置文件权限掩码,当进程创建新文件时,文件的权限为777
	umask(0);

	//创建新的会话,设置本进程为进程组的首领
	pid_t sid = setsid();
	if(sid < 0){
		return false;
	}

	if((chdir("/")) < 0){
		return false;
	}

	//关闭标准输入设备,标准输出设备,标准错误输出设备
	close(STDIN_FILED);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);

	//关闭其他已经打开的文件描述符
	//...... ...... .....
	//将标准输入,标准输出和标准错误输出都重定向到/dev/null文件
	open("/dev/null", O_RDONLY);
	open("/dev/null", O_RDWR);
	open("/dev/null", O_RDWR);
	return true;
}

linux中存在相同功能的库函数

#include<unistd.h>

int daemon(int nochdir, int noclose);

nochdir参数用于指定是否改变工作目录, 传递为0则代表工作目录被设置为根目录. 否则使用当前目录; 当noclose为0时, 标准输入,标准输出,标准错误输出都会被重定向到/dev/null中, 否则使用原来的设备

调用成功🍂返回0;

调用失败返回-1, 并设置为errno.

linux中存在相同功能的库函数

#include<unistd.h>

int daemon(int nochdir, int noclose);

nochdir参数用于指定是否改变工作目录, 传递为0则代表工作目录被设置为根目录. 否则使用当前目录; 当noclose为0时, 标准输入,标准输出,标准错误输出都会被重定向到/dev/null中, 否则使用原来的设备

调用成功🍂返回0;

调用失败返回-1, 并设置为errno.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值