Linux进程死锁等相关实验汇总

1. 在Linux下创建一对父子进程,实现wait等待

  创建子进程的fork()函数位于unistd.h头文件中,等待进程运行结束的waitpid()函数位于sys/wait.h头文件中。当成功创建子进程时,fork()函数对父进程返回该子进程的进程号,对子进程返回0;若创建失败则会返回-1。编写程序如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(){
    int status;
    pid_t child = fork();
    if(child < 0){
        printf("Sorry, Generate child fail!\n");
        exit(0);
    }
    else if(child == 0) printf("Hi, YigeLIU! This is the child process!\n");
    else{
        printf("Hi, YigeLIU! This is the father process!\n");
        waitpid(child, &status, 0);
        printf("END WAIT\n");
    }
    return 0;
}

  对该程序进行测试运行,结果如下:
在这里插入图片描述

2. 在 Linux下利用信号机制实现进程间通信

  子进程主体结构为一个死循环,当父进程发出kill信号时杀死子进程,同时需要注意的是,此时父进程不能直接结束,而应等待子进程被回收,以避免僵尸进程的产生。父进程杀死子进程时,子进程打印退出的信息。两者的通讯通过函数“kill()”与函数“signal()”实现。程序中使用sleep()来放缓程序执行,使得过程展现得更清晰。具体代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>

void exit_output(){
    printf("Bye, father!\n");
    exit(0);
}

int main(){
    pid_t child = fork();
    if(child < 0){
        printf("Generate child process failed!\n");
        exit(0);
    }
    else if(child == 0){
        signal(SIGUSR1, exit_output);
        while(1){
            printf("This is the child process!\n");
            sleep(1);
        }
    }
    else{
        sleep(5);
        kill(child, SIGUSR1);
        printf("Father : Child process has killed!\n");
        wait(NULL);  //等待子进程资源回收,防止僵尸进程出现
    }
    return 0;
}

  对程序进行测试,结果如下:
在这里插入图片描述

3. 在 Linux下创建2个线程A和B并循环输出数据或字符串

  笔者选取1~5进行输出,线程A在屏幕上用while循环顺序递增地输出1~5的自然数;线程B在屏幕上用while循环顺序递减地输出5~1之间的自然数。为避免输出太快,每隔1秒输出一个数。

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

void *myprint_a(){
    for(int i = 1; i <= 5; ++i){
        printf("A : %d\n", i);
        sleep(1);
    }
    return ;
}

void *myprint_b(){
    for(int i = 5; i > 0; --i){
        printf("B : %d\n", i);
        sleep(1);
    }
    return ;
}

int main(){
    pthread_t tid_a;
    int status_a = pthread_create(&tid_a, NULL, myprint_a, NULL);
    if(status_a != 0){
        printf("Generate thread A failed!\n");
        exit(0);
    }
    pthread_t tid_b;
    int status_b = pthread_create(&tid_b, NULL, myprint_b, NULL);
    if(status_b != 0){
        printf("Generate thread B failed!\n");
        exit(0);
    }

    pthread_join(tid_a, NULL);
    pthread_join(tid_b, NULL);

    return 0;
}

  编译(需要添加pthread链接库,因此需要加上编译选项-lpthread)并运行该程序,如下:
在这里插入图片描述

4. 在 Linux下利用线程实现“生产者-消费者”同步控制

  首先创建一个共享的存储区,其类型为int型,大小为32。创建两个信号灯“s_blank”和“s_full”分别用来表示该存储区是否为空和是否为满,两者的初始值分别为32和0。分别创建3个生产者和消费者进程。

  接着完成生产者Consumer()与消费者Producer()两个函数。对于生产者函数,首先对信号灯s_blank进行p操作占用一个存储区资源,接着拿到对资源区写的权限,得到后更新存储区中内容,释放该权限,然后对信号灯s_full进行v操作,使得消费者可以访问该资源。对于消费者函数,首先对信号灯s_full进行p操作以获取访问存储区资源的权限,接着拿到对资源区读的权限,得到后读取存储区内容,释放该权限,然后对信号灯s_blank进行v操作,使得生产者可以再度占用该存储区资源以便数据的写入。

  利用cnt来记录每个生产者的生产量和每个消费者的消费量,主进程等待1秒钟让生产者和消费者队列进行操作,之后修改flag值使得所有生产者和消费者退出线程。此时输出相关生产量和消费量。代码中在cnt[i]值自增前注释掉了一样输出对应线程对应操作数的代码,在测试时,可以清除展示每个线程的工作信息。这里为了防止信息输出过多而将其注释掉。

  具体代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

int shared_block[32];
sem_t s_blank, s_full;  //信号灯声明
int step_p = 0, step_c = 0;  //访问的数据区位置
pthread_mutex_t mutex;  //资源区是否被占用的锁
int cnt_p[3], cnt_c[3];  //记录各生产者和消费者的生产量和消费量
int flag = 1;  //进程退出条件

void *Producer(int id){
    while(flag){
        sem_wait(&s_blank);  //p操作
        if(pthread_mutex_trylock(&mutex) != 0){  //当无法占用资源区时
            sem_post(&s_blank);
            continue;
        }
        int data = rand() % 369;        
        shared_block[step_p] = data;
        step_p = (step_p + 1) % 32;
        //printf("Producer %d : %d\n", id, data);
        //sleep(1);
        cnt_p[id]++;
        pthread_mutex_unlock(&mutex);
        sem_post(&s_full);  //v操作
    }
    pthread_exit(0);
}

void *Consumer(int id){
    while(flag){
        sem_wait(&s_full);
        if(pthread_mutex_trylock(&mutex) != 0){  //当无法占用资源区时
            sem_post(&s_full);
            continue;
        }
        int data = shared_block[step_c];
        step_c = (step_c + 1) % 32;
        //printf("Consumer %d : %d\n", id, data);
        //sleep(1);
        cnt_c[id]++;
        pthread_mutex_unlock(&mutex);
        sem_post(&s_blank);
    }
    pthread_exit(0);
}

int main(){
    //初始化信号灯
    sem_init(&s_blank, 0, 32);  //0表明空间被进程内的线程所共享;空间大小为32
    sem_init(&s_full, 0, 0);
    
    //创建线程
    printf("HAVE STATED, PLEASE WAIT\n");
    pthread_t producer[3], consumer[3];
    for(int i = 0; i < 3; ++i){
        pthread_create(&producer[i], NULL, Producer, i);
        pthread_create(&consumer[i], NULL, Consumer, i); 
    } 

    sleep(1); flag = 0;
    printf("------THE RESULT------\n");
    for(int i = 0; i < 3; ++i){
        printf("Producer %d produce %d products\n", i, cnt_p[i]);
        printf("Consumer %d consume %d products\n", i, cnt_c[i]);
    }

    //销毁信号灯
    sem_destroy(&s_blank);
    sem_destroy(&s_full);
    pthread_mutex_destroy(&mutex);

    return 0;
}

  编译并运行该程序,观察到生产者与消费者形成同步,如下:
在这里插入图片描述

5. 在Linux下利用线程模拟哲学家就餐问题并提供死锁和非死锁解法

非死锁

  给5根筷子分别申请一个锁,用来表示该筷子是否被占用。给5位哲学家每人创建一个线程,用来模拟其取筷子的过程。这5个线程进行进行等待操作,以模拟实际情况。

  利用函数deal()来处理每个哲学家遇到的情况。首先根据哲学家所坐位置来确定其左手边和右手边筷子的编号;然后进入循环,哲学家先拿起左手边的筷子,然后尝试拿起右手边的筷子,若成功拿起,则开始吃饭;若右手边没有筷子,则放下左手刚拿起的筷子并进入下一次循环。

  具体代码实现如下:

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

pthread_mutex_t chopsticks[6];  //5个筷子的互斥锁,0不用,使用1~5号

void deal(char ch){
    int left, right;  //左右筷子的编号,i号筷子在i号哲学家左侧;哲学家逆时针坐
    if(ch == 'A') { left = 1; right = 5; }
    else if(ch == 'B') { left = 2; right = 1; }
    else if(ch == 'C') { left = 3; right = 2; }
    else if(ch == 'D') { left = 4; right = 3; }
    else { left = 5; right = 4; }

    while(1){
        sleep(1);  //思考
        pthread_mutex_lock(&chopsticks[left]);  //拿起左手的筷子
        printf("Left chopstick(pick) : %d\n", left);
        if(pthread_mutex_trylock(&chopsticks[right]) != 0){  //若右手边没有筷子
            pthread_mutex_unlock(&chopsticks[left]);  //放下左手的筷子
            printf("Left chopstick(put) : %d\n", left);
            continue;
        }
        //若右手边有筷子
        printf("Right chopstick(pick) : %d\n", right);

        //吃饭
        printf("---EATING TIME FOR : %c---\n", ch);
        sleep(1);

        //吃完饭
        pthread_mutex_unlock(&chopsticks[left]);  //放下左手的筷子
        printf("Left chopstick(put) : %d\n", left);
        pthread_mutex_unlock(&chopsticks[right]);  //放下右手的筷子
        printf("Right chopstick(put) : %d\n", right);
    }
}

int main(){
    //初始化锁
    for(int i = 0; i < 6; ++i) pthread_mutex_init(&chopsticks[i], NULL);

    //创建5个哲学家线程
    pthread_t A, B, C, D, E;
    pthread_create(&A, NULL, deal, 'A');
    pthread_create(&B, NULL, deal, 'B');
    pthread_create(&C, NULL, deal, 'C');
    pthread_create(&D, NULL, deal, 'D');
    pthread_create(&E, NULL, deal, 'E');

    //线程同步
    pthread_join(A, NULL);
    pthread_join(B, NULL);
    pthread_join(C, NULL);
    pthread_join(D, NULL);
    pthread_join(E, NULL);

    return 0;
}

  编译并运行该程序,得到哲学家吃饭的结果,如下:
在这里插入图片描述

死锁

  仅需对非死锁情况进行一定的修改即可。当哲学家拿起左手边的筷子时,对右手边的筷子不尝试拿起,而是直接进行资源的申请;同时,当右手边没有筷子可拿时也不放下左手的筷子而是持续等待;这样便会导致死锁的产生。

  具体代码实现如下:

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

pthread_mutex_t chopsticks[6];  //5个筷子的互斥锁,0不用,使用1~5号

void deal(char ch){
    int left, right;  //左右筷子的编号,i号筷子在i号哲学家左侧;哲学家逆时针坐
    if(ch == 'A') { left = 1; right = 5; }
    else if(ch == 'B') { left = 2; right = 1; }
    else if(ch == 'C') { left = 3; right = 2; }
    else if(ch == 'D') { left = 4; right = 3; }
    else { left = 5; right = 4; }

    while(1){
        sleep(1);  //思考
        pthread_mutex_lock(&chopsticks[left]);  //拿起左手的筷子
        printf("Left chopstick(pick) : %d\n", left);
        pthread_mutex_lock(&chopsticks[right]);  //拿起右手边有筷子
        printf("Right chopstick(pick) : %d\n", right);

        //吃饭
        printf("---EATING TIME FOR : %c---\n", ch);
        sleep(1);

        //吃完饭
        pthread_mutex_unlock(&chopsticks[left]);  //放下左手的筷子
        printf("Left chopstick(put) : %d\n", left);
        pthread_mutex_unlock(&chopsticks[right]);  //放下右手的筷子
        printf("Right chopstick(put) : %d\n", right);
    }
}

int main(){
    //初始化锁
    for(int i = 0; i < 6; ++i) pthread_mutex_init(&chopsticks[i], NULL);

    //创建5个哲学家线程
    pthread_t A, B, C, D, E;
    pthread_create(&A, NULL, deal, 'A');
    pthread_create(&B, NULL, deal, 'B');
    pthread_create(&C, NULL, deal, 'C');
    pthread_create(&D, NULL, deal, 'D');
    pthread_create(&E, NULL, deal, 'E');

    //线程同步
    pthread_join(A, NULL);
    pthread_join(B, NULL);
    pthread_join(C, NULL);
    pthread_join(D, NULL);
    pthread_join(E, NULL);

    return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

D-A-X

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

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

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

打赏作者

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

抵扣说明:

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

余额充值