进程,父子进程

1、进程,线程的概念

进程:

(1)正在运行的一个程序

(2)代表资源的载体(独立的应用程序)

(3)资源的最小单位

(4)每一个进程独立包括虚拟内存,文件描述符资源,信号资源等,不与其他进程共享资源

应用场景:

1、调度第三方程序

2、调度其他程序的时候,我们需要传输一定的资源或者是指令过去给另外一个程序时,我们需要应用进程间的通信

线程:

(1)调度的最小单位(CPU在轮询指令运行的最小单位)

(2)进程下面的一个子级单位(所有的线程都是在进程的基础上运行的),一个进程当中,可以运行多个线程

(3)所有的线程共享进程的所有资源

(4)每一个线程独立一片栈空间(栈空间默认大小8M)

应用场景:基本所有的多任务的开发,优先采用多线程

在lunux下,执行一个程序,就会开启相应的进程

1)查看整个linux系统进程之间关系的命令

gec@ubuntu:~$ pstree

//所有的进程的祖先进程都是init,有操作系统镜像启动文件产生

说明:最开始的系统进程叫systemd

其身份信息在系统启动前就已经存在于系统分区之中,在系统启动时直接复制到内存

 2)查看进程id号  --->  ps -ef (静态)常用

gec@ubuntu:~$ ps -ef

 3)查看进程CPU的占用率  ---> top (动态)

gec@ubuntu:~$ ps -ef

 2、进程的状态

进程是动态的活动的实体,因此会有很多种运行状态:一会儿睡眠、一会儿暂停、一会儿又继续执行。下图给出Linux进程从被创建(生)到被回收(死)的全部状态,以及这些状态发生转换时的条件:

 就绪态: TASK_RUNNING 等待CPU资源,不占用CPU资源,不运行代码

运行态: TASK_RUNNING 占用CPU资源,运行代码(执行态)

暂停态: TASK_STOPPED 占用CPU资源,不运行代码,可以到就绪态

睡眠态: 占用CPU资源,运行代码,可以到就绪态

                TASK_INTERRUPTIBLE 响应信号 ---> 浅度睡眠 pause() --> 一直等待下一信号                 TASK_UNINTERRUPTIBLE 不响应信号 ----> 深度睡眠

僵尸态:EXIT_ZOMBIE 占用CPU资源,不运行代码,不可以到运行态 进程退出的时候,就一定会变成僵尸态

死亡态:EXIT_DEAD 不占用CPU资源,不·运行代码 进程退出的时候,如果有人去帮自己回收资源,那么僵尸态就会变为死亡态

 孤儿进程与僵尸状态

孤儿进程:一般情况下,调用fork()函数创建的子进程, 父进程如果比子进程先退出,那么这个子进程称之为孤儿进程。 那么,祖先进程init就会成为该子进程的父进程,回收该子进程的资源。 (老爸走了 儿子还在 国家收养)

僵尸进程:父进程还存在,但是去做的别的事情了(比如在一个死循环,没有退出), 此时子进程退出之后,就变成了僵尸进程。 (可以用ps -ef 查看,进程的状态栏为defunct,这就是所谓的“僵尸”进程) (老爸还在,儿子在外面浪没人管)

总结: 1、孤儿进程是在失去父进程的时候马上寻找继父, 祖先进程init就会成为该子进程的父进程,回收该子进程的资源。

2、僵尸进程父进程还在,子进程先退出;但是子进程资源没有被回收,危险。

状态总结:

1、所有进程(除了系统初始进程systemd之外),都有一个父进程

2、进程在暂停态时(TASK_STOPPED)收到继续的信号时,是切换到就绪态(TASK_RUNNING ),而不是运行态(TASK_RUNNING)

3、进程不能没有父进程,也不能同时拥有两个父进程

4、祖先进程一定要帮其他的进程回收资源

5、孤儿进程特征就是失去父进程时,会马上寻找继父,而不是等到孤儿进程变成僵尸态(EXIT_ZOMBIE )再找(孤儿进程和僵尸进程的区别)

6、程序的main函数执行return 0就会导致进程的退出,一定会变成僵尸态(EXIT_ZOMBIE )

 3、进程的函数接口

单进程程序  ---》 只能一行行代码去执行

多进程程序  ---》 同时执行两行代码  ---》产生一个子进程,帮自己处理另外一件事情

产生一个子进程  ---》 fork()  ---》 man 2 fork

#include <unistd.h>
pid_t fork(void);
函数作用,创建一个进程,将父进程的资源复制一份,申请一片新的资源给子进程
返回值:成功::(一次调用,两次返回)
        >0:父进程    但是返回的id号表示的是子进程的id号
        ==0:子进程
        失败:-1
说明:如果进程id最大值达到系统进程数的上限,子进程比父进程id数值大(通常情况)
    但是如果进程id达到上限,系统会分配之前分配但是已经退出的进程id给进程
    这样有可能出现子进程id数值比父进程小

在一个进程中查看自己的pid号以及父进程的pid号

#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void); //获取自己的PID号 
pid_t getppid(void); //获取父进程的PID号
返回值:成功:
        getpid  ---->    返回自己的ID号
        getppid    --->    返回父进程的ID号
       失败:不会失败

#include<sys/types.h>
#include<unistd.h>
pid_t vfork(void);
专门为了exec系列函数服务的一个创建进程的函数
函数作用:创建一个自己子进程
特点:
1、子进程与父进程共享内存空间,子进程在调用exec函数与exit函数之前内存空间是共享的
2、一定是子进程先运行,而且是等子进程先结束之后,父进程才开始运行
3、当子进程调用exit之后,父进程才会往下执行
4、引用子进程的时候,最好尽快结束子进程
5、使用vfork创建出来的子进程,一定要使用exec或者exit退出进程,否则导致程序死锁,程序会出现异常

fork()函数在执行的过程中,会将父进程的资源复制一份,放到子进程里面运行,其中会被继承的资源有:

1、父进程的运行用户id跟组id

2、环境变量(库路径,命令路径等等)

3、进程组id跟会话id

4、打开的文件描述符

5、信号响应函数

6、虚拟内存(堆,栈,程序段落等等)

下面属性就是独立的,没有继承的

1、进程id

2、记录锁(文件锁)

3、挂起的信号

4、父子进程运行顺序

情况1、父进程先运行,子进程后运行 注意:只有父进程退出,才会出现命令行,子进程退出是不会出现命令行。

情况2:子进程先运行,父进程后运行

子进程不用睡眠,父进程需要睡眠 -> 父子进程任务不一样

1)父子进程执行顺序随机的。(前提是父子进程之间没有加延时来区分先后)

2)fork()之后的代码,两个进程都会执行。

父进程退出后,子进程会寻找继父帮自己回收资源

5、内存角度分析父子进程

两个陌生的进程之间的资源不共用,在进程一中声明的变量a,是不可以在进程2中使用

但是父子进程是由父子关系的,父子进程中的a的地址都是一样的,但互不影响 结论: 1、父进程在fork()时,会将父进程的空间拷贝一份给子进程

2、父进程与子进程拥有独立的内存空间,互不影响

6、解决僵尸态问题

父进程还在,主动回收资源  ---> wait()  -> man 2 wait
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
参数:status:监听子进程的退出状态
    填NULL,例如:wait(NULL)  -->  代表父进程只回收资源,但是不关心子进程的退出状态
     不填NULL,例如:wait(&x)  -->  代表父进程不仅回收资源,还关系子进程的退出状态,退出状态会保存到x变量中
返回值:成功:回收资源的那个子进程的id号
        失败:-1
注意:wait属于一个阻塞函数,如果子进程没有退出变成僵尸态,那么这个函数就会阻塞,
直到子进程变成僵尸态之后,才会将子进程的资源回收,status还可以保存子进程的退出状态

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int* status,int option)
函数功能:等待回收子进程,就算不在同一个进程组,也可以回收
参数:pid:>0:等待子进程为pid退出(不是一个进程组也没关系)
    wstatus:用来存放子进程当中的返回状态值(exit里面的返回值就会放在这里面)
    options:0则代表正常阻塞等待子进程退出

WEXITSTATUS(wstatus):获取子进程中调用exit中的值是多少(子进程的返回值)

7、测试代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
	//进程的创建
	pid_t id= fork();
	if(id < 0)
	{
		perror("fork fail");
		return -1;
	}
	else if(id > 0)    //父进程
	{
		printf("[%d][%d]\n",getpid(),id);    //父进程id,子进程id
		
		//进程的阻塞等待
		wait(NULL);
	}
	else if(id == 0)    //子进程
	{
		printf("[%d][%d]\n",getpid(),getppid());    //子进程id,父进程id
		
		//进程的退出
		exit(0);
	}
		
	return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值