一、嵌入式Linux多任务:进程、线程
1、区分
硬件条件:单个CPU单个核
单任务:一个任务执行完毕之后下个任务才能执行;
多任务:任务的执行可以被中断,中断之后可以执行其他任务(并发/并行)。
并发:指多个任务同时被触发(不一定同时执行);
并行:指多个任务同时进行。
单核CPU:并发;
多核CPU:既存在并发,也存在并行。
2、进程
进程:实现多任务
特点:
1)给每个进程分配独立的地址空间,4G大小(1G内核,3G用户空间:栈、堆、数据段、代码段);
2)互不干扰。
3、进程创建方式:fork > exec函数族 > system > vfork(按使用频率排序)
4、进程的退出:exit()库函数/清理缓冲、_exit()系统调用API/不清理缓冲;
5、进程等待:wait()
解决:僵尸进程
6、Linux下特殊进程:僵尸进程、孤儿进程、守护进程、控制台进程和后台进程
学习目标:学会创建多任务程序:进程
二、进程间通信(IPC)
1、广义上的进程间通信:通过文件、内存或数据库进行父子进程间的数据传输。
(普通文件)
2、狭义上的真正的“进程间通信”(由内核提供的方法:内核作为中转池)
(1)管道
1)无名管道:内核开辟一个“管道”,通信的进程通过共享这个管道,实现通信。
定义:int pipe(int pipefd[2]);
特点:
a.管道只允许具有血缘关系的进程间通信,如父子进程间的通信;
b.管道只允许单向通信;
c.读管道时,如果没有数据的话,读操作会休眠(阻塞),写数据时,缓冲区写满会休眠(阻塞)。
2)有名管道
定义:int mkfifo(const char *pathname,mode_t mode);
使用步骤:
a.进程调用mkfifo创建有名管道;
b.open打开有名管道;
c.read/write读写管道进行通信。
特点:
a.任意两个进程通信;
b.使用一个“有名管道”是无法实现双向通信的,因为也涉及到抢数据的问题。
c.实现双向通信:创建两个管道,一个管道用来读,一个管道用来写
(2)消息队列:由内核创建的用于存放消息的链表。
1)消息组成:
消息编号:识别消息用;
消息正文:真正的信息内容。
2)使用步骤:
a.使用msgget函数创建新的消息队列、或者获取已存在的某个消息队列,并返回唯一标识消息队列的b.标识符,后续收发消息就是使用这个标识符来实现的;
c.收发消息;
d.使用msgctl函数,利用消息队列标识符删除消息队列。
3)消息队列API:
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
4)特点:
a.传送有格式的消息流;
b.多进程网状交叉通信时,消息队列是上上之选;
c.能实现大规模数据的通信。
(3)共享内存:让同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新。
1)使用步骤:
a.进程调用shmget函数创建新的或获取已有共享内存;
b.进程调用shmat函数,将物理内存映射到自己的进程空间;
c.shmdt函数,取消映射;
d.调用shmctl函数释放开辟的物理内存空间。
2)共享内存API:
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
3)特点:
a.减少进入内核空间的次数;
b.直接使用地址来读写缓存时,效率会更高,适用于大数据量的通信。
(4)信号量:当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况。
1)资源保护的操作:
互斥:多进程共享操作时,多个进程间不关心谁先操作、谁后操作的先后顺序问题,只关心一件事,即我操作时别人不能操作;
同步:多个共享操作时,进程必须要有同一操作的步调,按照一定顺序操作;
解决方法:加锁。
2)使用步骤:
a.进程调用semget函数创建新的信号量集合,或者获取已有的信号量集合;
b.调用semctl函数给集合中的每个信号量设置初始值;
c.调用semop函数,对集合中的信号量进行pv操作(加锁解锁);
d.调用semctl删除信号量集合。
3)信号量API:
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, …);
int semop(int semid, struct sembuf *sops, unsigned nsops);
4)pv操作:
p操作(加锁):对信号量的值进行-1,如果信号量的值为0,p操作就会阻塞;
v操作(解锁):对信号量的值进行+1,v操作不存在阻塞问题。