多进程
进程相关的概念
linux提供的进程有关的接口函数
进程间的通信
多线程
线程相关的概念
linux提供的线程有关的接口函数
线程的同步与互斥
线程池(难)
综合运用了结构体,单链表,函数指针,多线程有关的知识点
多进程多线程:都是为了解决多任务的并发(同时)执行,能够实现同时做多件事情
孙悟空--》一个人打不过10万天兵天将
--》解决方法--》拔毛--》变小猴子出来
360安全卫士--》并发执行多个任务
windows系统--》并发执行多个任务
linux系统--》并发执行很多任务
多进程
==============================
1.进程相关的理论概念
什么是进程:动态的,一个正在运行的程序就是一个进程,一旦程序运行起来,操作系统会自动创建一个进程来代表你运行的这个程序
程序:用编译器编译得到的二进制文件,是静态的
linux提供的跟进程有关的shell命令
ps -elf (p --》process进程 s --》state状态)
进程的组成:代码段+数据段+进程控制块(PCB)三部分组成
代码段:用来存放你的程序代码
数据段:用来存放你运行的程序中用到的各种数据
进程控制块(process contrl block):指的是系统中提供的一个结构体,该结构体用来存放跟进程有关的状态信息
切换成超级用户,然后在根目录下查找
find / -name sched.h
/usr/src/linux-headers-4.15.0-142/include/linux/sched.h头文件的第560行定义了如下结构体
struct task_struct //把这个结构体叫做进程控制块
{
pid; //进程的id号,无符号的整数
}
父进程:调用fork的那个进程就是父进程
子进程:fork调用成功,产生的就是子进程
int main()
{
fork(); //产生子进程
}
孤儿进程(爸爸去哪了):父进程优先于子进程退出,导致子进程没有人回收它的资源,此时子进程就变成了孤儿进程
不同版本的Ubuntu针对孤儿进程采取的措施是不同:
有些ubuntu针对孤儿进程,采用阻塞的方式(孤儿进程卡住了)
有些ubuntu针对孤儿进程,让操作系统自己去回收孤儿进程
进程组:多个进程组成的一个集合
2.linux进程有关的接口函数
(1)创建子进程
pid_t fork(void);
返回值(重点):无符号的整数,该函数并不复杂,复杂的是如何理解返回值
> 0 表示父进程,此时返回值表示的是子进程的ID号
==0 表示子进程
< 0 表示fork调用失败
疑问一:两个死循环都没有退出条件,为什么可以同时执行(并发执行)??
fork调用成功,会给子进程分配独立的内存空间(子进程不依赖父进程的地址空间)
父子进程相互独立运行
疑问二:id>0和id==0两个矛盾的条件,竟然都成立--》见到鬼了
fork这个函数很特殊,一次调用,返回两次
id=fork();
子进程的代码如何区分表示呢??
父进程的代码如何区分表示呢??
if(id>0)
{
父进程的代码
}
else if(id==0)
{
子进程的代码
}
(2)进程的退出与回收
进程的退出:就是结束进程
void exit(int status);
参数:status --》进程的退出值
void _exit(int status);
参数:status --》进程的退出值
进程的回收:父进程在退出之前,主动调用函数去回收子进程的资源
为什么要回收子进程:如果你不回收,子进程很有可以变成孤儿进程
#include <sys/wait.h>
pid_t wait(int *stat_loc);
返回值:你回收到的那个子进程的ID号
参数:(重点)stat_loc --》存放进程退出时候的状态信息
状态信息:包含了退出值,还包含了其它信息(子进程是否正常退出,异常退出,是哪个信号搞死它的)
pid_t waitpid(pid_t pid, int *stat_loc, int options);
返回值:你回收到的那个子进程的ID号
参数:pid --》 <0 表示回收进程组ID号是pid绝对值的其中一个进程
比如:waitpid(-2000,) //我要回收进程组ID号是2000的这个组里面其中一个进程
-1 表示回收任意一个进程
比如:waitpid(-1,) //我要回收任意一个进程
0 表示回收本进程组内的任意一个进程
>0 表示我想回收id号是pid的这个进程
比如:waitpid(2000,) //我要回收ID是2000这个进程
stat_loc --》存放进程退出时候的状态信息
options --》WNOHANG 父进程回收子进程的时候,如果子进程还没有来得及退出,那么waitpid不会阻塞,会直接结束
0 阻塞等待,跟wait作用一样
特点:wait/waitpid会阻塞父进程,等待子进程退出,如果子进程一直不退出,wait/waitpid会让你的父进程一直阻塞
(3)获取当前进程的ID号/父进程的ID号
pid_t getpid(void);
返回值: 返回当前进程的ID号 %hu
pid_t getppid(void);
返回值: 返回父进程的ID号 %hu
#include "myhead.h"
//用多进程来模拟360安全卫士
//父进程死循环打印我正在杀毒
//子进程死循环打印我正在清除垃圾
int main() //主函数本身就代表一个进程
{
printf("主函数(父进程运行起来了)!\n");
//创建一个子进程出来--》生个仔
pid_t id=fork();
if(id>0) //父进程
{
while(1)
{
printf("我是父进程,我在杀毒!\n");
sleep(1);
}
}
else if(id==0) //子进程
{
while(1)
{
printf("我是子进程,我在清除垃圾!\n");
sleep(1);
}
}
else
{
printf("fork调用失败!\n");
return -1;
}
}
#include "myhead.h"
//定义全局变量
int globala=666;
int main() //主函数本身就代表一个进程
{
printf("主函数(父进程运行起来了)!\n");
//定义局部变量
int n=123;
//创建一个子进程出来--》生个仔
pid_t id=fork();
if(id>0) //父进程
{
while(1)
{
printf("我是父进程,全局变量的值是:%d\n",globala);
printf("我是父进程,局部变量的值是n:%d id:%lu\n",n++,id);
sleep(1);
}
}
else if(id==0) //子进程
{
//子进程偷偷摸摸修改全局变量的值
globala=888;
while(1)
{
printf("我是子进程,全局变量的值是:%d!\n",globala);
printf("我是子进程,局部变量的值是n:%d id:%lu\n",n,id);
sleep(1);
}
}
else
{
printf("fork调用失败!\n");
return -1;
}
}
#include "myhead.h"
int main()
{
int status;
printf("fork调用之前的代码!\n");
//创建一个子进程
pid_t id=fork();
if(id==0) //子进程
{
printf("子进程运行了!\n");
//while(1)
//{
//}
//结束/退出子进程
exit(3);
}
else if(id>0) //父进程
{
printf("父进程运行了,子进程的ID号是:%hu!\n",id);
}
printf("主函数(父进程)准备退出了.......!\n");
//回收子进程 --》防止子进程变成孤儿(僵尸进程/僵死进程)进程
//pid_t ret=waitpid(id,&status,WNOHANG); //WNOHANG非阻塞等待(等得了就等,等不了就退出)
//pid_t ret=waitpid(id,&status,0); //0表示阻塞等待
//pid_t ret=waitpid(-1,&status,0);
pid_t ret=waitpid(0,&status,0);
printf("我回收的那个子进程ID是:%hu\n",ret);
//判断子进程是不是正常退出的
if(WIFEXITED(status))
printf("你的仔是正常退出的!\n");
else
printf("你的仔是异常退出的!\n");
printf("子进程的退出值:%d\n",WEXITSTATUS(status));
return 0;
}
#include "myhead.h"
int main()
{
int status;
printf("fork调用之前的代码!\n");
//创建一个子进程
pid_t id=fork();
if(id==0) //子进程
{
printf("子进程运行了!\n");
while(1)
{
}
//结束/退出子进程
exit(3);
}
else if(id>0) //父进程
{
printf("父进程运行了,子进程的ID号是:%hu!\n",id);
}
printf("主函数(父进程)准备退出了.......!\n");
//回收子进程 --》防止子进程变成孤儿(僵尸进程/僵死进程)进程
//pid_t ret=wait(NULL);
pid_t ret=wait(&status);
printf("我回收的那个子进程ID是:%hu\n",ret);
//判断子进程是不是正常退出的
if(WIFEXITED(status))
printf("你的仔是正常退出的!\n");
else
printf("你的仔是异常退出的!\n");
printf("子进程的退出值:%d\n",WEXITSTATUS(status));
return 0;
}
#include "myhead.h"
int main()
{
printf("fork调用之前的代码!\n");
//创建一个子进程
pid_t id=fork();
if(id==0) //子进程
{
printf("子进程运行了!\n");
while(1) //目的让子进程不要结束
{
}
//结束/退出子进程
exit(1);
}
else if(id>0) //父进程
{
printf("父进程运行了,子进程的ID号是:%hu!\n",id);
}
printf("主函数(父进程)准备退出了.......!\n");
//回收子进程 --》防止子进程变成孤儿(僵尸进程/僵死进程)进程
pid_t ret=wait(NULL);
printf("我回收的那个子进程ID是:%hu\n",ret);
return 0;
}