1.fork的相关介绍 fork实际上就是一个函数,这个函数被调用的时候会创建两个进程,一个父进程,一个子进程,上面的这个实力里面,fork函数给子进程返回0,给父进程返回子进程的pid,这样做是为了区分不同的数据流 意思就是这个父进程的数据被拷贝一份,我们子进程对于数据进行修改的时候不会影响父进程的独立性; 当我们执行return语句的时候,这个时候子进程已经被创建完成了,因此子进程和父进程都会返回一个数值,这个代码是被执行了两次的 ,所以这个作为返回值的变量会有不同的数值; 2.进程状态 2.1运行状态 我们之前介绍过这个PCB就像类似于链表一样排队,这个时候就是处于运行状态,在一个时间段里面,所有的进程都会被执行,不可能只让一个程序一直执行而不给其他的程序执行时间 ,让后面的等待的进程继续去走下去,也有可能是我们的用户处于某需要,需要暂时停止这个进程; 下面还有两个进程,一个是僵尸进程,一个是孤儿进程,程序退出来的时候,这个进程并不会直接变成dead的死亡进程,而是有一个 z状态的僵尸进程,因为如果这个进程的父进程没有对于这个进程的相关信息进行回收,这个子进程就会一直处于z状态,也就是僵尸进程,进程的相关支援和PCB不可以被释放掉,这个就会导致这个子进程会一直占用内存空间
既为了不让子进程一直处于僵尸状态(必须),也为了能够知道子进程任务完成的如何,我们需要让父进程对僵尸状态的子进程进行回收—回收子进程资源,获取子进程退出信息。 #include<sys/wait.h> 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<stdlib.h> 5 6 int 2. waitpid方法 同样需要包含头文件sys/wait.h进行使用,不过与wait不同的是,它多了两个参数,一个pid,一个options。 //非阻塞状态实验代码 1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h 1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h> 5 6 int
. */ extern __pid_t vfork (void) __THROW; 它们都是克隆一份主调进程,如果成功就返回子进程的进程ID给父进程,返回0给子进程,出错就返回-1 区别是在内存中vfork argument to waitid: */ #define P_ALL 0 #define P_PID 1 #define P_PGID 2 #endif waitpid的参数options是一个或多个标致符按位“或”的结果 ---- 总结 以下这些函数可以进行进程创建和简单的管理 fork waitpid/wait 通过各方面资料弄懂其参数的意义和返回值的类型
程序的并发执行 一个有4条语句的程序段: S1:a:=x+2; S2: b:=y+4; S3: c:=a+b; S4: d:=c+b; S1和S2可以同时执行-》S3-》S4 程序并发执行的特征 间断性 进程和程序的关系 (1)进程是一个动态概念,程序是一个静态概念 (2)进程具有并行特征,程序没有 (3)进程是竞争资源的基本单位 (4)一个程序对应多个进程,一个进程为多个程序服务。 ,原先的进程的三种状态就变成了5种: (1)执行 (2)活动就绪 (3)静止就绪 (4)活动阻塞 (5)静止阻塞 ? PCB组织成若干个链表或队列 结构体(structure) for example:一个学生的自然信息(姓名,性别,年龄,生日……) PCB中的信息 (1)进程标识符 (2)处理机状态 (3)进程调度信息 (4)进程的控制信息 进程标识符 (1)内部标识符 进程唯一的数字编号,给OS使用 (2)外部标识符 由字母、数字组成,给用户使用 处理机状态 处理机中的主要寄存器 (1)通用寄存器8~32个,暂存信息用
1.通过系统调用获取进程标示符 进程id(PID) 父进程id(PPID) 每一个可执行程序运行起来之后都会成为一个进程,每个进程都有一个自己的id,以及一个父进程id,父进程就是创建自己进程的进程 pid=getppid(); cout<<"Im child: "<<id<<endl; cout<<"Im father: "<<pid<<endl; return 0; } 2. cout << "after fork:Im a process, pid:" << getpid() << ", ppid:" << getppid()<<endl; sleep(2) const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态 我们创建进程就是为了给我们完成某件事,那么这个进程退出时就需要给我们返回结果,一个进程在退出时可以释放掉代码和数据
前言: 本文会开始慢慢切入进程了,当然,切入进程之前,我们需要再次复习一下操作系统,后面接着是介绍什么是进程,如何查看进程,在Linux中对应的文件是哪个等相关的问题,进程大概会持续更新多节,所以说进程的知识点还是相当杂乱的 由上文的操作系统的结构图我们可以知道,操作系统里面有进程管理以及各种管理,那么操作系统管理进程的时候,肯定是允许多进程存在的,比如: 这些,都是进程,想看你的电脑里面存在哪些进程,你只需要esc + shift 我们应该回想上篇文章介绍的系统调用接口: 我们知道系统调用是操作系统给我们的函数,我们目前从未调用过它,现在,就是调用我们人生中第一个系统调用接口的时候了,我们使用man手册查询可知: 从手册的说明书我们就知道2号接口是系统库函数调用 ,也就是我们即将学习的getpid: 1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int main() 6 { 7 while(1
. */ extern __pid_t vfork (void) __THROW; 它们都是克隆一份主调进程,如果成功就返回子进程的进程ID给父进程,返回0给子进程,出错就返回-1 区别是在内存中vfork WAIT_INT (status)) # define WIFSTOPPED(status) __WIFSTOPPED (__WAIT_INT (status)) ---- 总结 以下这些函数可以进行进程创建和简单的管理
本节我们将从linux启动的第一个进程说起,以及后面第一个进程是如何启动1号进程,然后启动2号进程。 至此1号进程就完美的创建成功了,而且也成功执行了init可执行文件。 2号进程 2号进程,是由1号进程创建的。而且2号进程是所有内核线程父进程。 所以说所有的内核线程的父进程都是2号进程,也就是kthreadd。 总结: linux启动的第一个进程是0号进程,是静态创建的 在0号进程启动后会接连创建两个进程,分别是1号进程和2和进程。 2号进程会在内核中负责创建所有的内核线程 所以说0号进程是1号和2号进程的父进程;1号进程是所有用户态进程的父进程;2号进程是所有内核线程的父进程。 我们通过ps命令就可以详细的观察到这一现象。 至此有关0号进程,1号进程,2号进程的内容分析完毕。
2.进程通信的意义(为什么?): 并发进程之间的相互通信是实现多进程间协作和同步的常用工具。具有很强的实用性,进通信是操作系统内核层极为重要的部分。 二、进程通信方式(怎么做?) 2.消息传递系统 在消息传递系统中,进程间的数据交换以消息为单位,在计算机网络中,消息又称为报文。程序员直接利用系统提供的一组通信命令(原语)来实现通信。 Createthread() 2.线程与进程的关系 称线程为轻量级进程(lightweight process,LWP),而传统意义上的进程则被称为重量级进程(heavyweight process (2)并发性 在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间也可以并发执行,因而使操作系统具有更好的并发性,从而能更有效地使用系统资源和提高系统的吞吐量。 2)就绪状态:线程除处理机以外的其他资源已经全部获得。 3)阻塞状态:线程在其执行过程中因某事件受阻而暂停执行。 5.线程的同步: 常用线程的同步机制有: 1)互斥锁。 2)条件变量。
1.单道程序的顺序执行及特征 (1)程序执行有固定的时序: 输入进程1 -> 计算进程1 - 输出进程1 -输入进程2 -计算进程2 -输出进程2 ...... S3, S3→S2 二、进程同步 1.基本概念 (1)同步: 并发进程在执行次序上的协调,以达到有效的资源共享和相互合作,使程序执行有可再现性。 (2)互斥: 两个并行的进程A、B,如果当A进行某个操作时,B不能做这一操作,进程间的这种限制条件称为进程互斥,这是引起资源不可共享的原因。 互斥是一种特殊的同步。 //Parbegin 和 Parend中间会来夹着几个进程:process1、process2...,意思是这里面的进程 是可以并发执行的,里面的进程就需自要利用到信号量机制了。 // Begin 、 End : 例1: 设系统中有两个进程P1、P2,P1进程负责计算数据,将结果放入缓冲区Buf,P2进程从缓冲区Buf中取数据输出。试写出两个进程的同步算法。
2. 私有进程和公有进程的区别: 私有进程: android:process=":remote",以冒号开头,冒号后面的字符串原则上是可以随意指定的。 这种设置形式表示该进程为当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。 公有进程: 进程名称不以“:”开头的进程都可以叫全局进程,如android:process="com.secondProcess",以小写字母开头,表示运行在一个以这个名字命名的全局进程中,其他应用通过设置相同的 ShareUID可以和它跑在同一个进程。
通过上面的介绍和解释,我们该能够想到进程,应该必须包含哪些基本的要求。 1、2、描述进程-PCB 所以到底什么是进程呢?(我们可以通过先描述再组织来看到底什么是进程。) ok,是存在的,也就是说cwd修改成功,并且能够实现"在当前目录下创建文件" 1、5、2、PID 每一个进程都有自己唯一的标识符,叫做进程的PID。(process id)。 子进程的父进程就是一开始本来进行的进程。 1、7、子进程的使用 子进程的使用肯定不能是专注于和父进程使用相同代码的用处啊,既然有子进程,那就说明子进程应该能够做到和父进程运行不一样的代码啊。那我们怎么去像这样去使用子进程呢? 疑点: 1、同一个id怎么能够同时拥有两个值(与父子写时拷贝和虚拟地址空间有关,现在没法说明白,以后说) 2、fork有两个返回值,会返回两次?fork也是一个函数,只不过是OS提供的。
进程 当我们安装好Apache后,Apache会给我们创建一个Apache用户和Apache用户组。 可以查看最新的用户信息: ? 也可以查看最新的用户组: ? 查看apache进程(一个主进程控制多个子进程,子进程处理web请求: ? 主进程的进程号保存位置: ? 模块 在Apache的服务目录里,会有apache模块配置文件 ?
(2)怎么做? ① 语法格式 xlogo 输入该命令后,包含 X 标识的一个小窗口将在屏幕的某个地方出现。 2.&-使进程在后台运行 假设我们想要 shell 提示符返回,但又不终止 xlogo 程序,那么可以通过让该程序在后台运行来实现。 (1)后台是什么? 后台运行进程不会受到任何键盘输入的影响,包括试图用来中断它的 Ctrl-C 键。 (2)怎么做? 要想在启动程序时让程序在后台运行,可以在命令后面加上 &(和号字符)来实现。 4.停止(暂停)进程 如果我们只是想要暂停进程,而不是终止进程,那么通常需要我们将前台运行的进程移到后台去运行。 (2)bg-使进程在后台运行 ① 语法格式 bg [参数] '' 功能: 让进程移到后台运行。 (该命令的运行效果与在指令后面添加符号&的效果是相同的,都是将其放到系统后台执行。)
pm2 npm install -g pm2 npm install -g pm2-web //linux有效 command pm2 logs,实时显示日志 pm2 list,查看启动进程 pm2 stop id[name],关闭进程,all所有 pm2 kill,关闭所有 pm2 monit,查看详细信息 pm2 start,启动程序 –watch,修改app.js后自动重启 debug "request": "launch", "type": "node-terminal" }, package.json配置 "run":"pm2
一个实时性要求很高的进程和硬件进行通信,其他的线程可以慢条斯理的和其他process进行通信 2. Pool of worker threads. 它的作用是复制当前进程(包括进程在内存里的堆栈数据)为1个新的镜像. 然后这个新的镜像和旧的进程同时执行下去. 相当于本来1个进程, 遇到fork() 函数后就分叉成两个进程同时执行了. 要实现上面所说的功能, 实际上就是让子进程和主进程执行不同的代码啊. 所以fork() 实际上有返回值, 而且在两条进程中的返回值是不同的, 在主进程里 fork()函数会返回主进程的pid, 而在子进程里会返回0! 所以我们可以根据fork() 的返回值来判断进程到底是哪个进程, 就可以利用if 语句来执行不同的代码了! 2 线程Thread pthread_create()可以用来创建线程。
. */ #define W_OK 2 /* Test for write permission. */ #define X_OK 1 #define _ASM_GENERIC_ERRNO_BASE_H #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 call */ #define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG
同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。 这里先介绍使用AIDL 来进行进程间通信的流程,分为服务端和客户端两个方面。 2、下面继续《艺术探索》中关于AIDL的进阶知识: 1)进程间的Listener 假设有一种需求:用户不想时不时地去查询图书列表了,太累了,于是,他去问图书馆,“当有新书时能不能把书的信息告诉我呢?”。 2)RemoteCallbackList 如果你以为AIDL就这样结束了,那你就错了,AIDL远不止这么简单,目前我们还有一些难点还没有涉及。 别忘了对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程,这就是为什么AIDL中的自定义对象都必须要实现Parcelable接口的原因。 那么我们要怎么做才能实现解注册的功能?
/tcpclient.x 127.0.0.1 hello 2 -->OK emacs@ubuntu:~/c$ 服务端会打印信息并且返回 emacs@ubuntu:~/c$ . PF_INET #define AF_INET6 PF_INET6 emacs@ubuntu:/usr/include$ grep PF_INET bits/socket.h #define PF_INET 2
2、绝对没有问题,但是性能不佳的方案 当系统发生进程切换,从进程A切换到进程B,从而导致地址空间也从A切换到B,这时候,我们可以认为在A进程执行过程中,所有TLB和Cache的数据都是for A进程的, 2、TLB操作的基本思考 根据上一节的描述,我们了解到地址翻译有global(各个进程共享)和local(进程特定的)的概念,因而tlb entry也有global和local的区分。 比如A---B--->A这样的场景中,如果TLB足够大,可以容纳2个task的tlb entry(现代cpu一般也可以做到这一点),那么A再次切回的时候,TLB是hot的,大大提升了性能。 (2)如果A和B在一个地址空间中(一个进程中的两个线程),那么我们也暂时不需要flush TLB。 除了进程切换,还有其他的TLB flush场景。 (2)arm64支持ASID的概念,理论上进程切换不需要TLB的操作,不过由于HW asid的编址空间有限,因此我们扩展了64 bit的software asid,其中一部分对应HW asid,另外一部分被称为