目录
一、进程状态
1.看看Linux Kernel怎么说
1.1阻塞
2.进程状态查看
3.僵尸进程
3.1模拟僵尸进程的实验
3.2僵尸进程的危害
4.孤儿进程
4.1模拟孤儿进程实验
二、进程优先级
三、环境变量
3.1常见环境变量
3.2查看环境变量的方法:
3.3 加入环境变量
3.4 获取环境变量
四、命令行参数
一、进程状态
1.看看Linux Kernel怎么说
为了弄明白正在工作的进程是什么意思,需要标注进程的不同状态。一个进程有几个状态(在linux内核中,进程也叫做任务)
1.1阻塞
进程等待某种资源就绪,而导致不被推进的状态。从冯诺依曼体系结构上理解,比如现在在电脑的软件管家上下载东西,但是由于网络原因下载中断。os会让下载进程在运行队列里排队,等待网卡恢复正常,在网卡设备后面排队,等待网卡资源。这个过程称为阻塞。即进程在其他设备队列中排队
R(running),R状态并不一定是进程在cpu运行,而表示该进程在运行队列中排队。
S(Sleep),可中断休眠,是阻塞状态的一种。进程等待休眠完成。比如现在使用printf函数在显示器上打印,由于cpu的速度远远大于访问外设的速度,打印需要等待cpu资源。或者使用scanf输入,还没输入的时候,等待键盘数据,这个时候就是在等待键盘资源
D(Disk Sleep),不可中端休眠,在这个状态进程通常会等待IO结束。无法被kill,出现这种状态一般是cpu调度进程由内存往磁盘中写入数据,磁盘快满了就会导致很卡,此时若关机,会导致数据丢失。可能会导致宕机
T(Stopped),可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。在断点处停下,就是一种暂停状态
- X死亡状态(dead),这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
2.进程状态查看
命令:ps axj / ps aux
想要一直观察某个进程的状态,使用方法如下:
while :;do ps aux |grep test |grep -v grep; sleep 1 ; done //隔一秒打印一次test进程的状态
3.僵尸进程
如果一个进程退出了,立马X状态,作为父进程没有机会拿到子进程的退出结果。所以在Linux中,一般进程不会立即退出,而是要维持一个状态叫做Z,也叫做僵尸状态,方便后续父进程读取该子进程的退出结果。可以使用实验来模拟僵尸状态:fork一个子进程,让子进程先退出,但是不要回收子进程。
- 僵尸状态,是一个比较特殊的状态,当进程退出并且父进程没有读取到子进程退出的状态返回码,子进程就会产生僵尸状态。
- 僵尸状态会以终止状态保持在进程列表中,并且会一直等待父进程读取退出状态码
- 所以只要子进程退出,父进程还在运行,但是父进程没有读取子进程状态,子进程就进入Z状态。
3.1模拟僵尸进程的实验
写一段代码,让父进程运行30s,子进程运行5s,此时子进程先退出,父进程还在运行,同时父进程没有获取到子进程的退出码,子进程进入僵尸状态。代码如下:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 pid_t id = fork();
//创建失败
8 if(id<0)
9 {
10 perror("fork");
11 return 1;
12 }
// id >0 运行父进程 父进程运行30s
13 else if(id>0)
14 {
15 //parent
16 printf("parent[%d] is sleeping ...\n",getpid());
17 sleep(30);
18
19 }
// id == 0 运行子进程, 子进程运行 5s
20 else {
21 printf("child[%d] is begin Z ...\n",getpid());
22 sleep(5);
23 exit(EXIT_SUCCESS);
24 }
25 return 0;
26 }
27
同时 使用命令查看进程的状态如下:
3.2僵尸进程的危害
- 进程的退出状态必须被维持下去,因为它要告诉它的父进程,你交给我的任务,我办的怎么样了,可是父进程一直不读取,那么进程就处于Z状态
- 维护退出状态本身就是使用数据维护,属于进程的基本信息,所以要保存在tast_struct(PCB)中,Z状态一直不退出,PCB就需要一直维护
- 那么,一个父进程创建了很多子进程,但是不回收,就会造成资源的浪费,因为数据结构对象本身就要占用内存,就比如C语言中定义一个结构体变量,就需要在内存的某个位置进行开辟空间
- 内存泄露
4.孤儿进程
- 父进程如果提前退出,子进程后退出,进入Z状态之后,应该如何处理?
- 父进程先退出,子进程就称为孤儿进程
- 此时,子进程被1号进程init领养,由init进程回收
4.1模拟孤儿进程实验
写一段代码,让子进程运行10s,父进程运行3s,此时父进程先退出,子进程由1号进程收养。
#include <stdio.h>
2 #include <stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 pid_t id = fork();
8 if(id<0)
9 {
10 perror("fork");
11 return 1;
12 }
13 else if(id ==0)
14 {
15 //parent
16 // printf("parent[%d] is sleeping ...\n",getpid());
17 printf("I am child ,pid:%d\n",getpid());
18 sleep(10);
19
20 }
21
22 else {
23 // parent
24 printf("I am parent,pid:%d",getpid());
25 sleep(3);
26 }
27 return 0;
28 }
29
实验现象如下:
二、进程优先级
- CPU资源分配的先后顺序,就是指进程的优先权
- 优先权高的进程有优先执行权利,配置进程优先权对多任务环境的linux很有用,可以改善系统性能
- 还可以把进程运行到指定的cpu上,这样一来,把不重要的进程安排到某个cpu,可以大大改善系统性能
三、环境变量
环境变量本质上是一个内存级的一张表,这张表在用户登录系统的时候,进行给特定用户形成属于自己的环境变量表。
环境变量中的每个都有自己的用途,有的是进行路径查找,有的是进行身份认证,有的是动态库查找的,有的是用来确认当前路径等等,每个环境变量都有自己的特定应用场景。
每一个元素都是kv的
环境变量的数据由系统相关配置文件中读取进来的 .bashrc .bashprofile(bashprofile调用.bashrc)
make编译完程序之后,我们通常要使用./运行程序,带./就是相对路径,定位到当前的可执行程序
- 环境变量一般是指在os中用来指定os运行环境的一些参数
- 如我们在编写c/c++代码的时候,在链接的时候,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就有相关的环境变量帮助编译器进行查找
- 环境变量通常具有某些特殊用途,还在系统中通常具有全局属性
3.1常见环境变量
PATH 指定命令的搜索路径
HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录)
SHELL:当前shell,它的路径通常是/bin/bash
3.2查看环境变量的方法:
echo $Name //Name 要查的环境变量的名称
3.3 加入环境变量
a. export PATH = 要添加的路径,此时直接运行,发现只有当下路径的文件可以运行
b. export PATH = $PATH:要添加的路径 这种方法就是把当前路径拷贝到环境变量下
在linux中,把可执行程序拷贝到系统的默认路径下,让我们可以直接访问,这种就相当于在linux下软件的安装,删除路径就相当于卸载
在代码 int main(int argc, char * argv[ ], char * envp[ ] ) 这三个形参中,char * envp是环境变量表,是一个指针数组,里面存放着char * 类型的地址,比如:" /a/b/c:",如下图:前若干个指向有效地址,最后一个指向NULL
3.4 获取环境变量
a.通过命令行参数获取
#include<stdio.h>
int main(int argc , char * argv[], char * envp[])
{
int i = 0;
//提取特定的地址,最后一个为null就不执行
for(;envp[i];i++)
{
printf("%s\n",envp[i]);
}
return 0 ;
}
b.通过第三方变量environ获取
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以使用的时候要用extern声明
#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;
}
通过函数获取环境变量getenv,getenv("要获取的环境变量")
#inlcude<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
char * user = getenv("USER");
if(user == NULL)
perror("getenv");
else
printf("USER:%s\n",user);
return 0;
}
#inlcude<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
char * user = getenv("PWD");
if(user == NULL)
perror("getpwd");
else
printf("PWD:%s\n",user);
return 0;
}
export PATH = $PATH: 路径
可以在命令行直接执行
我们现在已经知道了进程内部已经有了环境变量,所以我们可以这样使用,通过strcpm来判断当前用户是谁,如果不是本人,就不能执行当前文件
#define NAME "abc"
int main()
{
char * own = getenv("USER");
if(strcmp(own,NAME) == 0)
{
printf("这个程序已经执行了");
}
else
{
printf("%s非法执行 ",own);
}
}
shell读取命令和命令行,echo export (不带export变量也可以被shell记住,但是没有在环境变量表中存放,这种叫做本地变量)自定义一个变量myval,然后shell就会在环境变量表中malloc一个64位的空间,把myval存放进去,可以使用env查看
环境变量可以被子进程继承,环境变量具有全局属性
四、命令行参数
通过输入不同的参数,就可以执行不同的程序
int main(int argc,char*argv[ ])
{
for(int i = 0; i<argc,;i++)
{
printf("agrv[%d]->%s\n",i,argv[i])
}
}
在命令行中输入 ./mytest -a -b -c -d
./mytest 可执行程序
-a -b -c -d 参数选项
argv[] 中 0号下标指向可执行程序 1号 1号子串 2号 2号子串
argc 代表命令行参数的个数
// .myproc arg
void Usage(const char * name)
{
printf("\nUsage:%s -[a|b|c]\n,name);
exit(0); //终止进程
}
int main(int argc,char *argv[])
{
if(argc !=2)
Usage(argv[0]);
if(strcmp(argv[1], "-a" == 0)
printf("打印当前目录下的文件名");
else(strcmp(agrv[1] ,"-b" ==0)
printf("....");
}