APUE学习之多线程编程

目录

一、多线程编程基本概念

二、多线程编程流程图

三、多线程编程详解

1、创建线程

2、互斥锁

3、死锁

四、多线程改写服务器程序

1、多进程服务端代码

2、客户端进行温度上报代码

3、运行结果


一、多线程编程基本概念

多线程编程是一种并发编程模型,允许程序同时执行多个独立的执行流(线程)。每个线程都是独立的、有自己的执行路径,但它们共享同一进程的资源,如内存空间、文件描述符等。多线程编程的基本概念包括以下几个方面:

1. 线程与进程:
线程(Thread):是进程中的一个执行流,每个线程都有独立的程序计数器(PC)和栈,但共享同一进程的地址空间和其他资源。线程之间的切换比进程切换更为轻量,因为它们共享大部分上下文。
进程(Process): 是一个独立运行的程序,拥有自己的地址空间、文件描述符等资源。进程之间的通信通常需要使用 IPC(进程间通信)机制。

2. 线程的创建和销毁:
创建线程: 通过调用线程库或操作系统提供的接口创建新线程。在C语言中,可以使用 `pthread_create` 函数来创建线程。
销毁线程: 线程可以在执行完任务后自行退出,也可以由其他线程通过调用 `pthread_cancel` 或类似的接口来终止。

3. 线程同步与互斥:                                                                                                                       互斥锁(Mutex): 用于保护共享资源,防止多个线程同时访问。一个线程在访问共享资源之前必须先获得互斥锁,然后在使用完资源后释放锁。
信号量(Semaphore): 用于控制对一组资源的访问,可以实现更复杂的同步机制。
条件变量(Condition Variable):用于线程间的条件同步,一个线程等待某个条件为真,而另一个线程在满足条件时通知等待的线程。

4. 线程安全
   线程安全(Thread Safety):表示一个函数或代码块在多线程环境中可以安全地被调用,而不会导致数据竞争或不一致性。
   临界区(Critical Section):是一段代码,如果多个线程同时执行它,可能导致数据竞争。使用互斥锁等机制来保护临界区,确保同一时间只有一个线程能够执行它。

5.并发与并行:
   并发(Concurrency):指程序具有交替执行的能力,即多个任务在一段时间内交替执行。并发可以在单核处理器上实现,通过时间片轮转或事件驱动的方式。
   并行(Parallelism): 指多个任务在同一时刻执行,通常在多核处理器或多机环境中实现。

6. 线程池:
   线程池(Thread Pool): 是一组预先创建好的线程,可以用于执行任务。线程池减少了线程创建和销毁的开销,提高了资源利用率。

多线程编程需要仔细处理共享资源的访问、避免数据竞争和死锁等问题。在设计和实现多线程程序时,深入理解这些基本概念是非常重要的。

二、多线程编程流程图

三、多线程编程详解

1、创建线程

一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程,C/C++程序中,主线程就是通过main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。

主线程和子线程默认的关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。在这种情况下,主线程和子线程通常定义为以下两种关系:

(1)可会合(joinable):

这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时主线程函数继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。当一个线程被标记为可合并时,它在结束时不会立即释放其资源,而是会保留一些信息供其他线程使用。其他线程可以通过调用 pthread_join(在 POSIX 线程中)等函数等待可合并的线程的结束,并获取其终止状态。这样可以保证主线程或其他线程能够等待子线程执行完成,获取其结果或处理相关的事务。

(2)相分离(detached):

表示子线程无需和主线程会合,也就是相分离的。当一个线程被标记为相分离时,它被设置为在其结束时自动释放其资源,无需等待其他线程显式地调用 pthread_join 等函数等待它的结束。相分离的线程在结束后会被系统回收,无法通过其他线程来获取其终止状态或进行等待。

线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是可会合状态的,这种情况下,原有的线程等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

接下来我们使用一个例子来看看线程创建的基本使用和相关概念吧:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

void *thread_worker1(void  *args);
void *thread_worker2(void *args);

int main(int argc,char *argv[])
{

        int             shared_var = 1000;
        pthread_t       tid;            //线程的标识符
        pthread_attr_t  thread_attr;    //线程的属性




        if( pthread_attr_init(&thread_attr) )   //函数初始化
        {

                printf("pthread_attr_init() failure:%s\n",strerror(errno));
                return -1;
        }

        if( pthread_attr_setstacksize(&thread_attr,120*1024) )  //线程栈的大小
        {

                printf("pthread_attr_setstacksize() failure:%s\n",strerror(errno));
                return -1;
        }

        if( pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED) ) //线程的分离状态
        {

                printf("pthread_attr_setdetachstate() failure:%s\n",strerror(errno));
                return -1;

        }


        pthread_create(&tid,&thread_attr,thread_worker1,&shared_var);   //相分离状态
        printf("Thread worker 1 tid[%ld] created OK\n",tid);


        pthread_create(&tid,NULL,thread_worker2,&shared_var);   //可会合状态
        printf("Thread worker 2 tid[%ld] created OK\n",tid);


        pthread_attr_destroy(&thread_attr);    //摧毁释放线程属性

        pthread_join(tid,NULL);         //等待,直到thread worker2 exit()


        while(1)
        {

                printf("Main thread shared_var:%d\n",shared_var);
                sleep(10);

        }
}


void *thread_worker1(void  *args)
{

        int             *ptr = (int *)args;

        if(!args)
        {

                printf("%s() get invalid arguments\n",__FUNCTION__);
                pthread_exit(NULL);

        }

        printf("Thread workder 1 [%ld] start running....\n",pthread_self());


        while(1)
        {

                printf("+++: %s before shared_var++:%d\n",__FUNCTION__,*ptr);
                *ptr  += 1;
                sleep(2);
                printf("+++: %s after sleep shared_var++:%d\n",__FUNCTION__,*ptr);

        }
        printf("Thread worker 1 exit...\n");

}



void *thread_worker2(void *args)
{

        int             *ptr = (int *)args;


        if(!args)
        {
                printf("%s() get invalid arguments\n",__FUNCTION__);
                 pthread_exit(NULL);

        }

        printf("Thread workder 2 [%ld] start running....\n",pthread_self());


        while(1)
        {

                printf("---: %s before share_var++:%d\n",__FUNCTION__,*ptr);
                *ptr += 1;
                sleep(2);
                printf("---:%s after sleep shared_var:%d\n",__FUNCTION__,*ptr);

        }

        printf("Thread worker 2 exit...\n");

}

 代码分析:

本代码调用了pthread_create()函数用来创建子线程,该函数的原型是:

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

     

 Compile and link with -pthread.

该函数共有四个参数:

第一个参数thread是一个pthread_t类型的指针,他用来返回该线程的线程ID。每个线程都能够通过pthread_self()来获取自己的线程ID(pthread_t类型)。

第三个参数start_routine是一个函数指针,这是所创建的子线程要执行的任务(函数)。

第四个参数arg就是传给了所调用的函数的参数,如果有多个参数需要传递给子线程则需要封装到一个结构体里传进去。

第二个参数是线程的属性,其类型是pthread_attr_t类型,其定义如下:

在创建两个线程时,我们都通过第四个参数将主线程栈中的shared_var变量地址传给了子线程,因为所有线程都是在同一进程空间中运行,而只是子线程有自己独立的栈空间,所以这时所有子进程都可以访问主线程空间的shared_var变量。所以编译运行结果如下:

从上面的运行结果我们可以看出,thread_worker1在创建后首先开始运行,在开始自加之前值为初始值1000,然后让该值自加后休眠2秒后再打印该值发现不是1001而是1002了。这是由于shared_var这个变量会被两个子线程同时访问修改导致。如果一个资源会被不同的线程访问修改,那么我们把这个资源叫做临界资源,那么对于该资源访问修改相关的代码就叫做临界区。那么如何解决多个线程之间共享同一个共享资源呢?让我们继续往下学习!

2、互斥锁

互斥锁(Mutex,全名为 Mutual Exclusion)是一种用于多线程编程的同步机制,用于保护共享资源,防止多个线程同时访问导致数据竞争和不一致性。互斥锁确保在同一时刻只有一个线程可以访问被保护的共享资源。

试想一下,一个房间只有一个洗手间,那末多人是怎么解决马桶共享的问题的呢?在这里,把马桶当做是临界资源,洗手间是临界区。在我们进入到洗手间(临界区)后,就首先上锁;然后用完洗手间(临界区)后,把锁释放供别人使用。如果有人想去洗手间时发现门锁上了,他也有两种策略:1、在洗手间那里等(阻塞);2、暂时先离开等会再过来看(非阻塞);

我们将上面的代码进行修改,看一下是如何利用锁的机制来解决共享资源的问题的:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void *thread_worker1(void *args);
void *thread_worker2(void *args);

typedef struct worker_ctx_s        //因为在创建线程给线程执行函数传参的时候只能传一个参数,而我们要传递共享变量shared_var和它相应的互斥锁lock,所以在这里用结构体将他们封装在一起
{

        int                     shared_var;
        pthread_mutex_t         lock;

}worker_ctx_t;


int main(int argc,char *argv[])
{

        worker_ctx_t            worker_ctx;    //结构体类型定义了转给子线程的变量参数
        pthread_t               tid;
        pthread_attr_t          thread_attr;


        worker_ctx.shared_var = 1000;
        pthread_mutex_init(&worker_ctx.lock,NULL);    //初始化互斥锁

        if( pthread_attr_init(&thread_attr) )
        {

                printf("pthread_attr_init() failure:%s\n",strerror(errno));
                return -1;

        }

        if( pthread_attr_setstacksize(&thread_attr,120*1024) )
        {

                printf("pthread_attr_setstacksize() failure:%s\n",strerror(errno) );
                return -1;

        }

        if( pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED) )
        {

                printf("pthread_attr_setdatechstate() failure:%s\n",strerror(errno));
                return -1;

        }

        pthread_create(&tid,&thread_attr,thread_worker1,&worker_ctx);
        printf("Thread worker 1 tid[%ld] created ok\n",tid);

        pthread_create(&tid,&thread_attr,thread_worker2,&worker_ctx);
        printf("Thread worker 2 tid[%ld] created ok\n",tid);

        while(1)
        {

                printf("Main thread shared_var:%d\n",worker_ctx.shared_var);
                sleep(10);

        }

        pthread_mutex_destroy(&worker_ctx.lock);    //释放摧毁互斥锁

}


void *thread_worker1(void *args)
{

        worker_ctx_t    *ctx = (worker_ctx_t *)args;

        if(!args)
        {

                printf("%s() get invalid arguments\n",__FUNCTION__);
                pthread_exit(NULL);

        }

        printf("Thread worker 1 [%ld] start running...\n",pthread_self());


        while(1)
        {

                pthread_mutex_lock(&ctx->lock);    //这里调用函数来申请锁(阻塞锁),如果锁被别的线程持有则该函数不会返回

                printf("+++: %s before shared_var++:%d\n",__FUNCTION__,ctx->shared_var);
                ctx->shared_var ++;
                sleep(2);
                printf("+++:  %s after sleep shared_var:%d\n",__FUNCTION__,ctx->shared_var);

                pthread_mutex_unlock(&ctx->lock);    //释放锁

                sleep(1);

        }

}


void *thread_worker2(void *args)
{

        worker_ctx_t    *ctx = (worker_ctx_t *)args;

        if(!args)
        {

                printf("%s() get invalid arguments\n",__FUNCTION__);
                pthread_exit(NULL);

        }

        printf("Thread worker 2 [%ld] start running...\n",pthread_self());

        while(1)
        {

                if( 0 != pthread_mutex_trylock(&ctx->lock) )    //申请锁(非阻塞锁),如果锁现在被别的线程占用则返回非0值,如果没有被占用则返回0;
                {

                        continue;

                }

                printf("---: %s before shared_var++:%d\n",__FUNCTION__,ctx->shared_var);
                ctx->shared_var ++;
                sleep(2);
                printf("---:  %s after sleep shared_var:%d\n",__FUNCTION__,ctx->shared_var);

                pthread_mutex_unlock(&ctx->lock);

                sleep(1);
        }

        printf("Thread worker 2 exit...\n");

        return NULL;

}

运行结果:

 

互斥锁是一种常见的同步机制,用于确保多线程程序中的线程安全。通过在临界区代码中使用互斥锁,可以避免多个线程同时修改共享资源而导致的问题。在实际应用中,需要小心设计互斥锁的使用,以避免死锁和性能问题。

3、死锁

死锁的形成:

如果多个线程要调用多个对象,则在上锁的时候可能会出现“死锁”。举个例子:A、B两个线程会同时使用到两个共享变量m和n,同时每个变量都有自己相应的锁M和N。这时A线程首先拿到M锁访问m,接下来他需要拿N锁来访问变量n;而如果此时B线程拿着N锁等待着M锁的话,就造成了线程“死锁”。

死锁产生的4个必要条件:

  1. 互斥条件(Mutual Exclusion): 一个资源每次只能被一个线程或进程占用。
  2. 占有并等待条件(Hold and Wait): 一个线程或进程可以持有一些资源,并等待其他线程或进程占有的资源。
  3. 不可抢占条件(No Preemption): 资源不能被强制性地从一个线程或进程中取走,只能由持有它的线程或进程显式释放。
  4. 循环等待条件(Circular Wait): 一系列线程或进程形成一个循环,每个线程或进程等待下一个线程或进程占有的资源。

当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机使用性能的。那么,解决死锁问题就是相当有必要的了。 

 死锁问题的解决:

产生死锁需要四个条件,那么,只要这四个条件至少有一个条件得不到满足,则不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应该加以保证,所以,主要是破坏产生死锁的其他三个条件。

1.破坏“占有且等待”条件

方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。(优点:简单易实施且安全;缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象)

方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。

2.破坏“不可抢占”条件

当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。

3.破坏“循环等待”条件

可以通过定义资源类型的线性顺序来预防。可以将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。

四、多线程改写服务器程序

如果之前没有学过多进程编程,建议在看本代码前,建议先看文章《APUE学习之多进程编程》

 在上面学习Linux下多线程编程后,我们就可以使用多线程编程模型改写服务器的多线程实现,其流程图和程序代码如下:

代码如下:

1、多进程服务端代码

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/in.h>

#define port    8889

/*void *(THREAD_BODY)表示THREAD_BODY通过typedef可以用作声明函数指针 */
/*(void *thread_arg)表示这个函数接受一个指向未知类型的参数thread_arg */
typedef void *(THREAD_BODY)(void *thread_arg);

void *thread_worker(void *ctx);
int thread_start(pthread_t *thread_id,THREAD_BODY *thread_workbody,void *thread_arg);

int main(int argc,char *argv[])
{

        int                     sockfd = -1;
        int                     rv = -1;
        struct sockaddr_in      servaddr;
        struct sockaddr_in      cliaddr;
        socklen_t               len = 0;
        int                     clifd;
        pthread_t               tid;


        /*创建socket文件描述符*/
        sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(sockfd < 0)
        {

                printf("create sockfd failure:%s\n",strerror(errno));
                return -1;

        }
        printf("create sockfd[%d] successfully!\n",sockfd);


        /*IPV4*/
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port   = htons(port);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   /*监听所有端口*/


        /*用bind函数绑定IP、端口*/
        rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
        if(rv < 0)
        {

                printf("socket bind on port failure:%s\n",strerror(errno));
                return -2;

        }

        /*listen函数进行监听*/
        listen(sockfd,13);
        printf("start to listen to port[%d]\n",port);


        /*调用accept函数与客户端进行通信*/
        while(1)
        {

                printf("Start to accept new client incoming....\n");

                clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
                if(clifd < 0)
                {

                        printf("accept new client failure:%s\n",strerror(errno));
                        return -3;

                }


                printf("accept new client [%s:%d] successfully!\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

                /*创建线程*/
                thread_start(&tid,thread_worker,(void *)clifd);

        }

        close(sockfd);

        return 0;

}



int thread_start(pthread_t *thread_id,THREAD_BODY * thread_workboby,void *thread_arg)
{

        int                     rv = -1;
        pthread_attr_t          thread_attr;

        if( pthread_attr_init(&thread_attr) )
        {

                printf("pthread_attr_init() failure:%s\n",strerror(errno));
                goto cleanup;

        }

        if( pthread_attr_setstacksize(&thread_attr,120*1024) )
        {

                printf("pthread_attr_setstacksize() failure:%s\n",strerror(errno));
                goto cleanup;

        }

        if( pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED) )
        {

                printf("pthread_attr_setdetachstate() failure:%s\n",strerror(errno));
                goto cleanup;

        }


        /*创建线程*/
        if( pthread_create(thread_id,&thread_attr,thread_workboby,thread_arg) )
        {

                printf("pthread_create() failure:%s\n",strerror(errno));
                goto cleanup;

        }

        rv = 0;

cleanup:
        pthread_attr_destroy(&thread_attr);

        return rv;

}



void *thread_worker(void *ctx)
{

        int             clifd;
        int             rv;
        char            buf[1024];
        int             i;


        if( !ctx )
        {

                printf("Invalid input arguments in %s()\n",__FUNCTION__);
                pthread_exit(NULL);

        }

        clifd = (int)ctx;

        printf("Child thread start to commuciate with socket client...\n");


        while(1)
        {

                memset(buf,0,sizeof(buf));

                rv = read(clifd,buf,sizeof(buf));
                if(rv < 0)
                {

                        printf("Read data from client sockfd[%d] failure:%s and thread will exit\n",clifd,strerror(errno));
                        close(clifd);
                        pthread_exit(NULL);

                }

                else if(rv == 0)
                {

                        printf("Socket[%d] get disconnected and thread will exit.\n",clifd);
                        close(clifd);
                        pthread_exit(NULL);

                }

                else if(rv > 0)
                {

                        printf("Read %d bytes data from server:%s\n",rv,buf);

                }



                rv = write(clifd,buf,rv);
                if(rv < 0)
                {

                        printf("write to client failure:%s and thread will exit\n",strerror(errno));
                        close(clifd);
                        pthread_exit(NULL);

                }
        }

}

2、客户端进行温度上报代码

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>


#define PORT 8889
#define IP "127.0.0.1"

int get_temperature(float *temp);

int main(int argc,char **argv)
{
        int                      fd = -1;
        int                      rv = -1;
        struct sockaddr_in       addr;
        char                     buf[1024];
        float                    temp;
        int                      r = -1;
        char                     t[16];
 


         if((r = get_temperature(&temp)) <0)
        {
                printf("get temperature failure\n");
                return -1;

        }                                                                       

        printf("temp:%.2f\n",temp);




        if((fd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
                printf("socket failure:%s\n",strerror(errno));
                return -1;
        }
        printf("create sockfd[%d]successfully\n",fd);




        memset(&addr,0,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        inet_aton(IP,&addr.sin_addr);

        if((connect(fd,(struct sockaddr *)&addr,sizeof(addr)))<0)
        {
                printf("Connect failure:%s\n",strerror(errno));
                return -2;
                goto cleanup;
        }


        memset(&t,0,sizeof(t));

        sprintf(t,"%.2f",temp);
        if((write(fd,t,strlen(t)))<0)
        {
                printf("Write failure:%s\n",strerror(errno));
                return -3;
                goto cleanup;
        }
        printf("write bytes:%s\n",t);

        memset(&buf,0,sizeof(buf));

        if((rv=read(fd,buf,sizeof(buf)))<0)
        {
                printf("Read failure:%s\n",strerror(errno));
                return -4;
                goto cleanup;
        }

        else if(rv==0)
        {
                printf("连接断开\n");
                return -5;
                goto cleanup;
        }

        printf("Read [%d] Byte data back from serve [%d] :%s \n",rv,fd,buf);

cleanup:
        close(fd);
}



int get_temperature(float *temp)
{
                int             fd;
                char            buf[128];
                char            *ptr;
                char            w1_path[64] = "/sys/bus/w1/devices/";
                DIR             *dirp = NULL;
                struct dirent   *direntp = NULL;
                char            chip_sn[32];
                int             found = 0;



                if(!(dirp = opendir(w1_path)))
                {

                        printf("open floder %s failure:%s\n",w1_path,strerror(errno));
                        return -1;

                }


                while((direntp = readdir(dirp)))
                {

                        if(strstr(direntp->d_name,"28-"))
                        {

                                strncpy(chip_sn,direntp->d_name,sizeof(chip_sn));
                                found = 1;

                        }

                }
                if(!found)
                {

                        printf("can not find ds18b20 chipset\n");
                        return -2;

                }
                strncat(w1_path,chip_sn,sizeof(w1_path)-strlen(w1_path));
                strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));

                closedir(dirp);


                fd = open(w1_path,O_RDONLY);
                if(fd < 0)
                {

                        printf("open file failed\n");
                        return -3;

                }




                lseek(fd,0,SEEK_SET);
                memset(buf,0,sizeof(buf));
                read(fd,buf,sizeof(buf));



                ptr = strstr(buf,"t=");
                if(NULL == ptr)
                {

                        printf("Can not find t= string\n");
                        return -4;

                }

                ptr += 2;
                *temp = atof(ptr)/1000;

                close(fd);

                return 0;


}

3、运行结果

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学代码的小信

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值