提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
目录
一、程序与进程
程序:是一种静态的概念,表示有序指令的集合。
进程:是一种动态的概念,表示程序运行的过程(运行过程包含:创建、调度、消亡)
程序中包含:
1.数据段(全局、静态、部分常量...)
2.代码段(程序的指令)
进程中包含:
1.数据段(全局、静态、部分常量...)
2.代码段(程序的指令)
3.堆区、栈区
4.进程控制块(PCB):其实就是一个结构体 task_struct
二、进程状态
1.运行态
R:表示运行态
+:前台运行
2.等待态
进程进入资源等过程,例如:getchar()等待终端输入
1.可中断(软件可中断):S 表示
2.不可中断(软件不可中断,但是硬件可以):D 表示这种状态一般表示与硬件正进行交互
3.停止态
T:表示停止,进程处于休眠的状态,需要通过特定信号将其唤醒
可以使用信号(Ctrl+Z)使进程进入停止态,同样gdb断点调试时也处于停止态(追踪模式)
4.僵尸态
Z:表示进程已经结束,但是资源没有回收完成
5.空闲状态
I :属于不可中断等待态D的内核线程,但是该内核线程可能实际上没有任何负载,该线程就是空闲状态
三、进程的系统调用
每一个进程都有自己的虚拟内存空间(32位系统为4G)
虚拟内存与物理内存有映射关系,但是虚拟内存的大小和物理内存的大小没有关系,与操作系统及CPU有关系
虚拟内存4G:1G内核内存 + 3G虚拟内存
与内核内存、物理内存
fork函数():
pid_t fork(void): 进程创建函数
{
宏观角度:
该函数正确调用的话有两个返回值(区分父子进程):一个>0的值 一个==0的值
错误返回 -1
pid_t p = fork();
if(p == -1):错误发生,常见错误原因:资源不足开辟不了进程
else if(p == 0):表示子进程(由于该子进程没有子进程,所以返回值为0)
else if(p > 0):表示父进程(其实就是子进程ID号,便于父进程管理子进程)
}
fork()一旦被调用,则子进程会拷贝所有父进程的资源(正文段(fork之后的)、数据段、代码段、堆、栈...)
int main(int argc, char *argv[])
10 {
11 pid_t pid = fork(); //创建子进程
12 if(pid == -1)
13 {
14 perror("fork error");
15 exit(1);
16 }
17 else if(pid == 0) //子进程
18 {
19
20 }
21 else if(pid > 0 ) //父进程
循环fork():
int main()
{
fork();
fork();
printf("hello\n");
}
for(i=0;i<2;i++)
{
fork();
printf("*\n");
}
打印多少个“*”号? 用二叉树的方法,寄存器中有"\n"则有6个,没有“”\n“”则有8个
四、进程资源回收
exit(0) //进程正常结束
exit(1) //进程异常结束
getpid():获取当前进程的ID号
void exit(int status)
{
status: 结束状态EXIT_SUCCESS 或 0;代表正常结束
EXIT_FAILURE 或 1 -1 :代表错误结束
要处理缓存区内容(比如:把缓存剩下的内容写入文件)
}
void _exit(int status)
{
status: 结束状态EXIT_SUCCESS 或 0: 代表正常结束 E
EXIT_FAILURS 或 1 -1 代表错误结束
不对缓存做任何处理,直接退出进程
}
资源回收函数:(子进程资源需要被父进程回收)
wait()与waitpid()
{
等待子进程退出,并且回收子进程资源
pid_t wait(int *status)
status:获取进程退出状态 不需要的获取的话 就为NULL
pid_t:返回子进程的进程号
为了避免僵尸进程的出现,通常会用wait来回收子进程的资源
pid_t waitpid(pid_t,int *status,int options)
pid: pid > 0 只等待进程ID等于pid的子进程,不管其他已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去
pid == -1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样
pid == 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬
ps axj 可以查看组进程
pid < 0 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值
status: 与wait 一致
options:为0 与wait函数一样,需要一直等待进程退出(阻塞)
为WNOHANG 不需要等待(不阻塞)
返回值:正常返回子进程的进程号
使用选项WNOHANG且没有子进程结束时:0
错误:-1;
}
五、写时拷贝技术
原因:如果子进程完整拷贝父进程资源,但是并不会修改拷贝后的数据。只会使用,那就没有必要拷贝空间了,直接共享数据。
意义:当子进程或父进程需要修改数据时,才实现拷贝
六、exec函数族
exec函数族提供了一种在进程中启动另一个程序执行的方法。
新的可执行程序把子进程原来的数据段、代码段和堆栈段,全部替换,除了进程号外
int execl(const char *path,const char *arg,...)
int execv(const char *path,char *const argv[]);
int execle(const char *path,const char *arg,...,char *const envp[])
int execve(const char *path,char *const argv[],char *const envp[])
int execlp(const char *file,const char *arg,..)
int execvp(const char *file,char *const argv[])
l: 代表后面的参数的传入是 list 形式(依次传入)
v: 代表后面的参数的传入是以指针数组传入的。例如:char *arv[]= {"ls","-l",NULL};
p: 第一个参数不需要指明路径,只需要传入可执行文件名就可以了(在系统默认PATH路径下寻找)
e: 最后一个参数可以传入环境变量
除了第一个参数以外后面参数所有形式都需要以NULL结尾
例如:
char *arg[] = {"ls","-l",NULL}
execvp("ls",arg);
七、守护进程
1.创建进程,父进程退出(变成孤儿进程,由init进程继续管理,脱离父进程)
2.创建新会话(fork已经从父进程拷贝很多内容:会话期、进程组等...所以要完成脱离,就需要新的会话)setsid()
3.修改工作目录 chdir()(为了提升文件权限,使一般用户不能直接操作)
4.修改文件掩码。umask(0)(创建的文件不受掩码限制,变得更加灵活)
5.关闭文件描述符 close() (0,1,2.对于守护进程没有作用,关闭可以节约资源)