进程
我们把一个正在运行的程序称作一个进程,即进程是执行中的程序是一个动态过程。操作系统为了管理进程,通过一个结构体task_struct记录进程的信息(进程标识符、优先级、进程状态、程序计数器、程序上下文、信号、打开的文件等等),每一个进程都有一个task_struct结构体变量,称之为PCB(进程控制块)。
1、进程运行状态
进程在其生命周期内可以存在多个状态,主要有:就绪、运行、阻塞状态
就绪态:所有资源准备完成,等待CPU空闲的状态
运行态:在CPU上真正执行的状态
阻塞态:等待某些事件发生,事件并未发生前,不能被CPU调度的状态。
2、进程创建 fork
fork函数原型:pid_t fork(void);
fork函数会新生成一个进程,调用fork函数的进程为父进程,新生成的进程为子进程。
fork函数调用一次,返回两次;在父进程中返回子进程的pid,在子进程中返回0,失败返回-1.
fork函数在生成子进程时用到了写实拷贝技术。
关于执行fork()函数时父子进程间哪些资源被复制?哪些被共享?
fork函数在生成子进程时用到的写实拷贝技术,其中:父进程的数据段、栈、堆由父子进程共享,内核将他们的访问权限改为只读。若父子进程中任何一个试图修改这些区域,则内核只为修改区域那块内存制作一个副本。
僵死进程:子进程先于父进程结束,并且父进程未获取到子进程的退出码(退出状态)。
特点:进程主体空间已经释放,只有PCB还未释放。
解决办法:父进程调用wait()或waitpid()函数来获取子进程的退出码
例:结果为(AAA)
int main()
{
fork()||fork();
printf(“A\n”);
}
例:结果为(AAAAAA)6
int main()
{
int i = 0;
for(;i<2;i++)
{
fork();
printf(“A\n”);
}
}
例:结果为(AAAA AA A A)8
int main()
{
int i = 0;
for(;i<2;i++)
{
fork();
printf(“A”);
}
}
例:结果BAA
int mian()
{
printf(“A”);//A在缓冲区
write(1,”B”,1);//B直接打印在屏幕上,第一个1表示标准输出,第二个1表示只有一个字符。
fork();//A被复制为AA,程序结束后打印AA
}
线程
线程是进程内部的一条执行序列。每一个进程至少有一条线程,成为主线。
Linux系统线程的实现:
Linux实现线程的机制非常独特:从内核的角度来说它并没有线程这个概念。Linux把所有的线程都当作进程来实现,内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反线程仅仅被看成是与其他进程共享某些资源的进程,每个线程都有属于自己的task_struct,所以在内核中它看起来就像是一个普通进程。
线程的实现方式:用户级、内核级、混合模式。
- 用户级:线程的创建、销毁、管理都在用户空间完成,不由内核管理。
- 内核级:线程的创建、销毁、管理都由操作系统内核完成但可以在多处理器上实现并行
- 混合模式:即就是一部分以用户线程创建,一部分由内核创建,是一个一对多的关系。
线程和进程的区别:
- 线程是系统调度的最小单位,进程是资源分配的最小单位。
- 线程创建和管理的代价小。