概念
进程与程序的区别
进程:一个正在运行的代码就叫做进程,是动态的,会占用内存
程序:一段封装好的待运行的代码或可执行文件,是静态的,会占用磁盘空间
单道与多道程序
单道:程序一个一个排好队,一个一个执行,若代码A阻塞,则代码B不能立即执行,需等待代码A执行结束代码B才会执行
多道:程序之间相互独立,各个程序之间同时执行,各执行各的,互不影响,它们在系统管理程序控制下,相互穿插执行(并行,并发)
并行与并发的区别
并行:有多个核,一个程序对应一个核,同时执行程序
并发:有一个核,多个程序对应一个核,程序之间按顺序相互交替执行,在宏观上也是并行。
进程控制块(PCB)了解
进程运行时,内核为每一个进程分配一个PCB(进程控制块),维护进程的相关信息,linux中的进程控制块是task_struct控制块
task_struct控制块
进程号
概念:
每一个进程都是由一个进程号来标识,其类型是pid_t,进程号范围是0~32767。进程号是唯一的,但是进程号是可以重复使用的(前提是当前程序1抢得cpu处理权后,执行1的代码,当1代码执行结束后,会释放该进程号,这时其他进程就可以使用该进程号)。
PID 进程号
PPID 父进程号
PGID 进程组号
获取进程id
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
pid_t getpid(void);
功能:获取当前进程号
参数:无
返回值:本进程号 (失败-1)
获取进程的父进程id
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
pid_t getppid(void);
功能:获取调用此函数进程的父进程id(ppid)
参数:无
返回值:调用此函数进程的父进程id(ppid)
获取组进程号
#include <unistd.h>
#include <sys/types.h>
pid_t getpgid(pid_t pid);
功能:获取当前组进程号,也就是同组进程中第一个进程的进程号
参数:pid 进程号
返回值:当前组进程号,也就是同组进程中第一个进程的进程号
创建进程fork
概念:
父子进程
系统允许一个进程创建新进程。此新进程就是子进程,这就是创建的父子进程。
int x = fork();
函数:
#include <unistd.h>
#include <sys/types.h>
pid_t fork(void);
功能:用于进程创建一个新进程,该新进程就是该进程的子进程,原进程被称为父进程。
参数:无
返回值:
成功:子进程返回0,父进程返回子进程id。getpid pid_t为整型。
失败:返回-1
注意:
fork创建失败的两个主要原因:
1.当前进程数已到达了系统规定的最大上限数,这时的errno的值被设为EAGAIN。
2.系统内存不足,这时errno的值被设为ENOM。
注意:
子进程会在fork函数后开始执行
示例一:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
printf("啦啦啦\n");
int id = fork();
if(id < 0)
{
printf("子进程创建失败id:%d\n",id);
}
else if(id > 0)
{
printf("父进程的进程号是:%d\n,子进程的进程号是:%d\n",getpid(),id);
}
else if(id == 0)
{
printf("父进程的进程号是:%d\n,创建的子进程的进程号是:%d\n,创建的id:%d\n",getppid(),getpid(),id);
}
printf("德玛西亚\n");
while(1);
return 0;
}
父子进程关系
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
printf("啦啦啦\n");
int id = fork();
printf("德玛西亚\n");
while(1);
return 0;
}
示例2:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
printf("啦啦啦");//没有\n所以啦啦啦在缓冲区
int x = fork();
printf("德玛西亚\n");
while(1);
return 0;
}
啦啦啦在缓冲区,所以父进程打印不出来“啦啦啦”,当子进程执行时\n才会将父进程中的“啦啦啦”冲刷出来,和“德玛西亚“拼接起来一起打印,打印两遍。
分析:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
//printf是库函数有缓冲区 hello world先放入缓冲区 被子进程复制了一份
printf("hello world1");
//write是系统调用 直接将字符串 写入1号文件 没有缓冲区 不被子进程复制
//0,输入
//1,输出
//2,错误输出
write(1,"hello world2",12);
//创建子进程
pid_t pid = fork();
return 0;
}
进程状态
分类:
进程资源回收
概述:
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
int x = fork();
if(x < 0)
{
printf("创建失败\n");
}
else if(x == 0)
{
for(int i = 0;i < 10; i++)
{
printf("子进程正在进行第%d次执行",getpid(),i);
sleep(1);
}
}
else if(x > 0)
{
printf("父进程正在等待子进程结束\n");
wait(NULL);
printf("父进程已回收子进程%u\n",x);
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int pid = fork();
if (pid < 0)
{
printf("创建进程失败");
}
else if (pid == 0)
{
for (int i = 0; i < 10; i++)
{
printf("子进程%u正在执行第%d次\n", getpid(), i);
/*
sleep:休眠
参数休眠时间
liunx下参数单位秒,windows下单位毫秒
*/
sleep(1);
}
// 进程退出
// 参数0正常退出,非0异常退出,参数就是子进程退出状态值
// exit(-1);//库函数,底层封装_exit,效率低
_exit(-1); // 系统调用函数,效率高
printf("Hello"); // 因为子进程已经退出,所以此代码不会执行
}
else if (pid > 0)
{
printf("父进程正在等待子进程执行完毕\n");
wait(NULL);
printf("父进程已经回收了子进程%u\n", pid);
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int pid = fork();
if (pid < 0)
{
printf("创建进程失败");
}
else if (pid == 0)
{
for (int i = 0; i < 3; i++)
{
printf("子进程%u正在执行第%d次\n", getpid(), i);
/*
sleep:休眠
参数休眠时间
liunx下参数单位秒,windows下单位毫秒
*/
sleep(1);
}
// 进程退出
// 参数就是子进程退出状态值
// exit(100);//库函数,底层封装_exit,效率低
_exit(100); // 系统调用函数,效率高
printf("Hello"); // 因为子进程已经退出,所以此代码不会执行
}
else if (pid > 0)
{
printf("父进程正在等待子进程执行完毕\n");
int status = 0;
pid_t ret = wait(&status);
printf("子进程%u,是否正常退出(WIFEXITED):%d\t,退出状态值(WEXITSTATUS)为:%d\n",ret,WIFEXITED(status),WEXITSTATUS(status));
printf("父进程已经回收了子进程%u\n",pid);
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h> //exit
int main(int argc, char *argv[])
{
// 创建子进程
pid_t pid = fork();
if (pid < 0)
{
perror("fork\n");
}
else if (pid == 0) // 子进程
{
int i = 0;
for (i = 10; i > 0; i--)
{
printf("子进程%u剩余的生命%d\n", getpid(), i);
sleep(1);
}
// 进程退出
// exit(-1);//库函数 底层调用的系统调用_exit
_exit(10); // 系统调用函数 10就是子进程退出的状态值
}
else if (pid > 0) // 父进程
{
int status = 0;
printf("父进程%u 等待子进程%u的结束\n", getpid(), pid);
pid_t ret = waitpid(-1, &status, 0); // 不关心状态 直接实参为NULL
if (WIFEXITED(status)) // 子进程正常退出
{
// 取出状态值
printf("子进程%u已经结束 状态值为:%d\n", ret,
WEXITSTATUS(status));
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
void fun()
{
printf("进程退出\n");
}
int main(int argc, char const *argv[])
{
// atexit()
int p_id = fork();
if (p_id == 0)
{
atexit(fun);
// 子进程
printf("子进程已开启\n");
sleep(1);
printf("子进程退出\n");
exit(10);
}
else if (p_id > 0)
{
// 主进程
wait(NULL);
sleep(5);
printf("父进程结束\n");
}
return 0;
}
示例2
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
void fun01()
{
printf("函数1\n");
}
void fun02()
{
printf("函数2\n");
}
void fun03()
{
printf("函数3\n");
}
int main(int argc, char const *argv[])
{
atexit(fun01);
atexit(fun02);
atexit(fun03);
atexit(fun01);
sleep(3);
return 0;
}
特殊进程
僵尸进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int pid = fork();
if (pid == 0)
{
printf("子进程");
_exit(-1);
}
else if(pid > 0)
{
while(1);
}
return 0;
}
孤儿进程
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>//exit
int main(int argc, char *argv[])
{
//创建子进程
pid_t pid = fork();
if(pid < 0)
{
perror("fork\n");
}
else if(pid == 0)//子进程
{
printf("子进程%u\n", getpid());
while(1);
_exit(-1);
}
else if(pid > 0)//父进程
{
}
return 0;
}
守护进程(重要)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char const *argv[])
{
// 1,忽略挂断信号SIGHUP,防止被终端误杀
//SIG_IGN:忽略指定的信号
signal(SIGHUP, SIG_IGN);
pid_t pid = fork();
//父进程结束
if (pid > 0)
_exit(-1);
//子进程设置会话
setsid();
//改变工作目录(非必须)
chdir("/");
//设置权限掩码
umask(0002);
//关闭文件描述符0 1 2
close(0);
close(1);
close(2);
//守护进程的核心任务
while (1)
{
//核心任务
}
return 0;
}
多进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
//错误演示:创建两个子进程
//实际创建了3个,主线程2个,子线程一个
// for (int i = 0; i < 2; i++)
// {
// int pid = fork();
// }
//正确演示:创建两个子进程
//主线程创建2个,子线程不创建
for (int i = 0; i < 2; i++)
{
int pid = fork();
if(pid == 0)
{
break;
}
}
return 0;
}
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>//exit
int main(int argc, char *argv[])
{
//创建3个子进程
int i=0;
for(i=0;i<3;i++)
{
pid_t pid = fork();
if(pid == 0)//子进程
break;
}
if(i==0)//子进程1
{
//任务1的代码
printf("子进程1:%u\n", getpid());
sleep(5);
_exit(-1);
}
else if(i==1)//子进程2
{
//任务2的代码
printf("子进程2:%u\n", getpid());
sleep(3);
_exit(-1);
}
else if(i==2)//子进程3
{
//任务3的代码
printf("子进程3:%u\n", getpid());
sleep(4);
_exit(-1);
}
else if(i == 3)//父进程
{
//回收子进程的资源
while(1)
{
//-1:等待任一子进程
//WNOHANG:不阻塞
pid_t ret = waitpid(-1, NULL, WNOHANG);
if(ret > 0)
{
printf("子进程:%u已经退出\n", ret);
}
else if(ret == 0)
{
continue;//还有子进程在运行 需要继续等待
}
else if(ret < 0)
{
break;//所有子进程都已经结束
}
}
}
return 0;
}
进程补充
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>//exit
int main(int argc, char *argv[])
{
pid_t pid = fork();
if(pid == 0)//子进程
{
sleep(3);
int num = 0;
scanf("%d", &num);
printf("子进程%u中的num=%d,所属终端名:%s\n", getpid(), num,ttyname(0));
}
else if(pid > 0)//父进程
{
int num = 0;
scanf("%d", &num);
printf("父进程%u中的num=%d,所属终端名:%s\n", getpid(), num,ttyname(0));
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h> //exit
int main(int argc, char *argv[])
{
pid_t pid = fork();
setpgid(pid, pid);
if (pid == 0) // 子进程
{
printf("子进程%d,所属组id:%d\n", getpid(), getpgrp());
}
else if (pid > 0) // 父进程
{
printf("父进程%d,所属组id:%d\n", getpid(), getpgrp());
}
while (1)
;
return 0;
}
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>//exit
int main(int argc, char *argv[])
{
pid_t pid = fork();
if(pid == 0)//子进程
{
printf("子进程1id:%d\t,所属组id:%d\t,会话
id:%d\n",getpid(),getpgrp(),getsid(getpid()));
sleep(2);
setsid();
printf("子进程2id:%d\t,所属组id:%d\t,会话
id:%d\n",getpid(),getpgrp(),getsid(getpid()));
while(1);
}
else if(pid > 0)//父进程
{
printf("主进程id:%d\t,所属组id:%d\t,会话
id:%d\n",getpid(),getpgrp(),getsid(getpid()));
while(1);
}
return 0;
}
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
int num = 10;
// pid_t pid = fork();
pid_t pid = vfork();
if(pid == 0)//子进程
{
num=100;
printf("子进程num=%d\n",num);
// sleep(3);
_exit(-1);
}
else if(pid > 0)//父进程
{
// wait(NULL);
printf("主进程num=%d\n",num);
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//which 命令,查看命令存储路径
/*
execl("命令存储路径","命令名","参数1","参数2",...,NULL);
*/
// execl("/usr/games/sl","sl",NULL);
// execl("/bin/ls","ls","-a","-l","-h",NULL);
// execl("/bin/ls","ls","-alh",NULL);
/*
execlp:从path环境变量下查找指定名,如果没有无法运行
execlp("命令名","命令名","参数1","参数2",...,NULL)
env命令查看环境变量
*/
// execlp("ls","ls","-alh",NULL);
// char *tem[] = {"/ls","-alh",NULL};
// execv("/bin/ls",tem);
char *tem[] = {"/ls","-alh",NULL};
//execvp("ls",tem);
execvpe("ls",tem);
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
printf("exec执行前\n");
//exec后的程序将不在执行
execl("/bin/ls","ls","-alh",NULL);
printf("exec执行后\n");
return 0;
}
#45_test.c文件
#include <stdio.h>
#include <unistd.h>q
int main(int argc, char const *argv[])
{
for (int i = 0; i < 3; i++)
{
printf("test:%d\n",i);
sleep(1);
}
return 0;
}
#使用gcc 45_test.c -o 45_test,将源文件编译为可执行文件
#45_code.c文件
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int pid = vfork();
if (pid < 0)
{
printf("创建进程失败");
return 0;
}
else if(pid == 0)
{
//子进程中代码
// for(int i = 0; i < 3; i++)
// {
// sleep(1);
// printf("子进程:%d\n",i);
// }
execl("./45_test","45_test",NULL);
_exit(-1);
}
else if(pid > 0)
{
// 父进程中代码
for(int i = 0; i < 6; i++)
{
printf("code:%d\n",i);
sleep(1);
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
printf("system前\n");
system("ls -alh");
printf("system后\n");
return 0;
}