进程
进程:进程就是处于执行期的程序。但进程并不局限于一段可执行程序代码,打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间,一个或多个执行线程(简称线程),用来存放全局变量的数据段等都可以是进程,实际上,进程就是正在执行的程序代码的实时结果。
在操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存
内核把进程的列表存放在任务队列(task list)的双向循环链表中,链表中的每一项都是类型为task_struct的结构,称为进程描述符,进程描述符中包括一个进程的所有信息(它打开的文件,地址空间,挂起的信号,进程的状态等)。
进程状态
进程描述符中的state域描述了进程的状态,系统中的每个进程都必然处于五种进程状态中的一种。
- TASK_RUNNING(运行):进程是可执行的。它或者正在执行,或者在运行队列中等待执行。
- TASK_INTERRUPTIBLE(可中断):进程被阻塞,等待某些条件的达成,一旦这些条件达成,内核就会把进程状态设置为运行。处于此状态的进程也会因为接收到信号而被提前唤醒并随时准备投入运行。
- TASK_UNINTERRUPTIBLE(不可中断):这个状态与可中断状态几乎一样,唯一的区别在于这个状态的进程就算接收到信号,也不会被提前唤醒并准备进入运行状态。
- _TASK_TRACED:被其他进程跟踪的进程。例如通过ptrace对调试程序进行跟踪。
- _TASK_STOPPED(停止):进程停止执行。
进程创建 fork()
Linux中通过调用fork()来创建子进程。
pid_t pid=fork(); pid_t本质是个int;
fork()在子进程中返回0,失败返回-1;
fork()特点:调用一次,返回两次;子进程和父进程谁先执行不一定
在调用fork()执行后,可能会产生僵尸进程或孤儿进程
僵尸进程:1.进程结束,但PCB没有被释放
2.子进程结束,父进程未结束,并且父进程未获取子进程的退出状态
解决方案:1.直接结束父进程
2.在父进程中调用wait()函数,wait()会阻塞,直到第一个子进程退出
3.子进程结束时给父进程发送给一个信号,告诉父进程我已结束,父进程接收到信号后再做处理
孤儿进程:父进程结束,但子进程还存在,此时子进程就称为孤儿进程。
系统会将所有的孤儿进程挂载到init下,init进程的pid=1
写实拷贝技术:Linux的fork()使用写实拷贝页实现。写实拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。也就是说,资源的复制只有在需要写入时才进行,在此之前,只是以只读方式共享。
子进程从父进程继承了用户号和用户组号,用户信息,目录信息,环境(表),打开的文件描述符,堆栈,(共享)内存等。