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

一、 实验目的

了解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;
}

在这里插入图片描述

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

五、实验总结

/************************************************ * * The classic producer-consumer example. * Illustrates mutexes and conditions. * by Zou jian guo * 2003-12-22 * *************************************************/ #include #include #include #include "pthread.h" #define BUFFER_SIZE 16 /* Circular buffer of integers. */ struct prodcons { int buffer[BUFFER_SIZE]; /* the actual data */ pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */ int readpos, writepos; /* positions for reading and writing */ pthread_cond_t notempty; /* signaled when buffer is not empty */ pthread_cond_t notfull; /* signaled when buffer is not full */ }; /*--------------------------------------------------------*/ /* Initialize a buffer */ void init(struct prodcons * b) { pthread_mutex_init(&b->lock, NULL); pthread_cond_init(&b->notempty, NULL); pthread_cond_init(&b->notfull, NULL); b->readpos = 0; b->writepos = 0; } /*--------------------------------------------------------*/ /* Store an integer in the buffer */ void put(struct prodcons * b, int data) { pthread_mutex_lock(&b->lock); /* Wait until buffer is not full */ while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) { printf("wait for not full\n"); pthread_cond_wait(&b->notfull, &b->lock); } /* Write the data and advance write pointer */ b->buffer[b->writepos] = data; b->writepos++; if (b->writepos >= BUFFER_SIZE) b->writepos = 0; /* Signal that the buffer is now not empty */ pthread_cond_signal(&b->notempty); pthread_mutex_unlock(&b->lock); } /*--------------------------------------------------------*/ /* Read and remove an integer from the buffer */ int get(struct prodcons * b) { int data; pthread_mutex_lock(&b->lock); /* Wait until buffer is not empty */ while (b->writepos == b->readpos) { printf("wait for not empty\n"); pthread_cond_wait(&b->notempty, &b->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

okfang616

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

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

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

打赏作者

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

抵扣说明:

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

余额充值