当前位置: 首页 > news >正文

Linux系统编程-会话、守护进程与系统日志

目录

一. 会话session

1.1 进程组

1.1.1 进程组

1.1.2 getpgid()

1.2 会话

1.2.1 概念

1.2.2 getsid()

1.2.3 创建会话

1、注意事项:

2、setsid()函数:

二. 守护进程daemon

2.1 概念

2.2 守护进程的创建

2.3 用到的函数

2.3.1 chdir

2.3.2 umask

2.4 示例代码1

2.5 示例代码2

三. 系统日志

3.1 系统日志

3.2 三个函数

3.2.1 openlog

3.2.2 syslog

3.2.3 closelog

3.3 修改后的示例代码2

一. 会话session

1.1 进程组

1.1.1 进程组

1、进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。eg:当父进程fork出子进程时,这个子进程就和父进程属于同一进程组,他们的进程组ID(PGID)相同,都是父进程的PID,父进程就是组长进程。

当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID

示例代码:

int main() { pid_t pid; pid = fork(); if(pid > 0){ wait(NULL); } else if(pid == 0){ while(1){ printf("===child===\n"); sleep(1); } } exit(0); }

结果:可以看到,子进程与父进程同属于一个进程组

2、在waitpid函数kill函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理

waitpid函数中第一个参数传0,代表等待与调用者同属于同一进程组的任何进程。

kill函数中第一个参数传0,代表发生信号给与调用者同属于同一进程组的所有进程

同时,可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。

3、组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

4、进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。

一个进程可以为自己或子进程设置进程组ID

1.1.2 getpgid()

作用:获取指定进程的进程组ID,即PGID

参数:pid:指定进程,若传入0,则返回调用者的进程组ID

返回值:成功返回进程组ID,失败返回-1并且设置errno

1.2 会话

1.2.1 概念

会话:多个进程组的集合

1.2.2 getsid()

作用:返回指定进程的会话ID

参数:pid:指定进程 如果pid传入0,则返回调用者的会话ID(SID)

返回值:成功返回会话ID 失败返回-1且设置errno

1.2.3 创建会话

1、注意事项:

创建一个会话需要注意以下6点注意事项:

  1. 调用进程不能是进程组组长,该进程变成新会话首进程(会长)(session header)
  2. 该进程成为一个新进程组的组长进程
  3. 需有root权限 (ubuntu不需要)
  4. 新会话丢弃原有的控制终端,该会话没有控制终端,不可与用户交互,后台执行。
  5. 该调用进程是组长进程,则出错返回
  6. 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
  7. 组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。

2、setsid()函数:

作用:setsid() 函数会在调用进程不是进程组组长的情况下创建一个新的会话。调用进程将成为新

会话的组长(会话ID=进程ID)。该调用进程还会成为该会话中新进程组的组长(进程组ID=

进程ID

参数:

返回值:成功:将返回调用进程的新会话ID 失败返回-1且设置errno

示例代码:

void sys_err(const char *str) { perror(str); exit(1); } int main() { pid_t pid; pid = fork(); if(pid < 0){ sys_err("fork()"); } else if(pid == 0){ /子进程创建会话前打印进程id,组id,会话id printf("child pid is %d\n",getpid()); printf("child gpid is %d\n",getpgid(0)); printf("child sid is %d\n",getsid(0)); sleep(2); pid_t sid = setsid();/子进程非组长,调用setsid成功创建会话,并且 /创建之后子进程成为进程组组长,会话的会长,pid=pgid=sid if(sid < 0){ sys_err("setsid()"); } printf("sid is %d\n",sid); printf("child pid is %d\n",getpid()); printf("child gpid is %d\n",getpgid(0)); printf("child sid is %d\n",getsid(0)); sleep(20);/这段时间可查看进程信息 } exit(0); }

结果:

查看进程信息:

二. 守护进程daemon

2.1 概念

1、Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字(httpd,sshd,vsftpd,nfsd)。

tty:文字终端pts:虚拟终端

2、Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。

3、创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。

4、使用kill命令可以结束它,或者使用脚本文件去管理它

2.2 守护进程的创建

1、创建子进程,父进程退出

所有工作在子进程中进行形式上脱离了控制终端

2、在子进程中创建新会话

setsid()函数

使子进程完全独立出来,脱离控制

3、改变当前目录为根目录,或者家目录也可以

chdir()函数

防止占用可卸载的文件系统

也可以换成其它路径

使用命令pwdx+pid:查看进程运行在哪个路径

4、重设文件权限掩码

umask()函数

防止继承的文件创建屏蔽字拒绝某些权限

增加守护进程灵活性,守护进程有时候需要创建系统日志文件等

5、关闭文件描述符

继承的打开文件不会用到,浪费系统资源,无法卸载

一般将0,1,2号文件描述符重定向到/dev/null这个空洞文件

6、开始执行守护进程核心工作守护进程退出处理程序模型

注意:由于守护进程不受用户的登录注销影响,最后要结束守护进程只能采用kill命令将其结束

2.3 用到的函数

2.3.1 chdir

作用:改变调用进程的工作路径

参数:指定的工作路径

返回值:成功返回0 失败返回-1且设置errno

2.3.2 umask

作用:改变当前进程的掩码

参数:mask要修改的值,一个八进制数

返回值:此函数总是成功

注意:系统默认:创建文件最大默认权限:0666,创建目录最大默认权限:0777

2.4 示例代码1

int main() { pid_t pid; int ret; pid = fork(); if(pid < 0){ sys_err("fork()"); } else if(pid > 0){/父进程退出 exit(0); } else{ pid_t sid = setsid();/子进程创建会话,脱离终端 if(sid < 0){ sys_err("setsid()"); } ret = chdir("/");/改变工作目录,防止占用可卸载的文件系统 if(ret < 0){ sys_err("chdir()"); } umask(0022);/防止继承父进程的掩码创建的文件权限过紧或者过松 /守护进程一般要创建日志文件 int fd = open("/dev/null", O_RDWR);/空洞文件 if(fd < 0){ sys_err("open()"); } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2);/将文件描述符0,1,2重定向,守护进程脱离终端 if(fd > 2){/防止fd原本就是0,1,2中的其中一个 close(fd); } while(1);/守护进程逻辑 } exit(0); }

结果:

2.5 示例代码2

static int my_daemon() { pid_t pid; pid = fork(); if(pid < 0){ perror("fork()"); return -1; } else if(pid > 0){ exit(0); } else{ pid_t sid = setsid(); if(sid < 0){ perror("setsid()"); return -1; } chdir("/"); umask(0022); int fd = open("/dev/null", O_RDWR); if(fd < 0){ perror("open()"); return -1; } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if(fd > 2){ close(fd); } return 0; } } int main() { int ret = my_daemon(); if(ret == -1){ exit(1); } FILE *fp = fopen("/tmp/out", "w"); if(fp == NULL){ perror("fopen()"); exit(1); } for(int i = 0;;i++){ fprintf(fp,"%d\n",i); fflush(fp);/文件为全缓冲,需要强制刷新 sleep(1); } exit(0); }

结果:

三. 系统日志

3.1 系统日志

1、在示例代码2中,创建了守护进程之后,往/tmp/out文件写东西来模拟守护进程的逻辑,但是也可能出现打开这个文件失败的情况,打开失败后,使用perror来在终端上进行报错打印,但是终端已经被重定向,这时,就需要向系统日志中来写报错信息。程序 / 服务器脱离终端,必须靠日志输出信息

2、注意:只有syslogd服务才有权限写系统日志(为了权限分割,不可能每个程序都有权限写系统日志)

3、系统日志在/var/log下,全部是系统日志,主日志文件:messages,ubuntu是在syslog中,可以使用命令tail /var/log/syslog来查看日志文件

3.2 三个函数

3.2.1 openlog

作用:用于为程序打开与系统日志记录器的连接

参数:

ident--字段

程序名

NULL

由 ident 指向的字符串会被添加到每条消息的开头,并且通常会被设置为程序名。如果 ident 为 NULL,则使用程序名
option--选项LOG_PID日志中带上pid
facility--来源

LOG_USER

LOG_DAEMON

用户进程

守护进程

返回值:

3.2.2 syslog

作用:syslog() 会生成一条日志消息,该消息将由 syslogd 进行分发

参数:

priority--级别

LOG_EMERG

LOG_ALERT

LOG_CRIT

LOG_ERR

LOG_WARNING

LOG_NOTICE

LOG_INFO

LOG_DEBUG

崩溃

需立即处理

严重错误

错误

警告

注意

普通信息

调试信息

format-

format:内容,类似printf,注意不用加\n等来控制格式,格式是由syslogd服务来控制的,只传入要提交的字符即可

返回值:

3.2.3 closelog

作用:关闭用于向系统日志写入数据的文件描述符。

参数:

返回值:

3.3 修改后的示例代码2

#define FNAME "/tmp/out" static int my_daemon() { pid_t pid; pid = fork(); if(pid < 0){ return -1; } else if(pid > 0){ exit(0); } else{ pid_t sid = setsid(); if(sid < 0){ return -1; } chdir("/"); umask(0022); int fd = open("/dev/null", O_RDWR); if(fd < 0){ return -1; } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if(fd > 2){ close(fd); } return 0; } } int main() { openlog("my_daemon", LOG_PID, LOG_DAEMON); int ret = my_daemon(); if(ret == -1){ syslog(LOG_ERR,"my_daemon() failed!");/报错,写在系统日志中 exit(1); } syslog(LOG_INFO, "my_daemon() successded!"); FILE *fp = fopen(FNAME, "w"); if(fp == NULL){ syslog(LOG_ERR, "fopen:%s",strerror(errno)); exit(1); } syslog(LOG_INFO, "%s was opened",FNAME); for(int i = 0;;i++){ fprintf(fp,"%d\n",i); fflush(fp); sleep(1); } fclose(fp); closelog(); exit(0); }

结果:

使用命令tail /var/log/syslog查看日志文件:

http://www.gsyq.cn/news/1486742.html

相关文章:

  • 2026深圳选店不迷茫!全品类黄金回收排行干货一次性看懂 - 奢侈品回收测评
  • 嵌入式UART转USB HID鼠标实现:基于NXP FRDM-KE15Z的协议桥接方案
  • 2026 内蒙古文旅市场合规旅行社榜单发布:图腾国际蝉联综合实力榜首 - 互联网科技品牌测评
  • 西安企业大模型可见度诊断服务科普:3 分钟看懂 AI 时代企业增长新密码
  • 如何在Android手机上实现专业FT8通信?FT8CN完整配置指南
  • Java招聘需求不断拔高,普通程序员如何破局?
  • MCX W72 BLE功耗优化:Buck与Bypass模式实测对比与选型指南
  • 重庆黄金回收市场深度解读:五大维度与便民服务全透视 - 余生黄金回收
  • 传世无双金装裁决·2026年6月最新官网下载地址,新手 1-70 级全阶开荒实操与避坑指南
  • 企业陪跑咨询值得关注的专业机构盘点:2026年纺织服装转型辅导指南 - 远大方略管理咨询
  • WebGL 数字孪生项目开发
  • 如何彻底解决GitHub下载慢的问题:Fast-GitHub浏览器插件终极指南
  • MPC56x Nexus调试接口硬件设计:连接器选型、信号完整性与实战指南
  • 英语阅读_In the digital age
  • 从贴标精度到售后响应:上海阿依重新定义自动流水线贴标机优质厂家 - 品牌推荐大师
  • 终极怪物猎人世界插件:HunterPie让你的狩猎效率提升300%
  • Web3安全实践
  • 用PLD/FPGA替代EEPROM实现MPC8260硬件配置字加载
  • 从PWM到DAC:在8位MCU上精准生成DTMF信号的底层原理与工程实践
  • 西安24小时灭鼠一般多少钱?2026家庭/仓库/城中村灭鼠费用明细 - GrowthUME
  • 《水浒传》108将关系可视化+自然语言问答实战包(Neo4j+LTP+Flask)
  • 徕芬高速吹风机vs康夫实测对比,真实参数选购指南 - 资讯快报
  • Python-100-Days:18万Star的Python系统学习路线
  • 文安县胡宇塑料制品:唐山粉碎料回收选哪家 - LYL仔仔
  • 达州市人口相关数据分析与应用
  • NXP Kinetis KE15Z到KE17Z MCU迁移实战:引脚、外设与中断向量表调整详解
  • 系统设计 - 设计 AI Agent 记忆系统(Memory System)
  • Schema标记在GEO优化中的实战应用
  • 2026 年长春财税公司实力榜单:全省覆盖,一站式工商财税解决方案 - 速递信息
  • 【字节跳动】FAISS索引增量更新/持久化、DIN完整离线训练数据集逻辑、Flink多流拼接、天盾全链路风控流水线、NCode协议二进制封包、GR3底层驱动帧格式、全局限流熔断、日志隐秘埋点、内存镜像