当前课程知识点:基于Linux的C++ >  第十三讲 进程编程 >  13.06 进程管理(三) >  LinuxCPP1306

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1306在线视频

LinuxCPP1306

下一节:LinuxCPP1307

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1306课程教案、知识点、字幕

我们刚才讲了 你fork()一个子进程

然后子进程就结束了

你没等它清除 没等它

那么那个子进程

不就变成了僵尸进程了吗

可是你能在父进程里边调用wait()等它吗

当然可以这么做 但是你要注意

如果你把wait()写在那个父进程里

那么在子进程没有结束的时候

父进程不就被阻塞了嘛

它自己的事情不就做不了了吗

所以 一个更好的策略

是子进程结束的时候

触发一个终止信号

然后父进程接收到这个信号

处理这个子进程的清除工作

所以最合适的方案

就是子进程的异步清除

有一个信号叫SIGCHLD

当子进程终止的时候

它会向父进程自动地发送这个信号

你不用管 它自动就会发

那么我们的父进程就可以

针对这个子进程写一个信号处理例程

完成子进程的最后清除工作

这个清除是属于异步清除

我们并不需要等待它

你父进程自己做自己的事

子进程结束了 我就响应这个事

去清除它 就完了

就不需要等待 等待那就是同步了

我们不需要等待 这就叫异步清除

看实际的程序代码

我们会定义一个信号处理例程函数CleanUp()

带着信号数

那个信号那个值作为它的参数

返回值是void

CleanUp()

当然依然是C函数

封装在extern “C”里

它要做的事情很简单

核心代码就一个 wait()

等待子进程结束 负责清除它

然后把子进程的状态返回出去

保存起来 就OK了

父进程要处理的就是SIGCHLD这个信号

我们要定义

struct sigaction结构体的一个对象sa

这样的一个变量sa被我们定义出来

然后我们memset() 全清零

然后设sa_handler这个字段

为CleanUp()这个函数的入口地址

我就把子进程发送SIGCHLD这个信号之后

我们父进程要做的事

它的信号活动给设置好了

接下来一步就是sigaction()函数调用

把sa这个信号活动和SIGCHLD这个信号

给挂接在一起

我就把它注册进去了

现在一旦父进程接受到子进程

发送过来的SIGCHLD的信号

它就会自动地调用CleanUp()这个函数

去做清除工作

你在主程序里就不用调用wait()去等待它了

你的主程序的正常代码

你就可以朝下写了

父进程也就可以正常往下执行了

什么时候那个信号来

什么时候去处理那个信号嘛

不就完了吗

这就叫子进程的异步清除

最后一类进程叫守护进程

我们前面其实谈到过

daemon它是非常特殊的一个进程

它是在后台默默地做事情

它没有输入 也没有输出

所以创建守护进程的方式

它是非常非常特殊的

它需要几个明确的步骤

而且理论上

大部分步骤实际上应该是顺序的

其中有些步骤可以颠倒 没关系

但是总体上必须按照这样的顺序来

首先你要创建一个新的进程

新的进程将会成为未来的守护进程

那么守护进程的父进程要退出

这样的话 我们能够保证

那个守护进程的父进程的父进程

也就是它的祖父进程

明确地能够确认到父进程已经结束了

且知道守护进程本身

不是那个进程组的组长

只有非组长

它才能够创建新的会话呀 对吧

这个地方也是需要注意的

守护进程这个时候

就会创建一个新的进程组和新的会话

它自己就升格了

就变成了新的这个进程组的组长

和会话的首领 创建的新会话

因为还没有来得及

和我们控制终端相关联

所以它是没有输入输出的

然后我们要改变工作目录

因为守护进程将来在后台做事情的时候

往往它可能会随系统一样启动

那么在这种情况下

工作目录本身不应该继续使用

创建这个守护进程的那个进程的工作目录

那个进程可能是另外一个目录

那个目录在文件系统中

甚至有可能被管理员给卸载掉

所以你的守护进程随系统启动的时候

可能没用它

用它就可能会导致问题

所以往往我们会改变它的工作目录

一般就改变到根目录下

或者在根目录的

其它的某个固定的子目录下

保证它不会被卸载的地方

然后我们要重设文件的权限掩码

这也一样的

守护进程很多时候我们不能继承

或者说不需要继承

父进程的文件访问权限可能不够

所以我们可能不使用它的掩码

我们可能创建自己的文件权限掩码

这是一个

还有的时候 权限多了我也不要

所以它是有一个

自己的文件权限掩码要重设

关闭所有的文件描述符

它不需要继承

父进程打开的那些文件描述符

它都不需要

标准流 它都不需要

它没有输入 没有输出

也没有错误流 不要

这三个错误流全都给我定向到哑终端

做完这些事情

就创建了一个守护进程

创建完了 你可以根用户权限去运行它

它就能够在后台默默地替你做事情了

你还可以设定它随系统一起启动

那么每次系统启动

这个守护进程就能够启动了

我们看实际的代码

我们在这里fork()一个新的子进程

看错误处理

if( pid == -1 ) 说明创建子进程失败

我们return -1

否则if( pid != 0 )

这个时候它是在父进程里

我们父进程直接退出

接下来就是pid为0的时候

那就全是子进程代码

首先创建一个新的会话 setsid()

自动地会创建一个新的进程组

子进程就是会话的首领和进程组的组长

成功了就向下去做

等于等于-1 说明不成功 我们返回

设置工作目录 chdir()

改变它的工作目录

重设它的文件权限掩码

umask()调用

关闭它的文件描述符

这个进程里面

可能会打开好多个文件描述符

理论上都应该一一把它关闭掉

但对我们这个程序来讲呢

因为我们前面没有打开任何文件描述符

它实际上只打开了三个标准流

所以我一个简单for循环

把它前三个文件描述符给它关掉就完了

标准输入流、标准输出流、标准错误流

文件描述符0、1、2

for循环从0到3

i小于3 从0、1、2

三个文件描述符全部关闭 over

关闭标准的文件描述符

三个标准流全关掉

子进程的三个标准流重设到哑终端

open( "/dev/null", O_RDONLY )

把这个哑终端这个设备打开

以只读模式打开

打开它就会创建一个新的文件描述符

注意这一点

它优先选择最小的文件描述符

最小的是哪一个

0号文件描述符

因为我们把三个标准流一关

这个子进程里

事实上什么文件描述符都没用

所以当它打开哑终端的时候

它自动地将使用0号文件描述符

0号文件描述符对应什么

标准输入流

这就意味着我们的标准输入流

被挂到了哑终端上

所有的数据输入都从哑终端来

能来吗 什么都来不了

就关掉了标准输入流

然后调用dup( 0 )

dup()这是一个文件描述符的复制操作

我们后面会讲到这个函数

现在同学们就记得

它就把0号描述符复制一个副本

这个函数很特殊

它这个副本

产生一个新的文件描述放哪呢

它优先选择最小的那个文件描述符

最小的文件描述符是谁

1号文件描述符

也就是把0号文件

描述符复制到1号文件描述符那里面去

1号描述符对应着什么

标准输出流

现在它就把标准输出流

也挂到了哑终端上

也就是所有的输出信息

全都仍到哑终端

那么什么内容都不再显示

再来一遍

它又把标准错误流 2号文件描述符

也挂到哑终端上去

这就意味着3个标准流

都被我们关闭了

全都定向到了哑终端

守护进程都不需要它们

全部都不需要 把它关掉

全都定位到哑终端 over

接下来你的守护进程的实际工作代码

就可以朝下去做了

做完了 当然得return 0

好复杂啊 刚才那几个步骤 对吧

所以Linux操作系统下边

提供了一个函数daemon()

帮助你减轻创建守护进程的负担

调用这一个函数就能搞定

daemon()这个函数超简单

两个参数 都是整型

nochdir(no change directory)、noclose

如果nochdir非0

实际上就是true

就是不改变工作目录

如果noclose非0 就是true

不关闭所有打开的文件描述符

你传进去

它就给你构造一个daemon

一个守护进程出来

一般情况下 我们调用daemon()

创建一个守护进程的时候

这俩参数传的都是0

成功的时候这个函数返回0

失败的时候返回-1

并设置errno的值

你就可以去查那个errno的值到底是什么

就知道它发生了什么错误

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1306笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。