Linux进程_02
3.进程的概念
- 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位。每个进程由程序、数据和一个PCB(进程控制块Process Control Block)组成。
- 一个进程需要时可以创建一个新的进程。被创建的进程称为子进程,创建进程的进程称为父进程。所有的进程都只能通过父进程创建。
- 进程有运行态(获得CPU的进程处于此状态,对应的程序当前在CPU上运行)、 阻塞态(为了等待某个外部事件的发生而暂时无法运行)、就绪态(具备了一切运行需要的条件,但是由于其他进程占用CPU而暂时无法运行)三种状态。
- 进程分为两种类型:
系统进程:操作系统用来管理资源的进程。
用户进程:操作系统可以独立执行的的用户程序段。
3.1进程和程序的区别
- 程序是一个静态的概念,它是完成某个功能的指令集和。
- 进程是一个动态的概念,它是程序的一次运行过程。
3.2进程id的获取
- 进程id是各进程的身份标识,程序一开始运行系统就会自动分配给该进程一个独一无二的id。进程终止后id被系统回收。
- getpid、getppid分别获取当前进程id和当前进程的父进程id。父进程就是创建出此进程的进程。
getpid, getppid - get process identification
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
写代码打印出当前进程id和它的父进程id:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char**argv)
{
pid_t p1 = -1, p2 = -1;
p1 = getpid();
printf("pid = %d.\n", p1);
p2 = getppid();
printf("parent id = %d.\n", p2);
return 0;
}
3.3多进程调度原理
- 操作系统同时运行多个进程。
- 多进程是宏观上的并行和微观上的串行。
- 实际上现代操作系统最小的调度单元是线程而不是进程。
4.创建(子)进程
每一次程序的运行都需要一个进程来执行,而进程都只能通过父进程创建而来,多进程可以实现宏观上的并行。
4.1 fork()创建子进程
#include <unistd.h>
pid_t fork(void);
4.2 fork()的细节
- 进程的创建就好比细胞的分裂生长。如果操作系统需要一个新的进程来运行一个程序,那么操作系统会用一个现有的进程来复制生成一个新进程。老进程叫父进程,复制生成的新进程叫子进程。
- 一个进程调用fork()后,系统先给新的进程分配资源,然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。这就相当于克隆了一个自己。
- fork()函数调用一次会返回2次,返回值等于0的就是子进程,而返回值大于0的就是父进程。一般使用fork()后然后用if判断返回值,返回值大于0时就是父进程,等于0时就是子进程。
- fork()的返回值在子进程中等于0,在父进程中等于本次fork创建的子进程的进程ID。
4.3 fork()实践
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char**argv)
{
pid_t p1 = -1;
p1 = fork(); // 返回2次
if (p1 == 0)
{
printf("子进程,任务a, pid = %d.\n", getpid());
}
if (p1 > 0)
{
// 这里是父进程
printf("父进程, 任务b,pid = %d.\n", getpid());
}
/* test */
printf("test,pid=%d.\n",getpid());
return 0;
}
代码理解:
- 在main()中fork()后,操作系统中就会有两个进程(当前进程和它创建出的子进程),它们都会各自执行上面代码中的test部分(执行顺序随机不一定)。
- 若想限定父进程完成a任务,子进程完成b任务,则可以通过判断fork()的返回值,即p1来限定,如程序中所示。
4.4父进程和子进程
- fork()完成后子进程和父进程就是相互独立的个体了,但是可能有很多相似的地方。
- 父子进程的堆区数据、栈区数据、全局数据等是不共享的,子进程有自己独立的PCB,被内核同等调度。
5.父子进程操作文件
在Linux中,父子进程的文件描述符以及文件偏移量,都是共享的。这是因为每个进程的PCB里存了一个结构体指针,它指向文件描述符的结构体struct file,而结构体struct file里才存了文件的id等。
5.1子进程继承父进程中打开的文件
- 父进程先open打开一个文件得到fd,然后在fork创建子进程。之后在父子进程中各自write向fd中写入内容,测试结论是:接续写。这是因为父子进程之间的文件描述符对应的文件指针是一致的(很像O_APPEND标志后的样子)。
*实际测试时有时候父子进程中只有一个进程成功向文件中写入内容,有点像分别写。但是实际不是,原因是程序较小运行较快,父子进程中的某一个写完文件后关闭了文件导致另一个进程写入失败。
5.2父子进程各自独立打开同一文件
- 父进程自己open打开文件然后写入,子进程自己open打开文件然后写入。结论是:分别写。原因是父子进程分离后才各自打开的文件,这时候这两个进程的PCB已经独立了,文件表也独立了,因此2次读写是完全独立的。
- O_APPEND标志可以把父子进程各自独立打开的fd的文件指针给关联起来,实现分别写(接续)。
5.3总结
- 父子进程间终究多了一些牵绊,父进程在没有fork之前自己做的事情对子进程有很大影响,但是父进程fork之后在自己的if里做的事情就对子进程没有影响了。本质原因就是因为fork内部实际上已经复制父进程的PCB生成了一个新的子进程,并且fork返回时子进程已经完全和父进程脱离并且独立被OS调度执行。
- 子进程最终目的是要独立去运行另外的程序。