1.实现功能
myecho.c的功能与系统echo程序相同,接受命令行参数,并将参数打印出来。
2.实现思路
main函数有两个参数:argc和argv,其中argc记录了main 函数的命令行参数总个数,包括可执行程序名;argv[] 是一个字符串数组,每个元素指向一个参数,在命令行输入的字符串会被自动分割为字符串数组,其长度为argc,argv[0]=可执行程序名,argv[1…argc-1]=可执行程序参数,argv[argc] = NULL。
利用main函数的两个参数可以实现echo功能:首先根据argc的值判断是否有需要打印的字符串,如果argc==1,说明只有可执行程序名,没有参数,直接输出 Character not received!
并返回;
/* user haven't input any char */
if(argc == 1){
printf("Character not received!\n");
return 0;
}
如果不为0,输出 argv 字符串数组中的值即可。需要注意的是:argv[0]中存储的是可执行程序名称,不应该被输出,所以下标应从 1 开始:
/* print char from argv */
for(int i = 1 ;i < argc ; i++)
printf("%s ",argv[i]);
printf("\n");
1.实现功能
mycat.c的功能与系统cat程序相同,mycat将指定的文件内容输出到屏幕,可以处理无参数和多参数的情况,要求使用系统调用open/read/write/close实现。
2.实现思路
mycat在输出文件内容时主要通过函数调用read/write
实现,read函数返回值为rd,如果读取失败,返回-1,如果读取成功,返回实际读取的字节个数;返回0则代表读取到了文件末尾,可以用if语句作为是否读完的条件判断,再将读到的rd个字节用write写出到标准输出 STDOUT_FILENO
。
mycat在没有参数时,也就是argc==1,只有一个程序名,那程序会将用户在屏幕输入的内容原封不动打印出来,此时read的文件描述符参数就是STDIN_FILENO
,表示从屏幕输入。由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:
if(argc == 1)
{
while(1)
{
int rd;
char buf[10];
if(rd = read(STDIN_FILENO,buf,MAX) > 0)
write(STDOUT_FILENO,buf,rd);
else
break;
}
}
mycat有多个参数时,会将这些文件依次打开并输出到标准输出,因此当 argc>1 时,对 argv 中的文件逐个打开,调用read 读取打开的文件,参数为fd,然后用 write 函数将当前文件的字符写到标准输出中去。同样,由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:
/* show multiple files */
for(int i = 1;i < argc;i++){
int fd = open(argv[i],O_RDONLY);
if(fd < 0)
panic("open file fail!");
while(1)
{
char buf[10];
memset(buf,0,MAX);
int rd;
if((rd = read(fd,buf,MAX)) > 0)
/* write to screen */
write(STDOUT_FILENO,buf,rd);
else
break;
}
close(fd);
}
1.实现功能
mycp.c的功能与系统cp程序相同, 将源文件复制到目标文件,要求使用系统调用open/read/write/close实现
2.实现思路
对于mycp,和之前两个程序不同,mpcp需要有可执行程序名argv[0],源文件argv[1],目的文件argv[2],当它的参数小于3个的时候一定是参数错误,因此要先判断 if(argc < 3)
if(argc != 3)
panic("Parameter error!");
对于目的文件,可能不存在,这时应该创建一个新的文件,因此open函数用 O_CREAT,mode
打开,赋予权限为0777,若两次执行程序都移动到同一文件中,需要覆盖操作,因此open函数还需 O_TRUNC
参数。当文件都打开/创建成功时,调用 read 函数对源文件逐个读取,调用 write 函数写入目标文件中。这里write的文件描述符不再是标准输出STDOUT,而是目标文件的fd2:
int fd1 = open(argv[1], O_RDONLY);
mode_t mode=0777;
int fd2 = open(argv[2], O_RDWR | O_TRUNC | O_CREAT,mode);
if(fd1 < 0 || fd2 < 0)
panic("Open file 1 failed!");
char buf[MAX];
memset(buf,0,MAX);
int rd;
while(1)
{
rd = read(fd1,buf,MAX);
if(rd > 0)
write(fd2,buf,rd);
else
break;
}
close(fd1);
close(fd2);
1.实现功能
mysys的功能与系统函数system相同,要求用进程管理相关系统调用自己实现一遍,使用fork/exec/wait系统调用。
3.实现思路
mysys的实现主要基于以下三个函数:execvp、strtok、fork
程序main函数读入读入一个字符串 command,传给mysys模块,在mysys模块中,先检查命令长度是否为0,如果没有要执行的命令,则直接返回退出;定义一个myargc表示这个命令的参数个数,定义一个myargv表示参数列表,调用change模块进行字符串处理,返回值为myargc参数个数:
int t=strlen(command);
/* if no command, continue to exec the next one */
if(t == 0)
return;
int myargc;
char *myargv[MAXARG];
myargc = change(command,myargc,myargv);
myargv[myargc]=NULL;
change模块:strtok不可以直接对字符串常量进行分割,要用strncpy得到一个char型数组buf, 对这个buf数组进行处理即可。用空格作为分隔符,首次调用时,第一个参数指向要分解的字符串,之后再次调用要把第一个参数设成NULL,将分割后的子字符串存入 myargv字符数组中,每次myargc++:
int change(char *command,int myargc,char *myargv[])
{
char buf[MAX];
memset(buf,0,MAX);
strncpy(buf,command,strlen(command));
char *delim=" ";
myargc=0;
myargv[myargc]=strtok(buf,delim);
while(myargv[++myargc]=strtok(NULL,delim));
return myargc;
}
分割字符串成功后就开始创建一个子进程,调用子进程执行模块chile,让子进程执行execvp,父进程等待子进程结束再退出:
pid_t pid;
pid = fork();
if(pid == 0)
child(myargc,myargv);
else
wait(NULL);
子进程模块调用execvp,第一个参数为可执行程序名,存在了myargv[0]中,之后的参数列表通过myargv传入,装入可执行程序,执行命令:
void child(int myargc,char *myargv[])
{
int status = execvp(myargv[0],myargv);
if(status < 0)
Perror("Arguments fault!");
exit(1);
}
1.实现功能
实现管道和文件重定向。
2.实现构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfaqS1mh-1593603294451)(C:\Users\lenovo\Desktop\2.PNG)]
3.实现思路
main函数中调用mysys模块:
int main()
{
while (1) mysys();
return 0;
}
mysys模块实现的是内置命令的处理,其他的命令交给parse_command模块。
首先通过fgets读取字符串,并去掉最后一个换行符变为\0:
char command[ARG_MAX + 1], argv[ARG_MAX + 1];
memset(command, 0, ARG_MAX + 1);
printf("> ");
fgets(command, ARG_MAX, stdin);
command[strlen(command) - 1] = '\0';
先把第一个空格之前的子串分离出来分类讨论
如果是内置命令,就不用fork一个子进程,而是直接执行,exit直接调用exit(0)退出,cd调用chdir(path)切换路径;
如果不是内置命令,就交给parse_command来分析命令:
char *p;
p = command;
/* first command */
char temp[10];
memset(temp,0,10);
int i=0,j=0;
while((*p!=' ')&& (*p))
{
temp[i++]=command[j++];
++p;
}
if(strcmp(temp,"exit") == 0)
exit(0);
else if(strcmp(temp,"cd") == 0)
{
++p;
int state = chdir(p);
char buf[ARG_MAX];
memset(buf,0,ARG_MAX);
printf("now in : %s\n",getcwd(buf,ARG_MAX));
}
else
{
p=command;
parse_commands(p);
}
parse_command模块中,用 |
作为分隔符,解析成子命令,并将子命令的个数记录在 cmd_count
中,子命令名记录在cmds[]
中,以便之后的执行。用一个for循环遍历这个command字符串。
以 cat
为例来说明这个过程:
在最开始,把第一个原始命令加入cmds,并把cmd_count+1。当遍历到 再将指针p定位到 ‘|’ 右边第一个字符的位置,去掉多于的空格后,p就指向了 继续执行循环,当再次遇到 ‘|’ 时,同样将p指向’|'之前的一个字符,由于cmds[cmd_count++]是指向p的,去掉空格并用\0截断之后,cmd[1]就会自动变成sort,p再次指向unique| cat >output.txt,并把unique| cat >output.txt写进cmds,cmd_count+1…执行完这个循环后,cmd_count=4, cmd[0]=cat cmd[1]=sort cmd[2]=uniq cmd[3]= cat >output.txt 当然,这只是针对这一个特例的情况。要想实现所有类型的命令,就要先判断cmd_count的值,根据不同的count交给split_file模块进行不同的处理。 先提前对于split_file模块进行说明,split_file模块的功能是解析重定向命令。 如存在文件重定向,把它们分离出来记录到char *file_input, char *file_output变量中并打开,并且使用文件描述符;否则使用管道描述符。 函数原型为: command是上一步parse_command的分析结果,pipe_read和pipe_write是读描述符和写描述符,可以有也可以没有,没有某一个描述符时用-1表示。 先定义一些变量: fd_read和fd_write或是一个打开的文件的描述符,或是一个管道的读端或写端,或两者都不是(-1);flagin/flagout用来标识有无’<’、’>’;file_input/file_output存储文件名。 命令从头开始遍历,遇到’>‘时,说明需要重定向输出到指定的文件,用一个指针p指向’>'后的第一个字符,去掉多于空格后向后减,将command截断,用一个指针s去掉多余空格后指向输出文件,赋给file_output,并去掉多于的换行符等。 例如对cmd[3]即 遇到 ‘<’ 的情况类似:例如对cat 之后对fd_read/fd_write进行赋值,如果flagin/flagout有效,表明这条命令中存在文件重定向,就打开它并作为该文件的描述符; 如果flagin/flagout无效,但是pipe_read/pipe_write有效,说明这是一条管道命令,那么把这个管道描述符赋给fd_read/fd_write; 如果二者皆无,说明只是一条普通命令,当做exec_simple处理,分割好输入输出文件之后,就交给exec_pipe模块。 再回到parse_command模块中,该模块剩下的部分就是根据不同的cmd_count进行不同情况的分割文件,如果cmd_count == 1,说明不是一条管道命令,就向split_file传参数(-1,-1)表示没有管道: 如果包含管道命令就创建一个管道,并返回读端和写段,从cmd[0]开始遍历,由于执行第一个子命令时,只有后面有管道,所以用不到read,将对应参数置位-1即可,即 执行最后一个子命令时只有前面有管道,所以用不到write,将对应参数位置置位-1,即 注意,对于非头尾的命令,可能前面有管道,后面也有管道,某一个子命令对应的子进程会用了两个管道,那么要在进入下一个子进程之前,抛弃老管道写端,建立新管道写端,即 将写描述符与上一条命令产生的的读描述符传入重定向解析函数: 至此,parse_command对于’|'的处理结束,调用split_file分类处理完并拿到重定向输入输出文件描述符后,就会转向exec_pipe模块,这一模块实现创建子进程执行命令。首先创建子进程,父进程什么都不做,关闭不必要的读写端,等待子进程结束,为下一个子进程做准备: 每一个子命令对应一个子进程,fd_read != -1,说明需要重定向,或将标准输出重定向到输入文件,或将标准输出重定向到管道的读端; fd_write != -1,说明需要重定向,或将标准输出重定向到输出文件,或将标准输出重定向到管道的写端; 例如当命令为 重定向处理完成后,就可以进行execvp装入可执行程序了: 1.实现功能 莱布尼兹级数公式: 1 - 1/3 + 1/5 - 1/7 + 1/9 - … = PI/4,主线程创建1个辅助线程,主线程计算级数的前半部分,辅助线程计算级数的后半部分,主线程等待辅助线程运行结束后,将前半部分和后半部分相加。 2.实现思路 主线程创建一个子线程,让它从compute处开始执行,计算级数前半部分 compute计算部分无参数,迭代次数越大,结果越精确,这里accy=1e8,前半段计算到1e4,注意类型转化: 主线程几乎做一样的步骤,只不过是从1e4计算到accy: 为了防止主线程提前退出,因此要等待子线程完成,再将前半部分和后半部分的值合并,输出: 1.实现功能 与上一题类似,但本题更加通用化,能适应N个核心,主线程创建N个辅助线程,每个辅助线程计算一部分任务,并将结果返回。主线程等待N个辅助线程运行结束,将所有辅助线程的结果累加 本题要求 1: 使用线程参数,消除程序中的代码重复 本题要求 2: 不能使用全局变量存储线程返回值 2.实现思路 定义NR_TOTAL表明全部计算量,NR_CPU表示内核数目,NR_CHILD表明每个核的计算量: 定义线程参数,记录每个核计算部分的开始和起始位置以及计算结果: 主线程创建NR_CPU个线程,并为每一个线程分配任务,把总任务分成等量的NR_CPU份,启动每一个线程,并把线程参数传给compute: compute计算部分一定要将接收到的参数进行强制类型转换,计算的结果要保存在result结构体中,保存之前要为它开辟空间,否则没有空间存储结果,最后将result结果返回: 主线程不做任何计算,只负责分配任务,等所有子线程结束后,调用 pthread_join汇总结果,并释放result占用的空间,打印输出: 1.实现功能 主线程创建两个辅助线程,辅助线程1使用选择排序算法对数组的前半部分排序,辅助线程2使用选择排序算法对数组的后半部分排序,主线程等待辅助线程运行结束后,使用归并排序算法归并子线程的计算结果 本题要求 : 使用线程参数,消除程序中的代码重复 2.实现思路 定义NR_TOTAL表明全部计算量,NR_CPU表示内核数目,NR_CHILD表明每个核的计算量,依据题目要求,NR_CPU的数量为2,创建两个子线程,数组元素个数为20: 用rand函数产生随机数组: 主线程为这两个线程分配任务,启动每一个线程,并把线程参数传给compute: 这两个子线程的计算部分都调用了选择排序函数,只不过第一个线程计算数组前半段,第二个线程计算数组后半段,根据param的start和end值来标识: 主线程要等待两个子线程排序完毕: 主线程再归并数组的前半部分和后半部分,直接在主线程里调用自定义的Merge函数即可将两个选择排序的结 1.实现功能 系统中有3个线程:生产者、计算者、消费者,系统中有2个容量为4的缓冲区:buffer1、buffer2,生产者生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符,放入到buffer1,计算者从buffer1取出字符,将小写字符转换为大写字符,放入到buffer2,消费者从buffer2取出字符,将其打印到屏幕上。 3.实现思路 使用长度为4的两个数组表示共享缓冲区,生产者生产八个字符,放入到buffer1,计算者从 buffer1 取出字符,将小写字符转换为大写字符,放入到 buffer2,消费者从 buffer2 取出字符,将其打印到屏幕上;变量out1,out2分别为两个共享缓冲区的读指针,变量in1,in2分别为两个共享缓冲区的写指针: 由于有两个共享缓冲区,所以要用flag来标识是1号还是2号,in指针和out指针相同时,缓冲区为空;n指针和out指针相邻时,缓冲区为满,用求模保证数组不越界: 对缓冲区的生产/消费动作要根据flag判断是对哪个缓冲区操作,get_item获取out指针指向的元素同时,移动out指针指向下一项;put_item将元素放置在in指针指向的位置同时,移动in指针指向下一项: 定义互斥信号量mutex用于进程间互斥,定义条件变量pthread_cond_t用于进程间同步: 生产者的任务是生产八个字符,放入到buffer1,为了实现互斥,在对缓冲区操作前必须加锁,为了避免缓冲区1满的情况,要先试探是否还有空间,再选择等待还是继续执行。生产结束后,释放一个满缓冲区,并解锁以唤醒计算者: 计算者从buffer1取出字符,将小写字符转换为大写字符,放入到buffer2,先作为 buffer1 的消费者,给 buffer1 加锁并取数,将小写字母变成大写字母;最后再作为buffer2 的生产者,给 buffer2 加锁并存数,完成对两个缓冲区的存取: 消费者作为 buffer2 的消费者,给 buffer2 加锁并取数字,取完后要释放一个空缓冲区并解锁唤醒计算者: 在主线程中创建三个线程,分别用于承担生产者,计算者与消费者: 定义两个锁用于线程间互斥: 定义四个条件变量用于线程间同步: 再将三个进程都调用 pthread_join()函数等待线程结束: 1.实现功能 功能和前面的实验相同,使用信号量解决 2.实现思路 实现的时候利用信号量,使用条件变量实现信号量sema_t,value记录了信号量的值 如果信号量的值小于等于0,则等待条件变量,将信号量的值减一: 对共享缓冲区的操作结束后,将信号量的值加一,唤醒等待条件变量的线程: 定义两个信号量 mutex_sema1,mutex_sema2,分别对(生产者-计算者)与(计算者-消费者)进行线程间互斥: 定义了四个信号量 对共享变量 buffer1,buffer2 进行线程间同步。 在生产者、计算者、消费者的函数中,先等待互斥信号量(上锁),再获取同步信号量,对 buffer 中的数据进行操作后,释放互斥信号量及解锁。要注意,需要先获取同步信号量再对互斥信号量进行上锁,不然可能造饥饿的现象: main 函数中开启三个线程分别对应生产者、计算者、消费者,再对两个互斥信号量以及四个同步信号量进行初始化,调用 pthread_join 函数等待三个进程的结束即可: ll_buffer_sema1); } 需源码可滴滴command[i] == '|'
时,让指针p指向 ‘|’ 前一个字符,去掉多余的空格之后,用\0
来截断,分离出第一个 ‘|’左边的部分,这时command命令就变成了cat cmds[cmd_count++] = command;
int len = strlen(command);
for (i = 0; i < len; ++i)
{
if (command[i] == '|')
{
/* deal left and remove space */
p = command + i - 1;
while (*p && (*p == ' '))
{
*p = '\0';
--p;
}
......
sort | uniq | cat >output.txt
这一字符串,写进cmds,并把cmd_count+1:......
/* deal right and remove space */
p = command + i + 1;
while (*p && (*p == ' '))
{
*p = '\0';
++p;
}
/* cmd add one */
cmds[cmd_count++] = p;
......
void split_file(char *command, int pipe_read, int pipe_write)
int fd_read = -1, fd_write = -1;
int i, len = strlen(command);
int flagout = 0, flagin = 0;
char *file_input, *file_output;
cat >output.txt
处理完后,command变成cat,file_output变成output.txt,最后将flagout置位1,表明该命令中有需要重定向输出的文件。for (i = 0; i < len; ++i)
{''
/* find dis file */
if (command[i] == '>')
{
p = command + i - 1;
while (*p == ' ')
*(p--) = '\0';
command[i] = '\0';
char *s = command +i+1;
while (*s == ' ')
++s;
file_output = s;
for (int j = 0; j < strlen(s); ++j)
if (s[j] == ' ' || s[j] == '\n')
s[j] = '\0';
flagout = 1;
}
......
......
/* find src file */
else if (command[i] == '<')
{
char *s = command + i + 1;
while (*s == ' ')
++s;
file_input = s;
for (int j = 0; j < strlen(s); ++j)
if (s[j] == ' ' || s[j] == '\n')
s[j] = '\0';
p = command + i - 1;
while (*p == ' ')
*(p--) = '\0';
flagin = 1;
}
/* not a pipe command */
if (cmd_count == 1)
{
split_file(cmds[0], -1,-1);
return;
}
split_file(cmds[i], -1, fd_pipe[1]);
split_file(cmds[i], fd_pipe[0], -1);
split_file(cmds[i], fd_tmp, fd_pipe[1]);
/* pipe command */
pipe(fd_pipe);
for (i = 0; i < cmd_count; ++i)
{
if (i == 0)
/* input to pipe,close fd[0] */
split_file(cmds[i], -1, fd_pipe[1]);
else if (i != cmd_count - 1)
{
fd_tmp = fd_pipe[0];
status = pipe(fd_pipe);
if (fd_tmp == -1)
{
printf("can't open pipe!\n");
return;
}
split_file(cmds[i], fd_tmp, fd_pipe[1]);
}
else
/* output from pipe,close fd[1] */
split_file(cmds[i], fd_pipe[0], -1);
}
pid_t pid;
pid = fork();
if (pid != 0)
{
if (fd_read != -1)
close(fd_read);
if (fd_write != -1)
close(fd_write);
wait(NULL);
}
cat /etc/passwd wc | -l
时,会将标准输入重定向到管道的入口,将cat /etc/passwd写进管道,然后执行wc | -l 对管道内容操作。else
{
if (fd_read != -1)
{
close(STDIN_FILENO);
status = dup2(fd_read, STDIN_FILENO);
close(fd_read);
if (status < 0)
printf("%s dup stdin failed!\n", argv[0]);
}
if (fd_write != -1)
{
close(STDOUT_FILENO);
status = dup2(fd_write, STDOUT_FILENO);
close(fd_write);
if (status < 0)
printf("%s dup stdout failed!\n", argv[0]);
}
argc=change(command, argc, argv);
argv[argc] = NULL;
if (execvp(argv[0], argv) == -1)
printf("%s exec failed!\n", argv[0]);
exit(0);
}
多线程
pi1
pthread_t tid;
int state = pthread_create(&tid,NULL,&compute,NULL);
if(state != 0)
{
printf("create error\n");
return 0;
}
void * compute(void * arg)
{
int N=accy/2;
for(int i = 1;i < N;i = i + 2)
{
double tmp = (double)1/i;
if(((i+1)/2)%2 == 0)
tmp = -tmp;
worker_output += tmp;
}
}
int N = accy/2;
for(int i = N;i <= accy;i = i+2)
{
double tmp = (double)1/i;
if(((i+1)/2)%2 == 0)
tmp = -tmp;
master_output += tmp;
}
pthread_join(tid, NULL);
sum = (worker_output+master_output);
printf("Leibniz series = %.15lf\n",sum);
printf("PI = %.15lf\n",sum*4);
pi2
#define NR_TOTAL 100000000
#define NR_CPU 8
#define NR_CHILD (NR_TOTAL/NR_CPU)
struct param{
int start;
int end;
};
struct result{
double res=0.0;
};
pthread_t tid[NR_CPU];
struct param params[NR_CPU];
for( int i = 0;i < NR_CPU;i++)
{
struct param *param;
param = ¶ms[i];
param->start = i * NR_CHILD;
param->end = (i + 1) * NR_CHILD;
int state = pthread_create(&tid[i], NULL, compute, param);
if(state != 0)
{
printf("create error!\n");
return 0;
}
}
void * compute (void * arg)
{
struct param *param;
struct result *result;
double sum = 0.0;
param = (struct param *)arg;
for (int i = param->start; i < param->end; i++)
{
if(i&1==1)
{
double tmp=(double)1/i;
if(((i+1)/2)%2 == 0)
tmp = -tmp;
sum +=tmp;
}
}
result = malloc(sizeof(struct result));
result->res = sum;
return result;
}
double sum = 0.0;
for (int i = 0; i < NR_CPU; i++)
{
struct result *result;
pthread_join(tid[i], (void **)&result);
sum += result->res;
free(result);
}
printf("Leibniz series = %.15lf\n",sum);
printf("PI = %.15lf\n", sum*4);
sort
#define NR_TOTAL 20
#define NR_CPU 2
#define NR_CHILD (NR_TOTAL/NR_CPU)
/* Randomly generated array */
for(int i = 0;i<NR_TOTAL;i++)
{
array[i]=(rand() % N);
printf("%d ",array[i]);
}
for (i = 0; i < NR_CPU; i++)
{
struct param *param;
param = ¶ms[i];
param->array = array;
param->start = i * NR_CHILD;
param->end = (i + 1) * NR_CHILD;
/* child thread do select sort */
pthread_create(&workers[i], NULL, compute, param);
}
void *compute(void *arg)
{
struct param *param;
int i;
param = (struct param *)arg;
select_sort(array,param->start,param->end);
printf("\n");
}
void select_sort(int A[],int start,int end)
{
for(int i = start; i < end; i++)
{
int k = i;
for(int j = i; j < end; j++)
if(A[j] < A[k])
k=j;
int temp = A[i];
A[i] = A[k];
A[k] = temp;
}
}
for (i = 0; i < NR_CPU; i++)
pthread_join(workers[i], NULL);
果归并排序完成:/* parent thread do merge sort */
merge_sort(array,0,NR_TOTAL-1);
printf("After sort : \n");
for (int i=0;i<NR_TOTAL;i++)
printf("%d ",array[i]);
pc1
#define CAPACITY 4
char buffer1[CAPACITY];
char buffer2[CAPACITY];
int in1=0,in2=0;
int out1=0,out2=0;
int buffer_is_empty(int flag)
{
if(flag == 1)
return in1 == out1;
if(flag == 2)
return in2 == out2;
}
int buffer_is_full(int flag)
{
if(flag == 1)
return (in1 + 1) % CAPACITY == out1;
if(flag == 2)
return (in2 + 1) % CAPACITY == out2;
}
char get_item(int flag)
{
char item;
if(flag == 1){
item = buffer1[out1];
out1 = (out1 + 1) % CAPACITY;
}
if(flag == 2){
item = buffer2[out2];
out2 = (out2 + 1) % CAPACITY;
}
return item;
}
void put_item(char item, int flag)
{
if(flag == 1){
buffer1[in1] = item;
in1 = (in1 + 1) % CAPACITY;
}
if(flag == 2){
buffer2[in2] = item;
in2 = (in2 + 1) % CAPACITY;
}
}
pthread_mutex_t mutex1,mutex2;
pthread_cond_t wait_empty_buffer1,wait_empty_buffer2;
pthread_cond_t wait_full_buffer1,wait_full_buffer2;
void *produce(void *arg)
{
char item;
for(int i = 0;i < ITEM_COUNT;i++){
pthread_mutex_lock(&mutex1);
/* producer use the first buffer */
while(buffer_is_full(1))
pthread_cond_wait(&wait_empty_buffer1, &mutex1);
item = 'a' + i;
put_item(item,1);
printf("produce item:%c\n",item);
pthread_cond_signal(&wait_full_buffer1);
pthread_mutex_unlock(&mutex1);
}
return NULL;
}
void *compute(void *arg)
{
char item;
for(int i = 0;i < ITEM_COUNT;i++)
{
pthread_mutex_lock(&mutex1);
/* computer get item from buf1 */
while(buffer_is_empty(1))
pthread_cond_wait(&wait_full_buffer1, &mutex1);
item = get_item(1);
pthread_cond_signal(&wait_empty_buffer1);
pthread_mutex_unlock(&mutex1);
/* change to capital */
item -= 32;
pthread_mutex_lock(&mutex2);
/* and put in to buf2 */
while(buffer_is_full(2))
pthread_cond_wait(&wait_empty_buffer2, &mutex2);
put_item(item,2);
printf(" compute put item:%c\n", item);
pthread_cond_signal(&wait_full_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
void *consume(void *arg)
{
char item;
for(int i = 0;i < ITEM_COUNT;i++){
pthread_mutex_lock(&mutex2);
/* consumer get item ,use the second buffer */
while(buffer_is_empty(2))
pthread_cond_wait(&wait_full_buffer2, &mutex2);
item = get_item(2);
printf(" comsume item:%c\n", item);
pthread_cond_signal(&wait_empty_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
pthread_t tids[3];
pthread_create(&tids[0],NULL,produce,NULL);
pthread_create(&tids[1],NULL,compute,NULL);
pthread_create(&tids[2],NULL,consume,NULL);
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
pthread_cond_init(&wait_empty_buffer1, NULL);
pthread_cond_init(&wait_full_buffer1, NULL);
pthread_cond_init(&wait_empty_buffer2, NULL);
pthread_cond_init(&wait_full_buffer2, NULL);
for(int i = 0;i < 3;i++)
pthread_join(tids[i],NULL);
pc2
typedef struct{
int value;
pthread_mutex_t mutex;
pthread_cond_t cond;
}sema_t;
void sema_wait(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
while(sema->value <= 0)
pthread_cond_wait(&sema->cond, &sema->mutex);
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
++sema->value;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
sema_t mutex_sema1,mutex_sema2;
sema_t empty_buffer_sema1,empty_buffer_sema2;
sema_t full_buffer_sema1,full_buffer_sema2;
void *consume(void *arg){
char item;
for(int i = 0;i < ITEM_COUNT;i++)
{
/* consumer get item ,use the second buffer */
sema_wait(&full_buffer_sema2);
sema_wait(&mutex_sema2);
item = get_item(2);
printf(" comsume item:%c\n", item);
sema_signal(&mutex_sema2);
sema_signal(&empty_buffer_sema2);
}
return NULL;
}
void *produce(void *arg){
char item;
for(int i = 0;i < ITEM_COUNT;i++)
{
/* producer use the first buffer */
sema_wait(&empty_buffer_sema1);
sema_wait(&mutex_sema1);
item = 'a' + i;
put_item(item,1);
printf("produce item:%c\n",item);
sema_signal(&mutex_sema1);
sema_signal(&full_buffer_sema1);
}
return NULL;
}
void *compute(void *arg){
char item;
for(int i = 0;i < ITEM_COUNT;i++)
{
/* computer get item from buf1 */
sema_wait(&full_buffer_sema1);
sema_wait(&mutex_sema1);
item = get_item(1);
sema_signal(&mutex_sema1);
sema_signal(&empty_buffer_sema1);
/* change to capital */
item -= 32;
sema_wait(&empty_buffer_sema2);
sema_wait(&mutex_sema2);
/* and put in to buf2 */
put_item(item,2);
printf(" compute put item:%c\n", item);
sema_signal(&mutex_sema2);
sema_signal(&full_buffer_sema2);
}
return NULL;
}
/* create three thread */
pthread_t tids[3];
sema_init(&mutex_sema1, 1);
sema_init(&mutex_sema2, 1);
sema_init(&empty_buffer_sema1,CAPACITY - 1);
sema_init(&full_buffer_sema1,0);
sema_init(&empty_buffer_sema2,CAPACITY - 1);
sema_init(&full_buffer_sema1,0);
pthread_create(&tids[0],NULL,produce,NULL);
pthread_create(&tids[1],NULL,compute,NULL);
pthread_create(&tids[2],NULL,consume,NULL);
for(int i = 0;i < 3;i++)
pthread_join(tids[i],NULL);
sema_wait(&mutex_sema1); item = get_item(1);
sema_signal(&mutex_sema1);
sema_signal(&empty_buffer_sema1);
/* change to capital */
item -= 32;
sema_wait(&empty_buffer_sema2);
sema_wait(&mutex_sema2);
/* and put in to buf2 */
put_item(item,2);
printf(" compute put item:%c\n", item);
sema_signal(&mutex_sema2);
sema_signal(&full_buffer_sema2);
}
return NULL;
main 函数中开启三个线程分别对应生产者、计算者、消费者,再对两个互斥信号量以及四个同步信号量进行初始化,调用 pthread_join 函数等待三个进程的结束即可:
```c
/* create three thread */
pthread_t tids[3];
sema_init(&mutex_sema1, 1);
sema_init(&mutex_sema2, 1);
sema_init(&empty_buffer_sema1,CAPACITY - 1);
sema_init(&full_buffer_sema1,0);
sema_init(&empty_buffer_sema2,CAPACITY - 1);
sema_init(&full_buffer_sema1,0);
pthread_create(&tids[0],NULL,produce,NULL);
pthread_create(&tids[1],NULL,compute,NULL);
pthread_create(&tids[2],NULL,consume,NULL);
for(int i = 0;i < 3;i++)
pthread_join(tids[i],NULL);