一、线程
1、概述
线程是包含在进程内部顺序执行流,是进程中的实际运作单位,是操作系统能够进行调度基本单位。
2、创建线程
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,否则返回错误码。
终止线程:
void pthread_exit(void *retval);
线程ID:
通过函数:my_tid = pthread_self( )
函数原型:pthread_t pthread_self(void)
比较相等:pthread_equal( )
运行结果如下:
线程连接与分离:
线程可以分为分离线程(DETACHED)和非分离线程(JOINABLE)两种:
连接:int pthread_join(pthread_t thread, void**retavl);
分离:int pthread_detach(pthread_t thread);
示例:
运行结果如下:
初始化与销毁:
基本的线程属性包括:栈大小、调度策略和状态属性。
初始化: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),又称为互斥锁,是用来保护临界区的特殊变量,每个互斥锁内部有一个线程等待队列,保存等待该互斥锁的线程。
有锁定(locked)和解锁(unlocked)状态:
锁定状态,某个特定的线程正持有这个互斥锁,其他线程将阻塞在互斥锁的等待队列内;
解锁状态,没有线程持有这个互斥锁,如果某个线程试图获取这个互斥锁,那么这个线程就可以得到这个互斥锁而不会阻塞。
示例:
效果:
死锁:
结果:
条件变量:
互斥锁用来给资源上锁,而条件变量是用来等待而不是用来上锁。
条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。
示例:
结果:
二、进程
1、进程环境
程序:
程序是静态的,程序 = 指令序列(完成特地任务) + 数据
进程:
是一个已经开始执行但还没有终止的程序实例。
包含有进程运行环境、内存地址空间、进程ID、和至少一个被称为线程的执行控制流等资源。
一个程序可以实例化为多个进程实体。
进程是一个动态的实体
程序到进程转换:
查找命令所对应程序文件的位置;
使用fork()函数为之创建一个新进程;
在新进程中调用exec族函数装载程序文件,并执行程序文件的main()函数。
进程的状态:
R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行;
D:不可中断的深睡眠状态,处于这种反状态的进程不能响应异步信号;
S:可中断的浅睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂起;
T:暂停状态或跟踪状态;
W:退出状态,进程即将被销毁;
Z:退出状态,进程成为僵尸进程。
进程的参数获取:
1、通过main()函数的第3个参数env获取
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()
运行结果:
终止进程:
正常终止
调用类exit()函数。
main()函数return返回,也是调用类exit()函数
异常终止
调用abort()函数实现,通过SIGABRT信号调用
被执行程序:负责打印参数个数、参数列表、变量环境。
wait()函数:
函数原型:pid_t wait(int *status)
status是用来保存子进程退出状态的指针。
帮助父进程获取其子进程的退出状态
如果父进程未调用wait()函数,则子进程的退出信息将一直保存在内存中
僵尸进程:子进程先终止,但父进程没有调用wait()一直占用系统资源。
孤儿进程:被init进程收官,由init进程负责收集它们的退出状态
示例:
结果:
守护进程:
后台运行的一种特殊进程,独立于控制终端,周期性地执行某种任务或等待处理某些事件
守护进程属于孤儿进程,父进程变为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屏幕
打开:$ fb-test
$ ./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
得到:
读取:$ i2c_usr_test /dev/i2c-0 0x50 w 0x33
读取0×33位置
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号按钮控制灯熄灭。
结束