一些问题
- 什么是进程?
??进程就是正在运行的程序, 是动态的. 是操作系统分配资源的最小单位. - 什么是程序?
??程序是存储在存储介质上的经过编译的可执行二进制文件, 是静态的.
一些概念
时间片
时间片: cpu分配运行时间的单位, 它是CPU在进程上运行的前提. CPU会保证每一个进程都会有一定数量的时间片, 轮巡地在各个进程之间切换. 由于CPU的运行速度非常快, 其轮巡的速度也远远在我们可以察觉的速度之上
进程ID
每个进程都有唯一的标识符,这个标识符就是进程ID。简称为“PID"。
??进程A与进程B之间交流的过程成为“进程间的通信”。进程并不能通过应用层直接交流,通过Linux内核进行交流。在内核中创造一个对象,利用对象进行交流。所以进程通信都是基于文件IO进行的。如通过open/close 打开/关闭交流对象,通过read 获取信息,通过write 发送信息。 ??像一些特殊的对象,如管道等,一般都使用文件IO进行而不是标准IO。
进程的三种基本状态
- 就绪态:除了CPU以外的其它资源全部准备好了。可以变成执行态,可以由执行态和阻塞态转变而来。
- 执行态:CPU正在处理执行这个进程。可以转变成阻塞态、就绪态,只能由就绪态转变而来。
- 阻塞态:进程再等待其他资源准备好。可以转变成就绪态,只能由执行态转变而来。
进程的控制:
进程的创建
fork 函数: 头文件:unistd.h 函数原型:pid_t fork(void) 返回值:fork函数返回:
- 父进程中,返回子进程的PID
- 子进程中,返回0
- 出现错误,返回一个负值
??什么是父进程、子进程?进程1中创建了进程2,那么它们之间存在的创建于被创建的关系就是父子关系。也即进程1是进程2的父进程;进程2是进程1的子进程。
#include<stdio.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid<0)
{
printf("process create failed!\n");
}
else if(pid == 0)
{
printf("This is child process!\n");
}
else
{
printf("This is parent process!Its child PID=%d\n",pid);
}
return 0;
}
??编译运行上述文件,根据PID的输出就可以判断进程创建是否成功、当前进程是子进程还是父进程。
获取进程的PID
函数:getpid() 和getppid() 分别可以获取当前、父进程的PID。所以可以修改上面的c文件以获取子进程时的父进程以及自身PID,修改如下:
#include<stdio.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid<0)
{
printf("process create failed!");
}
else if(pid == 0)
{
printf("This is child process!");
printf("PID=%d parent PID=%d\n",getpid(),getppid());
}
else
{
printf("This is parent process!");
printf("PID=%d Its child PID=%d\n",getpid(),pid);
}
return 0;
}
~
子进程中的父进程PID应该和父进程中的PID相同。测试上述文件结果如下: 为什么会输出两行结果?因为在创建进程之后,子进程在创建的位置复制父进程的资源后从fork语句下执行。与此同时父进程也继续从fork语句往下处理,所以打印出了两行结果:
父子进程的执行顺序
??父子进程的执行顺序是不定的,父子进程之间也是竞争CPU的。所以谁先抢占到CPU谁就先执行(父慈子孝),没有固定的谁先谁后。
ps命令和kill命令
ps命令
??ps命令可以列出系统中当前运行的那些进程。
Usage: ps [options]
功能:用来显示当前进程的状态
常用参数:
aux 注意没有-符号
a 显示关联终端所有进程(可以与用户进行交互的)
u 显示进程的归属用户,相关内存的使用情况
x 显示的a参数涉及的进程的补集(ax显示所有不关联终端的进程)
To see every process on the system using BSD syntax:
ps ax
ps axu
TTY是关联的终端。另外aux显示的进程太多了,如果我们需要特别地查找一些进程,该如何使用呢?这会在后面的管道中进行讲解,这里举一个例子(查找/usr/sbin/vmtoolsd相关的进程):
ps aux | grep /usr/sbin/vmtoolsd
kill命令
kill命令用来杀死进程
kill -9(SIGKILL) PID
这里-9或者SIGKILL是一种“信号”
可以用kill -l 查询总共有哪些信号
在后面的信号通信中会进一步讲解
假设我们在当前路径下有一个./a.out 进程,会一直循环无法退出。我们新开一个控制台,用如下命令查找进程的PID,并用kill命令杀死该进程:
ps aux | grep a.out
这里的第一行就是我们循环的程序,第二列就是PID号(第一列被模糊处理了)3220 。我们用kill 命令杀死该进程:
kill -9 3220
孤儿进程和僵尸进程
孤儿进程:父进程结束以后,子进程还没有结束,这个子进程就叫做“孤儿进程”。 将是进程:子进程结束后,父进程还在运行且不去释放进程进程控制块,这个子进程就叫做“僵尸进程”
孤儿进程
孤儿进程会被PID为1的Init 进程领养,也就是子进程的PPID变成1(对于Ubuntu系统,会用别的PID对应的进程作为Init进程)。我们可以做如下实验:
#include<unistd.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid<0)
{
printf("fork error!\n");
}
else if(pid==0)
{
printf("1st PPID=%d\n",getppid());
sleep(5);
printf("2nd PPID=%d\n",getppid());
}
else
{
printf("1st Father,PID=%d\n",getpid());
}
return 0;
}
执行上述程序,结果如下: 可以发现子进程最开始的PPID和最后的PPID并不相同,但是第二个PPID也不为1!我们查找一下这个进程:
ps aux | grep 868
这也印证了Ubuntu系统的Init 进程的PID是其他的进程(这里是systemd --user )。
僵尸进程
注意:父进程要没有时间去释放进程控制块(父进程的作用之一就是释放子进程的相关资源,这就是为什么孤儿进程需要被领养),子进程才是僵尸进程。 我们做如下实验:
#include<unistd.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid<0)
{
printf("fork error!\n");
}
else if(pid>0)
{
while(1);
}
else
{
printf("this is child proc\n");
}
return 0;
}
结果自然是卡死状态,我们使用ps命令查找相关进程如下: 第一行是正在运行的父进程,他的状态是R+ (运行);第二行是子进程,它的状态是Z+ (僵尸),后面的<defunct> 表示失灵的,死的。 我们可以使用kill 命令杀死正在运行父进程,子进程就变成了
进程间的通信的几种方法
管道通信
信号通信
包括信号的发送、接收、处理
IPC通信
包括共享内存,消息队列,信号灯
socket通信
网络编程,出在同一个网络下的两个进程之间的通信
|