一、进程的概念(了解)
- 程序:编译好的可执行文件
存放在磁盘上的指令和数据的有序集合。
静态的。
- 进程
执行一个程序所分配的资源的总称。
进程是程序的一次执行过程。
动态的,包括创建、调度、执行和消亡。
包括 :正文段 、用户数据段、系统数据段 (前两个称为程序)。
- 进程控制块(pcb) ------ 包括在系统数据段
进程标识(PID) :正整数。
进程用户
进程状态、优先级
文件描述符表
PC寄存器:program counter 程序寄存器 ,存放下一条指定的地址,去译码执行。
注:每一个进程有时间片。
二、进程包含的内容(理解)
- 进程的类型
交户进程:在shell下运行。可以在前台运行,也可以在后台运行。
批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行。
守护进程:和终端无关,一直在后台运行,直到系统关闭。(很重要)
三、进程的状态(理解)
- 运行态:进程 正在运行,或者准备运行(就绪态)
- 等待态:进程在等待一个事件的发生或某种系统资源
分为:可中断 (是否会被某种信号打断)
不可中断
- 停止态:进程被中止,收到信号和后可继续运行。
- 死亡态:已终止的进程,但pcb没有被释放。(多进程尽量避免此状态的出现)
四、查看进程信息
- ps 查看系统进程快照 ps –ef|grep test ps –aux|grep test
- top 查看进程的动态信息 top
- /proc 查看进程详细信息 cd /proc
- 前后台进程切换
运行一个后台进程: ./test & 加一个取地址符
- jobs 查看后台进程
- Ctrl + z : 将前台进程 放到后台 挂起。
- bg 将挂起的进程在后台运行
- fg 把后台运行的进程放到前台运行 fg 1(作业号) 也可把后台挂起的进程放到前台运行。
五、改变进程优先级
- nice 按用户指定的优先级运行进程。 nice -n 2 ./test 将./test的nice值修改为2,普通用户最小修改的nice值为0.
- renice 改变正在运行进程的优先级。renice –n 2 29070 将正在运行的nice值为29070进程的nice修改为2 ,普通用户只能降 低优先级,不能提高(nice值只能改大)。
- 小结
pos :
/proc : 一个放入进程详细信息的目录。
nice /renice :
六、创建子进程(熟悉)
#include <unistd.h>
pid_t fork(void);
- 创建新的进程,失败是返回-1;
- 成功时父进程返回子进程的进程号,子进程返回0;
- 通过fork的返回值区分父进程和子进程;
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid;
if((pid = fork()) < 0) //子进程返回0 ,父进程返回 子进程PID
{
perror("fork");
return -1;
}else if(pid == 0)
{
printf("child process: my pid is %d \n",getpid());
printf("chilie");
exit(0);
}else
{
printf("parent process :my pid is %d \n",getpid());
}
return 0;
}
七、父子进程(熟悉)
- 子进程继承了父进程的内容。
继承了父进程的 代码,数据,以及大部分系统数据
两者的PID、PPID不一样
- 父子进程有独立的地址空间,互不影响。
- 若父进程先结束
子进程成为孤儿进程,被init进程收养。 (子进程必须由父进程来回收,且子进程号不会改变,但子进程号的父进程号可能会改变)
子进程变成后台进程
- 若子进程先结束
父进程如果没有及时回收,子进程变成僵尸进程。(pcb由父进程来收回,释放)
- 思考
1、子进程从何处开始运行?
子进程从fork的下一条指令开始执行。
2、父子进程谁先执行?
由系统调度,不确定谁先执行。
3、父进程中能否多次调用fork?子进程呢?
父进程可多次调用fork,多次创建子进程。
子进程中也可调用fork,相当于子进程创建了子进程,称为孙进程。
八、结束进程(熟悉)
- exit/_exit
#include <stdio.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
- 结束当前的进程 并将 status的低八位返回。
- exit结束进程时会刷新(流)缓冲区。
- _exit结束进程时不会刷新(流)缓冲区,缓冲区中的数据将会直接丢弃掉。
九、exec函数族(熟悉) ------ 一般包含六个函数
- 作用:进程调用extc函数执行某个程序。
- 进程当前内容被指定的程序替换。
- 实现让父子进程执行不同的程序
注:父进程创建子进程,子进程调用exec函数族,父进程不受影响。
-
execl / execlp
#include <unistd.h>
int execl(const char *path,const char *arg,…); //后面的都是字符串
int execlp(const char *file,const char *arg,…);
- 成功时执行指定的程序;失败时返回EOF;
- path 执行的程序名称,包含路径;
- arg… 传递给执行的程序的参数列表;
- file 执行的程序的名称,在PATH中查找;
示例:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
pid_t pid;
if(execl("/bin/ls","ls","-a","-l","/etc",NULL) < 0)
{
perror("execl");
}
if(execlp("ls","ls","-a","-l","/etc",NULL) < 0)
{
perror("execl");
}
return 0;
}
-
execv /execvp
int execv(const char *path , char *const argv[]);
int execvp(const char *file , char *const argv[]);
- 成功时执行指定的程序;失败时返回EOF;
- arg… 封装成指针数组的形式
十、system(熟悉)
- 进程 ---- system
#include <stdlib.h>
int system(const char *command);
- 当前进程中,自动创建一个子进程,去执行command里面包含的程序,执行成功了返回命令command的返回值,失败了返回EOF。
- 父进程等到子进程command中的程序执行完了后,继续执行自己的之后的程序。
小结
- execl/execlp :使用字符串
- execv/execvp :使用数组
- system :直接创建一个子程序执行命令。
十一、主要讲解如何回收子进程(如何回收,什么时候回收)
- wait
- 子进程结束时由父进程回收。
- 孤儿进程由init进程回收。
- 若没有及时回收会出现僵尸进程。
1、进程回收 ----- wait
#include <stdio.h>
pid_t wait(int *status)
- 成功时返回回收的子进程的进程号;失败时返回EOF;
- 若子进程没有结束,父进程一直阻塞;
- 若有多个子进程,哪个先结束就先回收; ----- 调用wait 一个一个去回收;
- status 指定保存子进程返回值 和结束方式的地址;
- status 为NULL 表示直接释放 子进程 PCB ,不接收返回值;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int status;
pid_t pid;
if((pid = fork()) < 0)
{
perror("fork");
return -1;
}else if(pid == 0)
{
printf("zhob \n");
sleep(1);
exit(2);
}else
{
wait(&status);
printf("%x \n",status);
}
return 0;
}
- 该程序说明:程序 fork创建子进程后,进入执行子进程 sleep(1)睡眠一秒后,退出;
- 子进程退出后,父进程继续执行,wait回收子进程,将子进程的关闭参数返回status中,打印出来。
2、进程返回值和结束方式
- 子进程通过exit/_exit/return 返回某个值(0-255); 返回的值只有低八位有效。
- 进程非正常结束 ----- 信号接收 进程;
- 父进程调用wait(&status)回收;
- WIFEXITED(status) : 判断子进程是否正常结束,正常的话返回非零值
- WEXITSTATUS(status) : 获取子进程返回值;
- WIFSIGNALED(status) : 判断子进程是否被信号结束;
- WTERMSIG(status) : 判断结束子进程的信号类型;
如上图:status 第0为为0 表示进程正常结束,非0(大于0)表示进程是信号结束的。
第0--6位:是结束进程的类型;
第8-15位:存放子进程的返回值;
3、waitpid ------ 进程回收
#include <unistd.h>
pid_t waitpid(pid_t pid , int *status , int option);
- 成功时返回回收的子进程的pid 或0,返回0意味着子进程还没有结束;失败时返回EOF;
- 第一个参数pid 可用于指定回收哪个子进程或任意子进程;
- status 指定用于保存子进程返回值和结束方式的地址。
- option 指定回收方式,0(阻塞)或 WNOHANG(非阻塞)(方式0:子进程还在运行,父进程一直阻塞等待子进程结束后在返回子进程pid;方式WNOHANG:子进程还在运行也返回0,告诉父进程子进程还在运行);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int status;
pid_t pid;
if((pid = fork()) < 0)
{
perror("fork");
return -1;
}else if(pid == 0)
{
printf("zhob \n");
sleep(1);
exit(2);
}else
{
waitpid(-1,&status,0);
printf("%x \n",status);
}
return 0;
}
- waitpid(-1,&status,0); 以阻塞的方式 回收任意一个子进程。
十二、 守护进程
三大进程 : 交互进程 、 守护进程、 批处理进程;
- 守护进程特点(了解)
- 守护进程(Daemon)是Linux三种进程之一;
- 通常在系统启动时运行,系统关闭时结束;
- Linux 系统中大量使用,很多服务程序以守护进程形式运行。
1、守护进程的特点
- 前台进程能向终端输入也能向终端输出 ;后台进程只能向终端输出;
- 始终在后台运行;(可以在前台运行其他的进程)守护进程也不能向终端输出
- 独立于任何终端;
- 周期性的执行某种任务或等待处理特定事件;
2、会话、控制终端(了解)
— Linux 以会话(session)、进程组的方式管理进程;
— 每个进程属于一个进程组;如果一个进程创建了一个子进程,则这两个进程属于一个进程组。
- 会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话。
- 终端关闭时,所有相关进程会被结束。
- 为了避免终端关闭时所有相关进程被结束,所有守护进程独立于任何的终端。
3、创建守护进程(熟练)
1、创建守护进程第一步:
- 创建子进程,父进程退出;
if(fork() > 0)
{
exit(0);此时父进程退出;
}
- 子进程变成孤儿进程,被init进程收养;
- 子进程在后台运行;
2、创建守护进程第二步:
- 子进程创建新会话:
if(setsid() < 0)
{
exit(-1);
}
- 子进程成为新的会话组长;
- 子进程脱离原先的终端;
3、创建守护进程第三步:
- 更改当前工作目录:
chdir(“/”); 可读,可执行,不可写
chdir(“/tmp”); 所有用户可读可写 可执行
为啥需要更改当前工作目录:
- 守护进程一直在后台运行,其工作目录不能被卸载;
- 重新设定当前工作目录cwd;
4、创建守护进程第四步:
- 重设文件权限掩码;
if(umask(0) < 0) :设置的文件权限掩码 0 ,设置的是什么就是什么,不受掩码影响。
{
exit(0);
}
- 文件权限掩码设置为0;
- 只影响当前进程;
5、创建守护进程第五步:
- 关闭打开的文件描述符
int I;
for(i=0 ; i<getdtablesize();i++)
{
close(i);
}
- 关闭所有从父进程继承的打开文件;
- 已脱离终端,stdin/stdout/stderr无法再使用;
示例:创建守护进程,每个1秒将系统事件写入文件time.log
setsid(); //子进程创建新会话
umask(0); //重设文件权限掩码
chdir("/tmp"); // 更改当前工作目录
for(i = 0;i<getdtablesize();i++) //关闭打开的文件描述符
{
close(i);
}
if((fp = fopen("time.log","a")) == NULL)
{
perror("fopen");
exit(-1);
}
while(1)
{
time(&t);
fprintf(fp,"%s",ctime(&t));
printf("%s \n",ctime(&t));
fflush(fp);
sleep(1);
}