一、进程的概念
1.什么是程序:
存放在磁盘上的指令和数据的有序集合(文件)是静态的
2.什么是进程 :
进程是程序的一次执行过程,是动态的包括创建,调度,执行和消亡
3.进程内容:
系统数据包括:进程控制块(进程PID进程号,进程用户,状态,优先级,文件描述符表)CPU寄存器(进程调度,实现宏观上的并发),堆栈
4.进程类型:
交互进程
批处理进程
守护进程
5.进程状态
运行态
等待态
可中断
不可中断
停止态
死亡态
二、查看进程信息
1.ps 查看系统进程快照
2.top 查看进程动态信息
3. ./proc 文件查看
三 、进程相关命令
1.nice 按用户优先级运行进程
3.renice 改变正在运行的进程优先级
4.jobs 查看后台进程
5.bg 将挂起的进程在后台运行
6.fg 把后台运行的进程放到前台运行
四、进程相关命令
1.进程的创建-----fork()
创建信的进程,失败是返回-1;
成功是父进程返回进程的进程号,子进程返回0;
通过返回值区分父子进程;
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main ()
{
pid_t pid;
if((pid = fork()) < 0){
perror("fork");
exit(-1);
}else if(pid == 0){
printf("child process: my pid = %d\n",getpid());
}else{
printf("parent process: my pid = %d\n",getpid());
}
return 0;
}
2.父子进程的关系
子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束: 子进程成为孤儿进程,被init进程收养 ,子进程变成后台进程
若子进程先结束:父进程没有及时回收子进程变成僵尸态进程.
子进程从何时开始运行?
从fork下一条语句开始运行
父子进程谁先执行?
内核调度,都有可能先运行.
3.进程结束 exit/_exit
void exit(int status);
void _exit(int staus);
结束当前进程并将status返回
exit结束进程时回刷新缓冲区
int main ()
{
printf("hello");
exit(0);
printf("word");
}
运行结果:hello
int main ()
{
printf("hello");
_exit(0);
printf("word");
}
运行结果:没有任何显示.
4.exec 函数族
进程调用exec 函数族执行某个程序
进程当前的内容被指定程序替换
实现让父子进程执行不同的程序:
1.父进程创建子进程
2.子进程调用exec函数族
3.父进程不受影响
(shell终端)
① execl/execlp
int execl(const char *path,const char *arg,…);
const *path : 执行的程序名称,包含路径;
const char arg :传递给执行程序的参数列表,最后一个传NULL
成功返回指定程序;失败返回EOF
int execlp(const charfile,const char *arg,…);
const *file : 执行的程序名称,不包含路径;
在PATH中查找
const char *arg :传递给执行程序的参数列表,最后一个传NULL
成功返回指定程序;失败返回EOF
② execv/execvp
int execv(const char *path,char *const argv[],…);
对比:execl 只是把执行程序的参数换成字符指针数组
int execvp(const char *file,char *const argv[],…);
对比:execlp只是把执行程序的参数换成字符指针数组
比上面的更灵活
③system
int system (const char *command);
成功返回命令commamd 的返回值;失败返回EOF
自动创建子进程.
当前的进程等待commamd 进程结束才继续运行.
5.进程回收
①pid_t wait(int *status);
pid_t wait(int *status);
成功返回子进程进程号,失败返回EOF
若子进程没有结束,父进程一直阻塞
若多个子进程,哪个先结束就先回收.
status 指定保存子进程返回值的结束方式
status为NULL标识直接释放子进程PCB,不接收返回值
子进程通过exit/_exit/return 返回某个值(0-255);
父进程调用wait(&status)回收
WIFEXITED(status) 判断进程是否正常结束
WEXITSTATUS(status) 获取子进程返回值
WIFSIGNALED(status) 判断子进程是否被信号结束
WTERMSIG(status); 获取结束子进程的信号类型
② pid_t waitpid(pid_t pid ,int *status,int option);
pid_t waitpid(pid_t pid ,int *status,int option);
成功返回回收的子进程的pid或者0(子进程还没结束),失败返回EOF
pid 可用于指定回收哪个子进程或者任意子进程(传-1);
status指定用于保存子进程返回值和结束方式的地址
option 指定回收方式 0(阻塞方式) 或者WNOHANG(非阻塞)
五、守护进程
通常在系统启动是运行,系统关闭时结束
守护进程特点:始终在后台运行,独立于任何终端(避免终端关闭的时候守护进程退出),周期性的执行某种任务或者等待处理特定的事件.
1.守护进程的创建
1.创建子进程,父进程退出
if(fork()>0){
exit(0);
}
子进程变成孤儿进程,被init 进程收养
子进程在后台运行
2.子进程创建新会话
if(setsid()<0){
exit(-1);
}
子进程成为新的会话组组长
子进程脱离原先的终端
3.更改当前进程的工作目录
chdir("/");
chdir("/tmp");
守护进程一直在后台运行,其工作目录不能被卸载
4.重设文件权限掩码
if(umask(0)<0){
exit(-1);
}
5.关闭打开的文件描述符
int i;
for(i=0;i<gettablesize();i++){
close(i);
}
关闭所有从父进程继承的打开文件
已经脱离终端, stdin/stout/stderr无法再使用
创建守护进程,每隔一秒向time.log里写人系统时间
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid;
FILE *fp;
time_t t;
int i;
if((pid = fork())<0){
perror("fork");
exit(-1);
}else if(pid>0){
exit(0);
}
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));
fflush(fp);
sleep(1);
}
return 0;
}
六、进程间通信
1.无名管道(pipe)
无名管道的特点:
只能用于具有亲缘关系 的进程之间的通信(无名管道文件系统不可见,非亲缘关系的不认识此文件描述符)
单工(不能同时写和读)的通信模式,具有固定的读端和写端
无名管道创建是会返回两个文件描述符,分别用于读写管道
无名管道的创建:
int pipe(int pfd[2]);
成功返回0,失败返回-1;
pfd:包含两个元素的整形数组,用于保存文件描述符
pfd[0] 读管道 pfd[1] 写管道
实现子进程一和子进程二分别写消息,父进程读
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main ()
{
char buf[32];
pid_t pid1,pid2;
int pfd[2];
if(pipe(pfd) <0){
perror("pipe");
exit(-1);
}
if((pid1 = fork())<0){
perror("fork:");
exit(-1)