7.1 日志

Linux提供一个守护进程来处理系统日志:syslogd,默认配置文件为/etc/syslog.conf。这个服务下面有一系列子服务,如:mail,cron。当其他的程序或服务需要记录日志的时候,就可以直接调用这些子服务将日志记录到指定的地方。

目前很多linux系统使用syslogd的升级版:rsyslogd代替syslogd,其默认配置文件是/etc/rsyslog.conf。rsyslog守护进程既能接受用户进程输出的日志,又能接受内核日志:

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

(2)内核日志在老的系统上通过另外一个守护进程rklog来管理的,rsyslogd利用额外的模块实现了相同的功能。

下面是Linux系统日志体系图:

用户编写的代码可以通过syslog()的接口进行生成系统日志,syslog()将日志输出到/dev/log文件中,syslog()会通过socket将log文件发送给syslogd,syslogd在获取到log后,会对log进行处理,然后根据配置文件/etc/rsyslog.conf,将log保存到本地或者发送到其他服务器上。内核日志有printk()打印至内核换装缓存中,换装缓存的内容直接映射到/proc/kmsg文件中,rsyslogd通过读取该文件获得内核日志。

7.2 syslog函数

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

#include <syslog.h>void syslog(int priority, const char* message, ...);
参数解释:
priority:日志级别,按位或,默认值为LOG_USER,取值如上表。
message:日志内容

打开日志的函数openlog:

#include <syslog.h>
void openlog(const char* ident, int logopt, int facility);
参数解释:
ident:所表示的字符串将固定地加在每行日志的前面以标识这个日志,通常就写成当前程序的名称以作标记。
logopt:LOG_CONS        直接写入系统控制台,如果有一个错误,同时发送到系统日志记录。LOG_NDELAY    立即打开连接(通常,打开连接时记录的第一条消息)。LOG_NOWAIT   不要等待子进程,因为其有可能在记录消息的时候就被创建了(GNU C库不创建子进程,所以该选项在Linux上没有影响。)LOG_ODELAY    延迟连接的打开直到syslog函数调用。(这是默认情况下,需要没被指定的情况下。)LOG_PERROR   (不在SUSv3情况下)同时输出到stderr(标准错误文件)。LOG_PID          包括每个消息的PID。
facility:指定记录消息程序的类型,如:LOG_FTP ——文件传输协议:ftpd、tftpd LOG_KERN ——内核产生的消息 LOG_LPR ——系统打印机缓冲池:lpr、lpd LOG_MAIL ——电子邮件系统 LOG_NEWS ——网络新闻系统 

关闭日志的函数closelog:

#include <syslog.h>
void closelog();
#include <syslog.h>int main(int argc, char **argv) {openlog("zooyo", LOG_CONS | LOG_PID, 0);syslog(LOG_INFO,"This is a syslog test message generated by program %sn",argv[0]);closelog();return 0;
}运行一次程序将向/var/log/message文件添加一行信息如下:
gei zooyo[4554]: This is a syslog test message generated by program ./test1n

7.3 UID、EUID、GID、EGID

UID(user id):创建进程的用户信息

EUID(effective userid):创建进程的用户对进程所属可执行文件的操作权限信息,还包括是否有使用kill系统调用发送软中断信息到Linux内核结束进程的权限

GID:创建进程的用户所属群组的信息

EGID:用于标识进程目前所属用户组。和GID不同,因为进程执行时所属用户组可能改变

下面测试进程的UID和EUID的区别:

#include <unistd.h>
#include <stdio.h>int main()
{uid_t uid = getuid();uid_t euid = geteuid();printf("uid = %d, eid = %d\n", uid, euid);return 0;
}
输出:
uid = 1000, eid = 1000
内核主要是根据euid和egid来确定进程对资源的访问权限。一个进程如果没有SUID或SGID位,则euid=uid egid=gid,分别是运行这个程序的用户的uid和gid。
例如kevin用户的uid和gid分别为204和202,kevin运行myfile
程序形成的进程的euid=uid=204,egid=gid=202,内核根据这些值来判断进程对资源访问的限制

7.4 进程间关系

7.4.1 进程组

Linux下每个进程都隶属于一个进程组,因此每个进程除了具有PID信息外,还有进程组ID,即PID。进程组是一个或多个进程的集合。他们之间相互关联。进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID) 。这个过程有点类似于 QQ 群,组相当于 QQ 群,各个进程相当于各个好友,把各个好友都拉入这个 QQ 群里,主要是方便管理,特别是通知某些事时,只要在群里吼一声,所有人都收到,简单粗暴。但是,这个进程组号和 QQ 群号是有点区别的,默认的情况下,当前的进程号会当做当前的进程组号。通过如下函数获取指定进程的PGID。

#include <unistd.h>
//获取进程ID,成功时返回进程pid,失败时返回-1并设置errno
pid_t getpid();//获取进程组ID,成功时返回进程pgid,失败时返回-1并设置errno
pid_t getpgid(pid_t pid);

使用示例:

#include<unistd.h>
#include <stdio.h>
main()
{pid_t pid = getpid();printf("pid=%d\n",pid);printf("pgid=%d\n",getpgid(pid));
}
输出:
pid=5160
pgid=5160

下面的函数用来设置进程组ID。该函数具有如下性质:

性质1:一个进程只能为自己或子进程设置进程组ID,不能设置其父进程的进程组ID。

性质2:if(pid == pgid), 由pid指定的进程变成进程组长;即进程pid的进程组ID pgid=pid.

性质3:if(pid==0),将当前进程的pid作为进程组ID.

性质4:if(pgid==0),将pid作为进程组ID.

#include <unistd.h>//成功时返回进程pgid,失败时返回-1并设置errno
int setpgid(pid_t pid, pid_t, pgid);

使用实例:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main()
{pid_t pid;if ((pid = fork()) < 0) {perror("fork");exit(1);} else if (pid == 0) {printf("child process PID is: %d\n", getpid());printf("child process PGID is: %d\n", getpgrp());//设置子进程的进程组ID为自己的PID,同时子进程变成自己进程组的leadersetpgid(pid, pid);sleep(5);printf("child process PGID is: %d\n", getpgrp());exit(0);}return 0;
}
输出:
child process PID is: 5266
child process PGID is: 5265

7.4.2 会话

一些有关联的进程组将形成一个会话(session)。下面的函数用于创建一个会话:

#include <unistd.h>
//成功时返回新的进程组的PGID,失败返回-1并设置errno
pid_t setsid(void);//获取sid
pid_t getsid(pid_t pid);

该函数具有如下性质:

(1)不能由组长进程调用。

(2)创建一个新的会话,并担任该会话组的组长,此时该进程是新会话的唯一成员。

(3)会新建一个进程组,其PGID就是调用该函数的进程的PID,并且调用该函数的进程成为新进程组的组长。

(4)调用该函数的进程将不在受终端控制。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid = fork(); //fork a processif (pid < 0) exit(0); //fork errorif (pid > 0) exit(0); //father process exitprintf("pid = %d\n", getpid());pid_t pgid = setsid();   //creat a new session for a processprintf("pgid = %d\n", pgid);pid_t sid = getsid(pid);printf("sid = %d\n", sid);return 0;
}
输出:
pid = 8013
pgid = 8013
sid = 8013

7.4.3 用ps命令查看进程

执行ps命令可查看进程、进程组和会话之间的关系:

ps -o pid,ppid,pgid,sid,comm | less
PID   PPID   PGID    SID COMMAND
7585   7439   7585   7585 bash
8234   7585   8234   7585 ps
8235   7585   8234   7585 less该命令创建了1个会话,SID是7585,和2个进程组,PGID分别为7585和8234。
bash命令的PID,PGID,SID相同,因此它既是会话首领,也是组7585的首领。
ps命令则是组8234的首领。

7.6 服务器程序后台化

下面的代码是让一个进程以守护进程的方式运行。守护进程的父进程一般都是init进程,因为它真正的父进程在fork出守护进程后就直接退出了,所以守护进程都是孤儿进程,由init接管。

创建守护进程的步骤:

1)fork()创建子进程,父进程exit()退出。

        这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

 2)在子进程中调用 setsid() 函数创建新的会话

在调用了fork()函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

 3)在子进程中调用 chdir() 函数,让根目录 ”/” 成为子进程的工作目录

这一步也是必要的步骤,使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp,改变工作目录的常见函数是chdir。

4)在子进程中调用 umask() 函数,设置进程的文件权限掩码为0

文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。

5)在孙进程中关闭任何不需要的文件描述符

同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。

在上面的第2)步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。

6)守护进程退出处理

当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>#define MAXFILE 65535int main()
{pid_t pc;int i,fd,len;char *buf="this is a dameon /n";len = strlen(buf);pc =fork();     //创建一个进程用来做守护进程if(pc<0){printf("error fork /n");exit(1);}else if(pc>0)exit(0);    //结束父进程setsid();       //使子进程独立1.摆脱原会话控制 2.摆脱原进程组的控制 3.摆脱控制中端的控制chdir("/");     //改变当前工作目录,这也是为了摆脱父进程的影响umask(0);       //重设文件权限掩码for(i=0;i<MAXFILE;i++)  //关闭文件描述符(常说的输入,输出,报错3个文件),//因为守护进程要失去了对所属的控制终端的联系,这三个文件要关闭close(i);while(1){if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND))<0){printf("open file err /n");exit(0);}write(fd,buf,len+1);close(fd);sleep(1);}
}查看调用进程:ps aux | grep test
fei    9401    0.0    0.0    4380    72 ?    Ss    11.52    0:00    ./test
查看守护进程运行目录:pwdx 9401
/

第7章 Linux服务器程序规范相关推荐

  1. 7 linux服务器程序规范

    1. Linux服务器程序一般以后台进程形式运行.后台进程又称守护进程(daemon),它没有控制终端,因而不会意外接收到用户输入.父进程通常为init(PID为1的进程) 2. Linux服务器程序 ...

  2. Linux 服务器程序规范、服务器日志、用户、进程间的关系

    文章目录 服务器程序规范 日志 rsyslogd 守护进程 syslog函数 openlog函数 setlogmask函数 closelog函数 用户 进程间的关系 进程组 会话 系统资源限制 改变工 ...

  3. 《Linux高性能服务器编程》读书笔记:linux服务器程序规范

    服务器程序规范,需要强化和遵守的,作者给列了4条规范: 1.Linux服务器程序一般以后台进程形式运行,也就是以daemon守护进程的方式,守护进程的父进程通常是init进程(PID进程为1),作者在 ...

  4. Linux服务器程序规范化

    Linux日志体系 rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志.用户进程是通过调用syslog函数生成系统日志的.该函数将日志输出到一个UNIX本地域socket类型(AF_ ...

  5. linux rz 上传文件夹_第二章Linux服务器环境搭建之Tomcat安装

    一.下载tomcat安装包 官网下载地址: http://tomcat.apache.org/download-80.cgi 我下载的版本是tomcat 9,具体下载那个版本需要根据自己的需要进行选择 ...

  6. ifconfig 安装_第一章Linux服务器环境搭建之JDK安装

    一.工具准备 xshell Xftp7 二.下载JDK安装包 官网下载地址: https://www.oracle.com/java/technologies/javase/javase-jdk8-d ...

  7. vs生成linux服务器程序,从Visual Studio到Linux上调试C++代码

    从Visual Studio到Linux上调试C++代码 04/30/2015 5 分钟可看完 本文内容 [原文发表时间] 2015/4/29 10:00 PM 正如您可能已经听说的那样,Visual ...

  8. linux服务器程序乱码,Linux安装GBK/GB2312程序显示乱码的五种解决方法

    不少用户在Linux系统中安装GBK或GB2312的时候遇到了乱码问题,这主要是系统默认语言是uft8所导致,对于该问题可用五种方法进行解决,下面小编就给大家介绍下Linux安装GBK或GB2312程 ...

  9. linux服务器程序开发,怎样搭建一个linux开发服务器

    在开发过程中,必然会出现多人同时工作.协着的情况,在嵌入式开发项目中更为平常,这样可以加快项目周期,为产品上市占得时间先机.目前,使用Linux作为开发产品的操作系统情况越来越多,使用越来越广泛.为了 ...

最新文章

  1. IOS Animation-CAShapeLayer、UIBezierPath与Animation的结合
  2. 使用GPUImage实现视频滤镜
  3. wp insert post 插入文章到数据库
  4. 《影视特效镜头跟踪技术精粹(第2版)》——导读
  5. 用VC++6.0 Sockets API实现聊天室程序
  6. java 异常对象_在java中的异常处理中的异常对象是什么
  7. 年终总结 | 从Oracle到MySQL大家最关注的竟然是...
  8. python灰产_Python进行目录的对比
  9. Asp.Net Mvc4分页,扩展HtmlHelper类
  10. 微信支付来了,微信App来了,微信能力来了
  11. 结束任务管理器电脑黑屏怎么办
  12. 《管理的实践》读书心得
  13. 《测绘程序设计实习》实验报告(MFC,C++)
  14. 音视频开发1——绘制一张图片
  15. 把一个人的特点写具体作文_五年级下册第五单元同步作文《把一个人的特点写具体》范文4篇...
  16. Android 连续点击屏幕(次数可定,事件可定)后进行操作
  17. PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation 中文翻译
  18. LOL中那些不可错过的精彩对局!!!
  19. 这哥们儿的日志让我的心情好得一塌糊涂(ZZ)
  20. 2020年3月24日360笔试题目

热门文章

  1. html 勾选框点击文字勾选,ztree点击文字勾选checkbox,radio实现方法
  2. stax解析xml_使用StAX部分解析XML文档
  3. 蓝桥-2019-完全二叉树的权值
  4. 抛弃迅雷,Aria2 新手入门
  5. 如何管理3D制图软件停靠窗口?
  6. Decoupling Representation and Classifier for Long-Tailed Recognition论文笔记
  7. 欧洲杯赛场上的方块字
  8. 服务器搭建微信编辑器,ueditor编辑器实现微信上传和图片服务器上传
  9. Flaky Tests 不可靠的测试
  10. Python自动化——pytest常用插件详解