首先对线程和进程进行一个简单概述:
在一些操作中,等待的时间是比较长的,为避免卡顿,或者反应不及时引入线程和进程,进程是资源分配的最小单位,线程是CPU调度的最小单位。为了便于理解,我们不妨做以下比喻,单进程单线程就像一个人在一个桌子上吃菜;单进程多线程就是多个人在同一个桌子上一起吃菜;多进程单线程则是多个人每个人在自己的桌子上吃菜。
1.多线程
.准备工作-安装
Ubuntu默认是没有pthread库的,需要安装,输入命令:
$ sudo apt-get install -y glibc-doc manpages-posix-dev
$ sudo apt-get install manpages-posix manpages-posix-dev
线程创建
pthread_create()可以创建线程,其代码如下:
void *thread_function(void *index)
e = pthread_create(
pthread_t *thread_id,
const pthread_attr_t *attr,
thread_function,
void *index
);
注释: 1.thread_id 为所创建线程的id
2.attr为线程的属性
3.thread_function给所创建的线程附加内容
4.index给thread_function函数提供所需要的参数
值得注意的是,当线程创建成功时,其函数返回值为0,否则,为1。
线程终止
pthread_exit()可终止线程,其代码如下:
pthread_exit(void *retval)
注释:retval为线程的返回值,即退出码
线程连接
pthread_join()函数功能为等待指定线程结束,其放在主线程中目的是等待子线程结束,主线程再继续运行,其代码如下:
pthread_join(pthread_t thread, void **retval)
对属性对象进行初始化
pthread_attr_init()函数用于对线程属性对象的初始化,线程具有属性,在对该结构进行处理之前必须进行初始化,其代码如下:
int pthread_attr_init(pthread_attr_t *attr);
注释:attr为线程属性结构体的指针变量
线程销毁
线程属性在使用之后就要对其进行销毁,即去初始化,要用到pthread_attr_destroy()函数,其代码如下:
int pthread_attr_destroy(pthread_attr_t *attr);
互斥量、互斥锁
临界区:必须以互斥方式执行的代码段,即在临界区的范围内只能有一个活动的执行线程。
*互斥量(Mutex):又称为互斥锁,是用来保护临界区的特殊变量,每个互斥锁内部有一个线程等待队列,保存等待该互斥锁的线程。
*有锁定(locked)和解锁(unlocked)状态
*锁定状态:某个特定的线程正持有这个互斥锁,其他线程将阻塞在互斥锁的等待队列内;
*解锁状态,没有线程持有这个互斥锁,如果某个线程试图获取这个互斥锁,那么这个线程就可以得到这个互斥锁而不会阻塞。
互斥锁可用于使线程按顺序进行,其中互斥锁的初始化用到pthread_mutex_t mutex以及int pthread_mutex_init()函数,前者可静态初始化互斥锁,后者可动态初始化互斥锁其代码如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
注释:1.PTHREAD_MUTEX_INITIALIZER为一结构常量
2.restrict mutex为新建的互斥锁变量
3.restrict attr指定了新建互斥锁的属性
例子:
互斥锁的销毁
互斥锁的销毁意味着释放它所占用的资源,且要求锁当前处于开放状态。需要用到pthread_mutex_destroy()函数。由于在Linux中,互斥锁并不占用任何资源,pthread_mutex_destroy()除了检查锁状态以外没有其他动作。其代码如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
如何对互斥锁进行“加锁”or“解锁”
互斥锁可用来保护多个线程共享的数据和结构不会被他人修改,一个互斥锁只能有两个状态,即加锁和解锁状态,所用到pthread_mutex_lock()和pthread_mutex_unlock()函数。加锁可使其不被其他线程访问,只能由一个线程掌握,解锁即解除加锁的互斥锁,而这所用到的代码如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
注释:mutex为所要操作的互斥锁变量
死锁
这里通过让两个进程不断争夺运行资源来使程序进入死锁状态。
条件变量
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid_array[3];
int sum = 0;
pthread_mutex_t sum_lock = PTHREAD_MUTEX_INITIALIZER; /* 互斥量 (静态初始化)*/
pthread_cond_t condition_sum_ready = PTHREAD_COND_INITIALIZER; /* 条件量 (静态初始化) */
void * worker_thread_func(void *arg) {
int i;
long id = (long) arg;
for (i = 0; i < 60; i++) {
pthread_mutex_lock(&sum_lock); /* 使用互斥量保护临界变量 */
printf("t%ld: read sum value before = %d\n", id + 1, sum);
sum++;
printf("t%ld: read sum value after = %d\n", id + 1, sum);
pthread_mutex_unlock(&sum_lock); /* 结束互斥量保护临界变量 */
if (sum >= 100)
pthread_cond_signal(&condition_sum_ready); /* 通过条件量 发送条件通知 -> 唤醒等待线程 */
}
return NULL;
}
void * waiting_thread_func(void *arg) {
long id = (long) arg;
pthread_mutex_lock(&sum_lock);
while (sum < 100) /* 不满足条件将一直等待 */
pthread_cond_wait(&condition_sum_ready, &sum_lock); /* 通过条件量 等待条件通知 -> 唤醒等待线程 */
sum = 0;
printf("waiting_thread_func: clear sum value [我是等待线程,已被唤醒。 ]\n");
printf("t%ld: read sum value = %d\n", id + 1, sum);
pthread_mutex_unlock(&sum_lock);
return NULL;
}
int main(void) {
int err;
long i;
for (i = 0; i < 2; i++) {
err = pthread_create(&(tid_array[i]), NULL, &worker_thread_func, (void *) i); /* 创建线程 1 线程 2 */
if (err != 0) {
printf("Can't create thread :[%s]", strerror(err));
}
}
err = pthread_create(&(tid_array[2]), NULL, &waiting_thread_func, (void *) i); /* 创建线程 3 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
for (i = 0; i < 3; i++)
pthread_join(tid_array[i], NULL);
return 0;
}
2.进程
创建
进程的创建需要用到fork()函数,其作用是创建父子进程,且父子进程二者的进程号不一致,即二者分别走向不同的方向,互不干扰。且fork()函数的返回值有两个,第一个为父进程的进程号,第二个的返回值为0,是子进程返回的,若返回值为-1,则表明进程创建失败。
进程等待
进程等待用到wait()函数,进程一旦调用了wait()函数,该进程就会立刻阻塞,暂停运行,直至找到一个已经变为僵尸进程的子进程出现,wait()函数会收集这个子进程的信息,销毁后返回,都则会一直阻塞进程,其代码如下:
int wait(int* statloc);
注释:statloc用来保存被收集进程退出时的一些状态
守护进程
守护进程的创建需要用到daemon()函数,其独立于控制终端并且周期性地执行某种任务或等待 处理某些发生的事件,代码如下:
int daemon(int nochdir, int noclose);
当nochdir为0时,·函数会将进程的工作目录修改为根目录
当noclose为0时,daemon将进程的输入输出以及错误输出都重定向到/dev/null文件夹下
编译各个程序并运行,解释工作状态及结果
进程的创建与终止
例子
/* 线程的创建与终止 : */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5
void *thread_function(void *index) { /* 线程函数 */
long tid;
tid = (long) index;
printf("Hello World! This is thread #%ld!\n", tid); /* 打印线程对应的参数 */
pthread_exit(NULL); /* 退出线程 */
}
int main(int argc, char *argv[]) {
pthread_t tid_array[NUM_THREADS];
int returned_code_err;
long index;
for (index = 0; index < NUM_THREADS; index++) { /* 循环创建 5 个线程 */
printf("In main: creating thread %ld.\n", index);
returned_code_err = pthread_create(
&tid_array[index],
NULL,
thread_function,
(void *) index
); /* 创建线程 */
if (returned_code_err) {
printf("ERR: return code from pthread_create() is not 0, but %d\n", returned_code_err);
exit(-1);
}
}
printf("Main exits.\n");
pthread_exit(NULL); /* 主线程退出 */
return 0;
}
3.开发板
在安装完开发板以后打开
LCD图像和屏幕
分别输入下列命令打开LCD屏幕和图像
$ fb-test
$ ./myfb-test /dev/fb0
串口EEPROM
$ cd
$ i2cdetect -l #列出所有ic2总线
$ i2cdetect -y 0 #列出总线0上的设备
命令控制LED
安装LED驱动
$ cd
$ cd led_driver_qemu/
$ insmod 100ask_led.ko
控制LED零号灯亮,控制LED一号灯灭
$ ./ledtest /dev/100ask_led0 on
$ ./ledtest /dev/100ask_led1 off
按键控制LED
$ cd
$ cd button_driver_qemu/
$ insmod button_drv.ko #扫描你的按键
$ insmod board_100ask_qemu_imx6ull.ko
启动按键控制
$ ./button_led_test