线程、进程和使用开发板

线程、进程和使用开发板,第1张


一、线程 (一)线程的概述

线程是进程的一个执行分支,是在进程内部(线程本质是在进程的地址空间内运行)运行的一个执行流。


(二)多线程程序的优缺点

多线程程序作为一种多任务、并发的工作方式,有以下的优点:
1.提高应用程序响应。


这对图形界面的程序尤其有意义,当一个 *** 作耗时很长时,整个系统都会等待这个 *** 作,此时程序不会响应键盘、鼠标、菜单的 *** 作,而使用多线程技术,将耗时长的 *** 作置于一个新的线程,可以避免这种尴尬的情况。



2.使多CPU系统更加有效。


*** 作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。



3.改善程序结构。


一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会有利于理解和修改。



LIBC中的pthread库提供了大量的API函数,为用户编写应用程序提供支持。


(三)实验源代码 1.线程的创建与终止

创建线程 pthread_create( )

int pthread_create(
      	pthread_t *thread, 
		const pthread_attr_t *attr,
		void *(*start_routine)(void*),
	  	void *arg
); 

终止线程 pthread_exit( )

void pthread_exit(void *retval);

实验结果

 

2.线程的连接和分离

线程可以分为分离线程(DETACHED)和非分离线程(JOINABLE)

线程分离

int pthread_detach(pthread_t thread);

线程连接

int pthread_join(pthread_t thread, void**retavl);
3.线程属性 pthread_attr

线程基本的线程属性包括:栈大小、调度策略和状态属性封装于属性结构体(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()

4.使用互斥量保护多线程同时输出 pthread_mutex

互斥量(Mutex),又称为互斥锁,是用来保护临界区的特殊变量,每个互斥锁内部有一个线程等待队列,保存等待该互斥锁的线程。


有锁定(locked)和解锁(unlocked)状态:

(1)锁定状态,某个特定的线程正持有这个互斥锁,其他线程将阻塞在互斥锁的等待队列内;

(2)解锁状态,没有线程持有这个互斥锁,如果某个线程试图获取这个互斥锁,那么这个线程就可以得到这个互斥锁而不会阻塞。


例:

 5.死锁  pthread_dead_lock

通过让两个进程不断争夺运行资源来使程序进入死锁状态。


 

例:

6.条件变量使用  pthread_condition

使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。


而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。


使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。


一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被条件变量阻塞的线程。


这些线程将重新锁定互斥锁并重新测试条件是否满足。


一般说来,条件变量被用来进行线程间的同步。


1. pthread_cond_init函数
条件变量的结构为pthread_cond_t ,函数pthread_cond_init () 被用来初始化一个条件变量,它的原型为:
Intt pthread_cond_init(pthread_cond_t*cond,_const pthread_condattr_t *cond_attr)
cond是一个指向结构pthread_cond_t的指针
cond_attr是一个指向结构pthread_condattr_t的指针。



结构pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是
pthread_PROCESS_PRIVATE,即此条件被同一进程内的各个线程使用。


注意初始化条件变量只有未被使用时才能重新初始化或被释放。


释放一个条件变量的函数为pthread_cond_destroy(pthread_cond_t cond)

2. pthread_cond_wait函数
使线程阻塞在一个条件变量上。


函数原型:
extern int pthread_cond_wait(pthread_cond_t *_restrict_cond,pthread_mutex_t*_restrict _mutex )
线程解开mutex指向的锁并被条件变量cond阻塞。


线程可以被函数
pthread_cond_signal和函数pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点我们从后面的例子中可以看到。


线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待下一次唤醒。


这个过程一般用while语句实现。



3. pthread_cond_timewait函数
另一个用来阻塞线程的函数是pthread_cond_timewait(),原型为:
extern int pthread_cond_timewait_P(pthread_cond _t *_cond,pthread_mutex_t*mutex,const struct timespec*_abstime) ;
它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。



4.函数pthrcad_cond_signal
函数pthread_cond_signal(),原型为:
extern int pthread_cond_signal (pthread_cond_t *_cond);
它用来释放被阻塞在条件变量cond上的一个线程。


多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。


要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。


例:

 


二、进程

进程( process)是一个已经开始执行但还没终止的程序实例。


Linux系统下使用ps命令可以查看到当前正在执行的进程。


每个进程包含有进程运行环境、内存地址空间、进程ID、和至少一个被称为线程的执行控制流等资源。


同一个程序可以实例化为多个进程实体。


*** 作系统中所有进程实体共享着计算机系统的CPU、外设等资源。


(一)程序如何变为进程

程序是个静态的文件,进程是一个动态的实体,进程的状态会在运行过程中改变,那么程序是如何变为一个进程的呢?
通常在 Shell中输入命令运行就包含了程序到进程转换的过程。


整个转换过程主要包含以下3个步骤:
1.查找命令所对应程序文件的位置;

2.使用fork()函数为之创建一个新进程;

3.在新进程中调用exec族函数装载程序文件,并执行程序文件的main(函数。


(二)进程的状态

Linux是一个多用户多任务的 *** 作系统,可以同时运行多个用户的多个程序,就必然会产生多进程,而每个进程都会有不同的状态。


Linux的进程有以下6种状态:
D:不可中断的深度睡眠状态,处于这种状态的进程不能响应异步信号;
R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行。


而同一时刻可能有多个进程处于可执行状态;
S:可中断的睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂起。


;
T:暂停状态或跟踪状态;
X:退出状态,进程即将被销毁;Z:退出状态,进程成为僵尸进程。


(三)测试获取环境变量代码

进程在运行过程中可以通过以下3种方式来获取运行环境的环境变量:

1.通过main()函数的第3个参数env获取;
 int main(int argc,char*argv[ ],char*env[ ]);

2.通过environ全局变量获取;

 3.通过getenv()函数获取。


本实验测试前两种获取环境变量的方式。


2.进程基本 *** 作

创建进程-fork()

  • 函数以拷贝父进程的方式创建子进程。


    子进程与父进程有相同的代码空间、文件描述符等资源

  • 创建后,子进程与父进程开始并发执行,执行顺序由内核调度算法来决定
  • fork()对父子进程各返回一次,
    父进程:子进程的PID;
    子进程:0;
    失败:小于0错误码。


  • 例:

 

终止进程

  • 正常终止
    调用类exit()函数。



    main()函数return返回,也是调用类exit()函数

  • 异常终止
    调用abort()函数实现,通过SIGABRT信号调用

wait()函数
函数原型:pid_t wait(int *status)
status是用来保存子进程退出状态的指针

帮助父进程获取其子进程的退出状态
如果父进程未调用wait()函数,则子进程的退出信息将一直保存在内存中
僵尸进程:子进程先终止,但父进程没有调用wait()一直占用系统资源。



孤儿进程:被init进程收官,由init进程负责收集它们的退出状态

例:

 

守护进程

1.后台运行的一种特殊进程,独立于控制终端,周期性地执行某种任务或等待处理某些事件

2.守护进程属于孤儿进程,父进程变为init进程

3.Linux系统的大多数服务器就是通过守护进程实现

4.常用daemon()创建守护进程

函数原型:

int daemon(int nochdir,int noclose);

例:

3.系统信号

系统信号概念
信号(signal),即软中断,用来通知进程发生了异步事件。


是较为复杂的通信方式。



一般方向:进程之间、内核给进程。



仅通知事件类型,不传递数据
收到信号后进程的处理方式
(1)忽略
(2)系统默认(大部分的信号使得进程被终止)
(3)类似中断,进程调用预先指定的、对应的函数
信号函数
(1)sigaction函数
改变信号的处理方法,SIGKILL和SIGSTO例外。



(2)Kill()函数
向指定的进程发送一个指定的信号,成功返回0,否则返回-1。



信号函数用法示例

4.线程与进程的关系

一个线程只能是某一个进程的一部分,一个进程可以有多个线程(至少有一个主线程)

 

三、开发板的使用 (一)LCD屏幕/图像

安装完开发板之后打开

$  ~/ubuntu-18.04_imx6ul_qemu_system/gui-qemu-imx6ull-gui.sh

输入下列命令打开LCD屏幕

$ fb-test

 

输入下列命令打开图像

$ ./myfb-test /dev/fb0

 (二)串口 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位置的内容

(三)命令控制 LED 

打开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

 结果:1号按钮控制灯亮,2号按钮控制灯灭

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/634951.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-16
下一篇 2022-04-16

发表评论

登录后才能评论

评论列表(0条)

保存