一、多线程
1、线程
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
2、多线程
2.1 安装pthread库
Ubuntu默认是没有pthread库的,需要安装。输入命令:
sudo apt-get install -y glibc-doc manpages-posix-dev
2.2 创建线程
2.2.1创建线程与终止示例
(1)创建线程函数
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*),
void *arg
);
参数说明:
thread 指针指向新线程ID结构体;
attr 含有各种线程属性的属性对象;
start_routine 线程开始执行时调用的函数名;
arg 给start_routine函数提供参数,类型为void *.
返回值 成功返回0,否则返回错误码。
(2) 终止线程函数
void pthread_exit(void *retval);
retval是一个void类型的指针,可以将线程的返回值当作pthread_exit( )的参数传入。
在创建的线程的顶层执行return线程会隐式地调用pthread_exit( )。
(3)线程ID
线程ID可以看作为线程的句柄,用来引用一个线程。类型 “ pthread_t ” ,是一个结构体;
获取线程自身ID:
通过函数:my_tid = pthread_self( )
函数原型:pthread_t pthread_self(void);
比较线程ID是否相等:
pthread_equal( )函数
2.2.2 运行
(1)用makefile将.c编译后,输入命令以运行
./1.pthread_create_exit.out
(2)结果如下:
2.3 线程连接与分离
2.3.1 示例
(1)连接函数原型
int pthread_join(pthread_t thread, void**retavl);
(2)分离函数原型
int pthread_detach(pthread_t thread);
2.3.2 运行结果
2.4 线程初始化与销毁
示例
(1)初始化函数
int pthread_attr_init(pthread_attr_t *attr);
(2)销毁函数
int pthread_attr_destroy(pthread_attr_t *attr);
2.5 互斥锁 & 容量锁
2.5.1 示例
对互斥量进行加锁和解锁函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
2.5.2 结果
2.6 死锁
2.6.1 示例
2.6.2 结果
2.7 条件变量
2.7.1 示例
(1)互斥锁用来给资源上锁,而条件变量是用来等待而不是用来上锁。
(2)条件变量通常和互斥锁同时使用。
2.7.2 运行结果
./6.pthread_conditional_var.out
二、进程
1、简介
1.1 进程与程序
程序放置在存储媒体中(如硬盘,光盘,软盘,磁带等),为实体文件的型态存在
进程:程序被触发后,执行者的权限与属性,程序的程序码与所需数据等都会被载入内存中,操作系统并给与这个内存内的单元一个识别码(pid),可以说,进程就是一个正在运行的程序。
1.2 进程的状态
R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行;
D:不可中断的深睡眠状态,处于这种反状态的进程不能响应异步信号;
S:可中断的浅睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂起;
T:暂停状态或跟踪状态;
W:退出状态,进程即将被销毁;
Z:退出状态,进程成为僵尸进程。
2、线程
2.1 获取环境变量
示例
2.2 创建进程
2.2.1 示例
函数以拷贝父进程的方式创建子进程。子进程与父进程有相同的代码空间、文件描述符等资源
创建后,子进程与父进程开始并发执行,执行顺序由内核调度算法来决定
fork()对父子进程各返回一次,
父进程:子进程的PID;
子进程:0;
失败:小于0错误码。
2.2.2 运行结果
./3-fork-print-pid.out
2.3 进程创建程序
示例
2.4 使用子进程加载新程序
示例
在创建进程后子进程与父进程有相同的代码空间;
实际应用中,需要子进程 去执行另外一个程序,可以在子进程中调用exec族函数它的代码段完全替换为 新的程序,并从这个新进程的main函数开始执行。
2.5 wait()函数
2.5.1 示例
2.5.2 结果
2.6 创建守护程序
2.6.1 示例
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,它不需要用户输入就能运行并提供某种服务。
守护进程的父进程是init进程,因为它真正的父进程在fork出该子进程后就先于该子进程exit退出了,所以它是一个由init领养的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出(无论是向标准输出设备还是标准错误输出设备的输出)都需要特殊处理。
函数原型
int daemon(int nochdir,int noclose);
2.6.2 执行结果
2.7 信号函数
2.7.1 示例
信号(signal),又称为软中断信号,用来通知进程发生了异步事件。进程之 间可以互相发送信号,内核也可以因为内部事件而给进程发送信号。
注意,信号的作用仅仅是通知进程发生了什么事件,并不向该进程传递任何数据。
2.7.2 运行结果
进程子在循环等待信号,ctrl+c我们可以给它一个信号
ctrl+z退出
2.8 子进程:等待、退出及输出
2.8.1 示例
2.8.2 结果
2.9 管道
示例
(1)管道(pipe)是进程间通信的⼀种实现⽅式。在Linux系统中,管道本质上是⼀种特殊的⽂件,它的主要⽤途是实现进程间的通信。
(2)Linux管道分两种类型:匿名管道和命名管道也叫做有名或无名管道
匿名管道最常见的形态就是我们在shell操作中最常用的”|”。它的特点是只能在父子进程中使用,父进程在产生子进程前必须打开一个管道文件,然后fork产生子进程,这样子进程通过拷贝父进程的进程地址空间获得同一个管道文件的描述符,以达到使用同一个管道通信的目的。
此时除了父子进程外,没人知道这个管道文件的描述符,所以通过这个管道中的信息无法传递给其他进程。这保证了传输数据的安全性,当然也降低了管道了通用性,于是系统还提供了命名管道。
2.10 通过命名管道发送文件
示例
2.11 通过管道保存文件
示例
2.12 共享内存写数据
示例
2.13 共享内存读数据
示例
2.14 使用有名信号量同步共享内存实例
示例
(1)服务端
(2)客户端
三、开发板
1、 下载安装运行QEMU
下载成功后,进入ubuntu-18.04_imx6ul_qemu_system目录,执行install_sdl.sh
必须在Ubunut的桌面环境下启动终端,执行./qemu-imx6ull-gui.sh
~/ubuntu-18.04_imx6ul_qemu_system/gui-qemu-imx6ull-gui.sh
可以看到
2、操作QEMU
2.1 打开LCD图像和屏幕
(1)进入root,首先转到myfb-test
cd ./myfb-test
(2)打开屏幕
fb-test
(3)打开图像
./myfb-test /dev/fb0
2.2 串口EEPROM
2.2.1 列出总线与设备
(1)输入以下指令
i2cdetect -l #列出所有i2c总线
i2cdetect -y 0 #列出总线0上的设备
(2)结果
2.2.2 改写
(1)输入以下指令
i2c_usr_test /dev/i2c-0 0x50 w 0x01 0xff
/dev/i2c-0 是这个总线的设备
0x50是被操作地址
w是指改写地址内容
0x01是位置
0xff是写入的内容
(2)结果
2.2.3 读入
(1)指令
i2c_usr_test /dev/i2c-0 0x50 r 0x33
(2)结果
2.3 命令控制LED
2.3.1 安装LED驱动
cd ~
cd led_driver_qemu/
insmod 100ask_led.ko
2.3.2 控制LED
(1)控制LED零一号灯亮,控制LED二三号灯灭
./ledtest /dev/100ask_led0 on
./ledtest /dev/100ask_led1 on
./ledtest /dev/100ask_led2 off
./ledtest /dev/100ask_led3 off
(2)结果
2.4 按键控制LED
2.4.1 安装驱动
cd ~
cd button_driver_qemu/
insmod button_drv.ko
insmod board_100ask_qemu_imx6ull.ko
2.4.2 启动按键控制
./button_led_test
2.4.3 结果