进程和程序的区别
程序:保存在磁盘空间中的的一段代码的集合,是死的
进程:是一个程序动态执行的过程,包括进程的创建、调度和消亡的过程
进程相关的命令
PID:进程的标识符(进程的ID)
PPID:父进程的ID号
1.top
根据CPU占用率查看进程信息
q退出
2.ps -ef
查看当前所有进程的信息
ps -ef | grep a.out 查看a.out相关的进程信息
3.ps -aux
查看当前所有进程的信息
ps -aux | grep a.out 查看a.out相关的进程信息
4.kill
向进程发送信号,并让进程结束
kill -l
查看信号的类型
kill -信号名/信号编号 进程ID
kill -9 进程ID
killall -信号名/信号编号 进程名
5.后台运行代码:
./a.out &
6.jobs
查看后台执行的任务
7.fg 任务编号
将任务编号对应的后台任务放到前台执行
8.nice
nice -n 10 ./a.out
以10作为优先级运行a.out进程 在Linux系统中数字越小优先级越高
9.renice
renice -n 10 进程ID
10.pstree
查看进程的关系
进程的创建
32bit操作系统中,进程运行,操作系统开辟 0 - 4G虚拟内存空间
虚拟内存空间分为:
用户空间:
1.文本段:文本区 存放代码、指令
2.数据段:数据区 存放全局变量、静态变量、常量
3.系统数据段:堆、栈
内核空间:
1.用户无法访问内核空间,通过信号、系统调用实现用户层和内核层的交互
实际物理内存空间:
MMU内存映射单元实现虚拟地址和物理地址间的映射关系
两个进程所占虚拟内存空间并不是 8 G,而是 4 G,两个进程分时共用同一空间
两个进程物理内存空间是独立的,进程1和进程2空间独立
结论:
进程的空间是独立的,但是两个进程并不是8G
进程的调度
(微观串行,宏观并行)
进程调度算法:
1.先来先执行,后来后执行
2.高优先级调度算法
3.时间片轮转调度算法时间片:
CPU在一个进程任务中执行的时间称为时间片
4.抢占式调度算法
5.多级反馈队列调度算法
进程的状态
1.运行态、就绪态: R
运行态:正在被CPU执行的进程任务
就绪态:正在执行的任务,但由于CPU调度还没有被调度到
2.可唤醒等待态: S
睡眠态
由于缺少资源导致程序阻塞的状态,当拿到资源后,继续向下执行
3.不可唤醒等待态: D
不想被CPU任务调度打断
4.暂停态 T
用户主动让进程任务暂停(挂起)状态
5.僵尸态 Z
进程任务结束了,但空间没有被回收
6.结束态 X
进程任务执行完了,空间也被回收了
进程相关的函数接口
(1)fork
fork
pid_t fork(void);
功能:
创建一个子进程
参数:
void 缺省
返回值:
创建失败返回-1
成功父进程中返回创建的子进程的PID
子进程中返回0
写时拷贝:fork创建子进程,子进程拷贝父进程中的数据,如果只是读取时不会为子进程单独生成空间,只有做写入操作时,才会为子进程开辟独立空间,提高进程创建的效率
(2)getpid
getpid
pid_t getpid(void);
功能:
获取进程的PID
3.getppid
pid_t getppid(void);
功能:
获得父进程的PID
(3) exit
exit
void exit(int status);
功能:
进程结束 ,会刷新缓存区
参数:
status:进程结束的状态码
返回值:
缺省
主函数中使用exit等价于return
(4) _exit
_exit
void _exit(int status);
功能:
立即结束进程任务,不会刷新缓存区
进程消亡
僵尸态:进程代码执行结束,空间没有被回收
僵尸进程产生原因:
1.子进程任务结束,空间没有被回收,该进程成为僵尸进程
如何避免产生僵尸进程:
1.让父进程先结束,子进程再结束,子进程不会变成僵尸进程
2.子进程结束,父进程回收子进程空间,子进程不会变成僵尸进程
孤儿进程:
父进程先结束,子进程成为孤儿进程,被init进程(1号)进程收养,子进程结束,init系统进程自动回收进程空间
(5)wait
wait
pid_t wait(int *wstatus);
功能:
等待子进程结束,回收子进程空间
参数:
wstatu s:存放子进程结束的状态码空间首地址
返回值:
成功返回子进程的PID
失败返回-1
注意:
wait具有阻塞功能,如果子进程先结束,wait会不阻塞直接回收
如果子进程没结束,wait会阻塞直到子进程结束回收子进程空间
WIFEXITED(wstatus):判断子进程是否正常结束
WEXITSTATUS(wstatus):获取子进程正常结束的状态码
WIFSIGNALED(wstatus):判断子进程是否被信号打断结束
WTERMSIG(wstatus):获取子进程被信号打断结束的状态码
实现同步功能将本来异步的父子进程变为同步的
#include"head.h"
int main()
{
pid_t pid;
int ret =0;
int status;
pid=fork();
if(pid==-1)
{
perror("failed to fork");
return -1;
}
if(pid==0)
{
printf("子进程PID:%d正在运行\n",getpid());
sleep(10);
printf("我运行完了\n");
exit(10);//结束子进程
}
else if(pid>0)
{
printf("父进程PID:%d正在运行\n",getpid());
ret=wait(&status); // 等待子进程结束,回收子进程空间,不回收子进程空间,子进程会变成僵尸态。
printf("子进程PID:%d被回收\n",ret);
if(WIFEXITED(status))
{
printf("子进程正常结束,值为%d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("子进程被%d信号杀死", WTERMSIG(status));
}
}
return 0;
}
(6)waitpid
waitpid
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:
回收指定的子进程空间
参数:
pid:要回收的子进程的pid
wstatus:回收状态的指针
options:回收选项
0 阻塞回收
WNOHANG:非阻塞回收
返回值:
成功:返回回收的子进程的pid
失败:返回-1
#include "head.h"
int main()
{
pid_t pid1;
pid_t pid2;
pid_t ret=0;
int status;
pid1=fork();
if(pid1==-1)
{
perror("failed to fork");
return -1;
}
if(pid1==0)
{
printf("子进程1PID:%d正在执行\n",getpid());
sleep(3);
printf("子进程1即将结束\n");
exit(0);
}
if(pid1>0)
{
pid2=fork();
if(pid2==-1)
{
perror("failed to fork");
return -1;
}
if(pid2==0)
{
printf("子进程2PID:%d正在执行\n",getpid());
sleep(5);
printf("子进程2即将结束\n");
exit(0);
}
else if(pid2>0)
{
while(1)
{
ret=waitpid(-1,&status,WNOHANG);//回收第一个结束的进程,没结束的进程变为孤儿进程被系统回收
if(ret==-1)
{
perror("failed to waitpid");
return -1;
}
else if(ret==0)
{
printf("还没有子进程结束呢\n");
}
else if(ret>0)
{
break;
}
sleep(1);
}
printf("子进程PID:%d被回收\n",ret);
if (WIFEXITED(status))
{
printf("子进程正常结束,值为:%d\n", WEXITSTATUS(status));
}
else if (WIFSIGNALED(status))
{
printf("子进程被 %d 号信号杀死\n", WTERMSIG(status));
}
}
}
return 0;
}
exec函数族
extern char **environ;
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
功能:
利用当前的进程空间执行另外一份代码
l:参数以列表形式传递
v:参数以指针数组形式传递
p:在系统目录下查找文件
e:传递环境变量
环境变量
获取环境变量
extern char **environ;
/* 打印进程中所有环境变量的信息 */
for(i=0;environ[i]!=0;i++)
{
printf("%s\n",environ[i]);
}
(1) getenv
getenv
char *getenv(const char *name);
功能:
根据环境变量的名字获得环境变量对应的值
(2)setenv
setenv
int setenv(const char *name, const char *value, int overwrite);
功能:
设置环境变量的值
利用execvp函数和环境变量改变进程的执行代码,本代码由main.c转向执行hello.c,在系统路径下赵hello.c
hello.c
#include "../head.h"
int main(int argc, char const *argv[])
{
int i=0;
printf("helloPID:%d\n",getpid());//PID与main.c中的PID一致
for(i=0;i<argc;i++)
{
printf("argv[%d]=%s\n",i,argv[i]);
}
return 0;
}
main.c
#include "../head.h"
extern char **environ;
int main()
{
int i=0;
char *parg[10]={"hello","world","how","are","you",NULL};
char curpath[4096]={0};
char pathvalue[4096]={0};
/* 打印进程中所有环境变量的信息 */
for(i=0;environ[i]!=0;i++)
{
printf("%s\n",environ[i]);
}
/*获得环境变量中PATH的值*/
printf("==============================\n");
printf("PATH:%s\n",getenv("PATH"));
printf("==============================\n");
/**************************************
**方法一:
* 在PATH环境变量值原来的基础上加上当前目录
***************************************/
getcwd(curpath,sizeof(curpath));//获取当前目录
sprintf(pathvalue,"%s:%s",getenv("PATH"),curpath);//将当前目录加入pathvalue
setenv("PATH",pathvalue,1);//重新设置为新的PATH
printf("将PATH设定为:%s\n",pathvalue);
printf("==============================\n");
/*打印更新后PATH的值*/
printf("PATH:%s\n",getenv("PATH"));
printf("==============================\n");
printf("exec上面\n");
/* 利用该进程空间执行另外一份代码 */
// execl("hello","hello","world","how","are","you",NULL);
/* 在系统路径PATH下找hello文件名 */
execvp("hello",parg);
//方法二:也可以将hello文件拷贝到PATH中随便一个目录下
printf("exec下面\n");
return 0;
}
system
system函数原型:
int system(const char *command);
功能:
在代码中执行一条shell命令
用进程知识实现system函数的功能
#include "../head.h"
int Mysystem(const char *pcommand)
{
char ptmpbuff[1024]={0};
strcpy(ptmpbuff,pcommand);
char *parg[10]={0};
int cnt=0;
pid_t pid;
/*解析命令*/
parg[cnt]=strtok(ptmpbuff," ");
if(parg[cnt]==NULL)
{
return -1;
}
cnt++;
while(1)
{
parg[cnt]=strtok(NULL," ");
if(parg[cnt]==NULL)
{
break;
}
cnt++;
}
/*创建子进程*/
pid=fork();
if(pid==-1)
{
return -1;
}
if(pid==0)
{
/*在/usr/bin路径下查找命令,并执行*/
execvp(parg[0],parg);
}
else if(pid>0)
{
/*关闭进程*/
wait(NULL);
}
return 0;
}
int main()
{
printf("Mysystem上面\n");
//system("ls -l");
Mysystem("ls -l");
printf("Mysystem下面\n");
}