以下内容为我在学完linux入门shell命令后所学的一些内容,并把他稍微整理了一下,希望对大家能有所帮助
1. 多进程
a. 了解进程的创建(PCB)
在Linux中,父进程以分裂的方式来创建子进程,创建一个子进程的系统调用叫做fork()。
b. 了解僵⼫进程,孤⼉进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
孤儿进程并不会有什么危害。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
危害:保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
c. 了解进程的⼏种通信⽅式
管道、消息队列、共享内存、信号量、信号、Sockey
管道:管道这种通信方式效率低, 不适合进程间频繁地交换数据。当然,它的好处,自然就是简单,同时也我们很容易得知管道里的数据已经被另一个进程读 取了。
消息队列:进程间可以频繁地交换数据。缺:消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理 另一进程 读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。
共享内存:消息队列的读取和写入的过程,都会有发生用户态与内核态之间的消息拷贝过程。当多个进程向同一个共享内存中写入数据时可能会产生覆盖(如在工作中,当多人同时修改一份共享文档时别人可能会将你添加的内容修改删除或覆盖),如果只读则没有任何问题。
信号量:为了防止多进程竞争共享资源,而造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被一个进程访问。正好,信号量就实现了这一保护机制。
信号:信号一般用于一些异常情况下的进程间通信,是一种异步通信,它的数据结构一般就是一个数字。信号是进程间通信机制中唯一的异步通信机制
Sockey:跨网络与不同主机上的进程之间通信,Socket通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信。
i. 重点了解
1. 信号
信号一般用于一些异常情况下的进程间通信,是一种异步通信,它的数据结构一般就是一个数字。
在Linux操作系统中,为了响应各种各样的事件,提供了几十种信号,分别代表不同的意义。我们可以通过kill -l命令,查看所有的信号。
运行在shell终端的进程,我们可以通过键盘输入某些组合键的时候,给进程发送信号。例如
- Ctrl+C产生 SIGINT 信号,表示终止该进程;
- Ctrl+Z产生 SIGTSTP 信号,表示停止该进程,但还未结束;
如果进程在后台运行,可以通过kill命令的方式给进程发送信号,但前提需要知道运行中的进程PID号,例如:
- kill -9 1050,表示给PID为1050的进程发送SIGKILL 信号,用来立即结束该进程(例如:在任务管理器右键结束进程);
所以,信号事件的来源主要有硬件来源(如键盘Cltr+C)和软件来源(如kill命令)。
信号是进程间通信机制中唯一的异步通信机制
进程需要为信号设置相应的监听处理,当收到特定信号时,执行相应的操作,类似很多编程语言里的通知机制。
2. 共享内存
消息队列的读取和写入的过程,都会有发生用户态与内核态之间的消息拷贝过程。那共享内存的方式,就很好的解决了这一问题。
现代操作系统,对于内存管理,采用的是虚拟内存技术,也就是每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程A和 进程B的虚拟地址是一样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。
共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去, 大大提高了进程间通信的速度。
当多个进程向同一个共享内存中写入数据时可能会产生覆盖(如在工作中,当多人同时修改一份共享文档时别人可能会将你添加的内容修改删除或覆盖),如果只读则没有任何问题。
2. 多线程
a. 了解线程的创建
新增的线程可以通过调用pthread_create函数创建
1. #include <pthread.h> 2. 3. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);//Compile and link with -pthread. /* 在程序编译时,需要链接线程库 pthread */
参数:
参数1:thread表示,在线程创建成功的时候,用来接收线程的线程id;
参数2:attr表示的是所创建线程的属性,一般情况使用NULL,表示缺省属性。
参数3:start_routine是一个(参数是void*,返回值是void*)函数指针,子线程的执行函数(需要由用户自定义)的地址;
参数4: arg给参数3做为参数。
返回值:
成功返回0
失败返回错误号;
PS:
1. 创建线程成功,子线程和主线程的执行顺序是随机执行;
2. 在创建线程成功后,
如果子线程中没有使用exit,子线程退出,不会影响其他线程。
如果主线程不是因为pthread_exit退出,主线程退出,子线程会结束。
3. 创建线程后,主线程和子线程访问的是同一空间的数据
b. 了解线程的退出⽅式
- ⾃然退出
①从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。
ii. ⾃杀
②线程可以调用pthread_exit终止自己。
iii. 他杀
③一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
c. 了解线程的资源回收
一 系统自动释放
如果想在线程结束时,由系统释放线程资源,则需要设置线程属性为detach,是线程分离主线程
代码上,可以这样表示:
1. pthread_t t; 2. 3. pthread_attr_t a; //线程属性 4. 5. pthread_attr_init(&a); //初始化线程属性 6. 7. pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); //设置线程属性 8. 9. pthread_create( &t, &a, GetAndSaveAuthviewSDRStub, (void*)lp); //建立线程
二 由另一个线程将该资源释放
代码上,可以这样表示:
1. pthread_t t; 2. 3. pthread_create( NULL, NULL, GetAndSaveAuthviewSDRStub, (void*)lp); 4. 5. pthread_join( t); 6. 7. pthread_join( t)等待线程t退出,并释放t线程所占用的资源。
pthread_join函数会阻塞等待指定线程退出,然后回收资源,这样就有同步的功能,使一个线程等待另一个线程退出,然后才继续运行,但是对于服务器程序如果主线程在新创建的线程工作时还需要做别的事情,这种方法不是很好,就需要使用方法一
d. 了解线程的通信⽅式以及线程安全问题
线程安全
线程安全就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的,正确的,那么就说这些线程是安全的。
i. 安全机制
1. 原⼦变量
原子变量的操作是一种不可以被打断的操作,原子操作需要硬件支持,因此是架构相关。
类似于汇编的一条汇编指令,不可以被分割。
两种原子变量:
- 原子整型操作
- 原子位操作
2. 锁
ii. 通信⽅式
1. 条件变量
2. 信号量
信号量
信号量(semaphore)是一种用于提供不同进程之间或者一个给定的不同线程间同步手段的原语。信号量多用于进程间的同步与互斥,主要有三点:
①同步:处理竞争就是同步,安排进程执行的先后顺序就是同步,每个进程都有一定的先后执行顺序。
②互斥:互斥访问不可共享的临界资源,同时会引发两个新的控制问题(互斥可以说是特殊的同步)。
③竞争:当并发进程竞争使用同一个资源的时候,我们就称为竞争进程。
共享资源通常分为两类:一类是互斥共享资源,即任一时刻只允许一个进程访问该资源;另一类是同步共享资源,即同一时刻允许多个进程访问该资源;信号量是解决互斥共享资源的同步问题而引入的机制。
信号量工作机制:
可以直接理解成计数器,信号量会有初值(一般>0),每当有进程申请使用信号量,通过一个P操作来对信号量进行-1操作,当计数器减到0的时候就说明没有资源了,其他进程要想访问就必须等待,当该进程执行完这段工作(称之为临界区)之后,就会执行V操作来对信号量进行+1操作。
临界区:临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。
临界资源:只能被一个进程同时使用(不可以多个进程共享),要用到互斥。
信号量跟互斥锁很相似。
当有进程要求使用共享资源时,需要执行以下操作:
①系统首先要检测该资源的信号量;
②若该资源的信号量值大于0,则进程可以使用该资源,此时,进程将该资源的信号量值减1;
③若该资源的信号量值为0,则进程进入休眠状态,直到信号量值大于0时进程被唤醒,访问该资源;
当进程不再使用由一个信号量控制的共享资源时,该信号量值增加1,如果此时有进程处于休眠状态等待此信号量,则该进程会被唤醒。
3. 消息队列(不算系统⾥⾯的,⼀般是⼈为构造,可以作为了解)
3.
a. 线程和进程的区别
①进程是资源分配最小的单位,线程是CPU调度的最小单位
②进程有自己独立地址空间,线程共享进程中的地址空间
③进程的创建消耗资源大,线程的创建相对较小
④进程的切换开销大,线程的切换开销相对较小
b. 线程和进程的各⾃优缺点
1、 什么是进程?
答案:进程其实就是一个静态的概念。在机器上实际上运行的都是线程(线程,进程的一部分。)
2、 什么是线程?
答案:线程是一个程序内部的顺序控制流。一个进程里面有一个主方法叫main方法。是一个进程里面不同的执行路径。
3、 什么时候会用到线程?
答案:当系统中或者开发中。遇到高并发 并行的情况下为了解决负载均衡的问题,就会使用到线程。线程可以提高cpu的利用率。
4、 在一个时间点上。Cpu只能支持一个线程的运行(由于windows在一个进程中将多个线程执行速度非常的快。所以好多人认为是那是多线程。其实在同一个时间点上只有一个线程在运行)。
5、 注意:真正的多线程就是当你的机器为双cpu或者是双核的。那么这个时候确实是真正的多线程在运行。
为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等
多线程的好处:
1.使用线程可以把占据时间长的程序中的任务放到后台去处理
2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度
3.程序的运行效率可能会提高
4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了.
多线程的缺点:
1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.
2.更多的线程需要更多的内存空间
3.线程中止需要考虑对程序运行的影响.
4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生
多线程优点:
无需跨进程边界;
程序逻辑和控制方式简单;
所有线程可以直接共享内存和变量;
线程方式消耗的总资源比进程方式好;
多线程缺点:
每个线程与主程序共用地址空间,受限于2GB地址空间;
线程之间的同步和加锁控制比较麻烦;
一个线程的崩溃可能影响到整个程序的稳定性;
到达一定的线程数程度后,即使再增加CPU也无法提高性能;
线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU
多进程优点:
每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;
通过增加CPU,就可以容易扩充性能;
可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;
每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大
多线程缺点:
逻辑控制复杂,需要和主程序交互;
需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算
多进程调度开销比较大;
4. Makefile