《嵌入式系统开发实践》实验二 进程与线程

一、 实验目的

了解Linux中进程和线程的概念;
了解多线程程序的基本原理;
了解pthread库;
掌握用system、exec函数族、fork函数创建进程;
掌握使用pthread库中的函数编写多线程程序。

二、 实验任务与要求

应用fork函数创建子进程;
应用fork函数创建子进程,在父子进程中执行不同的任务;
新线程的创建,以及和主线程之间的关系。
三、 实验工具和环境

PC机、Linux Ubuntu操作系统。

四、 实验内容与结果

程序设计。
(1) 在父子进程中分别编写循环显示数字1-10和显示数字101-110的程序;

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        for (int i = 0; i <= 10; i++)
        {
            printf("subprocess: %d\n", i);
        }
    }
    else
    {
        for (int i = 101; i <= 110; i++)
        {
            printf("process: %d\n", i);
        }
    }
}

在这里插入图片描述

使用了fork()函数来创建一个新的进程。fork()函数是Unix-like操作系统中创建进程的主要方法,它会复制当前调用它的进程,并返回一个进程ID。复制出来的进程称为子进程,原来的进程称为父进程。

这段代码中,首先调用了fork()函数,然后根据返回值判断是父进程还是子进程。如果返回值是0,说明是子进程,它会打印出0到10的数字;如果返回值不是0,说明是父进程,它会打印出101到110的数字。由于父进程和子进程是并发执行的,所以打印的顺序可能不一定固定。

(2) 应用函数sleep的不同参数等,体现程序中父子进程的并发运行。

在父子进程中分别执行不同的任务,即在子进程中编写程序1+2+…+10的程序,将该程序写入到sum.c中,并编译并运行该程序;在父进程中执行网络连通情况的测试任务,要求子进程退出后父进程才退出。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    pid = fork();
    if (pid == 0)
    {
        int fd;
        int len, s_r, s_w;
        char *buf = "#include<stdio.h>\
        \nint main(){\
        \n  int i,s=0;\
        \n  for (i=1;i<=10;i++)\
        \n     s = s + i;\
        \n     printf(\"s=%d\\n\", s);\
        \n}";

        fd = open("sum.c", O_CREAT | O_RDWR, 0666);
        len = strlen(buf);
        s_w = write(fd, buf, len);
        char buf_r[500];
        lseek(fd, 0, SEEK_SET);
        s_r = read(fd, buf_r, s_w);
        buf_r[s_r] = "\0";
        printf("Read:\n %s \n", buf_r);
        system("gcc sum.c -o sum");
        close(fd);
        execlp("./sum", "sum", NULL);
    }
    else if (pid > 0)
    {
        system("ping 127.0.0.1 -c 2");
        sleep(5);
    }
    else // fork失败
    {
        perror("fork error");
        exit(0);
    }
}

在这里插入图片描述

使用了fork()函数来创建一个子进程。子进程中,首先用open()函数打开一个名为sum.c的文件,如果文件不存在,就创建一个。然后用write()函数向文件中写入一段C语言代码,这段代码的功能是计算1到10的和并打印出来。接着用read()函数读取文件中的内容,并打印出来。然后用system()函数调用gcc命令来编译sum.c文件,生成一个名为sum的可执行文件。最后用execlp()函数执行sum文件,并传入一个参数"sum"。父进程中,用system()函数调用ping命令来测试本地网络连接,然后等待5秒。

调试下列程序。程序中使用pthread线程库创建一个新线程,在父进程(也称为主线程)和新线程中分别显示进程id和线程id,并观察线程id数值。(代码test3.c)问题如下:

// test3.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
pthread_t ntid;
void printids(const char *s){ /*各线程共享的函数*/
    pid_t pid;
    pthread_t tid;
    pid = getpid();
    tid = pthread_self();
    printf("%s pid = %u tid = %u (0x%x) \n",s,(unsigned int)pid,(unsigned int)tid,(unsigned int)tid);
}

void *thread_fun(void *arg){ /*新线程执行代码*/
    printids(arg);
    return NULL;
}

int main(void){
    int err;
    /*下列函数创建线程*/
    err = pthread_create(&ntid,NULL,thread_fun,"我是新线程:");
    if(err != 0){
        fprintf(stderr,"创建线程失败:%s\n",strerror(err));
        exit(1);
    }
    printids("我是父进程");
    sleep(2);  /*等待新线程运行结束*/
    return 0;
}

在这里插入图片描述

(1) 进程在一个全局变量ntid中保存了新创建的线程的id,如果新创建的线程不调用pthread_self而是直接打印这个ntid,能不能达到同样的效果?为什么?

不一定能达到同样的效果,因为ntid是一个全局变量,可能被其他线程修改或覆盖。而pthread_self返回的是当前线程的id,不会受到其他线程的影响。所以,为了安全和准确地获取线程的id,最好使用pthread_self函数。

(2) 在本题中,如果没有sleep(2)函数,会出现什么结果?为什么?

如果没有sleep(2)函数,可能会出现只打印出父进程的id,而没有打印出新线程的id的情况。这是因为父进程在创建新线程后,没有等待新线程运行结束,就直接退出了。而当父进程退出时,它的所有子线程也会被终止。所以,为了让新线程有机会执行,需要在父进程中调用sleep函数,让父进程暂停一段时间。

调试下列程序。程序中只给出了线程产生、退出和等待的程序,(代码 test4.c)根据该程序完成提出的问题:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
void *thread_fun1(void *arg){
    printf("thread 1 returning\n");
    return ((void *)1);
}

void *thread_fun2(void *arg){
    printf("thread 2 exiting\n");
    pthread_exit((void *)2);
}

int main(void){
    int err;
    pthread_t tid1,tid2;
    void *tret;
    err = pthread_create(&tid1,NULL,thread_fun1,NULL);
    if(err!=0)
        fprintf(stderr,"can't create thread 1: %s\n",strerror(err));
    err = pthread_create(&tid2,NULL,thread_fun2,NULL);
    if(err!=0)
        fprintf(stderr,"can't create thread 2: %s\n",strerror(err));
    err = pthread_join(tid1,&tret);
    if(err!=0)
        fprintf(stderr,"can't join with thread 1: %s\n",strerror(err));
    printf("thread 1 exit code %d\n",(int)tret);
    err = pthread_join(tid2,&tret);
    if(err!=0)
        fprintf(stderr,"can't join with thread 2: %s\n",strerror(err));
    printf("thread 2 exit code %d\n",(int)tret);
    exit(0);
}

(1) 修改程序,在线程1、2中分别完成1到1000的加法,观察线程的执行情况与线程结束时的状态,并给出执行结果的解释;

void *thread_fun1(void *arg){
    int sum = 0;
    for(int i = 1; i <= 1000; i++){
        sum += i;
    }
    printf("thread 1 returning\n");
    return ((void *)&sum);
}

void *thread_fun2(void *arg){
    int sum = 0;
    for(int i = 1; i <= 1000; i++){
        sum += i;
    }
    printf("thread 2 exiting\n");
    pthread_exit((void *)&sum);
}

定义了两个线程函数thread_fun1thread_fun2,它们的功能都是计算1到1000的和,并把结果存储在一个int类型的变量sum中。不同的是,thread_fun1用return语句返回sum的地址,而thread_fun2用pthread_exit()函数退出线程并传递sum的地址。为了运行这两个线程函数,需要用pthread_create()函数创建两个线程,并把它们的线程ID存储在一个pthread_t类型的变量中。然后,需要用pthread_join()函数等待这两个线程结束,并获取它们的返回值。最后,需要打印出这两个线程的返回值,它们都是500500。

(2)改变线程2的程序为计算1-100的阶乘,观察线程1和线程2执行任务的时间,观察结果。

void *thread_fun2(void *arg)
{
    int i, j, num;
    unsigned long long fact;

    printf("计算1到100的阶乘\n");

    for (i = 1; i <= 100; i++)
    {
        num = i;
        fact = 1;

        for (j = 1; j <= num; j++)
        {
            fact *= j;
        }

        printf("%d! = %llu\n", num, fact);
    }
    printf("thread 2 exiting\n");
    pthread_exit((void *)1);
}

在这里插入图片描述

定义了一个线程函数thread_fun2,它的功能是计算1到100的阶乘,并把结果打印出来。阶乘是一个数的所有正整数因数的乘积,例如5! = 5 x 4 x 3 x 2 x 1 = 120。这段代码用一个for循环遍历1到100的每个数,然后用另一个for循环计算它的阶乘,并把结果存储在一个unsigned long long 类型的变量fact中。这个类型可以表示最大为18446744073709551615的无符号整数。然后,这段代码用printf()函数输出每个数和它的阶乘。最后,这段代码用pthread_exit()函数退出线程,并传递一个值为1的void*类型的指针。

编写程序,利用多线程实现生产者-消费者问题,生产者和消费者分别在2个线程中,生产者生产的产品放在链表的表头上,消费者从表头取走产品。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define MAX 12
sem_t empty;
sem_t full;
pthread_mutex_t mutex;
int buffer[MAX];
int count = 0;
void *producer(void *arg)
{
    int i;
    for (i = 0; i < MAX; i++)
    {
        sem_wait(&empty);
        pthread_mutex_lock(&mutex);
        buffer[count++] = i;
        printf("producer: %d\n", i);
        pthread_mutex_unlock(&mutex);
        sem_post(&full);
    }
}

void *consumer(void *arg)
{
    int i;
    for (i = 0; i < MAX; i++)
    {
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        int item = buffer[--count];
        printf("consumer: %d\n", item);
        pthread_mutex_unlock(&mutex);
        sem_post(&empty);
    }
}

int main()
{
    pthread_t tid1, tid2;
    sem_init(&empty, 0, MAX);
    sem_init(&full, 0, 0);
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&tid1, NULL, producer, NULL);
    pthread_create(&tid2, NULL, consumer, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    sem_destroy(&empty);
    sem_destroy(&full);
    pthread_mutex_destroy(&mutex);
    return 0;
}

在这里插入图片描述

生产者和消费者,它们共享一个固定大小的缓冲区作为队列。生产者生成数据并放入缓冲区,而消费者从缓冲区取出数据并消费它。这段代码使用了信号量和互斥锁来协调对缓冲区的访问,避免数据不一致或溢出。

五、实验总结

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
包含代码和文档 实验实验内容 编写程序,演示多进程并发执行和进程软中断、管道通信。 父进程使用系统调用pipe( )建立一个管道,然后使用系统调用fork()创建两个子进程,子进程1和子进程2; 子进程1每隔1秒通过管道向子进程2发送数据: I send you x times. (x初值为1,每次发送后做加一操作) 子进程2从管道读出信息,并显示在屏幕上。 父进程用系统调用signal()捕捉来自键盘的中断信号(即按Ctrl+C键);当捕捉到中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: Child Process l is Killed by Parent! Child Process 2 is Killed by Parent! 父进程等待两个子进程终止后,释放管道并输出如下的信息后终止 Parent Process is Killed! 实验 实验内容 过Linux多线程与信号灯机制,设计并实现计算机线程与I/O线程共享缓冲区的同步与通信。 程序要求:两个线程,共享公共变量a 线程1负责计算(1到100的累加,每次加一个数) 线程2负责打印(输出累加的中间结果) 实验实验内容 利用多个共享内存(有限空间)构成的环形缓冲,将源文件复制到目标文件,实现两个进程的誊抄。 实验实验内容 1、(1)Blink程序的编译和下载 (2)给Blink程序加入printf,在每次定时器事件触发点亮LED的同时通过串口显示信息 (3)修改BLink程序,只使用一个Timer,三个LED灯作为3位的进制数表示(亮灯为1,不亮为0),按照0-7的顺序循环显示,同时将数值显示在终端上。
本书是深圳市英蓓特信息技术有限公司《嵌入式教学平台EduKit-IV实验丛书》之一,主要基于英蓓特公司目前最新嵌入式教学平台EduKit-IV,搭配核心Xscale PXA270板和其他相关模块,主要介绍嵌入式操作系统Windows CE6.0及其BSP包的开发应用开发等。 经历过近几年的嵌入式ARM的推广,一般老师和学生对于嵌入式ARM已经有一定的了解,市面上也存在大量低端的ARM7和ARM9学习板以供课余学习。 本教程主要介绍Windows CE6.0在Xsacle PXA270上的移植,通过从浅入深的介绍,引领学生步入Windows CE6.0开发的大门,整个教程从Windows CE的理论介绍,到BSP的开发应用程序的设计,贯穿Windows CE学习整个过程。完整的学习本书,学生可以全面的掌握高端多媒体的嵌入式平台的整个开发流程和经验。其中本书还是国内首个把Windows CE架构提升到6.0的书籍,希望给老师和学生带来最新的知识。 本书各章节内容主要安排如下: 第一章:介绍EMBEST EDUKIT-IV嵌入式实验平台的软硬件资源,重点根据硬件接口定义,讲述了实验平台主板及CPU子板的原理、功能及结构。 第章:讲述嵌入式操作系统Windows CE 6.0开发平台的特点、系统原理,并且根据Windows CE开发实践,介绍了Windows CE设备开发的基本方法。 第三章:讲述嵌入式ARM处理器特点及应用选型,并且介绍了Intel Xscale270处理器的应用接口。 第四章:主要包含了Windows CE工6.0开发的基础实验,包括平台定制,Eboot烧写、映像下载运行等实验,力求引领读者了解Windows CE开发的基本流程。 第五章:主要介绍使用VS2005开发WinCE应用程序,内容涵盖图形、对话框编程,进程多线程处理,文件的访问与读写,注册表的访问等。 第六章:根据Intel Xscale270的接口资源以及实验平台的硬件配置,介绍基本接口实验,包括简单LED控制、中断、ADC/DAC控制、直流/步进电机等实验,重点引导读者了解Windows CE 6.0驱动开发应用程序开发。 第七章:本章节包含一些通信接口实验,包括串口、IIC、IIS、USB、UDP、TCP等通信实验。 第八章:本章节包含一些人机接口实验,包括LCD、TSP、DotLED等接口操作实验。 第九章:本章节包含一些高级应用实验,包括GPS、GPRS、蓝牙、摄像头等接口操作实验。 本书可作为电子,通信,自动化,计算机等电类专业嵌入式系统课程实验教学的教材,也可以供嵌入式系统相关工程技术人员参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

okfang616

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

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

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

打赏作者

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

抵扣说明:

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

余额充值