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