当前位置: 首页 > 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.cnnetsun.cn/news/2826168.html

相关文章:

  • 小批量PCB避开隐形损耗,精准把控预算
  • 5分钟实现音乐自由:Unlock Music开源工具全场景实战手册
  • 嵌入式低功耗设计:SSARC状态保持与唤醒机制在RT1170中的实践
  • PKHeX.Mobile:跨平台宝可梦存档编辑器完全指南
  • B站弹幕屏蔽词批量管理平台:基于TypeScript的现代化Web应用技术方案
  • 5分钟快速上手VinXiangQi:智能象棋AI连线终极指南
  • 深入解析MPC184数据包描述符:硬件加速加密的软硬件协同设计
  • 从零开始使用AlphaFold3-PyTorch:如何在10分钟内完成蛋白质结构预测
  • 完全指南:5步轻松解锁Office订阅版完整功能
  • 图论如何驱动可持续发展:从地铁图到固废协同处置的建模实践
  • S32G-VNP-RDB3开发板固件烧录全攻略:从QSPI到SJA1110
  • 实战教程:借助 Open Claw + 淘宝商品 API,低成本实现电商自动化监控与智能选品
  • python调用API接口,免费API调取,学习如何调取API接口并反馈你输入的内容
  • 如何免费解锁百度网盘macOS版SVIP功能:3步快速破解指南
  • Kimi 智能助手新手入门与高效使用指南
  • 行为模拟的艺术:如何让爬虫的鼠标轨迹像真人
  • 书匠策AI官网www.shujiangce.com 教你三分钟搞定期刊论文,这工具我后悔没早用!
  • 如何用DeTikZify将科研图表自动转为高质量TikZ代码?完整使用指南
  • 2026 AI 标书工具痛点解决指南:针对 7 大投标难题的最佳工具推荐
  • 如何用d2s-editor轻松编辑暗黑破坏神2存档:从新手到高手的完整指南
  • 2026钦州市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • MPC555中断机制实战:从硬件响应到C语言ISR优化
  • 为什么你的系统需要消息队列?别让“技术膨胀”毁了架构(深度干货)
  • Hadoop环境下可直接运行的网站日志分析实战项目(含源码+部署文档)
  • 安全生产与环保监管可视化管理平台方案
  • 计算机专业期末高分安卓音乐播放器源码包(Android Studio一键运行)
  • PotatoNV vs HCU Client:华为Bootloader解锁技术方案深度评估与实践指南
  • 网络研究观新闻简报第一期
  • MPC555EVB扩展接口HCE、CCE、MAPI-400+100实战解析与设计指南
  • Bugku CTF easy_nbt