菜鸟学习历程【17】进程控制编程
来源:互联网 发布:数据库安全性设计 编辑:程序博客网 时间:2024/06/08 17:25
进程控制编程
进程:进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元;
进程与程序组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信息)
进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。
进程的生命周期
创建: 每个进程都是由其父进程创建,进程可以创建子进程,子进程又可以创建子进程的子进程
运行: 多个进程可以同时存在,进程间可以通信
撤销: 进程可以被撤销,从而结束一个进程的运行
进程的状态(运行):
执行状态:进程正在占用CPU
就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片
等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒
Linux进程
Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点。
也就是说,每个进程都是一个独立的运行单位,拥有各自的权利和责任。其中,各个进程都运行在独立的虚拟地址空间,因此,即使一个进程发生异常,它也不会影响到系统中的其他进程。
进程ID:(PID)标准进程的唯一数字
父进程:(PPID)
启动进程的用户ID(UID)
进程互斥:进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。
临街资源:(共享资源)一次只允许一个进程访问的资源称为临界资源
临界区:进程中访问临界资源的那段程序代码称为临界区
进程同步: 一组并发进程按一定的顺序执行的过程称为进程间的同步,具有同步关系一组并发进程称为合作进程,合作进程间互相发送的信号称为消息或事件。
进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
调度方式:抢占(优先级) 、非抢占式
算法:
- 先来先服务调度算法
- 短进程优先调度算法
- 高优先级优先调度算法
- 时间片轮转法
目前,后两者最为常用。
死锁:多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进
获取ID
pid_t getpid(void) 获取进程IDpid_t getppid(void) 获取父进程ID例如:#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){ printf("PID = %d\n", getpid()); printf("PPID = %d\n", getppid()); while(1); return 0;}
进程创建
1.fork()
pid_t fork(void) 创建子进程
子进程的ID号为父进程的ID号 + 1
特别:返回值返回两次,父进程返回子进程的ID号,子进程返回0
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <stdlib.h>int main(){ pid_t pid; pid = fork(); if(-1 == pid) { perror("fork"); exit(1); } else if(0 == pid) //子进程返回0,子进程执行部分 { printf("Child Process Id = %d\nParent Process Id = %d\n", getpid(), getppid()); } else //父进程执行部分 { printf("Parent Process Id = %d\n", getpid()); } return 0;}
思考:下面这段代码的最终输出结果是???
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){ pid_t pid; pid = fork(); int count = 0; count++; printf("%d\n", count); return 0;}
注意:使用fork()创建进程后,会有两个进程存在,对于fork而言,这两个进程在不同的地址空间(之前我们讲过对于一个进程而言会有一个4G的虚拟内存),那么对于fork后的两个进程,会存在两个地址一模一样的空间,子进程会将父进程的所有代码(除去各自独有的代码)复制到自己的代码段。
写时复制:当某个进程访问某个变量,并要修改时,则会分配另一个空间。
对于上面这个情况,当子进程尝试count++时,会改变count的值,此时系统会开辟另一个空间给子进程,此时count为0,那么经过自加后,输出count为1;对于父进程而言,也是一样的。
所以,上面的代码最终结果是:
11
2.vfork()
pid_t vfork(void);
- vfork的子进程必须加exit()退出,否则会出错;
- 子进程先运行,子进程运行后,再执行父进程
- vfork的子进程与父进程共享相同的资源
下面这段代码的输出结果又是如何的呢?
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int main(){ pid_t pid; int count = 0; pid = vfork(); if(-1 == pid) { perror("vfork"); exit(1); } else if(0 == pid) { count++; printf("count = %d\n", count); exit(1); } else { count++; printf("count = %d\n", count); } return 0;}
我们知道,vfork的子进程和父进程共享相同的资源,那么在执行子进程中的count++时,count由原来的0变成1,输出1;
执行父进程的count++时,count又由1变成2,输出2;
所以最终结果是:
12
如果我将程序改动如下,结果又是什么呢?
......int main(){ pid_t pid; pid = vfork(); int count = 0; ...... }
将pid = vfork();与int count = 0;的位置交换,最终结果会是如何?
结果如下:
11
为什么呢?
此时子进程和父进程依旧共享相同的资源,但它也共享了”int count = 0”这句话,所以在子进程执行完毕后,count = 1;当父进程开始执行时,先执行的是int count = 0,将原来的1又置为0,再经过自加操作,那么输出结果还是1;
3.exec函数族
exec启动一个新程序,替换原有的进程,因此进程的PID不会改变
1.int execl(const char *path, const char *arg, …);
path:被执行程序名(含完整路径)。arg: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
例如:
使用execl在进程中调用ls,显示当前目录下的文件信息。
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int main(){ pid_t pid; pid = vfork(); if(-1 == pid) { perror("vfork"); exit(1); } else if(0 == pid) { printf("Child process:%d\n", getpid()); execl("/bin/ls",NULL); } else { printf("Parent process\n"); } return 0;}
2.int execv(const char *path, char *const argv[]);
参数:
path:被执行程序名(含完整路径)。
argv[]: 被执行程序所需的命令行参数数组。
例如:
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){ char *argv[] = {"ls",NULL}; execv("/bin/ls", argv); return 0;}
进程等待
先介绍两个概念,一个是孤儿进程,一个是僵尸进程。
孤儿进程:其父进程在它之前结束,不能再对它进行回收
僵尸进程:子进程结束后,没有被回收的时间段内,被称为僵尸进程。
1.pid_t wait(int *status);
功能:阻塞该进程,直到其某个子进程退出。
例如:
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>int main(){ pid_t pid; pid = fork(); int status; if(-1 == pid) { perror("fork"); exit(1); } else if(0 == pid) { sleep(2); printf("Child Process\n"); exit(6); } else { printf("Parent Process\n"); wait(&status); //如果不需要知道它的退出类型,wait的形参写NULL也可以 if(WIFEXITED(status)) { printf("Exit Normally %d\n", WEXITSTATUS(status)); } } return 0;}
如果不写wait(),在子进程sleep的过程中,其父进程就已经结束了,那么子进程就会变成孤儿进程,但加上wait后,父进程直到子进程结束才会退出。
...else if(0 == pid) { printf("Child Process\n"); exit(6); } else { sleep(2); printf("Parent Process\n"); wait(&status); if(WIFEXITED(status)) { printf("Exit Normally %d\n", WEXITSTATUS(status)); } } ...
如果我们将子进程中的sleep函数放在父进程中,那么在子进程执行结束后,父进程需要沉睡两秒后才执行,这两秒内,父进程没有去回收子进程,所以称子进程为僵尸进程,但两秒钟后,子进程又被回收。
2.pid_t waitpid(pid_t pid, int *status, int options);
参数pid:(欲等待的子进程识别码)
pid < -1: 等待进程组识别码为pid绝对值的任何子进程。
pid = -1: 等待任何子进程,相当于wait()。
pid = 0: 等待进程组识别码与目前进程相同的任何子进程。
pid > 0: 等待任何子进程识别码为pid的子进程。
参数option:(通常设置为0)
WNOHANG: 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED :如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
进程退出
exit()与_exit()
exit():在停止进程之前,要检查文件的打开情况,并把文件缓冲区中的内容写回文件才停止进程。
_exit():直接使进程停止,清除其使用的内存,并清除缓冲区中内容
所以,我们通常选择使用exit(),而不是用_exit();
- 菜鸟学习历程【17】进程控制编程
- linux历程--进程控制编程
- 【嵌入式学习历程13】Linux进程控制编程
- 菜鸟学习历程【16】文件编程
- 菜鸟学习历程【19】进程通信(1)
- 菜鸟学习历程【20】进程通信(2)
- 从菜鸟到编程高手的学习与认知历程
- 从菜鸟到编程高手的学习与认知历程
- linux学习 进程控制编程
- linux历程--进程控制理论基础
- 菜鸟之计算机学习历程
- java菜鸟的学习历程
- C_sharp:一个菜鸟学习历程
- 菜鸟学习历程【11】数据结构
- 菜鸟学习历程【12】链表
- 菜鸟学习历程【21】线程
- 学习linuxC编程实战-----linux进程控制
- Linux系统编程学习之《进程控制》
- 在SSM框架中出现Field error in object 'xxx' on field 'XXX': rejected value [];错误
- [RCA][2017-12-06] Django后台通过API返回CSS
- web前端项目同时集成JSP和FreeMaker___多视图解析器配置
- samba服务
- matlab实现人脸识别---PCA与最近邻法
- 菜鸟学习历程【17】进程控制编程
- leetcode编程记录16 #31 Next Permutation
- 课程小结——距离度量方法
- 51nod 2006 飞行员配对(二分图最大匹配)
- mysql-connector-java 6.x配置问题解决方案
- 学习~笔记2
- Google Gson官方教程
- C语言学习的第五天
- MongoDB 基础学习