进程
目录
一.进程的相关概念
1.进程和程序有什么不同?
程序:是一个静态的概念。 它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念,是数据 段、正文段。
进程:是一个动态的概念。是程序的执行过程和载体,是一个程序的一次执行的过程,是数据段、正文段、堆栈段。程序执行和资源分配的最小单位。
2.进程的状态
分别为:初始态 就绪态 执行态 阻塞态 结束态
(1)就绪状态:进程已获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
(2)执行状态:进程已获CPU,正在执行。单处理机系统中,处于执行状态的进程只一个;多处理机系统中,有多个处于执行状态的进程。
(3)阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)
3.进程悲苦的一生
出生 --> fork --> 成家立业 exec --> 消亡
其中消亡又分为:a.自然消亡 b.自杀(return 0) c.他杀(kill)
不得不说确实悲苦,根据是父亲还是孩子的消亡又分为“僵尸进程”和“孤儿进程”
僵尸进程:父进程没消亡 但不为子进程收尸 子进程是僵尸进程 避免僵尸进程
孤儿进程:父进程消亡 子进程还在运行 这时 子进程是孤儿进程 会被init进程领养并收尸
4.进程ID
init 进程是进程祖先,它的 PID 是1
ps aux 查看所有用户所有进程详细信息
pstree 查看进程树
top 动态查看进程信息
q 退出
二.进程的系统调用
1.进程的查看和创建
1.1获取进程的ID
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);//pid 是int别名
//功能:获取当前调用进程PID
pid_t getppid(void);
//功能:获取当前调用进程的父进程的PID
1.2 fork
#include <unistd.h>
pid_t fork(void);
功能:创建新进程
返回值:成功调用返回两次 在父进程中返回子进程PID 在子进程中返回0
出错返回-1
1.3咋区分两个进程
通过他们的返回值来进行区分
if(0==id)//说明在子进程中
{
//子进程
printf("子进程:%d father:%d\n",getpid(),getppid());
}
else if(id>0)//说明在父进程中
{
//父进程
printf("父进程:%d father:%d\n",getpid(),getppid());
}
1.4控制父子进程执行的顺序
父子进程执行的先后顺序不确定 取决于系统调度策略
比如我们可以通过sleep来控制进程执行的先后顺序
if(0==id)//说明在子进程中
{
//子进程
printf("子进程:%d father:%d\n",getpid(),getppid());
}
else if(id>0)//说明在父进程中
{
sleep(1);
//父进程
printf("父进程:%d father:%d\n",getpid(),getppid());
}
总结:
1.子进程复制父进程的数据空间 堆栈 代码段(共享正文段)
2.复制后 两个进程空间完全独立 子进程变量改变 父进程不会影响
2.进程的消亡
2.1进程的终止方式
有8种方式使进程终止,其中
有5种为正常终止,它们是
1:从 main 返回
2:调用 exit
3:调用 _exit 或 _Exit
4:最后一个线程从其启动例程返回
5:最后一个线程调用 pthread_exit
有3种为异常终止,它们是
6:调用 abort()
7:接到一个信号并终止
8:最后一个线程对取消请求做出响应
函数名: abort
功 能: 异常终止一个进程
用 法: void abort(void);
头文件:#include <stdlib.h>
说明: abort函数是一个比较严重的函数,当调用它时,会导致程序异常终止
2.2exit与_exit
exit 会刷新IO缓存
_exit 不刷新IO缓存
#include <stdlib.h>
void exit(int status);
//功能:结束进程
#include <unistd.h>
void _exit(int status);
//功能:立即结束进程
3.进程的收尸
3.1 wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);//scanf()
功能:等待一个子进程结束 取得子进程结束的状态 将其保存在status中
返回值:返回被收尸的子进程PID 如果没有子进程返回-1
注意:
a、父进程调用wait时 父进程会阻塞等待子进程结束
b、如果父进程不关心子进程退出的状态 wait参数可以传NULL 就丢弃结束信息
c、wait后 子进程占用的资源会被释放
参数:status 如果为空 就丢弃结束信息
WIFEXITED(status) 判断是否正常退出 真-->正常退出 假-->是异常退出
WEXITSTATUS(status) 取得进程结束时的返回值
WIFSIGNALED(status) 判断进程是否被信号终止 如果是 则返回真
WTERMSIG(status) 判断是被哪个信号终止的
3.2 waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能:收尸
参数1:
pid <- 1 等待其进程组组长ID(GID)等于pid的绝对值的任意子进程
pid == -1 该进程任意子进程
pid > 0 进程ID为pid的进程 5678
waitpid(-1,&sta,0)等价于wait(&sta)
参数2 同wait
参数3:
0 等待子进程结束
WNOHANG 不等待
返回值:
>0 被收尸的子进程的PID
0 参数3用WNOHANG 且没有子进程退出
-1 出错
三.进程的任务执行
exec族函数:在本进程中加载另一个程序 并且从头开始执行 本进程会完全被新进程替换
注意:在执行完毕后 除了进程号没变以外 其他内容都被替换掉了
应用:
1)本进程重生
2)通过fork创建新进程 让新进程执行其他任务—主要应用
1. execl
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
格式:execl(可执行文件的路径,可执行文件名称,参数1,参数2,参数3,….,NULL)
注意:可执行文件路径 必须是which结果 which ls
2. execvp
#include <unistd.h>
int execvp(const char *file, char *const argv[]);
格式:execvp(可执行文件名,指针数组);
四.进程的守护精灵
1.相关的概念
前台进程:依附终端 终端结束 进程结束
后台进程:不依附终端 自己独立存在
进程组: 多个进程
会话: 多个进程组
2.创建守护进程
1).创建子进程 父进程结束(init领养子进程)
2).创建新会话 setsid()---彻底脱离终端
3).改变当前工作目录 chdir("/tmp")
4).修改文件掩码
掩码:掩掉权限 umask(0)
5).关闭所有打开的文件描述符
int getdtablesize(void);//获取当前进程打开的文件总个数
int n=getdtablesize();
for(i=0;i<n;i++)
{
close(i);
}
实例
#include <time.h>
void mydaenon()
{
//1.创建子进程 父进程退出
pid_t id = fork();
if(id>0)
{
exit(-1);
}
//只剩下子进程
//2.创建会话
setsid();
//3.改变当前工作目录
chdir("/tmp");
//4.修改文件掩码
umask(0);
//5.关闭所有打开的文件描述符号
int n=gettablesize();
int i;
for(i=0;i<n;i++)
{
close();
}
}
int main()
{
//创建守护进程
mydaemon();
//守护进程任务
time_t t;
FILE *fp = fopen("time.log","a");
while(1)
{
//将系统时间写入日志文件
t = time(NULL);
fprintf(fp,"%s",ctime(&t));
fflush(fp);
sleep(1);
}
}