UNIX程序设计实验报告

题目

模拟实现shell

要求:
(1)能够输入带参数的命令,例如 ls -l , cd /home 的样式。
(2)能够正确处理信号,当ctrl+c,ctrl+z 时,当前正在执行的命令终止,但是shell不能退出。
(3)测试验证,至少选10组命令。
在这里插入图片描述
[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.csdnimg.cn/843b3a46-54M09az7Cl6e7dada1868f595.png43)(https://img-blog.csdniyyyyyyy9a6e7dada1868f565.png)]
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
源代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<string.h>
#include <sys/msg.h>
#define NUM 64
#define CMD_NUM 64
int msggnum = 0;//队列大小
int gt = 0;//模拟信号量
//char msg[NUM];
typedef struct
{
    char sig[NUM];//命令
}msg;
msg m[CMD_NUM];
void sigi(int signo)
{
	printf("\n进程中断\n");
	
}
void sigt(int signo)
{
	printf("\n进程暂停\n");
	gt=1;
}
int main()
{
	char command[NUM];
	int r=0,x;
	signal(SIGTSTP,sigt);
	signal(SIGINT,sigi);
	//signal(SIGCONT,NULL);
	for(r = 0 ;r<20 ; r++){		
	char *argv[CMD_NUM] = { NULL };
	command[0]=0;
	printf("[cathy@cathy-virtual-machine-shell]# ");
	fflush(stdout);//冲刷,马上输出,避免错误
	fgets(command,NUM,stdin);//输入命令字符串
	command[strlen(command) - 1]='\0';//字符串结束符号
	const char* sep=" ";
	argv[0]=strtok(command,sep);//以空格为界限分解字符串获取命令的第一个字符串
	int i=1;
	while(argv[i]=strtok(NULL,sep))//求命令中字符串的个数
	{
		i++;
	}
	if(strcmp(argv[0],"cd")==0){//比较字符串 如果第一个字符串是cd返回0	
		if(argv[1]!=NULL){
			chdir(argv[1]);
			continue;
		}
	}
	if(fork()==0){	
		execvp(argv[0],argv);
		exit(1);
		}
	int status = 0;
	waitpid(-1,&status,0);	
}
	return 0;
}

文件I/O缓冲效率

说明:read、write系统调用不带缓冲区,因此本题目要求大家自行定义缓冲区 buff[]大小,通过对比分析,在不同缓冲区下,数据读写的效率
要求:
(1)自行准备一个较大的文本文件。
(2)自行设置缓冲区buff[]的大小,128,256,512,…
(3)利用time()函数,记录文件开始读(或写)的时间,读(或写)结束的时间。
实验完成后,要制作表格,将各次实验数据进行统计,并分析说明实验结论。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实验结论:文件I/O是操作系统封装了一系列的与文件操作有关的函数构成的一套用来读、写文件的接口供应用程序,通过这些接口可以实现对文件的读写操作。文件I/O采用系统直接调用的方式,当时用这些接口进行对文件的操作时,就会立即触发系统调用过程,即向系统内核发出请求,系统内核收到执行相关代码处理的请求,然后决定是否将操作硬件资源或返回结果给应用程序。Read()和write()系统调用在操作磁盘文件是不会直接发起磁盘访问,而是仅仅在用户空间缓冲区和内核缓冲区告诉缓存之间复制数据。如果与文件发生大量的数据传输,通过采用大块空间缓冲数据,以及执行更少的系统调用,可以极大地提高I/O的性能。
如图 6所示,在执行读操作时,当缓冲区大小在21到218之间时,读取的数据量越大执行读操作的时间越长;当缓冲区大小不低于2^19时,读取数据的时间明显变短,这是因为发生了大量的数据传输,I/O操作采用大块空间缓冲数据,执行更少的系统调用;如图 7所示,在执行写操作时,写入数据的操作时间随数据量的增大而增长,但至少在缓冲区大小不超过2^20时不会使得I/O操作使用大块的空间缓冲数据。
在这里插入图片描述
在这里插入图片描述
该程序使用for循环进行文件的读取,从一个较大的文本文件中读取一定大小的数据存入缓冲区,然后将该缓冲区的数据写入另一个文本文件。图 6、图 7为程序运行结果,程序总共循环了20次,第一次缓冲区大小为2,第二次为4,第三次为8,以此类推,知道最后变为1048576,即2^20。读取时间函数使用了可以精确到毫秒级的gettimeofday()。
程序源码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <string.h>
#include <fcntl.h>
#define MI 16
//建立两个文件,一个用来测试read函数,一个用来测试write函数
int main(void){
        int n,i;
        int size = 262144;
	struct timeval t1;
   	struct timeval t2;
	int fd1,fd2;
	char buf1[3];char buf[4];
	buf1[0]='A';
	buf1[1]='B';
	buf1[2]='c';
	fd1 = open("IOWRITE.txt", O_RDONLY, 0777);//用来读
	fd2 = open("IOREAD.txt", O_WRONLY, 0777);//用来写
	if(fd1==0||fd2==0)
	{
		printf("文件打开失败\n");
	}
	int max;//缓冲区大小
	max = MI;
	double start,end;
	for(i = 0;i < 20;i++)
	{	char buf[max];//设置缓冲区
		gettimeofday(&t1, NULL);		
		read(fd1, buf,max);//从fd1读取数据到缓冲区buf
		gettimeofday(&t2, NULL);//精确到微秒
		printf("%d(num=%d):\n",i+1,max);
		start = t1.tv_sec * 1000000 + t1.tv_usec;
		end= t2.tv_sec * 1000000 + t2.tv_usec;
		printf("read begin:t1 = %f\n",start);
		printf("read   end:t2 = %f\n",end);
		printf("between read start and end:%f\n",end-start);
		//把读出的数据写入fd2(数据在buf缓冲区中)
		gettimeofday(&t1, NULL);
		write(fd2,buf,max);
		gettimeofday(&t2, NULL);
		start = t1.tv_sec * 1000000 + t1.tv_usec;
		end= t2.tv_sec * 1000000 + t2.tv_usec;
		printf("write begin:t1 = %f\n",start);
		printf("write end  :t2 = %f\n",end);
		printf("between write start and end:%f\n",end-start);		
		max = max*2;	
		printf("\n");
	}
        //exit(0);
	return 0;
}

请设计实例,验证以下观点:“Fork创建子进程后,父子进程占用独立的内存空间,各自独立运行”。

在这里插入图片描述
在这里插入图片描述
使用两个程序验证。
如图 11所示,在main函数中定义一个int类型m=7,在子进程中让m执行+1操作后输出,在父进程中直接输出m,可以看出子进程中的m改变不会影响父进程中的m值的改变。
如图 12所示,循环执行两个子函数,让他们分别输出子进程和父进程的进程号,从CPID和PID输出顺序没有什么特定的规律可以得出如下结论:fork()创建子进程后,父子进程占用独立的内存空间,各自独立运行。
源代码:

/home/cathy/2022UNIXLab/lab3/33.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
	int i = 0;
	int m = 7;
	pid_t fpid=fork();
	if(fpid==0)
	{
		m = m+1;
		printf("子进程:%d\n",m);}
	else
		printf("父进程:%d\n",m);
	return 0;
}
/home/cathy/2022UNIXLab/lab3/333.c:
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
void outC(int n)
{
	int i;
	for(i = 0;i<n;i++){
		printf("CPID=%d,...%d\n",getpid(),i);
		sleep(1);
}
}
void out(int n)
{
	int i;
	for(i = 0;i<n;i++){
		printf("PID=%d,...%d\n",getpid(),i);
		sleep(1);
}
}
int main()
{
	pid_t pid;
	pid = fork();
	if(pid>0)
	{
		out(5);
	}
	else if(pid==0)
	{
		outC(6);
	}
	return 0;
}

利用进程实现多生产者-多消费者问题

要求:
(1)多个生产者、多个消费者
(2)实现正确的进程间的同步和互斥。
在这里插入图片描述
本次实验完成了两个生产者、两个消费者问题。想要有n的生产者、消费者就创建执行n次fork(),创建n个进程,每个进程里父进程执行生产者函数,子进程执行消费者函数;如果想要实现n个消费者m个生产者问题,那么创建m个父进程让他们执行生产者问题,在n个子进程中实现生产者问题,比如创建一个父子进程,在父进程中再次创建进程并让其中的pid>0的进程执行生产者函数,就完成了两个生产者一个消费者问题。
进程的同步和互斥是通过共享内存来实现的。互斥指某一资源同时只允许一个访问者对其进行访问;同步是基于互斥,经其他机制实现访问者对资源的有序访问。使用shmat()函数获取或创建共享内存,在共享的内存中实现生产者将生产的产品和消费者消费的将产品有序地从缓冲区取出。生产者和消费者共同完成数据的填入或取出。同步:若缓冲区空,则消费者需要等待生产者往里填充数据,即当full(缓冲池中的产品数)>0时再消费;若缓冲区满则生产者需要等待消费者消费,即当empty(缓冲池中的剩余空间)>0时在进行生产。互斥:当生产者往缓冲区里填充数据的时候,消费者无法进行消费,需要等待生产者完成工作;当消费者消费的时候,生产者无法向缓冲区填充数据,需等消费者工作完成后再进行填充。使用mywait()和musiganl()函数实现信号量的互斥。
图 12中,semnum为第几个信号量,vlaue为信号量的值,product后跟的是生产的产品号,consumer是消费者取出的产品号。
在这里插入图片描述

/home/cathy/2022UNIXLab/lab4/4.c:
#include <stdio.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <stdlib.h>
union semun{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct semomfo *buff_er;
};
int Init() //创建信号量
{
	key_t key=ftok("a.text",1234);
	int semid;
	semid=semget(key,3,IPC_CREAT|IPC_EXCL|0777);//产生信号量集 获得三个权值为777的信号量
	if(semid==-1)
		semid=semget(key,3,0777);//建立信号量,初值是不确定的
	printf("semid=%d\n",semid);
	unsigned short val[3]={1,0,10};	//赋初值 mutex full empty
	int t;
	union semun se;
	se.array=val;
	t=semctl(semid,0,SETALL,se);//0:信号量编号 对指定信号量作SETALL操作:设置0号信号量的值
	if(t==0)
	{
	//	printf("init signal success!!\n");
	}
	else{
	//	printf("init signal error!!\n");
		return -1;
	}	
	return semid;//信号量集的ID号
}

void P(int semid, int semnum, int val)  //申请资源 wait val<0
{
	val=-1*val;
	struct sembuf ops={semnum, val, SEM_UNDO};
	int t = semop(semid, &ops, 1);//可用于进程间信号的同步和互斥 调用在semid标识的信号量集中的信号量上执行一个或多个up或down操作 信号量集操作 释放等操作
}

void V(int semid, int semnum, int val)//释放资源 signal val>0
{
        struct sembuf ops={semnum, val, SEM_UNDO};
	int t = semop(semid, &ops, 1);
}
int watch(int semid,int semnum)
{
	int n;
	n = semctl(semid,semnum,GETVAL);//在信号量集上进行操作 成功则返回semnum的值 查看semid的值
	printf("semnum=%d,value=%d;",semnum,n);//输出缓冲池空间以及空间中的数
	sleep(2);
	return n;
}
//可能有阻塞 
void mywait(int semid,int semnum,int val)
{
	int flag=1;
	while(flag)
	{
		if(semnum == 0)//信号量集中的第几个信号量 mutex=1不能申请
		{
		if(watch(semid,semnum)==1){//mutex=1
			P(semid,semnum,val);//对semnum个信号量(semid中的)进行val操作。
			flag = 0;}
		}	
		else if(semnum == 2)//empty>0
		{
		if(watch(semid,semnum)>0){
			P(semid,semnum,val);
			flag = 0;}
		}
		else if(semnum == 1)//full>0
		{
		if( watch(semid,semnum)>0){
			P(semid,semnum,val);
			flag = 0;}
		}
	}
}
void mysignal(int semid,int semnum,int val)//释放
{
	int flag = 1;
	while(flag)
	{
		if(semnum == 0)
		{
		if(watch(semid,semnum) == 0){//mutex=1不能释放
			V(semid,semnum,val);
			flag = 0;}
		}
		else if(semnum == 1)
		{
		if(watch(semid,semnum)<10){//full>10不能释放
			V(semid,semnum,val);
			flag = 0;}
		}
		else if(semnum == 2)
		{
		if(watch(semid,semnum)<10){//empty>10不能释放
			V(semid,semnum,val);
			flag = 0;}
		}	
	}
}
void producter(int semid,int shmid,int a)
{
	int x;
	int *pi = (int*)shmat(shmid,0,0);//允许进程通信共享内存 把共享内存区对象映射到调用进程的地址空间 返回共享内存地址
	while(1)
	{
		x = rand()%10 + 1;//随机生成产品号
		mywait(semid,2,1);
		mywait(semid,0,1);
		*(pi+*(pi+10))=x;//将产品号存入内存空间
		printf("\n%d:product:%d\n",a,x);//输出生产者生产的产品号
		*(pi+10) = (*(pi+10)+1)%10;//in指针+1
		mysignal(semid,0,1);
		mysignal(semid,1,1);
		sleep(1);
	}
	sleep(3);
	shmdt(pi);
}
void consumer(int semid,int shmid,int a)
{
	int x;
	int *pi = (int*)shmat(shmid,0,0);//获取共享内存
	while(1)
	{
		mywait(semid,1,1);
		mywait(semid,0,1);
		x=*(pi+*(pi+11));
		printf("\n%d:consumer:%d\n",a,x);
		*(pi+11) = (*(pi+11)+1)%10;//out指针+1
		mysignal(semid,0,1);
		mysignal(semid,2,1);
		sleep(1);
	}
	sleep(3);
	shmdt(pi);//断开共享内存连接
}
int main()
{
	int semid;
	int shmid;
	key_t key2 = ftok("b.txt",123);
	shmid = shmget(IPC_PRIVATE,1024,IPC_CREAT|IPC_EXCL|0777);//共享存储区
	int *pi = (int*)shmat(shmid,0,0);//共享内存
	for(int i = 0;i<12;i++)
	{
		*(pi+i) = 0;
	}
	shmdt(pi);//断开共享内存连接
	srand(time(0));
	semid=Init();
	pid_t pid,pid2,pid3;
	pid = fork();
	pid2 = fork();
	if(pid>0||pid2>0) 
	{		
		producter(semid,shmid,getpid());
		
	}
	else {
		consumer(semid,shmid,getpid());}
	return 0;
}

手机闹钟功能:早上6点闹钟响后,有2个选项:每隔5分钟再次响,或者按“停止”关闭闹钟。请利用“信号”模拟手机闹铃。

(1)设置闹钟
(2)每个n秒 自动提醒。
(3)按“关闭”停止闹钟。
在这里插入图片描述
如图 13所示,当闹钟开始后,输入1则选择闹钟再次响,输入2则选择关闭闹钟。本程序中使用的信号为SIGALRM信号,该信号是在进程终止时发送给进程的信号,在本程序中,alarm()函数是一个闹钟函数,它在进程中起定时器的作用,当定时器结束后,该函数会发送SIGALRM信号,进程收到SIGALRM信号后,会调用相关函数执行其代码。发送信号使用的是signal信号,比如signal(SIGALRM,sig_alarm),指的是当进程终止时将SIGALRM发送给sig_alarm()函数,让sig_alarm函数得以执行。;使用goto语句完成程序的循环;使用alarm(300)表示经过了5分钟。
在本程序中sig_alarm()函数的作用是输出“This is alarm”,即模拟闹钟响;go()函数的作用是输出提示信息“输入1:五分钟后再次提醒 输入2:关闭闹钟”。
源代码:

/home/cathy/2022UNIXLab/lab5/5.c:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int go()//每个5分钟闹钟开始
{
	int a;
	printf("输入1:五分钟后再次提醒 输入2:关闭闹钟\n");
	scanf("%d",&a);
	return a;
}
void sig_alarm(int signo)
{
	printf("This is alarm\n");
	sleep(2);
}
int main()
{
	int i;
	pid_t pid;
	signal(SIGALRM,sig_alarm);
	p1:
	pid = fork();
	if(pid>0)//父进程
	{
		int a = go();			
		if(a==2){		
			kill(pid,SIGINT);//给Pid进程发送SIGINT信号
			printf("闹钟关闭成功!\n");
			return 0;
			}
		else if(a==1){
			printf("五分钟后再次响闹钟!\n");
			alarm(300);//经过5分钟后把sigalrm信号发送给当前进程
			sleep(1);
			goto p1;
			}
	}
	else if(pid==0)//子进程
	{
	}
	return 0;
}

春季运动会,共有4个跑道,每个同学的配速不同,请利用多线程模拟4个跑道同学比赛情况,统计其100米用时,并进行排名。

在这里插入图片描述
图 14为百米比赛的运行结果,其中前边部分输出了ABCD四位同学的跑步速度(假设他们都做匀速直线运动)和时间,下边输出的是按由坏到好的成绩次序输出的排名。
使用多线程模拟本次比赛,pthread_create()函数创建线程,th_fn()函数计算跑步的时间,冒泡排序对所有同学的成绩进行排序。以pthread_create(&A,NULL,th_fn, (void*)&a[0])为例,创建一个进程号为A的线程,该线程执行的函数为th_fn()函数,a[0]为th_fn()函数的参数。由于该方法只能传递一个参数,所有使用了结构体的形式向在子线程和主线程之间传参。创建了一个名为a的,类型为race的结构体,race结构体中包含学生编号(A/B/C/D)、学生速度和学生成绩。
由于没有使用pthread_join()函数回收线程资源,所以可能造成创建线程时资源不够的问题,或者出现主线程不给子线程足够的时间运行完的问题,所以使用了sleep函数来为子线程提供尽可能多的运行时间,但这种方法是不保险的,如果子线程中执行的代码比较复杂,则还是可能出错。
在本程序中,th_fn()函数用来计算每位同学的比赛成绩并将成绩储存到结构体数组中。
结构体race,用来储存比赛信息:
typedef struct
{
char name;//名字
float rate;//速度
float time;//时间
}race;
结构体数组:
race a[4];
源代码:

/home/cathy/2022UNIXLab/lab6/66.c:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
	char name;//名字	
	float rate;//速度
	float time;//时间
}race;
race a[4];
void *th_fn(void *arg)     
{ 
	race *x=(race*)arg;
	printf("%c:rate:%lf ",x->name,x->rate);
	x->time = (float)100/x->rate;
	printf("time:%lf\n",x->time);
	sleep(1);
	}
int main(void)
{
	pthread_t  A,B,C,D;
	int err;
	a[0].name = 'A';
	a[0].rate = 8.88;
	a[1].name = 'B';
	a[1].rate = 9.88;
	a[2].name = 'C';
	a[2].rate = 10.88;
	a[3].name = 'D';
	a[3].rate = 7.88;
	err = pthread_create(&A,NULL,th_fn, (void*)&a[0]);
	if(err != 0){
		printf("A未参加\n");
		exit(1);
	}
	sleep(1);
	err = pthread_create(&B,NULL,th_fn, (void*)&a[1]);
	if(err != 0){
		printf("B未参加\n");
		exit(1);
	}
	sleep(1);
	err = pthread_create(&C,NULL,th_fn, (void*)&a[2]);
	if(err != 0){
		printf("C未参加\n");
		exit(1);
	}
	sleep(1);
	err = pthread_create(&D,NULL,th_fn, (void*)&a[3]);
	if(err != 0){
		printf("D未参加\n");
		exit(1);
	}
	sleep(1);
	int i,j;
	sleep(10);
	for(i = 0;i<3;i++)
	{
		for(j = 1;j<4-i;j++)
		{
			if(a[j-1].time<=a[j].time)
				{
					race s = a[j];
					a[j] = a[j-1];
					a[j-1] = s;
				}	
			}
	}
	printf("race was finished!grade is:\n");
	for(i = 0;i<4;i++)
	{
		printf("%c",a[i].name);
	}
	printf("\n");
	return 0;
}

利用多线程 统计成绩单。

要求:
(1)主线程提供 某科目学生成绩单。
(2)线程A负责计算平均成绩,并将平均成绩送给主线程。
(3)线程B负责找出成绩单中高于平均分的成绩,并统计个数,将结果送给主线程。
(4)主线程输出:学生的平均成绩,高于平均分的学生人数。
在这里插入图片描述
图 15为该程序运行结果。
该多线程程序要实现两个功能:计算平均数和计算高于平均分的人数,因此需要设计两个函数分别实现这两个功能:Ave()函数用来计算平均分,Num()函数用来计算高于平均分的人数。本程序使用两种方式在子线程和主线程之间传递参数:定义全局变量float avee,avee初始值为0,在ave()函数中计算完平均分后将计算结果赋值给该变量,在Num()函数中也用到了该变量,故设置为全局变量比较方便;利用结构体数组test传递学生的成绩和学生的人数,test结构体中定义了一个float类型的数组grade和一个float类型的变量studentnum,grade中储存的是学生的成绩,studentnum中储存的是学生的人数,由于需向Ave()函数和Num()函数中传递两个变量,所以用结构体传参更为方便、合理;而在子线程向主线程传参时,还使用了pthread_join()函数进行传参。使用pthread_create()函数创建了两个线程,一个线程来计算平均分,一个线程来计算高于平均分的学生成绩。使用pthread_join()函数终止线程,用来回收线程资源,避免出现由于一直创建线程而造成资源不足而无法创建线程的情况,并且如果没有该函数终止线程,可能会出现主线程不给子线程足够的时间执行完而退出的情况。如pthread_join(a,NULL)表示直接终止进程a,线程终止成功返回0,错误返回错误号;pthread_join(b,&t)表示终止线程b,t为用户定义的指针,用来储存被b线程的返回值。
源代码:

/home/cathy/2022UNIXLab/lab7/77.c:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define M 100
float avee = 0;
typedef struct
{
	float grade[M];
	float studentnum;
}test;
void *Ave(void *a)
{
	pthread_detach(pthread_self());	
	test *x = (test*) a;
	float t=0;
	for(int i = 0;i<x->studentnum;i++)
		t=t+x->grade[i];
	avee=t/x->studentnum;
	//printf("成绩单:\n");
	sleep(2);	
	return NULL;
}
void *Num(void *a)
{
	pthread_detach(pthread_self());	
	test *x = (test*) a;
	int t=0;
	for(int i = 0;i<x->studentnum;i++)
		if(x->grade[i]>avee)
			t++;		
	pthread_exit((void*)t);
}
int main(void)
{
	pthread_t a,b;
	int i,err;
	test student;
	float aver;
	printf("请输入学生人数:\n");
	scanf("%f",&student.studentnum);
	printf("请输入学生成绩:\n");
	for(i = 0;i<student.studentnum;i++)
		scanf("%f",&student.grade[i]);
	err=pthread_create(&a,NULL,Ave,(void*)&student);
	pthread_join(a,NULL);
	printf("平均成绩为:\n");
	printf("%f\n",avee);
	err=pthread_create(&b,NULL,Num,(void*)&student);
	int t;
	pthread_join(b,&t);
	printf("高于平均分的人数:\n");
	printf("%d\n",t);
	return 0;
}

火车站订票系统模拟:订票系统提供“票号、起始车站、终点车站、日期”信息,火车站12306可实现在线购票,请模拟实现在线订票,保证能正确出票

基本要求:
(1)同一时刻可多人查看车票。
(2)同一时刻仅1人可进行购票
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
首先新建一个PH.txt文件夹用来储存火车票的信息,如图 18所示是PH.txt文档中的信息。
图 16为查看车票信息的运行界面,可以看出,可以有三个人同时查看车票信息。图 17为购票界面,同一时刻只能由一个人购买车票。
该程序需要实现两个功能:查看信息和购票,则至少需要两个函数来完成这两项工作:使用show2()函数完成查看车票信息的功能;使用buy()函数实现购买车票的功能。若要实现多人可同时查看车票信息的功能,则应该创建多个线程,并且将回收线程资源函数写在完成查看车票信息的多个线程最后。若要实现同一时刻仅一人能够买票的功能,则需要定义信号量mutex,并首先使用pthread_mutex_init()函数对信号量进行初始化,以动态方式创建互斥锁,函数执行成功后,互斥锁初始化为未上锁状态,然后使用pthread_mutex_lock()函数锁定互斥锁,当线程执行完毕后再使用pthread_mutex_unlock()解开互斥锁。如果在执行线程时互斥锁没有被解开,那么这个线程就会被阻塞,直到互斥锁变为可用线程才能继续执行。使用互斥锁可以保证线程按顺序执行,确保一次只有一个线程执行某个代码。本程序的数据结构:
结构体数组PH,用来储存车票信息:
struct PH{
char code[10];//车次(票号)
char name1[30];//出发地
char name2[30];//目的地
char date[30];//日期
}PH[100];
车次总数:
int numbers = 6;
定义信号量:
pthread_mutex_t mutex;
在这里插入图片描述

/home/cathy/2022UNIXLab/lab8/8.c:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
struct PH{
	char code[10];
	char name1[30];//出发地
	char name2[30];//目的地
	char date[30];//日期
}PH[100];
int numbers = 6;//车次
pthread_mutex_t mutex;
void Read_from_file()
{
	FILE *fp;
	int i = 0;
	if((fp = fopen("PH.txt","r")) == NULL)//以读的方式打开文件
		printf("文件打开失败");
	setvbuf(fp,NULL,_IOFBF,BUFSIZ); //设置完全缓存模式
	while(i < numbers)
	{
	fscanf(fp,"%s\n%s\n%s\n%s\n\n",PH[i].code,PH[i].name1,PH[i].name2,&PH[i].date);
	i++;
	}
	//printf("%s\n",PH[1].name1);
	fclose(fp);
}
void *show2()//每个线程均可调用该函数查看车票信息
{
	printf("******************************************************\n");
	printf("票号\t起始车站\t终点车站\t日期\n");
	for(int i = 0;i<numbers;i++)
	{	printf("%s\t%s\t%s\t%s\n",PH[i].code,PH[i].name1,PH[i].name2,PH[i].date);
	}
	printf("******************************************************\n");
}
void *buy()
{
	pthread_mutex_init(&mutex,NULL);
	pthread_mutex_lock(&mutex);//上锁
	printf("请输入您想买的火车车次:\n");
	char a[10];
	scanf("%s",&a);
	int q = 0;
	for(int i = 0;i<numbers;i++)
	{
		if(strcmp(PH[i].code,a)==0)
			{
				printf("购买成功!\n");
				q=1;
			}
	}
	if(q==0)
		printf("无该车次信息!\n");
	pthread_mutex_unlock(&mutex);//解锁
}
int main(void)
{
	Read_from_file();
	pthread_t a,b,c,d;
	while(1){
	printf("1查看信息;2购买车票;输入其他数字退出应用\n");
	int x;
	scanf("%d",&x);
	if(x==1){
	pthread_mutex_init(&mutex,NULL);
	int err1 = pthread_create(&a,NULL,show2,NULL);
	if(err1==-1)
		printf("线程创建失败!\n");
		int err2 = pthread_create(&b,NULL,show2,NULL);
	if(err2==-1)
		printf("线程创建失败!\n");
		int err3 = pthread_create(&c,NULL,show2,NULL);
	if(err3==-1)
		printf("线程创建失败!\n");
pthread_join(a, NULL);
pthread_join(b, NULL);
	pthread_join(c, NULL);}
	else if(x==2){
	int err4 = pthread_create(&d,NULL,buy,NULL);
	if(err4==-1)
		printf("线程创建失败!\n");
	pthread_join(b, NULL);}
	else
		break;}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值