文章目录
主要内容:
- 进程和进程环境
- 进程控制
- 进程关系
- 守护进程Daemon
进程和进程环境
1. 进程
进程:一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源。
2. 进程启动
- fork()
- exec
- C程序输入
3. 进程终止
进程终止的五种方式:
正常终止:
- main函数的自然返回,return
- 调用exit函数,属于c的函数库
- 调用_exit()函数,属于系统调用
非正常终止
- 调用abort函数,异常程序终止。
- 接收能导致进程终止的信号,如ctrl+c SIGINT终止信号。
3.1 前台进程
4. 进程分类
4.1 后台进程
4.2 守护进程
进程控制
主要内容:
- 进程标识符:getpid,getppid
- 进程创建:fork,vfork,system,exec
- 进程终止: exit , _exit
- 父子进程关系:简单同步wait,waitpid
1. 进程标识符
会话组,进程组,进程的关系:
PID为进程ID;PPID为父进程ID;PGID为进程组ID;SID为会话组ID
进程组的组长ID为此进程组的PGID
会话组的组长ID为该会话组的SID
2. 进程创建fork
fork函数用于创建一个子进程。
fork()后,父子进程分别得到fork()函数返回的一个值。
fork函数执行失败,父进程得到返回值-1
fork函数执行成功,则:
- 在子进程中得到返回值为0
- 父进程中得到返回值为子进程的ID
2.1 文件共享
2.2 fork应用场合
2.3 vfork
2.4 sleep函数
2.5 exec系列函数
exec()并不创建新进程。
作用:根据指定的文件名或目录名找到可执行文件,并用它来取代当前进程的数据段、代码段、堆栈段。
但是新程序的进程还保留了原进程的以下信息:
进程ID 父进程ID 用户ID 组ID 会话ID 工作目录 根目录
函数:
2.6 fork和exec一起使用
使用fork的两种情况:
- 父子进程各自执行不同的代码。
- 进程要执行另一个程序。
linux使用exec函数族的两种情况: - 当进程不能在系统中发挥更多作用时,就可以调用exec函数族中的任意一个函数取代当前进程,完成后续工作。
- 如果一个进程想执行另外一个程序,那么它可以调用fork()函数新建一个进程,然后调用exec函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生一个新进程。
3. 进程终止
exit和_exit 函数都是用来终止进程的,当程序执行到exit或_exit时,进程会无条件的停止剩下的所有操作,清除各种数据结构,并终止本进程的运行。
3.1 exit()函数
exit(0) exit会将缓冲区中数据先输出,再终止进程。
exit 会刷新IO缓冲区。
3.2 _exit()函数
不刷新IO缓冲区,不输出缓冲区的内容。
4. 父子进程关系
父进程在子进程前终止—孤儿进程
子进程在父进程前终止—可能成为僵尸进程
4.1 孤儿进程
概念:父进程终止,子进程未终止,子进程成为孤儿进程。
这时,init进程成为其父进程,便于以后对孤儿进程的资源进行回收。
孤儿进程的父进程ID是1。
4.2 僵尸进程
僵尸进程概念:
父进程不退出子进程退出,此时父进程不会主动回收子进程的资源,子进程成为僵尸进程。
死亡进程与僵尸进程:
- 死亡进程:进程退出,释放所有资源。
- 僵尸进程:进程退出,但没有释放资源。
也就是说:僵尸进程不执行任何任务,但却占用系统资源,僵尸进程会导致系统浪费。
4.3 僵尸进程解决方法
为了避免僵尸进程产生,程序需要在子进程退出时,由父进程进行资源的回收。
方法1:wait waitpid
wait()函数被用来执行等待,直到其子进程终止。也就是说wait()函数可以用于使父进程阻塞,等待子进程退出。一旦子进程退出,则wait()函数立即返回,并获得子进程的退出时的状态值,并回收子进程使用的各种资源,以免子进程成为僵尸进程。
waitpid()函数的优点:
-
如果父进程创建了多个子进程,使用wait函数无法等到某个特定的子进程的完成,只能按顺序等待下一个子进程的终止。而父进程可以用waitpid()回收指定的子进程。
-
如果子进程没有退出,则wait函数总是保持阻塞,使用wait函数只能发现那些已经终止的子进程,而waitpid函数突破了这种限制。
waitpid函数被用来关注子进程的状态是否发生变化。
如果子进程状态变化为子进程退出,则waitpid() 函数可以对子进程的资源进行回收,让子进程资源得以释放。
方法二:两次fork
方法三:signal信号处理
守护进程Daemon
主要内容:
- 登录方式
- 进程组和会话期
- 守护进程
守护进程的概念:
- 守护进程为Linux的后台服务进程(独立于控制终端)。
- 该进程通常周期性的执行某种任务或等待处理某些发生的事件。
- 守护进程声明周期较长,通常在系统启动时开始执行,在系统关闭时终止。
- Linux中很多系统服务都是通过守护进程实现的。
1. 登录方式
2. 进程组和会话组
进程组:
- 一组进程的集合。
- getpgrp() setpgid()函数使用
- 进程组长Id即为进程组的组Id (PGID)
会话组:
- 一组进程组的集合。
- getsid()获得会话组id setsid()创建一个新会话
- 会话组的组长id是会话组id (SID)
2.1 getpgrp / setpgid
获得一个进程的进程组ID(PGID)可以通过getpgrp()函数与getpgid()函数。
设置(修改)进程组ID:
最下面的setpgrp()函数与setpgid()函数功能一致。
2.2 getsid / setsid
setsid() 函数调用成功后,该调用进程成为新的会话组的组长,在会话组中创建新的进程组并担任组长,同时脱离终端控制,运行在后台。该进程成为新会话组和进程组中的唯一进程。
3. 守护进程(Daemon)
3.1守护进程概念
后台执行,没有控制终端或登录Shell的进程
该进程通常周期性的执行某种任务或等待处理某些发生的事件。
其生命周期较长,通常在系统启动时开始,在系统关闭时终止。
Linux中有很多系统服务都是通过守护进程来实现的。
守护进程不受控制终端影响(即使控制终端关闭)
3.2 命令
父进程为init所以 ppid==1
tty为?—表示其为后台进程。
3.3 守护进程的实现
1.创建子进程(子进程不退出,父进程退出)
—子进程成为孤儿进程,此时子进程的父进程变成init进程。
2.在子进程中创建新会话
—使用setsid()函数。该子进程成为新会话组和进程组中的唯一进程,并脱离了终端控制,运行在后台。
3.改变当前工作目录
—使用fork()函数创建的子进程继承了父进程的当前工作目录。系统通常的做法是让根目录成为守护进程的当前工作目录。
—改变当前工作目录的函数时chdir()
4.重设文件权限掩码
—把文件权限掩码umask设置为0,则文件权限为初始权限0666,这样可以增强守护进程的灵活性。
—使用函数umask(),改变文件权限掩码。
5.关闭文件描述符
—新创建的子进程会从父进程处继承一些已经打开的文件描述符。这些描述符可能永远不会被守护进程访问,但他们却占有着一定的资源,所以应当关闭。
getdtablesize()函数的功能可以理解为获取进程打开的文件描述符的最大数量。
例子:
openlog:
syslog:(写系统日志文件)