在我们讲解进程之前,首先对操作系统做一个解释,操作系统是用来管理软硬件资源的,可以为应用程序提供一个良好的执行环境。在整个计算机中,操作系统的定位就相当于管理者。
我们如何理解管理这个词呢,很简单,就是先描述(利用结构体)被管理的对象,然后将被管理的对象组织起来(利用像链表等高效的数据结构),总结一下就是:先描述,后组织。
进程
进程是程序的一个执行实例,是占用资源的基本单位。
进程的所有信息通过一种数据结构PCB(进程控制块)描述,Linux下PCB叫做task_struct。
所有运行的进程的PCB都会以链表的数据结构组织起来。
task_struct结构体所包含的内容:
1. 标识符:进程ID,唯一标识这个进程。
2. 状态:进程状态,退出状态码,退出信号等。
3. 优先级:相对于其他进程的优先级。
4. 程序计数器:程序中即将被执行的下一条指令的地址。
5. 上下文数据。
6. 其他信息。
查看进程的命令:
ps -ef 显示所有进程信息
ps -aux 显示进程信息
ps -l 显示更多的信息,优先级等等。
进程状态:
1. R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
2. S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)。
3. D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
4. T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
5. X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
僵尸进程
僵尸状态(Z)是一个比较特殊的进程状态。当子进程退出,但是父进程没有读取到子进程的退出的返回代码时,子进程变成僵尸进程。僵尸进程会以终止状态保持的进程表中,并一直等待父进程读取它的进程退出代码。所以只要子进程退出,父进程还在运行,但没有读取到子进程退出状态,则子进程进入僵尸状态。
下面代码创建了一个维持三十秒的僵尸进程:
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id > 0){ //parent
printf("parent[%d] is sleeping...\n", getpid());
sleep(30);
}else{
printf("child[%d] is begin Z...\n", getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
当执行这段代码,然后用 ps -aux 命令查看该进程会发现已经成为僵尸进程(Z)。
僵尸进程的危害:
当一个进程变为僵尸进程时,因为该进程一直在等待其父进程读取它的退出状态,所以僵尸进程一直维持着退出状态,这也属于进程的信息,会保留在PCB中。也就是说,僵尸进程一直存在,其PCB也就一直存在着。这样就会操作资源浪费,产生内存泄漏问题。
孤儿进程
孤儿进程是指父进程先于子进程退出了,这个子进程就变成孤儿进程,孤儿进程会被1号init进程“领养”,由该init进程来回收释放孤儿进程所占的资源。
这也是一种解决僵尸进程的方法,那就是将僵尸进程的父进程退出(kill -9),那么僵尸进程编程孤儿进程,由1号init进程回收释放资源。
下面代码创建了一个孤儿进程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id == 0){//child
printf("I am child, pid : %d\n", getpid());
sleep(10);
}else{//parent
printf("I am parent, pid: %d\n", getpid());
sleep(3);
exit(0);
}
return 0;
}
进程优先级
cpu资源分配的先后顺序,就是指进程的优先级(priority)。
在Linux操作系统中,可以使用命令 ps -l来查看进程的信息包括优先级相关信息。
我们可以看到有PRI和NI两个选项
PRI表示进程可被执行的优先级,值越小优先级越高。
NI表示进程的nice值,取值范围是 -20~19。nice值不是进程的优先级,但是可以影响进程的优先级。
PRI(new)=PRI(old)+nice。
修改进程优先级:通过修改nice值来实现
进程的特点:
1. 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
2. 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
3. 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
4. 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
环境变量
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。可以被子进程继承下去。
环境变量组织方式
每个进程都有一张环境表,环境表是一个字符指针数组,每个指针指向一个以\0结尾的环境字符串。
通过代码获取环境变量
1. mian函数第三个参数。
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
2. 通过第三方变量environ获取
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
通过命令或者系统调用获取或设置环境变量
getenv():获取指定的环境变量
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
在Linux下运行: