参考博主http://t.csdn.cn/MV80H,感谢博主。
1.安装
Ubuntu默认是没有pthread库的,需要安装。输入命令:
$ sudo apt-get install -y glibc-doc manpages-posix-dev
$ sudo apt-get install manpages-posix manpages-posix-dev
安装完成后,我们进行代码测试演示。
2.线程的创建与终止
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为线程的返回值,即退出码
3.线程的连接
pthread_join()函数功能为等待指定线程结束,其放在主线程中目的是等待子线程结束,主线程再继续运行,其代码如下:
pthread_join(pthread_t thread, void **retval)
1.thread为一指定的需要等待运行结束的线程
2.retval为线程的退出码
4.属性对象的初始化与销毁
pthread_attr_init()函数用于对线程属性对象的初始化,线程具有属性,在对该结构进行处理之前必须进行初始化,其代码如下:
int pthread_attr_init(pthread_attr_t *attr);
attr为线程属性结构体的指针变量
线程属性在使用之后就要对其进行销毁,即去初始化,要用到pthread_attr_destroy()函数,其代码如下:
int pthread_attr_destroy(pthread_attr_t *attr);
attr为线程属性结构体的指针变量
5.互斥锁静态与动态初始化及销毁
互斥锁可用于使线程按顺序进行,其中互斥锁的初始化用到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);
mutex为需要销毁的互斥锁变量
6.互斥锁加锁与解锁
互斥锁可用来保护多个线程共享的数据和结构不会被他人修改,一个互斥锁只能有两个状态,即加锁和解锁状态,所用到pthread_mutex_lock()和pthread_mutex_unlock()函数。加锁可使其不被其他线程访问,只能由一个线程掌握,解锁即解除加锁的互斥锁,而这所用到的代码如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex为所要操作的互斥锁变量
7.进程的创建
进程的创建需要用到fork()函数,其作用是创建父子进程,且父子进程二者的进程号不一致,即二者分别走向不同的方向,互不干扰。且fork()函数的返回值有两个,第一个为父进程的进程号,第二个的返回值为0,是子进程返回的,若返回值为-1,则表明进程创建失败。
8.进程的等待
进程等待用到wait()函数,进程一旦调用了wait()函数,该进程就会立刻阻塞,暂停运行,直至找到一个已经变为僵尸进程的子进程出现,wait()函数会收集这个子进程的信息,销毁后返回,都则会一直阻塞进程,其代码如下:
int wait(int* statloc);
statloc用来保存被收集进程退出时的一些状态
9.进程的守护进程
守护进程的创建需要用到daemon()函数,其独立于控制终端并且周期性地执行某种任务或等待 处理某些发生的事件,代码如下:
int daemon(int nochdir, int noclose);
当nochdir为0时,·函数会将进程的工作目录修改为根目录
当noclose为0时,daemon将进程的输入输出以及错误输出都重定向到/dev/null文件夹下
编译各个程序并运行,解释工作状态及结果
10.进程的创建与终止
示例:
/* 线程的创建与终止 : */
#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;
}
运行结果 :
可以看到,在主线程进行的同时,所创造的五个子线程也在运行自己的内容。且子线程执行的顺序与创建的顺序不同,这是由于cpu何时分配时间片所决定的。由于没有进程之间的连接,主进程不会在所有子进程全部结束后再结束,故子进程可能会在主进程结束后仍然处于运行状态。
11.线程的连接
示例:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_THREADS 4
void *thread_func(void *index) { /* 线程函数 */
int i;
long tid;
double result=0.0;
tid = (long)index;
printf("Thread %ld starting...\n",tid);
for (i=0; i<1000000; i++) {
result = result + sin(i) * tan(i); /* 进行数学运算 */
}
printf("Thread %ld done. Result = %e\n",tid, result);
pthread_exit((void*) index); /* 带计算结果退出 */
}
int main (int argc, char *argv[]) {
pthread_t tid_array[NUM_THREADS];
int err;
long index;
void *status;
for(index=0; index<NUM_THREADS; index++) {
printf("Main: creating tid_array %ld\n", index);
err = pthread_create(
&tid_array[index],
NULL,
thread_func,
(void *)index
); /* 创建线程 */
if (err) {
printf("ERROR; return code from pthread_create() is %d\n", err);
exit(-1);
}
}
for(index=0; index<NUM_THREADS; index++) {
err = pthread_join(
tid_array[index],
&status
); /*等待线程终止,并获取返回值*/
if (err) {
printf("ERROR; return code from pthread_join() is %d\n", err);
exit(-1);
}
printf("Main: completed join with tid_array %ld having a status of %ld\n",index,(long)status);
}
printf("Main: program completed. Exiting.\n");
pthread_exit(NULL);
}
运行结果:
随着主线程的运行,所创建的四个紫禁城也在运行自己的内容,且主线程中加入了pthread_join()函数,主进程会暂停直到子进程按顺序结束运行后再继续运行,直至终止。
12.属性对象的初始化与销毁
示例:
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#define handle_error_en(en, msg) do {errno = en; perror(msg); exit(EXIT_FAILURE);}while (0)
#define handle_error(msg) do {perror(msg); exit(EXIT_FAILURE);}while (0)
struct thread_info {
pthread_t thread_id;
int thread_num;
char *argv_string;
};
static void *thread_func(void *arg) { /* 线程运行函数 */
struct thread_info *thread_info_struct_array = arg;
char *uargv, *p;
printf("Thread %d: top of stack near %p; argv_string=%s\n", /* 通过 p 的地址来计算栈的起始地址*/
thread_info_struct_array->thread_num, &p, thread_info_struct_array->argv_string);
uargv = strdup(thread_info_struct_array->argv_string);
if (uargv == NULL)
handle_error("strdup");
for (p = uargv; *p != '\0'; p++)
*p = toupper(*p); /* 小写字符转换大写字符 */
return uargv; /* 将转换结果返回 */
}
int main(int argc, char *argv[]) {
int s, index, opt, num_threads;
struct thread_info *thread_info_struct_array;
pthread_attr_t attr_struct;
int stack_size;
void *res;
stack_size = -1;
/* 处理参数 -s 所指定的栈大小 */
while ((opt = getopt(argc, argv, "s:")) != -1) {
switch (opt) {
case 's':
stack_size = strtoul(optarg, NULL, 0); // string to unsigned long
break;
default:
fprintf(stderr, "Usage: %s [-s stack-size] arg...\n", argv[0]);
exit(EXIT_FAILURE);
}
}
num_threads = argc - optind;
/* 初始化属性对象 */
s = pthread_attr_init(&attr_struct);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
if (stack_size > 0) {
/* 设置属性对象的栈大小 为 用户命令行参数 -s 指定值 */
s = pthread_attr_setstacksize(
&attr_struct,
stack_size
);
if (s != 0)
handle_error_en(s, "pthread_attr_setstacksize");
}
thread_info_struct_array = calloc(num_threads, sizeof(struct thread_info));
if (thread_info_struct_array == NULL)
handle_error("calloc");
for (index = 0; index < num_threads; index++) {
thread_info_struct_array[index].thread_num = index + 1;
thread_info_struct_array[index].argv_string = argv[optind + index];
s = pthread_create(
&thread_info_struct_array[index].thread_id,
&attr_struct, /* 根据属性创建线程 */
&thread_func,
&thread_info_struct_array[index]
);
if (s != 0)
handle_error_en(s, "pthread_create");
}
/* 销毁属性对象 */
s = pthread_attr_destroy(&attr_struct);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
for (index = 0; index < num_threads; index++) {
s = pthread_join(thread_info_struct_array[index].thread_id, &res); /* 等待线程终止,并获取返回值 */
if (s != 0)
handle_error_en(s, "pthread_join");
printf("Joined with thread %d; returned value was %s\n",
thread_info_struct_array[index].thread_num, (char *) res);
free(res);
}
free(thread_info_struct_array);
exit(EXIT_SUCCESS);
}
运行结果:
根据用户所输入的命令行数据设置栈的大小,并以此配置属性对象的栈的大小,创建结构体组用于存放不同线程的数据,根据属性对象及结构体组内存放的数据创建线程。此时,用户输入的命令行中的字符串会以寄存在结构体数组内的方式按顺序输入给不同的线程,线程以此创建,并输出其所对应栈的栈顶地址以及所存放的字符串,若字符串中含有小写字母,则进行大写的转换,随后主线程销毁属性对象并通过pthread_join()函数等待所有子线程按顺序结束,同时输出他们的返回值,随后主线程终止。
13.互斥量保护多线程同时输出
示例:
/* 使用互斥量保护多线程同时输出 */
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid[2];
pthread_mutex_t lock;
void* thread_func(void *arg)
{
int id = (long)arg;
int i = 0;
pthread_mutex_lock(&lock); /* 使用互斥量保护临界区 ================== begin ================== */
printf("Job %d started\n", id);
for (i = 0; i < 5; i++)
{
printf("Job %d printing\n", id);
usleep(10);
}
printf("Job %d finished\n", id);
pthread_mutex_unlock(&lock); /* 使用互斥量保护临界区 ================== end ================== */
return NULL;
}
int main(void)
{
long i = 0;
int err;
if (pthread_mutex_init(&lock, NULL) != 0) { /* (动态) 初始化 互斥量, 因为定义时候没有初始化 */
printf("\n Mutex init failed\n");
return 1;
}
while(i < 2) {
err = pthread_create(&(tid[i]), NULL, &thread_func, (void*)i);
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
i++;
}
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&lock); /* 用完销毁 */
return 0;
}
运行结果:
首先,创建了一个用于储存线程id的数组以及未初始化的互斥锁。主线程开始运行,初始化互斥锁,随后创建子线程,每创建一个子线程,该子线程都会使互斥锁上锁并循环输出5次正在打印,以及一次完成的信息,后再使互斥锁解锁。此后,主线程对不同子线程调用pthread_join()函数,等待其终止后,主线程终止。
14.死锁的产生
示例:
/* 死锁产生的范例 */
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid[2];
pthread_mutex_t mutexA = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化互斥量 */
pthread_mutex_t mutexB = PTHREAD_MUTEX_INITIALIZER;
void * t1(void *arg) {
pthread_mutex_lock(&mutexA); /* 线程 1 获取 mutexA */
printf("t1 get mutexA\n");
usleep(1000);
pthread_mutex_lock(&mutexB); /* 线程 1 获取 mutexB */
printf("t1 get mutexB\n");
pthread_mutex_unlock(&mutexB); /* 线程 1 释放 mutexB */
printf("t1 release mutexB\n");
pthread_mutex_unlock(&mutexA); /* 线程 1 释放 mutexA */
printf("t1 release mutexA\n");
return NULL;
}
void * t2(void *arg) {
pthread_mutex_lock(&mutexB);
printf("t2 get mutexB\n");
usleep(1000);
pthread_mutex_lock(&mutexA);
printf("t2 get mutexA\n");
pthread_mutex_unlock(&mutexA);
printf("t2 release mutexA\n");
pthread_mutex_unlock(&mutexB);
printf("t2 release mutexB\n");
return NULL;
}
int main(void) {
int err;
err = pthread_create(&(tid[0]), NULL, &t1, NULL ); /* 创建线程 1 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
err = pthread_create(&(tid[1]), NULL, &t2, NULL); /* 创建线程 2 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
return 0;
}
运行结果:
主线程创建了两个子线程t1和t2,此时t1使互斥锁mutexA上锁,并获取了mutexA,t2使互斥锁mutexB上锁,并获取了mutexB,之后,由于t1,t2对分别使mutexA和mutexB上锁导致两个线程无法给另外的互斥锁上锁,也就无法获取到互斥锁,继而阻塞了程序的进行,出现死锁。
15.条件变量
示例:
#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;
}
运行结果:
首先,主线程会先创建两个子线程后再创建一个线程,由于cpu分配时间片的时间不同前二者交替进行对互斥量sum_lock上锁,打印,sum++,打印,解锁互斥量,执行if语句的操作,当sum=100时,发送一个唤醒命令到一个条件变量队列等待的线程,后者会在sum大于等于100时,随时有可能接收到来自pthread_cond_signal()函数的唤醒信号使得sum=0,并打印执行信息,但无论如何主线程都会在sum与120个1相加(即前两个创建的子线程所执行的循环中i都为60时)终止线程。
16.运行开发板
- 下载成功后,进入ubuntu-18.04_imx6ul_qemu_system目录,执行install_sdl.sh
- 必须在Ubunut的桌面环境下启动终端,执行./qemu-imx6ull-gui.sh
我们可以看到下面界面
16.1.打开LCD图像和屏幕
输入指令:
$ fb-test
$ cd myfb-test
$ ./myfb-test /dev/fb0
16.2.串口EEPROM
$ cd
$ i2cdetect -l #列出所有ic2总线
$ i2cdetect -y 0 #列出总线0上的设备
矩阵的最左列是十位,最上面是个位。
16.3.命令控制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