持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
2.31 说说进程有多少种状态?
参考回答
进程有五种状态:创建、就绪、执行、阻塞、终止。一个进程创建后,被放入队列处于就绪状态,等待操作系统调度执行,执行过程中可能切换到阻塞状态(并发),任务完成后,进程销毁终止。
答案解析
创建状态
一个应用程序从系统上启动,首先就是进入创建状态,需要获取系统资源创建进程管理块(PCB:Process Control Block)完成资源分配。
就绪状态
在创建状态完成之后,进程已经准备好,处于就绪状态,但是还未获得处理器资源,无法运行。
运行状态
获取处理器资源,被系统调度,当具有时间片开始进入运行状态。如果进程的时间片用完了就进入就绪状态。
阻塞状态
在运行状态期间,如果进行了阻塞的操作,如耗时的I/O操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。等待再次获取处理器资源,被系统调度,当具有时间片就进入运行状态。
终止状态
进程结束或者被系统终止,进入终止状态
相互转换如图:
2.32 进程通信中的管道实现原理是什么?
参考回答
操作系统在内核中开辟一块缓冲区(称为管道)用于通信。管道是一种两个进程间进行单向通信的机制。因为这种单向性,管道又称为半双工管道,所以其使用是有一定的局限性的。半双工是指数据只能由一个进程流向另一个进程(一个管道负责读,一个管道负责写);如果是全双工通信,需要建立两个管道。管道分为无名管道和命名管道,无名管道只能用于具有亲缘关系的进程直接的通信(父子进程或者兄弟进程),可以看作一种特殊的文件,管道本质是一种文件;命名管道可以允许无亲缘关系进程间的通信。
管道原型如下:
#include <unistd.h>
int pipe(int fd[2]);
pipe()函数创建的管道处于一个进程中间,因此一个进程在由 pipe()创建管道后,一般再使用fork() 建立一个子进程,然后通过管道实现父子进程间的通信。管道两端可分别用描述字fd[0]以及fd[1]来描述。注意管道的两端的任务是固定的,即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另 一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将发生错误。一般文件的 I/O 函数都可以用于管道,如close()、read()、write()等。
具体步骤如下:
- 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
- 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
- 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
答案解析
给出实现的代码,实现父子进程间的管道通信
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define INPUT 0
#define OUTPUT 1
int main(){
//创建管道
int fd[2];
pipe(fd);
//创建子进程
pid_t pid = fork();
if (pid < 0){
printf("fork error!\n");
exit(-1);
}
else if (pid == 0){//执行子进程
printf("Child process is starting...\n");
//子进程向父进程写数据,关闭管道的读端
close(fd[INPUT]);
write(fd[OUTPUT], "hello douya!", strlen("hello douya!"));
exit(0);
}
else{//执行父进程
printf ("Parent process is starting......\n");
//父进程从管道读取子进程写的数据 ,关闭管道的写端
close(fd[OUTPUT]);
char buf[255];
int output = read(fd[INPUT], buf, sizeof(buf));
printf("%d bytes of data from child process: %s\n", output, buf);
}
return 0;
}