父子进程之间的关系:
区别:
1.fork()函数的返回值不同
父进程中:>0 返回的子进程的ID
子进程中:=0
2.pcb中的一些数据
当前的进程的id pid
当前的进程的父进程的id ppid信号集
共同点:
某些状态下:子进程刚被创建出来,还没有执行任何的写数据的操作
-用户区的数据
-文件描述符表
父子进程对变量是不是共享的?
-刚开始的时候,是一样的,共享的。如果修改了数据,不共享了。
-读时共享(子进程被创建,两个进程没有做任何的写的操作),写时拷贝
GDB多进程调试
设置调试父进程或者子进程
show follow-fork-mode
set follow-fork-mode child
设置调试模式
show detach-on-fork
set detach-on-fork [on|off]
默认为on,表示调试当前进程的时候,其它的进程继续运行,如果为off,调试当前进程的时候,其它进程被GDB挂起
查看调试的进程 info inferiors
切换当前调试的进程 inferior id
使进程脱离GDB调试 detach inferiors id
exec函数族
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。
exec函数族的函数执行成功后不会返回,调用失败了,会返回-1
进程控制
进程退出
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
孤儿进程
父进程运行结束,但子进程还在运行(未运行结束)
每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init , 而 init进程会循环地 wait () 它的已经退出的子进程。这样, 当一个孤儿进程凄凉地结束了其生命周期的时候, init 进程就会代表党和政府出面处理它的一切善后工作。
孤儿进程没有什么危害
僵尸进程
每个进程结束之后,都会释放自己地址空间中的用户区数据,内核区的PCB没有办法自己释放掉,需要父进程去释放。
进程终止时,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
僵尸进程不能被kill -9杀死
这样就会导致一个问题,如果父进程不调用wait()或waitpid()的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的, 如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程,此即为僵尸进程的危害,应当避免。
父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程
wait()函数会阻塞
waitpid()可以设置不阻塞,waitpid()还可以指定等待哪个子进程结束
注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环
进程间通信 IPC(Inter Processes Communication)
不同进程之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源
管道
管道其实是一个在内核内存中维护的缓冲期,这个缓冲期的存储能力是有限的,不同的操作系统大小不一定相同
管道拥有文件的特质:读操作、写操作,匿名管道没有文件实体,有名管道有文件实体,但不存储数据。可以按照操作文件的方式对管道进行操作。
匿名管道只能在具有公共祖先的进程之间使用
有名管道(FIFO)
不同于匿名管道之处在于它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO相互通信,因此,通过FIFO 不相关的进程也能交换数据。
内存映射 Memory-mappend I/O
将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件
信号
信号是Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断.它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
前31个信号为常规信号,其余为实时信号
编号 | 信号名称 | 对应事件 | 默认动作 |
2 | SIGINT | <Ctrl+C>用户终端向正在运行中的由该终端启动的程序发出此信号 | 终止进程 |
3 | SIGQUIT | <Ctrl+\>用户终端向正在运行中的由该终端启动的程序发出信号 | 终止进程 |
6 | SIGABRT | 调用abort函数时产生该信号 | 终止进程并产生core文件 |
9 | SIGKILL | 无条件终止进程。该信号不能被忽略,处理和阻塞 | 终止进程,可以杀死任何进程 |
11 | SIGSEGV | 指示进程进行了无效内存访问(段错误) | 终止进程并产生core |
13 | SIGPIPE | Broken pipe向一个没有读端的管道写数据 | 终止进程 |
17 | SIGCHILD | 子进程结束时,父进程会收到这个信号 | 忽略这个信号 |
18 | SIGCONT | 如果进程已停止,则使其继续运行 | 继续/忽略 |
19 | SIGSTOP | 停止进程的执行,信号不能被忽略,处理和阻塞 | 为终止进程 |
共享内存
共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种IPC机制无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。
守护进程
终端
在UNIX系统中,用户通过终端登录系统后得到一个shell进程,这个终端成为shell进程的控制终端(Controlling Terminal) ,进程中,控制终端是保存在PCB中的信息,而fork()会复制 PCB中的信息,因此由shell 进程启动的其它进程的控制终端也是这个终端。
进程组
进程组和会话在进程之间形成了一种两级层次关系∶进程组是一组相关进程的集合,会话是一组相关进程组的集合。进程组合会话是为支持shell 作业控制而定义的抽象概念,用户通过shell能够交互式地在前台或后台运行命令。
会话
会话是一组进程组的集合。会话首进程是创建该新会话的进程,其进程ID会成为会话ID。新进程慧继承其父进程的会话ID
一个会话中的所有进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时被建立。一个终端最多可能会成为一个会话的控制终端。
守护进程
守护进程(Daemon Process),也就是通常说的 Daemon进程(精灵进程),是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d 结尾的名字。