进程创建与销毁实践指南:广东工业大学实验课程的核心技能
发布时间: 2024-12-06 13:39:16 阅读量: 9 订阅数: 13
Python进程池:高效并行处理的实践指南
![进程创建与销毁实践指南:广东工业大学实验课程的核心技能](https://n.sinaimg.cn/sinakd20221019s/0/w1024h576/20221019/9584-f898aaf31753d569676b41aaf88531d0.jpg)
参考资源链接:[广东工业大学 操作系统四个实验(报告+代码)](https://wenku.csdn.net/doc/6412b6b0be7fbd1778d47a07?spm=1055.2635.3001.10343)
# 1. 进程概念与生命周期管理
## 1.1 进程的定义与特征
进程是操作系统进行资源分配和调度的基本单位,它代表了程序执行时的动态活动。一个进程包括了程序代码、其当前的活动以及分配到的资源集合。进程的五个基本特征是动态性、并发性、独立性、异步性和结构性。
## 1.2 进程的生命周期
进程的生命周期从创建开始,经过就绪、运行、等待(阻塞)、终止等状态。不同操作系统对进程状态的定义可能略有不同,但通常都包括这些核心状态。创建后,进程会进入就绪状态,等待CPU调度;获得CPU后开始运行,之后可能会因为I/O操作或资源请求而进入等待状态;任务完成后进入终止状态。
## 1.3 进程状态管理
操作系统通过进程控制块(PCB)管理进程的状态信息。PCB包括进程标识符、状态、程序计数器、寄存器集合、CPU调度信息、内存管理信息、账户信息和I/O状态信息等。进程状态的转变通常伴随着PCB信息的更新。
为了更好地管理进程,操作系统还提供了一系列的系统调用,如fork()用于创建新的进程,wait()用于等待子进程结束,kill()用于终止进程等。通过这些系统调用,进程可以有效地创建、同步、通信以及被销毁。
# 2. 进程创建技术
## 2.1 进程创建基础
### 2.1.1 进程控制块(PCB)的作用
进程控制块(Process Control Block, PCB)是操作系统中用于存储进程状态信息和管理信息的数据结构。每一个进程在操作系统中都对应一个PCB,它是进程存在的唯一标识。
PCB包含了诸如进程状态、程序计数器、CPU寄存器集合、CPU调度信息、内存管理信息、记账信息以及I/O状态信息等重要数据。这些信息对于操作系统管理进程和调度进程至关重要。
1. **进程状态**:表示进程的当前状态,如就绪、运行、等待、终止等。
2. **程序计数器**:指示了进程将要执行的下一条指令的地址。
3. **CPU寄存器集合**:包含了进程切换时需要保存和恢复的信息。
4. **CPU调度信息**:包括进程优先级、调度队列指针等。
5. **内存管理信息**:涉及程序代码和数据的存储空间分配情况。
6. **记账信息**:记录了进程的使用资源情况,如CPU时间、实际使用时间等。
7. **I/O状态信息**:包括分配给进程的I/O设备列表等。
PCB的管理是操作系统内核管理进程的基础,PCB的组织方式也影响了进程调度的效率。例如,一些现代操作系统可能会使用链表、树或其他数据结构来存储和管理PCB。
### 2.1.2 fork()系统调用机制
在UNIX系统中,`fork()`是一个用于创建新进程的系统调用。当一个进程调用`fork()`后,系统为其创建一个新的进程,这个新的进程是调用进程的副本,称为子进程。而原进程则称为父进程。
`fork()`系统调用的一个重要特点是它会返回两次,一次返回到父进程,返回值为子进程的PID;另一次返回到子进程,返回值为0。父进程可以通过返回值来区分它是在操作子进程还是继续执行自己的代码。
子进程和父进程之间的数据是通过copy-on-write(写时复制)技术实现的。这意味着在子进程创建之初,它的地址空间内容是父进程的完全复制。但当任何一个进程试图写入这些数据时,操作系统才会真正进行数据的复制。
```c
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
// fork失败
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("Hello from the child process!\n");
} else {
// 父进程
printf("Hello from the parent process!\n");
wait(NULL); // 等待子进程结束
}
return 0;
}
```
在上面的代码示例中,`fork()`被调用后,父进程和子进程分别打印一条消息。父进程使用`wait()`函数来等待子进程结束,确保子进程的输出不会因进程结束而丢失。
## 2.2 高级进程创建方法
### 2.2.1 vfork()和exec系列函数
`vfork()`是一种特殊的`fork()`调用,它在创建子进程时不会复制父进程的地址空间,而是让子进程共享父进程的内存。这样做是为了减少内存复制带来的开销,特别是当子进程将执行新的程序映像时。然而,由于`vfork()`共享了父进程的地址空间,因此它必须在子进程调用`exec()`之前结束,否则可能会导致不可预期的结果。
`exec()`系列函数用于在当前进程的上下文中加载并运行一个新的程序。这组函数包括`execl()`, `execle()`, `execlp()`, `execv()`等,它们允许开发者指定新程序的参数和环境。
```c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t pid = vfork();
if (pid == 0) {
// 子进程
// 使用exec调用新的程序
char *args[] = {"/bin/ls", "-l", NULL};
execvp(args[0], args); // 如果execvp返回,说明出错了
perror("execvp failed");
return 1;
} else if (pid > 0) {
// 父进程
wait(NULL); // 等待子进程结束
}
return 0;
}
```
在这个例子中,我们使用`vfork()`创建了一个新的子进程,然后使用`execvp()`在子进程中加载并执行`ls -l`命令,列出当前目录的内容。
### 2.2.2 线程创建与管理
线程是程序执行流的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程创建相比进程创建,具有更低的开销,因为它可以共享进程的地址空间和资源。
在UNIX系统中,线程的创建通常使用POSIX线程库(pthread),其创建函数为`pthread_create()`。
```c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_function(void* arg) {
printf("Hello from a thread!\n");
return NULL;
}
int main() {
pthread_t thread_id;
int res = pthread_create(&thread_id, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
return 1;
}
pthread_join(thread_id, NULL); // 等待线程结束
printf("Thread joined\n");
return 0;
}
```
上述代码创建了一个线程,它将执行`thread_function`函数,这个函数输出一条消息表示线程已启动。之后,主线程等待线程结束,再继续执行。
## 2.3 进程创建实践案例分析
### 2.3.1 多进程程序设计实战
在实际开发中,开发者可能需要编写多进程程序,比如一个需要同时处理多个任务的服务器程序。这里我们可以采用`fork()`或`vfork()`结合`exec()`来实现。
```c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
execlp("ls", "ls", "-l", NULL);
// 如果execlp返回,说明出错了
perror("execlp failed");
return 1;
} else {
// 父进程
wait(NULL); // 等待子进程结束
printf("Child process is done!\n");
}
return 0;
}
```
在这个例子中,我们通过`fork()`创建了一个子进程,子进程接着使用`execlp()`执行`ls`命令,列出当前目录的内容。父进程则通过`wait()`等待子进程结束。
### 2.3.2 进程间通信(IPP)机制应用
进程间通信(Inter-Process Communication, IPC)允许运行在同一个系统上的进程相互通信和交换数据。一些常见的IPC机制包括管道(Pipe)、消息队列、共享内存等。
管道是一种最基本的IPC机制,它允许将一个进程的输出直接作为另一个进程的输入。在UNIX中,可以使用`pipe()`系统调用来创建管道,并使用`write()`和`read()`来进行数据传输。
```c
#include <stdio.h>
#incl
```
0
0