LInux4_进程

进程创建,等待,终止

进程:操作系统创建进程,就会有一个PCB的结构来保存相关所有信息,PCB放在内存中,类似链表的形式存储.

PCB(进程)包含的内容:对应的代码和数据位置,进程标识符(pid),进程优先级信息,进程状态信息,内存指针,上线问(CPU各种寄存器),记账信息,其他…

进程的状态:

  • running:在运行中或在运行队列中
  • sleep:在等待时间完成
  • disk sleep:磁盘休眠状态,在这个状态中的进程通常会等待I/O结束
  • stopped:可以通过发送SIGSTOP信号给进程来停止这个进程,这个被暂停的进程可以通过发送SIGCONT信号继续执行
  • dead:这个装填只是一个返回状态,不会再任务列表里看到这个状态.

进程控制分为:

  • 进程创建
  • 进程终止
  • 进程等待
  • 进程程序替换

1.进程创建:

1. fork失败的原因:内存不足&操作系统规定的进程数量上限

fork之前的父进程独立执行,fork之后,父子连个执行流分别执行,从fork之后执行,谁先执行完全由调度决定
fork的返回值:子进程返回0,父进程返回子进程的pid,小于零表示进程创建失败

fork的执行逻辑:

  1. 子进程以父进程为模板(子进程的pcb从父进程拷贝而来,包括代码和数据)
  2. 子进程继承了父进程的PC指针,子进程会从fork范湖IDE位置继续执行
  3. 使用写时拷贝:子进程和父进程公用一份代码,但是各自有一份数据,也就是通常情况下父子共享,父子在不写入时,数据也会共享,当任意一方试图写入,便以写实拷贝的方式各自创建一个副本.
  4. fork之后父子进程执行顺序取决于操作系统调度器

vfork:

vfork和fork的用法大致相同
区别:
1. 保证子进程先执行,在子进程调用exec&_exit之后释放(这个过程将main的栈帧释放,所以当父进程再次调用到vfork之前的变量时候,就会出现段错误),vfork之后的主函数不再是父进程的函数体,已被破坏之后,父进程才可以被调度执行
2. 创建一个子进程,而子进程和父进程共享地址空间,而fork的子进程有独立地址空间
vfork为什么必须调用exit退出?

2. 进程终止

进程终止场景:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
进程正常退出:
- 从main return 0 ,结果正确
- 调用exit(0)
- 调用_exit(0)
ps:exit():终止当前进程,exit(n),n表示进程的退出码(进程的返回值|执行结果),exit调用_exit()
- 执行用户的清理函数
- 刷新缓冲区,关闭流(文件流),_exit并不进行此操作
- 调用退出函数atexit&onexit
结果不正确:
- 从main return !=0 ,结果正确
- 调用exit(!=0)
- 调用_exit(!=0)
异常退出:
Ctrl+c 信号终止
使用echo $ ? 查看上一个进程的运行结果
异常终止:
异常终止之后使用kill命令强行终止进程.

3. 进程等待(wait&waitpid)

作用:父进程通过进程等待拿到子进程的结果,通过进程等待,避免僵尸进程,保证子进程先执行,父进程后执行
(1)wait(阻塞等待)
执行逻辑:
1. 子进程还没有终止,父进程就会阻塞在wait函数中,直到子进程终止,父进程才从wait函数中返回
2. 调用wait函数时,子进程其实已经执行完毕名字进程成为了僵尸进程,wait立刻返回,拿到子进程的退出信息,并且释放子进程的相关资源
3. 调用wait的进程并没有子进程,wait函数返回-1,表示调用失败.
wait函数的返回值调用成功时是等待的子进程的pid.
函数原型:pid_t wait(int *status)
status的值相当于exit(n) n&0x7f之后的值,不为0表示进程异常退出,具体的值表示该进程退出的信号的编号.次第八位表示进程退出码,被那个信号退出
输出型参数,有操作系统填充,获取任意子进程退出状态,不关心则可以设置成NULL
(2)wait_pid(非阻塞等待)
如果要等待的子进程已经执行完了,那么wait_pid就返回子进程的pid同时释放子进程对应的资源.如果还没有执行完,wait_pid就会立刻返回,返回值为0.
函数原型pid_t wait_pid(pid,int &status*status,WNOHANG)

4.进程程序替换:

基本原理:

  • 并没有创建一个新进程(没有生成一个新的pcb)
  • 代码段数据段替换成可执行文件所对应的代码和数据
  • 堆和栈都要重新开始
  • 从要替换的可执行程序的main函数开始

替换函数:

exec函数组无返回值,因为被替换哼了目标程序的代码
l:列表形式出入argv
v:数组形式传入argv
e:使用自定义的环境变量
p:自动在pase中寻找可执行文件
execl,execlp.execle
execv,execvp,execve

popen/system,理解这两个函数和fork的区别

  • fork(),一个程序一旦开始调用fork函数,系统让新的进程与旧进程使用同一份代码,因为他们的程序还是相同的,对于数据段和堆栈段,系统则赋值一份给新的进程,这样父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上网络却已分开,互相之间不会再有影响.也就是说,它们之间不再共享任何数据了.而如果连个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作,现在已经是两个进程了,对于父进程,fork函数返回了子进程的新称号,对于子进程,fork返回0,这样,对于程序,只要判断fork的值就可以知道自己是出于父进程还是子进程.事实上,目前大多数的Linux系统在实现上并美欧做到真正的拷贝.一般的,都是CPU以”页”为单位分配空间的,像Intel的CPU,其一页在通常情况下都是4k字节大小,而无论是数据段还是堆栈段都是由许多”页”构成的.fork函数赋值这两个段,只是”逻辑”上,并非”物理”上,也就是说.实际执行fork函数时,物理空间上两个进程的数据段和堆栈段还是共享的,当有一个进程写入了某个数据是,这时两个进程之间的数据才有了区别,系统就讲有区别的”页”从物理上也分开.系统在空间上的开销就可以达到最小.
  • 对于exec系列函数一个进程一旦调用exec函数,它本身就”死亡”,系统吧代码段替换成新的程序的代码,废弃了原有的数据段和堆栈段,并为新程序分配新的数据段和对斩断,唯一留下的,就是进程号,也就是说,对于程序而亚麻,还是同一个进程,不过已经是另一个程序了.不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到.
  • 对于popen函数,他会通过command参数重新启动shell命令,并建立连接进程间的铜套通信
  • 对于system函数,它也会重启shell命令,当执行完毕之后,程序会继续system下一行代码执行

你可能感兴趣的:(学习,心得,体会,linux)