Linux 基础入门操作-实验七 进程的介绍
实验七 进程的介绍
7.1 进程基础概念
Linux进程在内存中包含三部分数据:码段、堆栈段和数据段。代码段存放了程序的代码。代码段可以为机器中运行同一程序的数个进程共享。堆栈段存放的是子程序的返回地址、子程序的参数及程序的局部变量。而数据段则存放程序的全局变量、常数以及动态数据分配的数据空间,如用malloc函数申请的内存。与代码段不同,如果系统中同时运行多个相同的程序,它们不能使用同一堆栈段和数据段。
Linux进程主要有如下几种状态:
1用户状态:用户状态下运行的状态;
2内核状态:进程在内核状态下运行的状态;
3内存中就绪:进程没有执行,但处于就绪状态,只要内核调度它,就可以执行;
4内存中睡眠:进程正在睡眠并且处于内存中,没有被交换到SWAP设备:
5就绪且换出:进程处于就绪状态,但是必须把它换入内存,内核才能再次调度它进行运行:
6睡眠且换出:进程正在睡眠,且被换出内存,
7被抢先:进程从内核状态返回用户状态时,内核抢先于它,做了上下文切换,调度了另一个进程,原先这个进程就处于被抢先状态;
8创建状态:进程刚被创建,该进程存在,但既不是就绪状态,也不是睡眠状态,这个状态是除了进程0以外的所有进程的最初状态;
9僵死状态:进程调用exit结束,进程不再存在,但在进程表项中仍有记录,该记录可由父进程收集。
Linux进程状态从创建到消亡的整个过程:
1进程被父进程通过系统调用fork创建而处于创建态
2 fork调用为子进程配置好内核数据结构和子进程私有数据结构后,子进程进入就绪态,或者在内存中就绪,或者因为内存不够而在SWAP设备中就绪。
3若进程在内存中就绪,进程可以被内核调度程序调度到CPU运行。
4内核调度该进程进入内核状态,再由内核状态返回用户状态执行。该进程在用户状态运行一定时间后又会被调度程序所调度而进入内核状态,由此转入就绪态。有时进程在用户状态运行时,也会因为需要内核服务,使用系统调用而进入内核状态,服务完毕,会由内核状态转回用户状态。要注意的是进程在从内核状态向用户状态返回时可能被抢占,这是由于有优先级更高的进程急需使用CPU,不能等到下一次调度时机,从而造成抢占。
5进程执行exit调用,进入僵死状态,最终结束。
进程的作用
进程是操作系统管理计算机资源的基本单位之一,它在多任务处理和资源管理中扮演着重要角色。进程解决了以下几个关键问题:
- 资源管理
进程帮助操作系统有效管理计算机的硬件资源(如CPU、内存、I/O设备等),确保不同进程之间的资源分配合理。通过进程,操作系统能够监控和分配资源,防止资源的浪费和冲突。 - 并发执行
进程允许多个程序同时运行,提高了系统的效率和利用率。在多核CPU和多线程环境下,操作系统能够通过进程管理来实现真正的并行处理,从而加快计算速度。 - 隔离性和安全性
进程提供了隔离性,使得一个进程的执行不会直接影响其他进程。每个进程都有独立的地址空间,这样可以防止一个进程错误或恶意操作对其他进程造成影响,提高了系统的安全性和稳定性。 - 异常处理
进程可以通过独立的执行环境来处理错误和异常情况。当一个进程发生错误时,操作系统可以通过终止该进程而不影响其他进程的运行,提升了系统的容错能力。 - 上下文切换
进程允许操作系统进行上下文切换,使得CPU可以在多个进程之间快速切换,给用户提供“同时执行”的感觉。操作系统通过调度算法来决定哪个进程在何时获得CPU的控制权,从而实现高效的资源利用。 - 状态管理
进程具有不同的状态(如就绪、运行、等待、终止),操作系统通过管理这些状态来跟踪进程的生命周期,使得对进程的管理和调度更加灵活高效。 - 通信与协作
进程间可以通过各种机制(如管道、消息队列、共享内存等)进行通信和协作,从而实现复杂的计算任务。通过这种进程间通信(IPC),多个进程可以共享数据和协调工作。 - 简化应用程序设计
通过使用进程,开发者可以将复杂的应用程序划分为多个模块,每个模块可以在一个独立的进程中运行,简化了应用程序的设计和维护。 - 多用户支持
操作系统通过进程来支持多用户环境,每个用户可以在自己的进程中运行应用程序,确保各个用户之间的操作不会相互干
7.2 常见的API函数
所在头<unistd.h>
7.2.1 fork系统调用
#include <unistd.h>
pid_t fork(void)
pid_t是用来保存进程PID信息的结构体,当调用执行成功时候,该调用对父进程返回子进程的PID,对子进程返回0,调用失败返回-1,子进程没有创建。
7.2.2 wait函数调用
当子进程由于父进程退出时候,如果父进程没有调用wait函数,子进程就会陷入僵尸的状态,如果父进程调用wait函数,就不会使子进程变为僵尸进程,wait函数声明如下:
pid_t wait ( int *statloc)
wait函数使父进程暂时停止执行,直到他的一个子进程结束为止,该函数返回值是终止运行的PID,参数statloc所指向的变量存放在子进程的退出码。
7.3 运行案例
#include <sys/types.h>//该头文件提供系统调用的标志
#include <sys/stat.h> //提供系统状态信息和相关函数
#include <sys/uio.h> //提供进程I/O操作相关函数
#include <unistd.h> //标准函数库
#include <fcntl.h> //文件操作函数
#include <string.h> //字符操作库
#include <sys/wait.h> //wait调用库
#include <stdio.h> //标准输入输出库
#include <stdlib.h> //常用工具库
int main ()
{
char buf[100]={0}; //定义缓存区
pid_t cld_pid; //定义该结构保存子进程PID
int fd;
int status; //用于wait函数调用参数
if ((fd=open("temp",O_CREAT | O_RDWR | O_TRUNC, 0664))== -1)
{ //打开或者新建文件
perror("创建文件");
exit(1);
}
strcpy(buf,"父进程数据");
if ((cld_pid = fork())==0){
strcpy(buf,"子进程数据");
printf("子进程正在工作:");
printf("子进程PID是%d\n",getpid()); //输出子进程PID
printf("父进程PID是%d\n",getppid()); //输出父进程PID
write(fd,buf,strlen(buf));
close(fd);
exit(0);
}
else{
printf("父进程正在工作:");
printf("父进程PID是%d\n",getpid());//输出父进程PID
printf("子进程PID是%d\n",cld_pid); //输出子进程PID
write(fd,buf,strlen(buf));
}
wait(&status); //等待子进程结束
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pid;
char *msg;
int k;
int exit_code; //子进程退出代码
printf("Study how to get exit code\n");
pid = fork(); //创建进程
switch(pid) //观察进程是否创建成功
{
case 0:
msg = "Child process is running"; //代表进程创建成功
k=5;
exit_code=37;
break;
case -1: //进程创建不成功
perror("Process creation failed\n");
exit(1);
default:
exit_code = 0;
break;
}
if(pid !=0) //父子进程都会执行这段代码,子进程PID为0,父进程PID为子进程PID
{ // 父进程等待子进程结束
int stat_val;
pid_t child_pid;
child_pid = wait(&stat_val);
printf("Child procee has exited, pid = %d\n",child_pid);
if(WIFEXITED(stat_val))
printf("Child exited with code %d\n",WEXITSTATUS(stat_val));
else
printf("Child exited abnormally\n");
}
else{ //子进程暂停5秒,在这个过程中运行命令ps aux查看父进程状态
while(k-->0)
{
puts(msg);
sleep(1);
}
}
exit(exit_code);
}