一、进程的概念: 执行一个程序所分配的资源的总称,进程是程序的一次执行过程动态的,包括创建、调度、执行和消亡 程序就是我们写好的代码,编译成可执行文件后成为程序,存放在硬盘里面。
但是,当我们点击./a,out去执行的时候,它运行起来:把硬盘程序数据读到内存中,占用的cpu资源。等等运行所要的资源的总称,称为进程。 PCB:进程控制块 我们的进程是由一个一个进程链表的来管理的,这个表叫进程控制块PCB,里面有进程标识pid,进程用户,进程状态,,优先级文件描述符表的信息。通过唯一标识pid号进行管理,这个pid其实和fd文件描述符的用法类似,就是一个结构体下标。
一个进程有四种状态:运行态(正常就是运行态) ,等待态(休眠),停止态()进程被命令中止,僵尸态(进程已经被中止,但是pcb资源没有被回收)
进程创建
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值区分父进程和子进程
子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束
子进程成为孤儿进程,被init进程收养
子进程变成后台进程
若子进程先结束
父进程如果没有及时回收,子进程变成僵尸进程
进程回收: 子进程结束时由父进程回收 孤儿进程由init进程回收 若没有及时回收会出现僵尸进程
回收全部子进程
#include <unistd.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,哪个先结束就先回收
status 指定保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值
回收指定进程:
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int option);
成功时返回回收的子进程的pid或0;失败时返回EOF
pid可用于指定回收哪个子进程或任意子进程
status指定用于保存子进程返回值和结束方式的地址
option指定回收方式,0 或 WNOHANG
二、线程的概念; 由于进程间通信数据交互非常麻烦且进程开销非常大。所以我们引入了线程的概念。线程是轻量级的进程: linux其实不区分进程和线程:他们都是一个task_struct链表来管理。 进程右不同的pid号,同一进程下所有线程都共享同一个pid。并且就算你不创建线程,那么进程里面也会有一个线程。就是你一个./a.out这个进程,执行mian函数即是一个进程也是一个线程。多线程下的task_structlm里面的pid号是一样的。
线程的好处: 大大提高了任务切换的效率 避免了额外的TLB & cache的刷新
线程的创建:
#include <pthread.h>
int pthread_create(pthread_t *thread, const
pthread_attr_t *attr, void *(*routine)(void *), void *arg);
成功返回0,失败时返回错误码
thread 线程对象
attr 线程属性,NULL代表默认属性
routine 线程执行的函数
arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,
pthread_t pthread_self(void) 查看自己的TID
创建一个线程去处理 void *(*routine)(void *)这个函数,处理完毕,线程结束并注销。如果进程结束,那么所有的线程都结束,不管你的程序有没有执行完。一个线程用exit();退出,所有的线程都会退出。exit()直接退出程序。
线程的回收:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
成功返回0,失败时返回错误码
thread 要回收的线程对象
调用线程阻塞直到thread结束
*retval 接收线程thread的返回值
线程的取消:
int pthread_cancel(pthread_t thread);
取消是停止线程但是资源没有释放 线程的结束:
#include <pthread.h>
void pthread_exit(void *retval);
结束当前线程
retval可被其他线程通过pthread_join获取
线程私有资源被释放
—————————————————————————————————————————————— 三、线程间的同步和互斥 线程间的同步: 信号量 适用于,当某事件发生后,另一个事件才能发生的情况:例如: 我一个线程要往一个buf里面写数据,我另一个线程要读数据。 肯定要先写才能读数据啊,所以我可以定义一个信号量,起始信号量为0 ,用于信号的同步,当我写好之后我的信号量+1,这样就可以读数据了。读完信号量减一。这样就不能再次读数据了。
信号量操作:
1)初始化信号量:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int val);
成功时返回0,失败时EOF
sem 指向要初始化的信号量对象
pshared 0 – 线程间 1 – 进程间
val 信号量初值
2)信号量的P/V操作:
#include <semaphore.h>
int sem_wait(sem_t *sem); P操作
int sem_post(sem_t *sem); V操作
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
线程间的互斥: 锁机制:mutex 适用于:多线程访问共享资源时,对共享资源上锁。让其它线程不可访问。以避免出现数据紊乱的情况。
比如我一个线程在往一个buf里面写数据 我另一个也在往buf里面写数据 因为线程是并行的,这样这个数据你永远都预测不到它长什么样子了 一个线程申请锁后必须释放锁。 要不然共享资源就变成你家的资源了,别人都用不了。
初始化锁:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t * attr);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
attr 互斥锁属性,NULL表示缺省属性
2)申请锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
执行完临界区要及时释放锁
3)释放锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
执行完临界区要及时释放锁
—————————————————————————————————— 多进程与多线程的比较:
进程: 进程有独立的地址空间 Linux为每个进程创建task_struct 每个进程都参与内核调度,互不影响
线程: 进程在切换时系统开销大 很多操作系统引入了轻量级进程LWP 同一进程中的线程共享相同地址空间 Linux不区分进程、线程
多线程共享的资源: 一个进程中的多个线程共享以下资源: 可执行的指令 静态数据 进程中打开的文件描述符 当前工作目录 用户ID 用户组ID
线程间私有的资源: 每个线程私有的资源包括: 线程ID (TID) PC(程序计数器)和相关寄存器 堆栈 错误号 (errno) 优先级 执行状态和属性
什么时候适用多进程,什么时候使用多线程:
1、首先你得理解多进程的怎么执行的?它是怎么做到多线程并行的。 这个问题其实,多线程是假的并行,根据时间片,一会执行这个线程,一会执行另一个线程。其实他是一个时间处理一条语句,然后时间片轮转着去执行各线程的语句。这比较费时间。 类似于:单核的多进程 时间片轮询进程调度算法,它的思想简单介绍如下: 在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。但实际上在任何一个时间内有且仅有一个进程占有CPU及CPU的运算器。
2多进程的实现: 要是你的进程数小于cpu的核数。比如我exynos4412这个板子是4核的,买手机的时候会看的参数就是什么四核,八核。就是cpu的个数。要是你的进程数小于cpu的核数。那么你的进程就可以做到真正的并发。就是在同一时间片下,会有1,2,3,4等进程同时执行程序的可能。要是进程数大于ccpu核数,那么也将采用 ——时间片轮询进程调度算法。不过有四各时间片可以调度。
结合上面的多进程多线程的实现,以及线程核进程的特性:
如果你想要少消耗资源:多线程
如果你两个程序要频繁交互数据:多线程
如果你想要两程序间数据少交互为了安全:多进程
如果你想要程序跑的快:多进程
选择多进程核多线程要更据实际来选。
比如我做到安防监控的item。我绝对一个选多进程核多线程一起用:
我数据有上行把zigbee的数据交给web。数据下行有web发送命令到硬件实现控制。 那么这上行,和下行完全不用数据交互,而且我的开发板有足够多的空间,而且总共的进程有:cgi 上下行。好像才三个。不过好像还有boa,那这样也才4个进程。我开发板是4核的牛逼。所以在总的A9框架上我选择fork多进程。 然后我数据采集进程要采集很多一个的数据,这时候我就采用多线程。nice————
|