什么是进程?
程序被加载到内存中运行,系统中基本的执行单元。
什么是程序?
可执行的二进制代码文件。
1、进程和程序之间的联系?
具有一定独立功能的程序的一次运行活动,操作系统动态执行的单元,包含程序从调度到消亡的整个过程是动态的过程
运行着的程序都是一个进程,系统本身也运行着许多管理系统资源和用户访问的程序
windows任务管理器中就显示很多进程
2、如何查看进程
在终端输入ps -ef查看进程
从左到右是
UID PID PPID C STIME TTY TIME CMD
用户ID 进程ID号 父进程ID CPU占用率 开始时间 启动的终端 占用CPU总时间 启动命令
ps -aux查看进程的详细信息
3、杀死进程的shell命令:kill -9 进程号
演示:打开两个终端
可以看到这两个终端的是两个进程,进程号分别是72882、72897.通过kill -9命令杀死一个进程
4、进程有进程号和运行状态
4.1进程号:pid是无符号整型,进程号唯一就像身份证。内核限制进程号小于等于32767,达到时重置进程号计数器,计数器重置从300开始,1-300被系统进程和守护进程占用,32位开发平台最大是32767,64位达2的22次方
4.2进程的状态:
执行态:该进程正在运行,即进程正在占用 CPU, 任何时候都只有一个进程。
就绪态:进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。
等待态:进程正在等待某些事件,当前不能分配时间片, 进程不能使用 CPU,若等待事件发生(等待的资源分配到)则可将其唤醒,变成就绪态
父进程:
创建/启动一个进程的进程称之为该进程的父进程。
子进程:
相对于该程序的父进程,该进程为子进程
5、进程的内存分布:
每个进程相互独立,都运行在自己的虚拟内存空间中,
与其他进程相互隔离(系统为每个进程维护了一个内存
映射表,表明物理内存和虚拟内存的对应关系)。
文本段:包含进程运行的程序机器语言指令
文本段的只读性:防止进程通过错误指针以外修改自身的指令
数据段:初始化数据段:包含显示初始化的全局变量和静态变量,程序加载到内存的时候读取这部分变量
未初始化数据段(BSS段):未进行限行初始化的全局变量和静态变量,程序启动之前,系统将本段所有的内存初始化为0。
数据段就是分为给过值的全局变量和静态变量、没给过值(默认值为0)
堆:动态开辟的内存
栈:动态增长和收缩的段,由栈帧组成,存放局部变量、函数参数值等
6、如何查看一个程序的内存分布?
使用size命令查看
text表示正文段大小,单位全为byte
data表示包含静态变量和已经初始化(可执行文件包含了初始化的值)的全局变量的数据段大小
bss由可执行文件中不含其初始化值的全局变量组成
dec前三个的总和
详细请看https://www.cnblogs.com/z3286586/p/14817832.html
7、任务调度
进程间是以调度的方式并行运行的。
8、进程的特点
1、动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
如何理解?动态性是进程最基本的特性,可表现为由创建而产生、由调度而执行,因得不到资源而暂停执行,以及由撤销而消亡,因此进程有一定的生命周期。而程序只是一组有序指令的集合,是静态实体。
2、并发性:任何进程都可以同其他进程一起并发执行
如何理解?并行是指两个或多个事件可以在同一个时刻发生。并发是指两个或多个事件可以在同一个时间间隔发生
3、独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
4、异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
比如,当正在执行的进程提出某种资源请求时,如打印请求,而此时打印机正在为其他某进程打印,由于打印机属于临界资源,因此正在执行的进程必须等待,且放弃处理机,直到打印机空闲,并再次把处理机分配给该进程时,该进程方能继续执行。可见,由于资源等因素的限制,进程的执行通常都不是“一气呵成”,而是以“停停走走”的方式运行。
问题:进程的执行态只能有一个进程,为什么进程具有并发性?
需要理解并发和并行的区别。
9、获得进程号函数getpid
函数原型:pid _t getpid(void),pid_t是unsigned int
函数功能:获得当前进程id
函数参数:无
函数返回值:调用该函数的进程id
头文件: #include <sys/types.h>
#include <unistd.h>
编程:运行一段程序,打印进程id(程序结束进程消失,进程号也不存在了)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argv,char * argc[])
{
unsigned int pid;
pid =getpid();
printf("进程id:%d\n",pid);
return 0;
}
可以看到每次的进程号都不一样,说明程序运行完进程消失、进程号被回收。
10、获得当前进程的父进程的id函数getppid
函数原型:pid_t getppid(void)
函数功能:获得当前进程的父进程的id
函数参数:无
函数返回值:调用该函数的进程的父进程id
头文件: #include <sys/types.h>
#include <unistd.h>
编程:打印进程的父进程id
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argv,char * argc[])
{
unsigned int pid;
pid =getpid();
printf("进程id:%d\n",pid);
pid=getppid();
printf("父进程id:%d\n",pid);
return 0;
}
可以看到几次运行由不同的进程号,但是父进程的id都一样,使用ps -ef查看进程信息,发现75057是你开的终端这个进程的id
11、创建新进程函数fork
函数原型:pid_t fork(void)
函数功能:创建新进程
函数参数:无
函数返回值:在父进程中返回子进程的PID,在子进程中返回0,失败返回-1
头文件:#include <unistd.h>
特点:fork成功后,会创建一个子进程,子进程会复制父进程资源父子进程同时从fork函数以下开始并行运行。互不干扰。拥有独立的数据段、堆栈,但无法确定父子进程的运行顺序
编程:创建一个子进程,分别在父子进程中打印进程id
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argv,char * argc[])
{
unsigned int pid;
pid=fork();
printf("进程id:%d\n",pid);
return 0;
}
可以看到,打印了两行pid,第一行显然是在父进程中打印的,第二行0是在子进程中打印的。
12、vfork函数
函数原型:pid_t vfork(void)
函数功能:创建子进程,并且阻塞父进程
函数参数:无
函数返回值:在父进程中返回子进程的PID,在子进程中返回0,失败返回-1
头文件:#include <unistd.h>
#include <sys/type.h>
特点:vfork成功后,会创建一个子进程,子进程共用(独占)父进程资源,子进程退出父进程才会得到执行。分享父进程的数据段、堆,一定是子进程先运行
编程:fork和vfork的区别
1、fork子父进程不知道哪个先运行,vfork子先运行父后运行
2、fork父子进程拥有各自独立的空间,vfork公用空间
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argv,char * argc[])
{
unsigned int pid,i=0;
pid=vfork();
if(pid!=0)
{
printf("父进程\n");
}
else
{
printf("子进程\n");
}
i++;
printf("i:%d\n",i);
_exit(0);//这里必须使用_exit(0);否则会段错误
}
可以看到i被加到2所以父子进程公用空间。
换成fork
输出两个1,所以父子进程都是独立的空间
13、特殊进程
0号进程:操作系统的引导程序
祖先进程:操作系统启动的第一个程序,1号进程
孤儿进程:父进程先退出,子进程被init接管,子进程退出后init会回收其占用的相关资源,缺点:子进程的相关资源无法清理回收
僵尸进程:子进程退出,父进程没有做清理工作。一种非常特殊的进程,它几乎已经放弃了所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间
父进程退出会清理子进程
查看僵尸进程的命名 ps -elf | grep defunct
14、进程等待wait
函数原型: pid_t wait(int *status);
函数功能:等待调用它的进程,直到子进程结束
函数参数:status 若为空,则代表任意状态结束的子进程,status 若不为空,则代表指定状态结束的子进程
函数返回值:成功返回终止的那个子进程的id,失败返回-1
头文件:#include <sys/types.h><sys/wait.h>
这个函数配合fork使用,因为当子进程的父进程先死去后子进程变成僵尸进程,使用这个函数可以让父进程先阻塞,等子进程结束后父进程再执行。
编程:子进程先运行,睡眠一秒后父进程运行
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argv,char * argc[])
{
unsigned int pid,i=0;
int ss=0;
pid=fork();
if(pid!=0)
{
wait(NULL);
printf("父进程\n");
}
else
{
printf("子进程\n");
sleep(1);
}
_exit(0);//这里必须使用_exit(0);否则会段错误
}
15、waitpid函数
函数原型: pid_t waitpid(pid_t pid,int * status,int options);
函数功能:暂时停止目前进程的执行,直到有信号来到或子进程结束
函数参数:pid>0:等待指定的pid,pid-1:等待任意的PID。
status:保存进程退出时状态 一般为NULL
options:WNOHANG:若由 pid 指定的子进程不立即可用,则 waitpid 不阻塞,此时返回值为 0,
0:同 wait,阻塞父进程,等待子进程退出
函数返回值:成功返回子进程识别码(PID) ,如果有错误发生则返回-1
头文件:#include <sys/types.h><sys/wait.h>
waitpid(pid,NULL,0);//指定的进程退出
waitpid(-1,NULL,0)//同wait(NULL);
编程
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pid1 = fork();
if(pid1 == 0)
{
sleep(5);
printf("i'm son father:%d \n",getppid());
}
else
{
waitpid(pid1,NULL,0);//阻塞 分别把这两句给注释就可以看到区别
waitpid(pid1,NULL,WNOHANG);//非阻塞
printf("im father son'spid:%d\n",pid1);
printf("父进程死了\n");
}
return 0;
}
16、进程退出
exit和_exit的区别:
exit会清理缓冲区,将缓冲区的内容写入到文件。
_exit不会清理缓冲区,内容直接丢弃。
17、exec函数族
exec 函数族就提供了一个在进程中启动另一个程序执行,会覆盖原有进程,一般和vfork连用。
1)execl函数
函数原型: int execl(const char *path, const char *arg, ...);
函数功能:使用完整的文件目录来查找对应的可执行文件。注意目录必须以“/”开头,否则将其视为文件名
函数参数:path:执行文件的路径,arg可执行文件所需要的参数,参数以NULL结尾。
函数返回值:无
头文件: #include <unistd.h>
当调用execl函数的时候,程序的代码段发生变化,变为execl要执行的功能的代码段,必须以 NULL 表示结束,如果使用逐个列举方式,那么要把它强制转化成一个字符指针
编程:使用execl函数在一个子进程中执行一个进程,进程为打印一个参数
//execl函数运行的程序
#include <stdio.h>
int main(int argc,char *args[])
{
printf("%s\n",args[1]);
}
//主程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pid1 = vfork();
if(pid1 > 0)
{
wait(NULL);
printf("im father son'spid:%d\n",pid1);
printf("父进程死了\n");
}
else if(pid1==0)
{
printf("son\n");
execl("/home/xxx/Desktop/shell/2.out","2.out","execl重塑函数",NULL);
}
return 0;
}
现象:
18、execlp函数从环境变量中调用函数
函数原型: int execlp(const char * file,const char * arg,...,(char *)0);
函数功能:从环境变量中,运行命令
头文件: #include <unistd.h>
当调用execl函数的时候,程序的代码段发生变化,变为execl要执行的功能的代码段,必须以 NULL 表示结束,如果使用逐个列举方式,那么要把它强制转化成一个字符指针
用法:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
execlp("ls","ls","-l",NULL);
return 0;
}
现象:
如何查看环境变量?
在终端输入echo $PATH即可查看
这个文件夹中全是命令ls、cat、mount等命令,可以将你的程序复制到这个文件夹中即可使用execlp函数运行。
19、shell命令调用函数system
函数原型:int system(const char *command);
函数功能:在程序中调用shell命令
函数参数:command:需要调用的shell命令
函数返回值:如果发生错误返回对应的错误码。否则返回0。
函数头文件:#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
i=system("ls -l");
printf("commond:%d\n",i);
return 0;
}
20、glob函数
函数原型:int glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno),glob_t *pglob);
函数功能:找出某一路径下的某一种类型的文件,并将文件数和文件名存储在glob_t类型的结构体中
函数参数:pattern:路径下的文件格式例如:”/home/yym/mp3file/*.mp3” flag:0,errgunc:NULL,PGLOB:要保存的位置
typedef struct {
size_t gl_pathc; /*找到的文件个数*/
char **gl_pathv;/*文件名:gl_pathv[0] gl_pathv[1] 。。。。 */
size_t gl_offs; /* 不管 */
} glob_t;
头文件:#include <glob.h>
编程找出一个文件中的所有txt文件并且打印名字
#include <stdio.h>
#include <glob.h>
int main()
{
glob_t g={0};
glob("./*.txt",0,NULL,&g);
printf("文件数:%ld\n",g.gl_pathc);
for(int i=0;i<g.gl_pathc;i++)
{
printf("%s\n",g.gl_pathv[i]);
}
}
现象:
21、安装MP3播放器:mgp123
shell命令:sudo apt-get install mpg123
mpg123的使用方式
格式:mpg123 XXX.mp3