Linux下线程概述

1.1、线程和进程的关系     

         线程是计算机中独立运行的最小单位,运行时占用很少的系统资源,每个线程占用的CPU时间是由系统分配的,因此可以把线程看成是操作系统分配CPU时间的基本单位。在用户看来,多个线程是同时执行的,但从操作系统的调度上看,各个线程是交替执行的。系统不停在各个线程之间切换,每个线程只有在系统分配给它的时间片内才能取得CPU的控制权,执行线程中的代码。

        注意:这里只是单CPU单核的情况,在多CPU多核的主机上,多个线程是可以同时运行的。

        Linux系统是支持多线程的,它在一个进程内生成了许多个线程,一个进程可以拥有一至多个线程。那么为什么在支持多进程的情况下又要引入多线程呢?这是因为多线程相对于多进程有以下优点:

  • 在多进程情况下,每个进程都有自己独立的地址空间,而在多线程情况下,同一进程内的线程共享进程的地址空间。因此,进程的资源开销要比线程大得多
  • 系统调度方面,由于进程地址空间独立而线程共享同一个进程的地址空间,线程间的切换速度要远快于进程间的切换速度
  • 通信机制方面,进程间的数据空间相互独立,彼此通信要用专门的通信方式进行,通信时必须通过操作系统。而同一进程内的多个线程共享数据空间,一个线程的数据可以直接提供给另一个线程使用,而不必经过操作系统。因此,线程间的通信更加方便和省时。
        以上线程的优点可以用2个字概括:“节约”:节约资源、节约时间。这些对操作系统的设计来说是非常重要的,线程还有以下优点:
  • 提高应用程序的响应速度,在图形界面程序中,如果有一个非常耗时的操作,它会导致其它操作不能进行而等待,这时界面响应用户的操作速度就会变得很慢。多线程环境下,可以将这个非常耗时的操作放到一个单独的线程中来完成
  • 可以提高多处理器效率
  • 可以改善程序的结构,对于要处理多个命令的应用程序,可以将对每个命令的处理设计为一个线程,从而避免设计大程序时,造成的程序结构复杂
        虽然线程共享进程的地址空间、打开的文件描述符等资源,但是线程也有自己私有的数据信息,包括:
  • 线程号(Thread ID):每个线程都有一个唯一的线程ID号
  • 寄存器:包括程序计数器和堆栈指针
  • 堆栈
  • 信号掩码
  • 优先级
  • 线程私有的存储空间
        Linux支持POSIX多线程接口,称为pthread(Posix Thread的简称)。编写Linux下的多线程应用程序,需要使用头文件pthread.h,链接时需要使用库pthread.a.
1.2、创建线程
1.2.1、线程创建函数pthread_create
        如果在主线程里面创建线程,程序就会在创建线程的地方产生分支,变成2个分支执行,这似乎和多进程一样,其实不然,子进程是通过拷贝父进程的地址空间来实现的;而线程与进程内的线程共享程序代码,一段代码可以同时被多个线程执行。
        线程的创建通过函数pthread_create来完成,该函数的声明如下:
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr, void* (*start_routine)(void*), void* arg);
参数含义如下:
  • thread:该参数是一个指针,当线程创建成功时,用来返回创建的线程ID
  • attr:该参数用于指定线程的属性,NULL表示使用默认属性
  • start_routine:该参数为一个函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也称为线程函数
  • arg:该参数指向传递给线程函数的参数
注意:线程创建成功时,pthread_create函数返回0,若不为0则说明线程创建失败,常见的错误码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如:线程数目过多;后者表示第二个参数代表的线程属性值非法。线程创建成功后,创建的线程开始运行第三个参数所指向的函数,原来的线程继续运行。
pthread_t pthread_self(void);//获取本线程的线程ID
int pthread_equal(pthread_t thread1, pthread_t thread2);//判断2个线程的ID是否指向同一个线程
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));//用来保证init_routine线程函数在进程中仅执行一次
线程创建过程示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void* thread(void* arg)
{
	pthread_t newthid;
	printf("this is new thread, thread id = %x\n", pthread_self());
	return NULL;
}

int main(void)
{
	pthread_t thid;
	printf("main thread ID is %x\n", pthread_self());//main thread id
	if(pthread_create(&thid, NULL, (void*)thread, NULL) != 0){
		printf("thread creation failed.\n");
		exit(1);
	}
	sleep(1);
	return 0;
}
运行结果如下:

说明:程序首先打印出主线程的ID,然后打印新创建的线程ID。
        有些情况下,函数执行次数被限制为一次,这种情况下要使用pthread_once函数,下面例子说明了该函数的用法:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_once_t once = PTHREAD_ONCE_INIT;

void run()
{
	printf("Function run is running in thread %u\n", pthread_self());
}

void* thread1(void* arg)
{
	pthread_t thid = pthread_self();
	printf("Current thread's ID is %x\n", thid);
	pthread_once(&once,run);
	printf("thread1 ends\n");
}
void* thread2(void* arg)
{
	pthread_t thid = pthread_self();
	printf("Current thread's ID is %x\n", thid);
	pthread_once(&once,run);
	printf("thread2 ends\n");
}

int main(void)
{
	pthread_t thid1,thid2;

	pthread_create(&thid1, NULL, thread1, NULL);
	pthread_create(&thid2, NULL, thread2, NULL);

	printf("main thread exit.\n");//main thread id
	
	sleep(3);
	return 0;
}
函数执行结果如下:

从执行结果可以看出来,线程函数执行了一次。
2.2.2、线程属性
        线程创建函数pthread_create有一个参数类型为pthread_attr_t,该结构定义如下:
typedef struct{
        int                datachstate;
        int                schedpolicy;
        struct sched_param schedparam;
        int                inheritsched;
        int                scope;
        size_t             quardsize;
        int                stackaddr_set;
        void*              stackaddr;
        size_t             stacksize
}pthread_attr_t;
各个字段的含义如下:
  • datachstate:表示新创建的线程是否与进程中的其它线程脱离同步。datachstate的缺省值为PTHREAD_CREATE_JOINABLE状态,这个属性也可以用函数pthread_detach()来设置。如果将datachstate设置为PTHREAD_CREATE_DETACH状态,则datachstate不能再恢复到PTHREAD_CREATE_JOINABLE状态。
  • schedpolicy:表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)、SCHED_FIFO(实时、先入先出)这三种,缺省为SCHED_OTHER(正常、非实时),后两种调度策略仅对超级用户有效。
  • schedparam:一个struct sched_param结构体,其中有一个sched_priority成员,表示线程的运行优先级。这个参数仅当调度策略为实时(SCHED_RR或SCHED_FIFO)时才有效,缺省为0.
  • inheritsched:有两种值可供选择,PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程显式指定调度策略和调度参数(即attr中的值),后者表示继承调用者线程的值,缺省为PTHREAD_EXPLICIT_SCHED.
  • scope:表示线程间竞争CPU的范围,也就是说,线程优先级的有效范围,POSIX标准中定义了两个值,PTHREAD_SCOPE_SYSTEM:表示与系统中所有线程一起竞争CPU。PTHREAD_SCOPE_PROCESS:表示仅与同进程中的线程竞争CPU。
  • guardsize:警戒堆栈的大小
  • stackaddr_set:堆栈地址集
  • stacksize:堆栈的大小
2.3、线程终止
    Linux下有2种方式可以使线程终止,一种是return从线程函数直接返回,另一种是通过调用函数pthread_exit()使线程退出。pthread_exit()在头文件pthread.h中声明,该函数的原型如下:
#include <pthread.h>
void pthread_exit(void* retval);
    有2种情况需要注意:一种情况是:在主线程中,如果从main函数返回或是调用了exit函数退出主线程,则整个进程终止,此时进程中的所有线程也将终止。因此在主线程中不能过早的从main函数返回;另一种情况是主线程调用pthread_exit函数,则仅仅是主线程消亡,进程不会结束,进程内的其它线程也不会终止,直到所有线程结束,进程才会结束。
    线程终止最重要的问题是资源释放的问题,特别是一些临界资源。临界资源在一段时间内只能被一个线程所持有,当线程需要使用临界资源时提出需求,如果该资源未被使用则申请成功,否则等待。临界资源使用完毕后要释放以便其它线程可以使用。例如,某线程要写一个文件,在该线程写文件时不允许其它线程对该文件进行写操作,否则会导致文件数据混乱,这里的文件就是一种临界资源,临界资源为一个线程所独占,当一个线程终止时如果不释放占用的临界资源,其它需要使用该资源的线程可能就会一直等待下去,这就形成了死锁,而往往这是灾难性的。
    为此,Linux系统提供了一对函数pthread_cleanup_push()和pthread_cleanup_pop()用于自动释放资源。从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(如调用pthread_exit函数)都将执行pthread_cleanup_push()所指定的清理函数,这两个函数是以宏的形式提供的。
   注意pthread_cleanup_push()带有一个“{”,而pthread_cleanup_pop()带有一个“}”,因此这两个函数必须成对出现,而且必须位于程序的同一代码段中才能通过编译。
    线程终止的另外一个需要注意的问题是线程间的同步问题。一般情况下,进程中各个线程的运行是相互独立的,线程的终止并不会相互通知,也不会影响其它线程,终止的线程所占用的资源也不会随着线程的终止而归还系统,而是仍为线程所在的进程所持有。正如进程之间可以使用wait系统函数来等待其它进程结束一样,线程也有类似的函数,pthread_join()函数,该函数在pthread.h中声明:
#include <pthread.h>
void pthread_exit(void* retval);
int pthread_join(pthread_t th, void* thread_return);
int pthread_detach(pthread_t th);
    函数pthread_join用来等待一个线程的结束。pthread_join的调用线程将被挂起并等待th线程终止,如果thread_return不为NULL,则*thread_return=retval。需要注意的是,一个线程仅允许一个线程使用pthread_join等待它的终止,并且被等待的线程处于可join状态,即非DETACHED状态。DETACHED状态是指对某个线程执行pthread_detach后所处的状态,处于DETACHED状态的线程无法由pthread_join同步。
    一个可join的线程所占用的内存仅当有线程对其执行了pthread_join后才会释放,因此为了避免内存泄露,所有的线程终止时,要么已被设为DETACHED,要么使用pthread_join来回收资源。
    注意:一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程返回错误代码ESRCH.
示例代码如下:
#include <stdio.h>
#include <pthread.h>

void* assisthread(void* arg)
{
	printf("I am helping to do some jobs\n");
	sleep(3);
	pthread_exit(0);
}

int main(void)
{
	pthread_t 	assistthid;
	int 		status;

	pthread_create(&assistthid, NULL, (void*) assisthread, NULL);
	pthread_join(assistthid, (void*)&status);
	printf("assisthread's exit is caused %d\n", status);
	return 0;
}
运行结果如下;

        从运行结果可以看出pthread_join会阻塞主线程,等待assistthread结束。pthread_exit结束时的退出码为0,pthread_join通过参数获得的status也是0,两者是一致的。
2.4、私有数据
        在多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程共有,在程序设计中有时需要保存线程自己的全局变量,这个特殊的变量仅在某个线程内部有效。如常见的变量errno,它返回标准的出错代码。errno不应该是一个局部变量,几乎每个函数都应该可以访问它,但它又不能作为一个全局变量,否则在一个线程里输出的很可能是另一个线程的出错信息。这个问题可以通过创建线程的私有数据(Thread-specific Data 或TSD)来解决。在线程内部,线程私有数据可以被各个函数访问,但它对其它线程都是屏蔽的。
    线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个值。访问数据时都是通过键来访问,就像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。操作线程私有数据的函数主要有4个,声明如下:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*));//创建一个键
int pthread_setspecific(pthread_key_t key, const void* pointer);//为一个键设置线程私有数据
void* pthread_getspecific(pthread_key_t key);//从一个键读取线程私有数据
int pthread_key_delete(pthread_key_t key);//删除一个键
  • pthread_key_create:从Linux的TSD池中分配一项,将其值赋给key供以后访问使用,它的第一个参数key为指向键值的指针,第二个参数为一个函数指针,如果指针不为空,则在线程退出时,将以key所关联的数据作为参数调用destr_function(),释放分配的缓冲区。key一旦被创建,所有线程都可以访问它,但各个线程可以根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。一键多值靠的是一个关键数据结构数组,机TSD池,其结构如下:
    static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = {(0,NULL)};
    创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回个*key,然后设置destructor函数为destr_function。
  • pthread_setspecific:该函数将pointer的值(不是内容)与key相关联,用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间
  • pthread_getspecific:该函数获得与key相关联的数据
  • pthread_key_delete:该寒水鱼用来删除一个键,删除后,键所占用的内存将被释放。需要注意的是,键占用的内存被释放,与键关联的线程数据所占用的内存并不释放,因此,线程数据的释放必须在释放键之前完成。
    如何创建和使用线程的私有数据,具体示例代码如下:
#include <stdio.h>
#include <string.h>
#include <pthread.h>

pthread_key_t key;

void* thread2(void* arg)
{
	int tsd = 5;
	printf("Thread2 start running.\n");
	pthread_setspecific(key, (void*)tsd);
	printf("Thread2 end running return tsd is %d\n", pthread_getspecific(key));
}

void* thread1(void* arg)
{
	int tsd = 0;
	pthread_t thid2;

	printf("Thread1 start running.\n");
	pthread_setspecific(key, (void*)tsd);
	pthread_create(&thid2, NULL, thread2, NULL);
	sleep(1);
	printf("Thread1 end running return tsd is:%d\n",pthread_getspecific(key));
}

int main(void)
{
	pthread_t 	thid1;

	printf("main thread begins running\n");
	pthread_key_create(&key, NULL);
	pthread_create(&thid1, NULL, thread1, NULL);
	sleep(3);
	pthread_key_delete(key);
	printf("main thread exit.\n");
	return 0;
}
运行结果如下所示:

说明:程序中,主线程创建了线程thread1,线程thread1创建了线程thread2,两个线程分别将tsd作为线程私有数据,并有key做索引,从运行结果可以看出来,两个线程虽然用了同一个key,但是tsd数据并不相同。thread2先于thread1结束。
2.5、线程同步
        线程最大的特点就是资源的共享,然而资源共享中的同步问题是多线程编程的难点。Linux系统提供了多种方式处理线程间的同步问题,其中最常用的有互斥锁、条件变量、异步信号,下面主要介绍这3种同步技术的使用。
2.5.1、互斥锁
    互斥锁通过锁机制来实现线程间的同步,在同一时刻它通常只允许一个线程执行关键部分代码。下面互斥锁的几个常用函数,这些函数在pthread.h中定义:
pthread_mutex_init函数----------初始化一个互斥锁
pthread_mutex_destroy函数-------注销一个互斥锁
pthread_mutex_lock函数----------加锁,如果不成功,阻塞等待
pthread_mutex_unlock函数--------解锁
pthread_mutex_trylock函数-------测试加锁,如果不成功则立即返回,错误码为EBUSY
    使用互斥锁前必须进行初始化操作,初始化有2种方式,一种是静态赋值法,将宏结构常亮PTHREAD_MUTEX_INITIALIZER;赋给互斥锁,操作语句如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
另外一种方式是通过pthread_mutex_init函数初始化互斥锁,该函数原型如下:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
其中参数mutexattr表示互斥锁的属性,如果为NULL则使用默认属性,互斥锁的属性及意义,可查看该函数文档。
        初始化以后就可以使用互斥锁了,加锁有2个函数,pthread_mutex_lock和pthread_mutex_trylock().它们的函数原型如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t * mutex);
      用pthread_mutex_lock加锁时,如果mutex已经被锁住,当前加锁的线程就会阻塞,直到互斥锁被其它线程释放。当pthread_mutex_lock函数返回时,说明互斥锁已经被当前线程成功加锁。pthread_mutex_trylock函数则不同,如果mutex已经被加锁,它将立即返回,返回的错误码为EBUSY,而不是阻塞等待。
      注意:加锁时不论哪种类型的锁,都不可能被两个不同的线程同时得到,其中一个必须等待解锁,在同一进程中的线程,如果加锁后没有解锁,则其它线程就无法再获得该锁。
      pthread_mutex_unlock用来解锁,函数原型如下:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
用该函数解锁时,要满足两个条件,一是互斥锁必须处于加锁状态,调用该函数的线程必须是给互斥锁加锁的线程,解锁后如果有其它线程在等待互斥锁,等待队列中的第一个线程将获得互斥锁。
当一个互斥锁使用完毕后,必须进行清除,清除互斥锁使用函数pthread_mutex_destroy,该函数的原型如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
清除一个互斥锁意味着释放它所占用的资源,清除锁时要求该锁处于开放状态,如果锁处于锁定状态,该函数返回EBUSY,该函数成功执行时返回0。由于在Linux中互斥锁并不占用内存,因此该函数除了只是清除互斥锁,并没有其它操作。
下面的代码展示了互斥锁的用法:
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t number_mutex;
int		globalnumber;

void write_globalnumber()
{
	pthread_mutex_lock(&number_mutex);
	globalnumber++;
	pthread_mutex_unlock(&number_mutex);
}

int read_globalnumber()
{
	int temp;
	pthread_mutex_lock(&number_mutex);
	temp = globalnumber;
	pthread_mutex_unlock(&number_mutex);
	return temp;
}
        上面代码中,两个函数对全局变量进行读写操作,write函数使用互斥锁保证在修改变量的时候操作一次执行完毕,不会中断。而read函数使用互斥锁保证在读取数据的时候,全局变量不会被修改,确保读到正确的数据。

2.5.2、条件变量
    条件变量是利用线程间共享的全局变量进行同步的一种机制。条件变量宏观上类似if语句,符合条件就能执行某段代码,否则只能等待条件成立。使用条件变量主要包括2个动作,一个等待使用资源的线程等待“条件变量被设置为真”;另一个线程在使用完资源后“设置条件为假”,这样就可以保证线程同步了。这种机制存在一个问题,就是要保证条件变量能被正确的修改,条件变量要受到特殊的保护,实际使用中互斥锁扮演着这样一个保护者的角色。Linux也提供了一系列对条件变量操作的函数,如下:
  • pthread_cond_init函数----------初始化条件变量
  • pthread_cond_wait函数----------基于条件变量阻塞,无条件等待
  • pthread_cond_timedwait函数-----阻塞直到指定事件发生,计时等待
  • pthread_cond_signal函数--------解除特定线程的阻塞,存在多个等待线程时按入队顺序激活其中一个
  • pthread_cond_broadcast函数-----解除所有线程阻塞
  • pthread_cond_destroy函数-------清除条件变量
与互斥锁一样,条件变量的初始化也有2种方式,一种是静态赋值法,将宏结构常量PTHREAD_COND_INITIALIZER赋给互斥锁,操作语句如下:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
另一种方式是使用函数pthread_cond_init,它的原型如下:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
其中cond_attr参数是条件变量的属性,由于其并没有得到实现,所以它的值通常是NULL。
等待条件成立有2个函数,函数原型如下:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec* abstime);
pthread_cond_wait函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量阻塞,直到条件信号被唤醒。通常条件表达式在互斥锁的保护下求值,






















  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Linux系统下,可以使用线程实现串口通信例程。 首先,需要引入一些头文件,如`<stdio.h>, <stdlib.h>, <unistd.h>, <fcntl.h>, <termios.h>, <pthread.h>`,以便使用相关函数和数据结构。 接下来,打开串口设备文件,使用`open()`函数,并通过`<fcntl.h>`中的`O_RDWR`参数设置为可读写模式。例如,打开`/dev/ttyS0`串口设备: ```c int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd == -1) { perror("打开串口失败"); exit(EXIT_FAILURE); } ``` 然后,配置串口属性,包括波特率、数据位、停止位等。首先需要获取当前串口属性,使用`tcgetattr()`函数,并通过`<termios.h>`中的数据结构`struct termios`进行配置。例如,设置波特率为115200: ```c struct termios attr; if (tcgetattr(fd, &attr) == -1) { perror("获取串口属性失败"); close(fd); exit(EXIT_FAILURE); } cfsetispeed(&attr, B115200); cfsetospeed(&attr, B115200); if (tcsetattr(fd, TCSANOW, &attr) == -1) { perror("设置串口属性失败"); close(fd); exit(EXIT_FAILURE); } ``` 接下来,创建一个线程,用于接收串口数据。使用`pthread_create()`函数,并编写线程函数。例如,以下为接收串口数据的线程函数: ```c void *receiveThread(void *arg) { char buffer[256]; int len; while (1) { len = read(fd, buffer, sizeof(buffer)); if (len > 0) { // 处理接收到的数据 // ... } } return NULL; } pthread_t tid; pthread_create(&tid, NULL, receiveThread, NULL); ``` 最后,主线程(或其他线程)可以通过`write()`函数向串口发送数据。例如,向串口发送一个字符串: ```c char *str = "Hello, Serial!"; write(fd, str, strlen(str)); ``` 整个程序运行时,主线程可以继续执行其他任务,而串口数据的接收则在单独的线程中进行。 这样,就完成了一个简单的Linux线程实现串口通信的例程。 ### 回答2: 在Linux下,可以通过使用线程来实现串口通信。下面是一个简单的示例代码: ```c #include <stdio.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <pthread.h> int fd; // 串口文件描述符 pthread_t thread_id; // 线程ID void* read_thread(void* arg) { char buf[255]; while(1) { int len = read(fd, buf, sizeof(buf)); // 从串口读取数据 if (len > 0) { buf[len] = '\0'; // 添加字符串结束符 printf("接收到的数据: %s\n", buf); } } } int main() { // 打开串口 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { printf("无法打开串口\n"); return -1; } // 配置串口 struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tcsetattr(fd, TCSANOW, &options); // 创建读取数据的线程 pthread_create(&thread_id, NULL, read_thread, NULL); // 主线程继续执行其他任务 while(1) { // 发送数据到串口 char msg[] = "Hello, Serial Port!"; write(fd, msg, sizeof(msg)); usleep(1000000); // 等待1秒 } // 关闭串口 close(fd); return 0; } ``` 在上述代码中,通过`open`函数打开了串口设备文件`/dev/ttyS0`(请根据实际情况更改),然后使用`termios`结构体配置了串口的波特率、数据位、停止位等属性。接下来,通过`pthread_create`函数创建了一个线程,该线程负责读取串口数据。主线程则负责发送数据到串口。 需要注意的是,该例程只是一个简单的示例,仅用于说明线程实现串口通信的基本思路。实际应用中,还需考虑数据的解析、错误处理、线程同步等问题。 ### 回答3: 在Linux下,可以使用串口通信库来实现线程的串口通信例程。下面是一个简单的例子: 1. 首先,我们需要安装和配置串口通信库。常用的库包括`libserialport`和`termios`。你可以使用包管理工具来安装这些库。 2. 在程序中,我们需要引入相关的头文件和库: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <serialport.h> ``` 3. 然后,创建一个线程来读取串口数据。可以定义一个函数作为线程入口点,例如`serial_read`: ``` void *serial_read(void *data) { struct sp_port *serial_port = (struct sp_port *)data; char buffer[256]; int n; while (1) { n = sp_blocking_read(serial_port, buffer, sizeof(buffer), 100); if (n > 0) { // 处理接收到的数据 printf("Received: %.*s\n", n, buffer); } else { // 读取数据出错或超时 printf("Serial read error or timeout\n"); } } return NULL; } ``` 4. 接下来,创建一个线程来发送串口数据。可以定义一个函数作为线程入口点,例如`serial_write`: ``` void *serial_write(void *data) { struct sp_port *serial_port = (struct sp_port *)data; char message[] = "Hello, Serial Port!\n"; while (1) { sp_nonblocking_write(serial_port, message, sizeof(message) - 1); usleep(1000000); // 暂停1秒钟 } return NULL; } ``` 5. 在主函数中,打开串口设备并创建两个线程: ``` int main() { struct sp_port *serial_port; pthread_t read_thread, write_thread; // 打开串口设备(例如:/dev/ttyS0) sp_get_port_by_name("ttyS0", &serial_port); sp_open(serial_port, SP_MODE_READ_WRITE); // 创建读取线程 pthread_create(&read_thread, NULL, serial_read, (void *)serial_port); // 创建写入线程 pthread_create(&write_thread, NULL, serial_write, (void *)serial_port); // 等待线程结束 pthread_join(read_thread, NULL); pthread_join(write_thread, NULL); // 关闭串口设备 sp_close(serial_port); sp_free_port(serial_port); return 0; } ``` 这是一个简单的例程,通过两个线程实现了线程的串口通信。读取线程通过不断调用`sp_blocking_read`函数读取串口数据,而写入线程通过不断调用`sp_nonblocking_write`函数发送串口数据。你可以根据需求来修改和扩展这个例程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值