线程(thread)
1.线程概述
线程是包含在进程内部顺序执行流,是进程中的实际运作单位,是操作系统能够进行调度基本单位。
2.线程管理
创建线程 pthread_create( )
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,否则返回错误码。
终止线程 pthread_exit( )
void pthread_exit(void *retval);
- retval是一个void类型的指针,可以将线程的返回值当作pthread_exit( )的参数传入。
- 在创建的线程的顶层执行return线程会隐式地调用pthread_exit( )。
线程ID (tid)
- 线程ID可以看作为线程的句柄,用来引用一个线程。类型pthread_t,是一个结构体;
- 获取线程自身ID:
通过函数:my_tid = pthread_self( )
函数原型:pthread_t pthread_self(void); - 比较线程ID是否相等:
pthread_equal( )函数
线程创建与终止示例
运行结果如下:
线程连接与分离
- 连接与分离线程
线程可以分为分离线程(DETACHED)和非分离线程(JOINABLE)两种: - 线程分离
函数原型:
int pthread_detach(pthread_t thread);
- 线程连接
函数原型:
int pthread_join(pthread_t thread, void**retavl);
线程连接与分离示例
运行结果如下图所示:
线程初始化与销毁
基本的线程属性包括:栈大小、调度策略和状态属性封装于属性结构体(pthread_attr_t结构体类型)中,其中初始化及销毁函数为:
- 初始化
int pthread_attr_init(pthread_attr_t *attr);
- 销毁
int pthread_attr_destroy(pthread_attr_t *attr);
线程初始化与销毁示例
创建与销毁函数原型
- 静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - 动态初始化
int pthread_mutex_init( ); - 销毁
int pthread_mutex_destroy( ); - 加锁
pthread_mutex_lock()
pthread_mutex_trylock() - 解锁
pthread_mutex_unlock()
3.互斥量/互斥锁 Mutex
什么是互斥量
临界区:必须以互斥方式执行的代码段,即在临界区的范围内只能有一个活动的执行线程。
- 互斥量(Mutex),又称为互斥锁,是用来保护临界区的特殊变量,每个互斥锁内部有一个线程等待队列,保存等待该互斥锁的线程。
- 有锁定(locked)和解锁(unlocked)状态:
- 锁定状态,某个特定的线程正持有这个互斥锁,其他线程将阻塞在互斥锁的等待队列内;
- 解锁状态,没有线程持有这个互斥锁,如果某个线程试图获取这个互斥锁,那么这个线程就可以得到这个互斥锁而不会阻塞。
互斥量/互斥锁 Mutex示例
运行结果如下图所示:
死锁
这里通过让两个进程不断争夺运行资源来使程序进入死锁状态。
死锁范例
运行结果如下图所示:
条件变量
- 互斥锁用来给资源上锁,而条件变量是用来等待而不是用来上锁。
- 条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。
注:条件变量通常和互斥锁同时使用。
条件变量示例
运行结果如下图所示:
进程
1.进程环境
程序与进程
程序(program)
- 程序是静态的
- 程序 = 指令序列(完成特地任务) + 数据
进程(process)
- 是一个已经开始执行但还没有终止的程序实例。
- 包含有进程运行环境、内存地址空间、进程ID、和至少一个被称为线程的执行控制流等资源。
- 一个程序可以实例化为多个进程实体。
- 进程是一个动态的实体
程序到进程转换
- 查找命令所对应程序文件的位置;
- 使用fork()函数为之创建一个新进程;
- 在新进程中调用exec族函数装载程序文件,并执行程序文件的main()函数。
进程的状态
- R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行;
- D:不可中断的深睡眠状态,处于这种反状态的进程不能响应异步信号;
- S:可中断的浅睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂起;
- T:暂停状态或跟踪状态;
- W:退出状态,进程即将被销毁;
- Z:退出状态,进程成为僵尸进程。
进程的参数
- 环境变量
进程获取环境变量的3种途径:
- 通过main()函数的第3个参数env获取;
main()函数的三种原型:
int main();
int main(int argc,char*argv[ ]);
int main(int argc,char*argv[ ],char*env[ ]);
agrc表示命令行参数的个数;argv是指向参数的各个指针所构成的数组,env参数是指向环境变量字符串的数组
2. 通过environ全局变量获取;
3. 通过getenv()函数获取。
进程环境示例
2.进程基本操作
创建进程-fork()
- 函数以拷贝父进程的方式创建子进程。子进程与父进程有相同的代码空间、文件描述符等资源
- 创建后,子进程与父进程开始并发执行,执行顺序由内核调度算法来决定
- fork()对父子进程各返回一次,
父进程:子进程的PID;
子进程:0;
失败:小于0错误码。
创建进程-fork()示例
运行结果如下图所示:
终止进程
- 正常终止
调用类exit()函数。
main()函数return返回,也是调用类exit()函数 - 异常终止
调用abort()函数实现,通过SIGABRT信号调用
被执行程序:负责打印参数个数、参数列表和环境变量
wait()函数
函数原型:pid_t wait(int *status)
status是用来保存子进程退出状态的指针
- 帮助父进程获取其子进程的退出状态
- 如果父进程未调用wait()函数,则子进程的退出信息将一直保存在内存中
- 僵尸进程:子进程先终止,但父进程没有调用wait()一直占用系统资源。
- 孤儿进程:被init进程收官,由init进程负责收集它们的退出状态
wait()函数示例
运行结果如下图所示:
守护进程
- 后台运行的一种特殊进程,独立于控制终端,周期性地执行某种任务或等待处理某些事件
- 守护进程属于孤儿进程,父进程变为init进程
- Linux系统的大多数服务器就是通过守护进程实现
- 常用daemon()创建守护进程
- 函数原型:
int daemon(int nochdir,int noclose);
守护进程示例
3.系统信号
系统信号概念
- 信号(signal),即软中断,用来通知进程发生了异步事件。是较为复杂的通信方式。
- 一般方向:进程之间、内核给进程。
- 仅通知事件类型,不传递数据
收到信号后进程的处理方式
- 忽略
- 系统默认(大部分的信号使得进程被终止)
- 类似中断,进程调用预先指定的、对应的函数
信号函数
- sigaction函数
改变信号的处理方法,SIGKILL和SIGSTO例外。 - Kill()函数
向指定的进程发送一个指定的信号,成功返回0,否则返回-1。
信号函数用法示例
由于没有信号发生,因此一直在sleep,运行结果如下所示:
4.线程与进程的关系
一个线程只能是某一个进程的一部分,一个进程可以有多个线程(至少有一个主线程)
开发板
安装完开发板之后打开
$ ~/ubuntu-18.04_imx6ul_qemu_system/gui-qemu-imx6ull-gui.sh
1.LCD屏幕/图像
分别输入下列命令打开LCD屏幕和图像
$ fb-test
打开LCD屏幕
$ ./myfb-test /dev/fb0
打开图像
2.串口EEPROM
$ cd ~
$ i2cdetect -l #列出所有i2c总线
$ i2cdetect -y 0 #列出总线0上的设备
写入:
$ i2c_usr_test /dev/i2c-0 0x50 w 0x01 0xff #w = write
注:/dev/i2c-0 是这个总线的设备夫,0x50是被操作地址,w是指改写地址内容,0x01是位置,0xff是写入的内容。
读取:
$ i2c_usr_test /dev/i2c-0 0x50 w 0x33
读取0x33位置的内容
3.命令控制LED
打开LED控制以及安装LED驱动:
$ cd ~
$ cd led_driver_qemu/
$ insmod 100ask_led.ko
控制LED零号灯亮,LED一号灯灭:
$ ./ledtest /dev/100ask_led0 on
$ ./ledtest /dev/100ask_led1 off
4.按键控制LED
$ cd ~
$ cd button_driver_qemu/
$ insmod button_drv.ko
$ insmod board_100ask_qemu_imx6ull.ko
启动按钮控制:
$ ./button_led_test
可以看到1号按钮控制灯亮了,2号按钮控制灯灭了。