目录
第五章 多进程
(1)程序是包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程。
进程是正在运行的程序的实例。
(2)并行:同一时刻,有多条指令在多个处理器上同时执行。
并发:同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,宏观上同时执行,微观上并不是同时执行,只是把时间片分成若干段,使多个进程快速交替的执行
(3)进程控制块PCB:为了管理进程,内核必须对每个进程所作的事情进行清楚的描述。
Linux的进程控制块是task_struct结构体。
里面主要有:
进程id、状态、可使用资源的上限;
切换时要保存和恢复的CPU寄存器;
虚拟地址空间信息;
终端信息、信号相关信息;
当前工作目录;
umask掩码;
文件描述符表;
用户id、组id、会话和进程组。
(4)在终端输入ulimit -a可以查看资源上限。
可以使用ulimit -s 具体数值进行修改。
top //实时显示进程动态
kill 信号值 PID //杀死进程
kill -l //列出所有信号
kill -9 PIDpid_t getpid(void);//获取当前进程号
pid_t getppid(void);//获取当前进程的父进程号
pid_t getpgid(pid_t pid);//如果传None获取当前进程的进程组id,如果传进程号获取进程号的进程组IDpid_t fork(void); //创建进程 //读时共享 写时拷贝 //会返回两次
//pid_t pid = fork(); //pid > 0时,执行父进程代码,此时的pid为子进程ID;pid = 0时,执行子进程代码;
(5) 进程创建
关于fork()
其实大体来说,我们可以将fork函数分为三步
1.用户态切换位内核态,调用_CREATE函数,也就是进程创建部分
2.调用_CLONE函数,也就是资源拷贝部分
3.进程创建成功,return 0; 失败,return -1:
前2步也就是父进程通过fork函数创建子进程的步骤,在执行完_CLONE函数后,fork函数会有第一次返回,子进程的pid会返回给父进程。要注意的是,在第3步中,fork函数不是由父进程来执行,而是由子进程来执行,当父进程执行完_CLONE函数后,子进程会执行fork函数的剩余部分,执行最后这个语句,fork函数就会有第二次返回,如果成功就返回0,失败就返回-1。
我们就可以总结得出,父子进程都执行fork函数,但执行不同的代码段,获取不同的返回值。所以fork函数的返回值情况如下:
父进程调用fork,返回子线程pid(>0)
子进程调用fork,子进程返回0,调用失败的话就返回-1
这也就说明了fork函数的返回值是2个
这个例子中可以得出,在第一行创建子进程后,父子进程交替进行,根据返回值的不同而判断在执行哪个进程。(子进程从fork()后开始执行)
多进程GDB调试
exec函数族
简单来说就是想实现进程中执行另一个进程替换原进程需要用的函数,arg是命令行执行时的参数列表(若./app txt arg[0]为./app arg[1]为txt,只有在调用错误的时候才有返回值-1.
比如在子进程执行ps aux
execlp的话 第一个参数 路径会自动去环境变量找
execlv:
进程退出
孤儿进程和僵尸进程
孤儿进程:子进程创建后,父进程中止,只剩下子进程,子进程交给init处理,pid为1
僵尸进程:子进程中止后,用户区内存被回收,但是内核区,比如pcb无法被回收,需要父进程处理,父进程不处理的话就会一直占用资源导致下一个进程创建的时候没有可以用的资源,不能被kill -9杀死,只能父进程调用wait或者手动ctrl+c终止;
关于虚拟地址
进程PCB映射到物理内存中是共享的一块区域,PCB,结构体
进程通信
wait()
wait函数: 回收子进程退出资源, 阻塞回收任意一个。
pid_t wait(int *status)
参数:(传出) 回收进程的状态。
返回值:成功: 回收进程的pid
失败: -1, errno
函数作用1: 阻塞等待子进程退出
函数作用2: 清理子进程残留在内核的 pcb 资源
函数作用3: 通过传出参数,得到子进程结束状态
wait的形参是传出参数,对status传出,wait的返回值为中止的子进程的id,对于WIFEXITED等宏,传入status的返回值如果为73(子进程的返回值) 对于WIFSIGNALED宏,返回值为终止信号的编号,例如子进程进行中 ,用kill -9 子id ,会返回9。
wait(NULL)的话,不关心子进程结束原因
waitpid:
指定某一个进程进行回收。可以设置非阻塞(阻塞状态的话需要等子进程结束才进行,非阻塞的话,父进程不用等子进程结束,可以执行)。 waitpid(-1, &status, 0) == wait(&status);
pid_t waitpid(pid_t pid, int *status, int options)
参数:
pid:指定回收某一个子进程pid> 0: 待回收的子进程pid
-1:任意子进程
0:同组的子进程。
status:(传出) 回收进程的状态。
options:WNOHANG 指定回收方式为,非阻塞。
返回值:
> 0 : 表成功回收的子进程 pid
0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。
-1: 失败。errno
进程间通信
进程间通信的常用方式
管道:简单 (共同祖先的进程才能)
信号:开销小
mmap映射:非血缘关系进程间
socket(本地套接字):稳定
管道:
实现原理: 内核借助环形队列机制,使用内核缓冲区实现。
特质; 1. 伪文件
2. 管道中的数据只能一次读取。
3. 数据在管道中,只能单向流动。
局限性:1. 自己写,不能自己读。
2. 数据不可以反复读。
3. 半双工通信。
4. 血缘关系进程间可用。
pipe函数: 创建,并打开管道。int pipe(int fd[2]); fd为传出参数
参数: fd[0]: 读端。
fd[1]: 写端。
返回值: 成功: 0
失败: -1 errno
管道的读写行为:
读管道:
1. 管道有数据,read返回实际读到的字节数。2. 管道无数据: 1)无写端,read返回0 (类似读到文件尾)
2)有写端,read阻塞等待。
写管道:
1. 无读端, 异常终止。 (SIGPIPE导致的)2. 有读端: 1) 管道已满, 阻塞等待
2) 管道未满, 返回写出的字节个数。
exit()
2、
exit()
函数和_exit()
函数的参数
status
是一个整型参数,可以利用这个参数传递 进程结束时的状态,一般来说,exit(0)
表示程序正常退出,exit(1)
或者exit(-1)
表示程序异常退出。标准C里有EXIT_SUCCESS 和 EXIT_FAILURE 两个宏,定义于头文件
stdlib.h
中:#define EXIT_FAILURE 1 /* Failing exit status. */ #define EXIT_SUCCESS 0 /* Successful exit status. */