一.查看源代码
线程
1. 线程的创建与终止
-
pthread_create (thread, attr, start_routine, arg)
pthread_create 创建一个新的线程,并让它可执行。
下面是关于参数的说明:
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。
创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
-
pthread_exit (status)
pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。
2. 线程的连接与分离
-
pthread_join (threadid, status)
-
pthread_detach (threadid)
pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。
3. 线程属性
- 线程属性初始化与销毁
注意:应先初始化线程属性,再pthread_create创建线程
初始化线程属性(给结构体分配空间,相当于malloc)
int pthread_attr_init(pthread_attr_t *attr); 成功:0;失败:错误号
销毁线程属性所占用的资源(释放结构体资源,相当于free)
int pthread_attr_destroy(pthread_attr_t *attr); 成功:0;失败:错误号
- 线程属性之分离属性
线程的分离状态决定一个线程以什么样的方式来终止自己。
非分离状态:线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。
只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
分离状态:分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,
马上释放系统资源。应该根据自己的需要,选择适当的分离状态。
线程分离状态的函数:
设置线程属性,分离or非分离
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
获取程属性,分离or非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
参数 :attr:已初始化的线程属性
detachstate: PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD _CREATE_JOINABLE(非分离线程)
4. 使用互斥量保护多线程同时输出
互斥量,又称为互斥锁,是一种用来保护临界区的特殊变量,它可以处于锁定状态,也可以处于解锁状态:
(1)如果互斥锁是锁定的,就是一个特定的线程持有这个互斥锁
(2)如果没有线程持有这个互斥锁,那么这个互斥锁就处于解锁状态
每个互斥锁内部都有一个线程等待队列,用来保存等待该互斥锁的线程。当互斥锁处于解锁状态时,一个线程试图获取这个互斥锁时,这个线程就可以得到这个互斥锁而不会阻塞;当互斥锁处于锁定状态时,一个线程试图获取这个互斥锁时,这个线程将素色在互斥锁的等待线程队列内
-
创建互斥量
(1)静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
(2)动态初始化
int pthread_mutext_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数mutex是一个指向要初始化的互斥量的指针;参数attr传递NULL来初始化一个带有默认属性的互斥量,否则就要用类似于线程属性对象所使用的方法,先创建互斥量属性对象,再用该属性对象来创建互斥量
函数成功返回0,否则返回一个非0的错误码,
- 销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex指向要销毁的互斥量的指针。
- 加锁
Pthreads中有两个试图锁定互斥量的函数,pthread_mutex_lock()和pthread_mutex_trylock(),pthread_mutex_lock()函数会一直阻塞到互斥量可用为止,而pthread_mutex_trylock()会尝试加锁,通常立即返回,函数原型如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数mutex是需要加锁的互斥量。函数成功返回0,否则返回一个非0的错误码,其中另一个线程已持有锁的情况下,调用pthread_mutex_trylock()函数的额错误码是EBUSY
- 解锁
pthread_mutex_unlock()函数是用来释放指定的互斥量。函数原型如下:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数是需要解锁的互斥量。成功返回0;否则返回一个非0的错误码
5. 死锁
死锁是指两个或两个以上的执行序在执行过程中,因争夺资源而造成的一种互相等待的现象
当多个线程需要相同的一些锁, 但是按照不同的顺序加锁, 死锁就很容易发生, 如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生
6. 条件变量使用
- 初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condatrr_t *restrict attr);
参数:
cond:要初始化的条件变量;
attr:NULL。
- 销毁
int pthread_cond_destroy(pthread_cond_t *cond);
- 等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待;
mutex:互斥量
- 唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);广播唤醒所有等待条件的休眠线程。
int pthread_cond_signal(pthread_cond_t *cond);按顺序唤醒一个休眠的线程。
进程
1. 通过env获取环境变量(l);通过environ获取环境变量(2)
/* 通过env参数获取环境变量 */
#include <stdio.h>
int main(int argc, char * argv[], char *env[])
{ int i = 0;
while (env[i])
puts(env[i++]);
return 0;
}
/* 通过environ获取环境变量 */
#include <stdio.h>
extern char ** environ;
int main(int argc, char * argv[])
{ int i = 0;
while (environ[i]) puts(environ[i++]);
return 0;
}
2. 进程创建程序
/* fork创建进程 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t pid;
pid = fork(); /* 创建子进程 */
if (pid == 0) { /* 子进程返回0 */
printf("Here is child, my pid = %d, parent's pid = %d\n", getpid(), getppid()); /* 打印父子进程 PID */
exit(0);
} else if(pid > 0) { /* 父进程返回子进程PID */
printf("Here is parent, my pid = %d, child's pid = %d\n", getpid(), pid);
} else { /* 创建出错 */
perror("fork error\n");
}
return 0;
}
3. 子进程加载新程序
/* 子程序执行程序-命名为sample3 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char * *environ; /* 全局环境变量*/
int main(int argc, char *argv[]) {
int i;
printf("argc=%d\n",argc); /* 打印参数个数 */
printf("args:");
for(i=0;i<argc;i++)
printf("%s",argv[i]);/* 打印参数表 */
printf("\n");
i = 0;
while (environ[i])
puts(environ[i++]); /* 打印环境变量表 */
printf("\n");
return 0;
}
/* 子进程加载新程序 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
char *env_init[] = {"USER=ujn", "HOME=/home/ujn/", NULL}; /* 为子进程定义环境变量 */
int main(int argc, char *argv[]) {
pid_t pid;
if ((pid = fork()) < 0) { /* 创建进程失败判断 */
perror("fork error");
} else if (pid == 0) { /* fork 对子进程返回 0 */
execle("/home/ujn/sec9/sample4", "sample4", "hello", "world", (char *) 0,env_init);/*子进程装载新程序*/
perror("execle error"); /* execle 失败时才执行 */
exit(-1);
} else {
exit(0); /* 父进程退出 */
}
return -1;
}
4. 使用daemon创建守护进程
/* 用daemon创建守护进程 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
int main(void) {
int fd;
time_t curtime;
if (daemon(0, 0) == -1) {
perror("daemon error");
exit(-1);
}
fd = open("/tmp/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
if (fd < 0) {
perror("open error");
exit(-1);
}
while (1) {
curtime = time(0);
char *timestr = asctime(localtime(&curtime));
write(fd, timestr, strlen(timestr));
sleep(60);
}
close(fd);
return 0;
}
5. 信号函数sigaction的使用
/* sigaction函数的用法 */
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void ouch(int sig) { /* 信号处理函数 */
printf("\nOuch! - I got signal %d\n", sig);
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = ouch; /* 设置信号处理函数 */
sigemptyset(&act.sa_mask); /* 清空屏蔽信号集 */
act.sa_flags = SA_RESETHAND; /* 设置信号处理之后恢复默认的处理方式 */
sigaction(SIGINT, &act, NULL); /* 设置 SIGINT 信号的处理方法 */
while (1) { /* 进入循环等待信号发生 */
printf("sleeping\n");
sleep(1);
}
return 0;
}
二 .编译并运行
1. 线程的创建与终止
/* 线程的创建与终止 : */
#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;
}
用数组加循环创建了5个线程,线程创建时会调用线程函数,所以会先打印出循环的printf再打印线程函数的
2. 线程的连接与分离
/* pthread_join 函数示例
gcc -pthread ... -lm
Note: math lib need to be specifically linked; and link libraries after object files.
*/
#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);
}
创建了4个线程,并再线程函数中进行数学运算并打印结果,调用 pthread_join() 函数来实现获取线程执行结束时返回的数据并释放它的内存空间
3. 线程属性
#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);
}
首先根据参数输入处理参数 -s 所指定的栈大小, 然后初始化属性对象,设置属性对象的栈大小,根据属性创建线程,线程运行函数中打印线程id,栈的起始地址和栈大小,最后销毁属性对象,等待线程终止,并获取返回值
4. 使用互斥量保护多线程同时输出
/* 使用互斥量保护多线程同时输出 */
#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;
}
首先定义了两个线程和一个互斥量,(动态) 初始化互斥量,然后创建了两线程1和2,先调用线程函数的线程获取了互斥量,互斥量加锁,后者线程处于阻塞,当前线程完成即循环结束,互斥量解锁,后线程才能获取
5.死锁
/* 死锁产生的范例 */
#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;
}
静态初始化了两个互斥量A,B,创建了线程1,2,线程1获取了mutexA,线程2获取了mutexB,
mutexA,B锁定,线程1想但无法获取mutexA,而线程2无法获取mutexB,从而发生了死锁。
6. 条件变量的使用
#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;
}
静态初始化了一个互斥量和一个条件量,首先创建了线程1,2会调用worker这个线程函数,抢这个互斥量,进行循环使得sum++,创建的线程3会调用waiting这个线程函数,pthread_cond_wait函数会等待执行,当全局变量sum通过线程1,2会调用worker这个线程函数加到100时通过条件量pthread_cond_signal函数发送条件通知给唤醒等待线程3,线程3才继续执行
三, 运行开发板
~/ubuntu-18.04_imx6ul_qemu_system/gui-qemu-imx6ull-gui.sh
四, 使用开发板
1, LCD屏幕/图像
fb-test; myfb-test /dev/fb0
2, 串口EEPROM
cd ~ ; i2cdetect -l && i2cdetect -y o
i2c_usr test /dev/i2c-0 0x50 w1 0xff
i2c_usr_test /dev/i2c-0 0×50 r 0×1 0xff
3,命令控制LED
cd ~/led_driver_qemu/
insmod 100ask_led .ko
./ledtest /dev/100ask_led0 on
./ledtest /dev/100ask_led1 off
4, 按键控制LED
cd ~/ button_driver_qemu/
insmod button_drv .ko
insmod board_100ask_qemu_imx6u
./button_led_test