1 - 进程
程序:计算机指令集合,一组静态的指令集,比如写的微信小程序的代码、记事本.exe等
程序在自己地址空间的一次活动,就是进程,更多细节 查看 进程详解
进程简单举例
- 进程: 拥有独立地址空间, 是程序在自己的地址空间的一次执行活动。 比如打开一次记事本,就是一个进程
- 主要目的 :操作系统都是多任务的,一个进程如果失败,最好不要影响其他进程,提高操作系统的强壮性
- 下图 就是 我打开两次记事本,window进程中就会发现存在两个进程,进程pid不相同
进程控制块(PCB)
- 在linux中,PCB就是 task_struct结构体 ,pcb是进程的唯一标识
- 在进程创建时,为该进程生成一个pcb , 进程终止时,回收pcb
- pdb 包含 进程状态 state、进程标识信息(uid,gid)、定时器、用户可见寄存器,控制器,栈指针
- 每个进程都有唯一进程id (pid)
- 进程的状态转换,如下
进程的创建
- 新进程的创建
- 首先在内存中为新进程创建一个task_struct结构,
- 然后 将父进程的task_struct内容复制其中
- 再修改部分数据,分配新的内核堆栈,新的pid,
- 再将task_struct这个node添加到链表中
- 子进程刚开始,内核并没有为他分配物理内存,而是以只读的方式共享父进程内存
- 只有子进程写时,才复制 ,即 copy-on-write
fork函数
- fork 翻译过来,即时派生,分支等意思
- fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,
- 也就是两个进程可以做完全相同的事,
- 但如果初始参数或者传入的变量不同,两个进程也可以做不同的事
- 如果成功调用,则返回值
- 父进程,调用,返回子进程的pid
- 子进程,调用,返回0
- 一般来说,fork之后,父子进程的执行顺序器不确定,看内核调度算法
- 什么时候fork
- 一个父进程,希望子进程同时执行不同代码
- vfork 和fork的不同是, fork是复制一份父进程的内存空间、堆和栈, vfork是共享内存数据
- vfork用时,一般都是紧接着调用exec,所以不会访问父进程数据空间
- 注意 : 结束子进程的调用是exit()而不是return
- return 会把父进程的 栈空间给返回了,导致不可预知错误 (与内核有关)
进程终止
- 正常终止
- 从main返回、等效调用exit
- 调用exit
- exit首先调用各终止处理程序,然后按需多次调用fclose,关闭各个流
- 最后一个线程从启动例程返回
- 最后一个线程调用pthread_exit
- 异常终止
- 调用abort
- 接到一个信号并终止
- 最后一个线程对取消请求做相应
wait和waitpid
- wait用于是父进程阻塞,等待子进程退出
- waitpid有若干选项,比如可以提供一个非阻塞的wait,也能实现和wait相同功能(linux中wait就是这样实现的)
- 下图运行过程,见代码
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t child1,child2,child;
if((child1 = fork()) < 0){
perror("failed in fork 1");
exit(1);
}
if((child2 = fork()) < 0){
perror("failed in fork 2");
exit(1);
}
if(child1 == 0){
//run ls -l
if(child2 == 0){
printf("in grandson\n");
}
else if(execlp("ls", "ls", "-l", NULL) < 0){
perror("child1 execlp");
}
}
else if(child2 == 0){
sleep(5);
exit(0);
}
else{
do{
sleep(1);
printf("child2 not exits\n");
child = waitpid(child2, NULL, WNOHANG);
}while(child == 0);
if(child == child2){
printf("get child2\n");
}
else{
printf("Error occured\n");
}
}
}
后台进程
-
守护进程 daemon , 是linux的后台服务进程,是一个生存周期较长的进程,没有控制终端
-
守护进程创建步骤:
1、创建子进程,父进程退出,子进程被init自动收养;fork exit
2、调用setsid创建新会话,成为新会话的首进程,成为新进程组的组长进程,摆脱父进程继承过来的会话、进程组等;setsid
3、改变当前目录为根目录,保证工作的文件目录不被删除;chdir(“/”)
4、重设文件权限掩码,给子进程更大的权限;umask(0)
5、关闭不用的文件描述符,因为会消耗资源;clos
僵尸进程
- 在进程调用了exit之后,该进程并非马上就消失掉,而是留下了一个成为僵尸进程的数据结构,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间