项目实训01

第一部分:进程管理

  1. 创建进程
    在Linux系统中,我们一般使用系统调用fork()函数来创建进程,为此,我们在工作目录下创建一个测试文件processTest.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	pid_t pid;
        int i = 100;	
	pid = fork();
	if(pid == -1)
	{
		printf("Creat fork error!!!\n");
		exit(1);
	}
	else if(pid)
	{
		i++;
		printf("The father i = %d.\n",i);
		printf("The father return %d.\n",pid);			
		printf("The father pid is %d.\n",getpid());		
		printf("The father ppid is %d.\n",getppid());	
		while(1);		
	}
	else
	{
		i++;	
		printf("\nThe child i = %d.\n",i);
		printf("The child return %d.\n",pid);	
		printf("The child pid is %d.\n",getpid());	
		printf("The child ppid is %d.\n",getppid());
		while(1);	
	}
	return 0;
}

这个测试程序可以很好地帮助我们理解fork()函数的原理以及Linux进程创建的机制。
首先,pid可以给fork函数返回一个值,一般为一个正实数或零,如果返回-1则说明发生错误。
然后,我们打印父进程基本信息,即父进程的pid和父进程的父进程的ppid,这里用while(1);因为父、子进程是并发了,父进程退出了就看不到打印信息了,后面的子进程也是同理。
写好代码后,我们使用命令gcc processTest.c -o processTest.exe进行手动编译并生成可执行文件processTest.exe,然后运行命令./processTest.exe,可以看到如下运行结果:
在这里插入图片描述

我们可以看到子进程的pid是0,而子进程的ppid即是父进程的pid

  1. 进程软中断通信
    Linux中的信号一个正实数,它定义在系统头文件<signal.h>中,它一般用来通知某进程有中断异常,我们这个实验就是来了解信号软中断的基本机制。
    在本实验当中,我们要创建2个进程,通过系统调用signal()捕捉键盘上的中断信号,捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出信息后终止。
    现在我们创建一个测试文件interrupt.c,代码如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
void stop(), waiting();
int wait_mark;

void stop(){
	wait_mark = 0;
}

void waiting(){
	while(wait_mark == 1); 
}


int main(){
    int p1, p2;
    while((p1 = fork()) == -1);
    if(p1 == 0){
        wait_mark = 1;
	  signal(SIGINT, SIG_IGN);
        signal(16, stop);
        waiting();
        printf("\nchild process p1 is killed by parent!\n");
	  sleep(3);
        exit(0);
    }else{
        while((p2 = fork()) == -1);
        if(p2 == 0){
            wait_mark = 1;
		signal(SIGINT, SIG_IGN);
            signal(17, stop); 
            waiting();
            printf("\nchild process p2 is killed by parent!\n");
		sleep(3);
	      exit(0);

        }else{
            wait_mark = 1;
            signal(SIGINT, stop);
            waiting();
            kill(p1, 16);
            kill(p2, 17);
            wait(0);
            wait(0);
            printf("\nparent process is killed !\n");
            exit(0);
        }
    }
}

可以看到,我们让子进程1接受父进程软中断信号17然后转向stop()函数,而如果等待标记不为0那么就一直等待父进程的信号,其中非常重要的一部就是signal(SIGINT, SIG_IGN),它实现了屏蔽ctrl+c的键盘中断信号。
分别运行编译和执行命令:
gcc interrupt.c -o interrupt.exe
./ interrupt.exe
运行结果:
在这里插入图片描述

  1. 进程共享内存通信
    共享内存通信即让两个进程访问同一块内存空间并以此传递数据,为了达到这一目的,Linux也内置了相应的头文件,比如sys/shm.h,我们需要实现将其include进来。
    首先,我们现在工作目录下建立一个写文件,命名为shmread.c:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
 
int main()
{
	int running = 1;
	void *shm = NULL;
	struct shared_use_st *shared;
	int shmid;
	shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
	if(shmid == -1)
	{
		fprintf(stderr, "shmget failed\n");
		exit(EXIT_FAILURE);
	}
	shm = shmat(shmid, 0, 0);
	if(shm == (void*)-1)
	{
		fprintf(stderr, "shmat failed\n");
		exit(EXIT_FAILURE);
	}
	printf("\nMemory attached at %X\n", (int)shm);
	shared = (struct shared_use_st*)shm;
	shared->written = 0;
	while(running)
	{
		if(shared->written != 0)
		{
			printf("You wrote: %s", shared->text);
			sleep(rand() % 3);
			shared->written = 0;
			if(strncmp(shared->text, "end", 3) == 0)
				running = 0;
		}
		else
			sleep(1);
	}
	if(shmdt(shm) == -1)
	{
		fprintf(stderr, "shmdt failed\n");
		exit(EXIT_FAILURE);
	}
	if(shmctl(shmid, IPC_RMID, 0) == -1)
	{
		fprintf(stderr, "shmctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}
	exit(EXIT_SUCCESS);
}

从上述代码可以看出,我们首先创建共享内存空间:
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
然后用一个while循环来一直读取内存中的数据,当written标志不为0的时候,我们进行读操作,并且如果读到的数据是end,则退出,读取完成之后,我们先将共享内存从当前进程分离,然后将其删除。
写者的程序采用相同的机制,代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"
 
int main()
{
	int running = 1;
	void *shm = NULL;
	struct shared_use_st *shared = NULL;
	char buffer[BUFSIZ + 1];
	int shmid;
	shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
	if(shmid == -1)
	{
		fprintf(stderr, "shmget failed\n");
		exit(EXIT_FAILURE);
	}
	shm = shmat(shmid, (void*)0, 0);
	if(shm == (void*)-1)
	{
		fprintf(stderr, "shmat failed\n");
		exit(EXIT_FAILURE);
	}
	printf("Memory attached at %X\n", (int)shm);
	shared = (struct shared_use_st*)shm;
	while(running)
	{
		while(shared->written == 1)
		{
			sleep(1);
			printf("Waiting...\n");
		}
		printf("Enter some text: ");
		fgets(buffer, BUFSIZ, stdin);
		strncpy(shared->text, buffer, TEXT_SZ);
		shared->written = 1;
		if(strncmp(buffer, "end", 3) == 0)
			running = 0;
	}
	if(shmdt(shm) == -1)
	{
		fprintf(stderr, "shmdt failed\n");
		exit(EXIT_FAILURE);
	}
	sleep(2);
	exit(EXIT_SUCCESS);
}

shmwrite取得共享内存并连接到自己的地址空间中。检查共享内存中的written,是否为0,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的written为0,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的written为1,表示写完成,其他进程可对共享内存进行读操作。
分别编译shmread和shmwrite文件得到两个可执行文件,然后使用命令./shmread.exe &来进行读操作,可以看到执行的结果是我们已经从成功实现了进程与1一块共享内存区域的关联。
然后执行写操作命令./shmwrite.exe,可以看到我们也关联了另一块内存区域,这个时候输入一段text进行测试,即可以进行读写通信,然后输入end命令即可终结通信过程。
在这里插入图片描述

  1. 进程信号量通信
    信号量是操作系统内部通信的一种原子操作,主要由两种操作signal()和wait()组成,本实验以经典的生产者消费者模型为例,使用信号量操作来模拟和实现进程间的通信。
    我们在工作目录下建立mutex.c文件:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define true 1

int product_id = 0;
int consumer_id = 0;
int N;
int producerNum;
int consumerNum;
typedef int semaphore;
typedef int item;
item* buffer;
int in = 0;
int out = 0;
int proCount = 0;
semaphore mutex = 1, empty , full = 0, proCmutex = 1;
void * producer(void * a){
    int id = ++product_id;
    while(true){
        int flag = 0while(empty <= 0){
            printf("生产者%d:缓冲区已满!阻塞中……\n",id);
            flag =1;
            sleep(1);
        }
        if(flag == 1)
            printf("生产者%d因缓冲区有空位唤醒!\n",id);
        flag = 0;
        while(proCmutex <= 0){printf("生产者%d生产阻塞中……\n",id);flag = 1;sleep(1);};
        proCmutex--;
        if(flag == 1)
            printf("生产者%d生产唤醒!\n",id);
        proCount++;
        printf("生产者%d:生产一个产品ID%d!\n",id,proCount);
        flag = 0;
        while(mutex <= 0){printf("生产者%d装入阻塞中……\n",id);sleep(1);flag=1;};
        mutex--;

        if(flag == 1)
            printf("生产者%d装入唤醒,装入产品ID%d,缓冲区位置为%d!\n",id,proCount,in);
        else
            printf("生产者%d装入产品ID%d,缓冲区位置为%d!\n",id,proCount,in);
        empty--;
        buffer[in] = proCount;
        in = (in + 1) % N;
        full++;
        mutex++;
        proCmutex++;
        sleep(1);
    }
}

void * consumer(void *b){
    int id = ++consumer_id;
    while(true){
        int flag = 0;
        while(full <= 0){
            printf("\t\t\t\t消费者%d:缓冲区为空!阻塞中……\n",id);
            flag = 1;
            sleep(1);
        }
        full--;
        if(flag ==1)
            printf("\t\t\t\t消费者%d因缓冲区有产品唤醒!\n",id);
        flag = 0;

        while(mutex <= 0){printf("\t\t\t\t消费者%d消费阻塞中……\n",id);sleep(1);};
        mutex--;
        if(flag == 1)
            printf("\t\t\t\t消费者%d消费唤醒!\n",id);
        int nextc = buffer[out];
        buffer[out] = 0;//消费完将缓冲区设置为0
        empty++;
        printf("\t\t\t\t消费者%d:消费一个产品ID%d,缓冲区位置为%d\n",id, nextc,out);
        out = (out + 1) % N;
        mutex++;
        sleep(1);
    }
}

int main()
{
    int tempnum;
    printf("请输入生产者数目:\n");
    scanf("%d",&tempnum);
    producerNum = tempnum;
    printf("请输入消费者数目:\n");
    scanf("%d",&tempnum);
    consumerNum = tempnum;
    printf("请输入缓冲区大小:\n");
    scanf("%d",&tempnum);
    N = tempnum;
    empty = N;
    buffer = (item*)malloc(N*sizeof(item));
    for(int i=0;i<N;i++)
    {
        buffer[i]=0;
    }
    pthread_t threadPool[producerNum+consumerNum];
    int i;
    for(i = 0; i < producerNum; i++){
        pthread_t temp;
        if(pthread_create(&temp, NULL, producer, NULL) == -1)
        {
            printf("ERROR, fail to create producer%d\n", i);
            exit(1);
        }
        threadPool[i] = temp;
    } 
    for(i = 0; i < consumerNum; i++){
        pthread_t temp;
        if(pthread_create(&temp, NULL, consumer, NULL) == -1){
            printf("ERROR, fail to create consumer%d\n", i);
            exit(1);
        }
        threadPool[i+producerNum] = temp;
    } 

    void * result;
    for(i = 0; i < producerNum+consumerNum; i++){
       
        if(pthread_join(threadPool[i], &result) == -1){
            printf("fail to recollect\n");
            exit(1);
        }
    }
    return 0;
}


如上所示首先我们在该文件中先定义好生产者,消费者的ID和数目,缓冲区的大小,商品的类型和大小,缓冲区首尾指针,然后我们定义4个信号量:分别为互斥信号量mutex,资源信号量empty和full,以及生产者互斥信号量promutex。
紧接着,我们在生产者函数中,先判断缓冲区是否已满,完成产品数量自增,然后申请资源,阻塞进程,互斥信号量mutex自减,同时更新缓冲区的大小,即缓冲区非空白大小自加,然后mutex自加释放资源,实现互斥。
生产者的实现原理大致相同,不再赘述。
编号程序之后,执行命令cc mutex.c -o mutex.exe编译mutex.c并生成可执行文件mutex.exe,这里需要注意pthread线程操作函数库不是Linux系统默认的库,编译时需要指定使用线程库libpthread.a,所以在使用pthread_create创建线程时,在gcc编译时要加-lpthread参数选项,即执行命令cc mutex.c -o mutex.exe -lpthread,然后运行命令./ mutex.exe执行mutex.exe文件,然后我们设置生产者消费者初始数目均为5,缓冲区大小为10,执行算法
运行结果:
在这里插入图片描述

最后按ctrl+c可退出程序

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值