- 进程概述
- 进程:就是进行中的程序
- 程序:存放指令的程序文件,存放在磁盘上,固定不变的,保存着指令的有序集合。
- 程序执行过程:将程序从硬盘导入到内存,内存上分为代码区、静态变量区、堆栈区等等
- 文本区:储存处理器执行的代码
- 数据区:存储变量和动态分配的内存:全局变量,局部变量,静态变量,
- 堆栈区:存储着活动进程调用的指令和本地变量,指针变量->栈区,动态分配的内存->堆区
- 进程为程序执行提供各种各样的条件,程序需要各种各样操作,磁盘操作、网络操作等等。需要各种管理,这种动态的管理行为就可以通俗的称之为进程。所 谓程序开始活动起来了。进程是linux系统调度的最小单位
- 进程状态
- 进程是程序的执行过程,根据他的生命周期可以分为3种状态。
- 执行态:该进程正在运行,即进程正在使用CPU
- 就绪态:进程已经具备执行的一切条件,正在等待分配CPU的处理
- 等待态:进程不能使用CPU,若等待事件发生(等待的资源分配到),将其唤醒
- ps
- process:是os的最小单元,地址空间为4g,其中1g给os,另外3g给进程
- ps:查看活动的进程
- ps -aux:查看各个进程的状态,包括运行、就绪、等待等状态(S睡眠 T中断 R运行 Z僵尸)
- ps -aux | grep
aa
:查看指定(aa)进程 - ps -ef:查看所有进程的pid,ppid等信息
- ps -aux :查看cpu内存的使用状态
一些概念
- 并发:宏观上看起来,同时发生,cpu轮转非常快,给人一种错觉,所有的事情在同时发生
- 并行:同时执行,微观上真的同时执行,多个cpu同时执行不同的进程,多个进程真的在同时执行。
- 同步:相同的步伐,进程间相互联系,或者共同完成某件事,需要相互协调
- 异步:不同的步伐,进程间毫无关联
进程标识
进程不能用程序区分
因为不同进程可能执行的是同一个程序
所以,使用ID号来标识区分不同的进程
OS会为每个进程分配一个唯一的整型ID,作为进程的标识号(pid)
进程0时调度进程,常被称为交换进程,他不执行任何程序,是内核的一部分,因此被称为系统进程。
进程除了自身ID外,还有父进程ID,每个进程都会有一个父进程,操作系统不会无缘无故产生一个新进程,所有的进程的祖先进程是同一个进程,叫做init进程,进程号是1.init进程是内核自举后的第一个启动进程。
init进程负责引导系统、启动守护(后台)进程并且运行必要的程序。他不是系统进程,但它以系统的超级用户特权运行。
父进程是负责子进程空间的清理
但linux中,一个人在炒菜,快递打电话来了,让去取快递,他可以叫他的儿子去取快递,自己继续炒菜。从cpu角度想,由于cpu执行速度较快,看起来任务在同时进行(并发执行),这样所有的事情都不耽误。这就是进程的意义,在某个层面上也能说明父进程子进程的意义。
说白了进程的意义就是:让任务并发执行
销毁进程
主动
- main函数自然返回(不是return 返回)
- 调用 exit()函数,标准函数
- 调用 _exit()函数,系统调用函数
exit和__exit函数最大的区别在于exit函数退出之前会检查文件的打开情况,把文件缓冲区的内容写回文件,而__exit直接退出,什么意思?比如打开文件向文件写入内容,如果在文件没有关闭,也没有调用同步到磁盘的函数,文件并没有同步到磁盘,只存在缓冲区内,这时调用exit,那么进程结束时,缓冲区的内容可以同步到文件中,内容已经存在在文件之中了,调用__exit进程直接结束,文件不会有写入的内容。
- 调用abort函数,产生SIGABRT信号
被动
- 接受某种信号(ctrl+c,SIGINT,ctrl+\ SIGOUT);
- 通过kill向进程发信号
前四四种正常的终止,后两种非正常的终止,
但无论哪种方式,进程终止都会执行相同的关闭打来的文件,释放占用的内存资源,后两种终止会导致程序有些代码不能正常执行,比如对象的析构、atexit函数的执行。
启动新进程
#include <stdlib.h>
int system(const char *command);
功能:打开命令或程序 参数: 带路径的启动文件,或者在启动变量里声明的程序直接写程序名
返回值:-1 失败;
- 打开的程序是另一个进程,也可以成为此程序的子进程,因此子进程不一定和父进程视同一个程序,在成功打开所要执行的文件之后,父进程才能继续执行。
进程替换,exec()族函数
全部见文尾
- 重点:>
int execl( const char *path, const char *arg , ... );
int execlp( const char *file, const char *arg , ... );
int execv( const char *path, char * const argv[] );
int execvp( const char *file, char* const argv[]);
四个函数第一个参数都是可执行程序或脚本的程序名,execl.execv需要带有完整的路径
第二参数为任意字符, 起占位作用, 第三个或者后面的字符为调用者的参数,参数列表最后以NULL结尾.
execlp.execvp只需要带有可执行程序的名称即可 系统自动去环境变量去寻找同名文件,execlp.execl需要NULL结尾
函数后缀说明
- l v :参数成现形式
- l: list 参数一个个的列出来
- v: vector参数用数组储存起来
- p: 目标程序,可以省略路径
- e: 环境变量,不考虑
- 把system程序修改,
父进程程序
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("当前进程id:%d\n",getpid());
printf("调用son进程\n");
execl("./son",NULL);
printf("调用子进程结束\n");
return 0;
}
子进程程序
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("这是子进程在执行\n");
printf("son进程id:%d\n",getpid());
sleep(5);
}
> 输出 <
[root@CentOS workdir]# gcc sys.c
[root@CentOS workdir]# gcc son.c -o son
[root@CentOS workdir]# ./a.out
当前进程id:39606
调用son进程
这是子进程在执行
son进程id:39606
[root@CentOS workdir]#
程序运行效果,不会打印调用子进程结束,父子进程ID号相同,表明当前进程被son进程替换
wait进程同步
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * status);
pid_t waitpid(pid_t pid,int * status,int options);
-
pid_t wait(int * status);
返回值为结束的子进程的进程号,无子进程则返回-1,参数为子进程结束状态指针,若不关心结束状态可写入NULL作为参数,若想获得子进程结束状态,将参数地址写入即可,例如定义int statue存储子进程的结束状态,函数调用wait(&statue)即可;
一旦调用wait(),就会立即阻塞自己并自动判断当前进程中是否有某个子进程退出,wait()返回该子进程的状态,终止子进程的ID -
pid_t waitpid(pid_t pid, int * status,int options);
- 参数1 pid:这里需要一个进程ID 但当pid取不同的值时 有着不同的意义
pid > 0 : 只等待进程ID等于pid的子进程,不管已经有多少子进程结束退出 , 只要目标pid未出现 waitpid就会一直等待;
pid==-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid==0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值 - 第二个参数与wait()相同,储存子进程的终止状态,为整型指针类型;
- options: options提供了一些额外的选项来控制外waitpid() 目前LInux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"把他们链接起来使用
WNOHANG: 若pid指定进程不是立即可用(终止状态),waitpid()不阻塞,返回0
WUNTRACED: 若实现支持作业控制··· ···涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多说了。//不清楚
- 参数1 pid:这里需要一个进程ID 但当pid取不同的值时 有着不同的意义
-
返回值和错误
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
函数
- fork()
#include<unistd.h>
pid_t fork(void );
#include<sys/types.h>
#include<suistd.h>
id_t vfork(void);//虚拟创建进程//与父进程先后执行
成功时,子进程的PID在在父进程中返回,子进程返回0,若失败 在父进程返回-1, 子进程不被创建并设置errno;
- 由于 fork()函数有一个函数返回两次的特点,我们可以利用返回值的不同,来实现子进程与父进程之间不同的功能
在创建子进程的时候,父进程的所有代码通过映射的方式与子进程共享,只有子进程进行数据写入时才会真正为子进程复制,“读时共享,写时复制” - vfork:子进程先运行,父进程等到子进程退出或着程序替换后,父进程才会运行
- getpid();
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
getpid()函数返回调用进程的进程ID.(这通常用于生成唯一临时的例程文件名)
getppid()函数返回进程的副进程ID
3. getuid()函数
#include <sys/types.h>
#include <unistd.h>
uid_t getuid(void );
uid_t geteuid(void );
getuid()函数返回实际用户ID;
geteuid()函数返回有效用户ID;
4. getgid()
#include <sys/types.h>
#include <unistd.h>
gid_t getgid(void );
gid_t getegid(void );
getgid()函数 返回实际用户组ID
getegid()函数 返回有效用户组ID
5. exec族
#include <unistd.h>
extern char **environ;
//在当前路径下找path, 命令, 命令参数, NULL
int execl( const char *path, const char *arg, ...);
//在path环境变量里找file, 命令, 命令参数, NULL
int execlp( const char *file, const char *arg, ...);
//在当前路径下找path, 命令, 命令参数, NULL, envp[] = {环境变量}
int execle( const char *path, const char *arg , ..., char * const envp[]);
//在当前路径下找path, argv[] = {命令, 命令参数..., NULL}
int execv( const char *path, char *const argv[]);
//在path环境变量里找file, argv[] = {命令, 命令参数..., NULL}
int execvp( const char *file, char *const argv[]);
//在设置的环境变量里找filename, 命令, 命令参数, NULL, envp[] = {环境变量}
int execve (const char *filename, char *const argv [], char *const envp[]);
函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果失败则返回-1;
事实上只有execve是真正的系统调用,以上5个函数最终都调用execve,
exec函数族 第一个参数必须是有效的路径,第二个参数就行当与一个占位符 地第三个参数才是函数真正关心的;
6. wait
#include<sys/types.h>
#include <sys/wait,h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int* status,int options);
int waitpid(idtype_t idtype,id_t id,siginfo_t *infop,int options);
waitpid是一个阻塞函数,调用wait的父进程会一直等待回收子进程资源,如果没有子进程,wait返回-1,之后才会向下执行 即wait成功返回子进程的ID号,失败返回-1
waitpid是阻塞函数但可以设置为非阻塞,pid设置回收范围,status,接受退出值,options为选项
waitpid如果返回成功,返回状态发生改变的子进程id,如果设定了WNOHANG并且有一个或多个进程被指定了pid的回收,但是状态没有改变则返回 0 ;失败则返回-1
pid的值为:
pid < -1 回收指定进程组内的任意子进程
pid = -1 可以回收任何子进程
pid = 0 回收和当前调用waitpid一个组的所有子进程
pid > 0 回收子进程,该子进程的pid是你所设置的pid。
options的值是以下常数的一个或者多个:
WNOHANG 如果没有子进程,立即返回,非阻塞.
WUNTRACED 如果子进程已经停止,则返回
WCONTINUED 如果通过交付SIGCONT恢复了以停止的子进程,则返回