目录
1、如何查看进程
Window查看进程
Ctrl+Alt+Del
Linux查看进程
ps -ef 查看所有进程及其PID(进程号),系统时间,命令详细目录,执行者等。
ps aux 除可显示-ef所有内容外,还可显示CPU及内存占用率,进程状态。
top:动态显示系统中运行的程序
pstree 以树形方式显示系统中的进程
kill 输出特定的信号给指定PID(进程号)的进程
kill -l 查看所有可用的信号名称
2、什么是进程
2.1 程序和进程的区别
程序:程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念。
进程:进程是动态的,它是程序执行的过程,正在运行的程序,进程是程序执行和资源管理的最小单位。
2.2 进程是程序执行和资源管理的最小单位
每个进程都有独立的资源空间,当进程创建时,OS会为进程分配4G的进程空间,且每个进程都有1G内核空间和3G用户空间
进程资源主要分为两部分:内核空间资源,用户空间资源
内核空间资源:即PCB相关的信息,这些资源只有通过系统调用才能使用资源,例如PID、PPID、打开的文件描述符等等,这一资源在当前进程退出,只能由另一进程(父进程)来回收,若无回收,会造成僵尸进程,为了更好的管理Linux所访问的资源,在系统内核文件include/linux/sched.h中定义一个结构体struct task_struct专门管理进程的资源。
用户空间资源:进程的代码段,数据段,堆栈以及可以共享访问的库的内存空间,这些资源进程可以直接访问,该资源在进程退出时释放。
2.3 进程分类
1.交互式进程(经常使用),可以在前台运行,也可以在后台运行。
jobs:查看后台进程,只能在同一终端下使用.
fg 作业号 :将后台进程放前台运行.
2.批处理进程,这类进程不必与用户进行交互,因此通常在后台运行。
3.守护进程(重点)。这类进程一直在后台运行,和任何终端都不关联。通常系统启动时开始执行,系统关闭时才结束。
2.4 进程的状态
(1)运行态:R 进程当前正在运行或者正在运行队列中等待调度(就绪态)
(2)等待态:
可中断的等待 S sleep(1)
不可中断的等待 D
(3)停止态:T 进程的执行被暂停,当进程收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号,都会被停止
(4)死亡态:X 这是最终状态
(5)僵尸态:Z 是个错误,需要我们去处理
进程中的优先级
-20到19,值越大,优先级越低,普通用户设置优先级最大为0
修改优先级
nice 按用户指定的优先级运行进程
renice 改变正在运行进程的优先级
2.5 进程的相关函数
2.5.1 创建进程
pid_t fork
#include<sys/types.h> //提供类型pid_t的定义
#include<unistd.h>
pid_t fork(void);
返回值:0:子进程
子进程PID(大于0的整数):父进程
-1:出错
pid_t getpid(void); 获得进程的ID号
pid_t getppid(void); 获得父进程的ID号
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid;
//创建子进程
pid=fork();
if(pid<0)//子进程创建失败
{
perror("fork error");
return -1;
}else if(pid==0)//返回为0表示该进程是子进程
{
printf("i am child process\n");
}else if(pid>0)//返回0表示该进程是父进程,返回的是子进程
{
printf("i am father process\n");
}
sleep(10);
return 0;
}
vfork
为了提高效率,Unix系统设计者创建了vfork。vfork也是创建新进程,但不产生父进程的副本。
fork和vfork的区别
1、fork():子进程拷贝父进程的数据段,代码段。
vfork():子进程与父进程共享数据段。
2、fork():父子进程的执行次序不确定。
vfork():保证子进程先运行,在调用exec或_exit之前与父进程数据是共享的,在它调用exec或_exit之后父进程才可能被调度运行。
3、vfork()保证子进程先运行,在它调用exec或_exit之后父进程才可能被调用运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。当需要改变共享数据段中变量的值,则拷贝父进程。
2.5.2 进程退出
exit(): #include<stdlib.h>
_exit(): #include<unistd.h>
void exit(int status); //自带清理缓冲区
void _exit(int status); //不清理缓冲区
status:
是一个整型的参数,可以利用这个参数传递进程结束时的状态。通常0表示正常结束;其他的数值表示出现了错误,进程非正常结束。在实际编程时,可以用wait系统调用接收子进程的返回值,进行相应的处理。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
printf("helloworld");
//exit(0);//0一般表示正常结束 exit()自带清理缓冲区
fflush(stdout);//stdin 输入(2) stdout(1) 输出 stderr标准出错(2)
//刷新缓冲区
_exit(0); //不清理缓冲区
printf("hellobeijing");
return 0;
}
2.5.3 回收内核资源
wait() 阻塞等待子进程结束回收资源
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status)
参数:
返回值:成功:子进程的进程号
失败:-1
waitpid()回收子进程资源
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options)
参数1:pid>0:只等待进程ID等于pid的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去
pid=0:等待其组ID等于调用进程的组ID的任一子进程
pid<1:等待其组ID等于pid的绝对值的任一子进程
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
int main(int argc, const char *argv[])
{
pid_t pid;
//创建子进程
pid=fork();
if(pid<0)//子进程创建失败
{
perror("fork error");
return -1;
}else if(pid==0)//返回为0表示该进程是子进程
{
int j = 0;
while(1)
{
if(j>=15) //当j>=15时,让子进程退出,可以把j当时的值返回
{
exit(j);
}
printf("recv cam\n");
printf("i am child process,PID=%d PPID=%d\n",getpid(),getppid());
sleep(1);
j++;
}
}else if(pid>0)//返回0表示该进程是父进程,返回的是子进程
{
//阻塞等待子进程退出
//弊端:子进程不结束,父进程永远没法进行
//wait(NULL); //参数若为空,忽略子进程退出的状态
// 参数若不为空,表示保存子进程退出时的状态
while(1)
{
//回收子进程资源 需要轮询(写在while(1)内)
//参数1 -1表示等待任意一个子进程退出
//参数2 NULL表示忽略进程退出的状态
// 不为空可以保存子进程退出时的状态
//参数3 0表示阻塞等待 WNOHANG表示非阻塞等待,
// 若无子进程退出,返回值为0
// 若有子进程退出,返回值为退出的子进程号
//waitpid(-1,NULL,0) <--等价--> wait(NULL)
//waitpid(-1,NULL,WNOHANG);//非阻塞等待任意一个子进程退出
//判断子进程是正常结束还是异常结束
int status;//子进程退出的状态保存在status的低8位到低16位
int id = waitpid(-1,&status,WNOHANG);
if(id>0) //id>0表示子进程退出
{
if(WIFEXITED(status))
{
printf("正常退出的状态值:%d\n",WEXITSTATUS(status));
}else if(WIFSIGNALED(status))
{
printf("信号值为:%d\n",WTERMSIG(status));
}
printf("send data\n");
printf("i am father process,PID=%d\n",getpid());
sleep(1);
}
}
}
sleep(10);
return 0;
}
2.5.4 父子进程
(1)fork成功之后,分为父进程(pid>0)和子进程(pid==0),子进程几乎是父进程的完全复制,子进程独有进程号、资源使用和计时器等。
(2)父子进程谁先运行?看内核调度,不确定。
(3)能否多次调用fork()?可以,每调用一次,创建一个子进程。
(4)父进程先退出,子进程未结束,子进程成为孤儿进程,被systemd的进程收养,后台运行。
(5)子进程先退出,父进程未结束,并父进程没有回收子进程的资源,子进程成为僵尸进程。