已阅读5页,还剩16页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
实验四 进程及进程管理4.1使用ps命令查看进程信息【实验内容】ps是基本的Linux命令,通过本实验,不仅要熟悉ps命令方法,更重要的是可以了解Linux进程的组成。【实验原理】ps:查看系统中的进程,Linux中可以使用ps -aux查看所有进程。其中PID代表进程ID,TTY是该进程是由哪个控制台启动的,CMD则是命令。 如果想列出更详细的信息,则可使用命令:“ps -auxw”。参数w表示加宽显示的命令行,参数w可以写多次,通常最多写3次,表示加宽3次,这足以显示很长的命令行了。【实验内容】在shell提示符下输入如下命令,并解释输出的结果:rootvm root#psPID TTY TIME CMD 16767 pts/1 0:00 ps 18029 pts/1 0:00 bashrootvm root#ps auxPID TTY TIME CMD 4.2使用proc文件系统查看进程信息【实验目的】本实验将指导学员了解proc文件系统,通过proc文件系统查询进程信息,可以扩展到修改系统参数。【实验原理】/proc文件系统是一个虚拟文件系统,通过文件系统接口实现对内核的访问,输出系统运行状态。它以文件系统的形式,为操作系统本身和应用进程之间的通信提供了一个界面,使应用程序能够安全,方便的获得系统当前的运行状况和内核的内部数据信息,并且可以修改某些系统的配置信息。【实验内容】1)认识proc文件系统的文件和目录rootvm root#cd /procrootvm root#ls2)通过proc文件系统查看系统当前进行状态rootvm root#cat /proc/self/status3)查询文件句柄的当前使用情况# cat /proc/sys/fs/file-nr426 15252458file-nr 文件显示了三个参数:分配的文件句柄总数、当前使用的文件句柄数以及可以分配的最大文件句柄数。如果需要增大 /proc/sys/fs/file-max 中的值,请确保正确设置 ulimit。对于 2.4.20,通常将其设置为 unlimited。使用 ulimit 命令来验证 ulimit 设置: rootvm root# ulimitunlimited4)通过proc文件系统修改内核中预定的一些变量1)修改整个系统中文件句柄的最大数量rootvm root#ls /proc/sys/fs/file-max52458rootvm root#echo 65536 /proc/sys/fs/file-maxrootvm root#ls /proc/sys/fs/file-max655362)修改网络TTLrootvm root#ls /proc/sys/net/ ipv4/ip_default_ttl64rootvm root#echo 128 /proc/sys/net/ipv4/ip_default_ttlrootvm root#ls /proc/sys/net/ ipv4/ip_default_ttl1283)修改系统中最大进程数量rootvm root#ls /proc/sys/kernel/pid_max32768rootvm root#echo 65536 /proc/sys/kernel/pid_maxrootvm root#ls /proc/sys/kernel/pid_max655364)修改普通用户的最大RTC频率rootvm root#ls /proc/sys/dev/rtc/max-user-freq64rootvm root#echo 128 /proc/sys/dev/rtc/max-user-freqrootvm root#ls /proc/sys/dev/rtc/max-user-freq1285)其他一些信息rootvm root#cat /proc/cpuinfo - CPUrootvm root#cat /proc/interrupts - 中断 rootvm root#cat /proc/ioports - 设备IO端口 rootvm root#cat /proc/meminfo - 内存信息(i.e. mem used, free, swap size) rootvm root#cat /proc/partitions - 所有设备的所有分区 rootvm root#cat /proc/pci - PCI设备的信息 rootvm root#cat /proc/swaps - 所有Swap分区的信息 rootvm root#cat /proc/version - Linux的版本号4.3使用fork、exit和exec系统调用编写多进程程序【实验目的】本实验将通过编写fork等系统调用的程序,加深对系统进程及其控制的了解。【实验原理】fork后父子进程会同步运行,但父子进程的返回顺序是不确定的。设两个变量global和test来检测父子进程共享资源的情况。同时在进程退出时对exit和_exit的区别进行测试和说明。【实验内容】1fork#include #include #include #include #include #include /#include intglobal=22;charbuf=the test content!n;int main(void)int test=0,stat;pid_t pid;if(write(STDOUT_FILENO, buf, sizeof(buf) != sizeof(buf) perror(write error!); printf( fork test!n);/* fork */pid = fork(); /*we should check the error*/if (pid = -1) perror(fork); exit(0); else if (pid = 0)global+; test+;printf(global=%d test=%d Child,my PID is %dn,global,test,getpid();exit(0);/*else be the parent*/ global+=2;test+=2;printf(global=%d test=%d Parent,my PID is %dn,global,test,getpid(); exit(0);/printf(global=%d test=%d Parent,my PID is %d,global,test,getpid(); /_exit(0);编译执行,并分析结果:rootlocalhost root# ./testthe test content! fork test!global=23 test=1 Child,my PID is 2751global=24 test=2 Parent,my PID is 2750可以看出父子进程打印出了各自的进程号和对应变量的值,显然global和test在父子进程间是独立的,其各自的操作不会对对方的值有影响。将上述代码最后的两行代码替换为注释掉的_exit(0)行,重新编译,查看结果,解释原因:rootlocalhost root# ./testthe test content! fork test!global=23 test=1 Child,my PID is 2771父进程的信息没有打印出来,其原因是:_exit()函数直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;而exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,即会 清理I/O缓冲。若将上述_exit(0)改为exit(0),则肯定会有打印。另外,需要注意换行符n会引起IO的清理操作,若下面的语句printf(global=%d test%d Parent,my PID is %d,global,test,getpid(); 加上n,则调用_exit(0)的结果和调用exit(0)的结果是一样的。2vfork的特点将上述代码的pid = fork(); 改为pid = vfork();编译后运行结果如下:rootlocalhost root# ./testthe test content!fork test!global=23 test=1 Child,my PID is 2849global=25 test=3 Parent,my PID is 2848 可以看出,vfork与fork区别在于共享的资源不一样,vfork调用后,子进程先对global和test加1,父进程运行时,在其基础之上再加2,得到上述运行结果。即vfork的特点是:在调用execv或者exit前子进程对变量的修改会影响到父进程,即他们是共享的;特别注意:父进程等待子进程调用execv或exit才继续执行。则若子进程依赖父进程的进一步动作时,父进程又必须阻塞到子进程调用execv或者exit才会往下执行,此时就会造成“死锁”。读者可自己设计测试一下这种“死锁”状态。4.4使用fork和exec系统调用编写执行命令的程序【实验目的】本实验将通过编写fork和exec等系统调用的程序,加深对系统进程及其控制的了解。【实验原理】fork后调用exec族函数来调用系统命令或者程序来实现系统shell功能。【实验内容】execv函数族的使用注意点:调用execv后,程序不再返回!在上述代码基础上,在子进程的退出代码前加入如下代码:printf(global=%d test%d Child,my PID is %dn,global,test,getpid();if(execl(/bin/ps,ps,-au,NULL)0)perror(execl error!);printf(this message will never be printed!n);exit(0);编译运行后结果为:rootlocalhost root# ./testthe test content! fork test!global=23 test=1 Child,my PID is 2909USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 2719 0.0 0.6 4360 1032 pts/1 S 23:14 0:00 /bin/bashroot 2908 0.0 0.1 1340 276 pts/1 R 23:38 0:00 ./testroot 2909 0.0 0.4 2684 736 pts/1 R 23:38 0:00 ps -auglobal=25 test=3 Parent,my PID is 2908waitpid的作用是等待子进程退出并回收其资源,同时可以通过WIFEXITED等宏调用可以检测子进程退出的状态。在第一个示例fork使用的代码基础上进行修改,添加检测进程退出状态的子函数,参考代码如下:void exit_check(int stat) if (WIFEXITED(stat) printf(exit normally!the return code is: %d n,WEXITSTATUS(stat); else if (WIFSIGNALED(stat) printf(exit abnormally!the signal code is: %d n,WTERMSIG(stat); 在父进程处理global和test变量前加入如下代码:if (waitpid(pid,&stat,0) = pid) exit_check(stat); / the status of exit check编译运行后结果为:rootlocalhost root# ./testthe test content! fork test!global=23 test=1 Child,my PID is 2973exit normally!the return code is: 0global=24 test=2 Parent,my PID is 2972可以看出父进程回收了退出的子进程的资源,检测到了它的退出状态。4.5编写一个守护进程【实验目的】守护进程是Linux系统开发中很重要的知识点,本实验要求学员编写一个守护进程,通过本实验,学员可以熟悉守护进程的编写过程。【实验原理】守护进程编写的主要步骤如下:1) 将程序进入后台执行。由于守护进程最终脱离控制终端,到后台去运行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。这就是常说的“脱壳”。子进程继续函数fork()的定义如下:pid_t fork(void);2) 脱离控制终端、登录会话和进程组。开发人员如果要摆脱它们,不受它们的影响,一般使用 setsid() 设置新会话的领头进程,并与原来的登录会话和进程组脱离。3) 禁止进程重新打开控制终端。4) 重设文件权限掩码5) 关闭打开的文件描述符,并重定向标准输入、标准输出和标准错误输出的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如果不关闭,将会浪费系统资源,引起无法预料的错误。关闭三者的代码如下:for (fd = 0, fdtablesize = getdtablesize(); fd fdtablesize; fd+)close(fd);6) 改变工作目录到根目录或特定目录进程活动时,其工作目录所在的文件系统不能卸下【实验内容】守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。 1 init.c #include #include #include #include void init_daemon(void) int pid; int i; if (pid = fork() exit(0); /是父进程,结束父进程 else if (pid 0) exit( -1 ); /fork失败,退出 setsid(); /第一子进程成为新的会话组长和进程组长 /并与控制终端分离 if (pid = fork() exit(0); /是第一子进程,结束第一子进程 else if (pid 0) exit(1); /fork失败,退出 /第二子进程不再是会话组长 for(i=0; i getdtablesize(); +i) /关闭打开的文件描述符 close(i); chdir(/tmp); /改变工作目录到 /tmp umask(0); /重设文件创建掩模 return; 2 test.c #include #include void init_daemon(void); /守护进程初始化函数 int main() FILE *fp; time_t t; init_daemon(); /初始化为Daemon while(1) /每隔2秒钟向test.log报告运行状态 sleep(2); /睡眠2秒钟 if(fp = fopen(test.log,a) != NULL) t=time(0); fprintf(fp,Im here at %sn,asctime(localtime(&t) ); fclose(fp); return 0;编译:rootvm root#gcc g o test init.c test.c 执行:rootvm root#./test 查看进程:rootvm root#ps ef 从输出可以发现test守护进程的各种特性满足上面的要求。实验五 进程间通信UNIX/LINUX系统的进程间通信机构(IPC)允许在进程间交换数据。本实验的目的是了解和熟悉LINUX支持的管道机制、信号机制、消息队列通信机制及共享存储区机制。5.1 无名管道通信实验【实验目的】1、了解什么是管道2、熟悉UNIX/LINUX支持的管道通信方式【实验内容】编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话: Child 1 is sending a message! Child 2 is sending a message!父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。参考程序#include #include int pid1, pid2;main( ) int fd2;char outpipe100, inpipe100;pipe(fd); /*创建一个管道*/while (pid1 = fork( ) = = -1);if (pid1 = = 0)lockf(fd1, 1, 0); sprintf(outpipe, child 1 process is sending message!); /*把串放入数组outpipe中*/ write(fd1, outpipe, 50); /*向管道写长为50字节的串*/ sleep(5); /*自我阻塞5秒*/ lockf(fd1, 0, 0); exit(0); else while(pid2 = fork( ) = = -1); if (pid2 = = 0)lockf(fd1,1,0); /*互斥*/ sprintf(outpipe, child 2 process is sending message!); write(fd1, outpipe, 50); sleep(5); lockf(fd1, 0, 0); exit(0); else wait(NULL); /*同步*/ read(fd0, inpipe, 50); /*从管道中读长为50字节的串*/ printf(%s/n, inpipe); wait(NULL); read(fd0, inpipe, 50); printf(%s/n,inpipe); exit(0); 运行结果,延迟5秒后显示child 1 process is sending message!再延迟5秒child 2 process is sending message!5.2 有名管道通信实验【实验目的】1、掌握有名管道的创建和读写方式2、熟悉UNIX/LINUX支持的有名管道通信方式【实验内容】1. 创建有名管道2. 本进程执行循环等待数据被写入到管道中并读有名管道3. 打开有名管道并写数据到名管道参考代码:/read_fifo.c#include #include #include #include #include #include #include #define BUFFER_SIZE1024int main(int argc, char *argv) int fd; if (argc 2) fprintf(stdout, Usage: %s n, argv0); exit(1); /int open(const char *path, int oflag, .); if (fd = open(argv1, O_RDONLY) 0) fprintf(stderr, open fifo %s for reading failed: %sn, argv1, strerror(errno); exit(1); fprintf(stdout, open fifo %s for reading successed.n, argv0); char bufferBUFFER_SIZE; ssize_t n; while (1) again: /ssize_t read(int fd, void *buf, size_t count); if (n = read(fd, buffer, BUFFER_SIZE) 0) if (errno = EINTR) goto again; else fprintf(stderr, read failed on %s: %sn, argv1, strerror(errno); exit(1); else if (n = 0) fprintf(stderr, peer closed fifo.n); break; else buffern = 0; fprintf(stdout, read %d bytes from fifo: %sn, n, buffer); return 0;/ write_fifo.c#include #include #include #include #include #include #include #include #define BUFFER_SIZE1024void signal_handler(int s);int main(int argc, char *argv) int fd; if (argc 2) fprintf(stdout, Usage: %s n, argv0); exit(1); signal(SIGPIPE, signal_handler); /int open(const char *path, int oflag, .); if (fd = open(argv1, O_WRONLY) 0) fprintf(stderr, open fifo %s for writting failed: %sn, argv1, strerror(errno); exit(1); fprintf(stdout, open fifo %s for writting successed.n, argv0); char bufferBUFFER_SIZE; ssize_t n; /char *fgets(char *s, int size, FILE * stream); while (fgets(buffer, BUFFER_SIZE, stdin) again: /ssize_t write(int fd, const void *buf, size_t count); if (n = write(fd, buffer, strlen(buffer) 0) if (errno = EINTR) goto again; else fprintf(stderr, write() failed on fifo: %sn, strerror(errno);break; / end if / end while return 0;void signal_handler(int signo) fprintf(stdout, Caught signal %dn, signo);/ create_fifo.c#include #include #include #include #include int main(int argc, char *argv) if (argc 2) fprintf(stdout, Usage: %s n, argv0); exit(1); /int mkfifo(const char *path, mode_t mode); if (mkfifo(argv1, 0644) 0) fprintf(stderr, mkfifo() failed: %sn, strerror(errno); exit(1); return 0;5.3信号机制实验(一)【实验目的】1了解什么是信号。2熟悉LINUX系统中进程之间软中断通信的基本原理。【实验原理】利用signal来实现发送信号和接受信号的原理【实验内容】1编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到父进程发来的信号后,分别输出下列信息后终止: Child process 1 is killed by parent!Child process 2 is killed by parent!父进程等待两个子进程终止后,输出以下信息后终止: Parent process exit! # include# include# includeint wait_mark;void waiting(), stop();int main(int argc, char *argv) int p1, p2;signal(SIGINT,stop);while(p1 = fork() = -1);if (p1 0)/*在父进程中*/ while(p2 = fork() = = -1); if (p2 0)/*在父进程中*/ wait_mark = 1; waiting(); kill(p1, 10); kill(p2, 12); wait(NULL); wait(NULL); printf(“parent process exit!n”); exit(0); else/*在子进程2中*/ wait_mark = 1;signal(12, stop);waiting();lockf(1, 1, 0);printf(“child process 2 is killed by parent!n”);lockf(1, 0, 0);exit(0); else/*在子进程1中*/ wait_mark = 1; signal(10, stop); waiting(); lockf(1, 1, 0); printf(“child process 1 is killed by parent!n”); lockf(1, 0, 0); exit(0);void waiting() while(wait_mark != 0);void stop() wait_mark = 0;实验要求: 运行程序并分析结果。 如果把signal(SIGINT,stop)放在号和号位置,结果会怎样并分析原因。 该程序段前面部分用了两个wait(NULL),为什么? 该程序段中每个进程退出时都用了语句exit(0),为什么?5.4信号机制实验(二)【实验目的】学习signal的函数的使用【实验原理】利用signal的函数的机制来实习我们发送截获信号的功能【实验内容】修改上面的程序,增加语句signal(SIGINT,SIG_IGN)和语句signal(SIGQUIT,SIG_IGN),再观察程序执行时屏幕上出现的现象,并分析其原因。# include# include# includeint pid1, pid2;int EndFlag = 0;pf1 = 0;pf2 = 0;void IntDelete() kill(pid1, 10); kill(pid2, 12);EndFlag=1;void Int1() printf(“child process 1 is killed by parent !n”); exit(0);void Int2() printf(“child process 2 is killed by parent !n”); exit(0);main() int exitcode; signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN);while (pid1 = fork() = -1); if (pid1 = 0) signal(SIGUSR1, Int1);/signal(SIGINT, SIG_IGN);pause();exit(0); else while (pid2 = fork() = = -1); if (pid2 = 0) signal(SIGUSR2, Int2);/signal(SIGINT, SIG_IGN);pause();exit(0); else signal(SIGINT, IntDelete); waitpid(-1, &exitcode, 0);/*等待任何子进程中断或结束*/ printf(“parent process exitn”); exit(0); 实验要求:运行程序并分析结果。司机售票员问题(选做题)创建子进程代表售票员,父进程代表司机 ,同步过程如下:售票员捕捉SIGINT(代表开车),发SIGUSR1给司机,司机打印(“lets gogogo”)售票员捕捉SIGQUIT(代表停车),发SIGUSR2给司机,司机打印(“stop the bus”)司机捕捉SIGTSTP(代表车到总站),发SIGUSR1给售票员,售票员打印(“please get off the bus”)5.5共享内存通信实验【实验目的】1、掌握共享内存的创建和读写方式2、熟悉UNIX/LINUX支持的共享内存通信方式【实验内容】reader和writer两个进程通过共享内存交换数据。writer从标准输入读入字符串写入共享内存,reader把共享内存里的字符串打印到标准输出。reader和writer通过信号实现同步 思路:1. reader和writer通过信号通信必须获取对方的进程号,可利用共享内存保存双方的进程号2. reader和writer运行的顺序不确定,可约定先运行的进程创建共享内存并初始化。 pid buf5.6用消息队列编写一个客户端服务器通信的程序【实验目的】通过此实验,学员可以熟悉消息队列的概念,并能够用消息队列编写一个客户端服务器通信的程序。【实验原理】本实验需要用消息队列设计一个简易的双人聊天程序(一个服务器,两个客户端)。消息队列重点在于消息类型的匹配,客户端和服务端的“通信协议”的设计。设计思想如下:服务器端:接受客户端发来的任何信息,并根据其消息类型,转发给对应的客户端。同时,检测是否有退出标志,有则给所有的客户端发送退出标志,等待1s后,确定客户端都退出后,删除消息队列,释放空间,并退出。客户端:A和B。A给B发送信息,先发给服务器,由服务器根据自定义协议转发该消息给B。同B可以也通过服务器给A发消息。【实验方法】服务器端程序:#include #include #include #include #include #define KEY_MSG 0x101 /使用共有的IPC key#define MSGSIZE 64typedef struct /定义消息结构体:消息类型和消息数据 long mtype; char mtextMSGSIZE; msgbuf;#define LEN sizeof(msgbuf) sizeof(long)int main() int msgid; msgbuf buf1, buf2; msgid = msgget( KEY_MSG, IPC_CREAT|0666 ); while( 1 ) /无限循环,退出标志则会break msgrcv( msgid, &buf1, LEN, 1L, 0 ); /接受客户端1的消息printf( Receive client1 message: %sn, buf1.mtext ); /打印收到的消息if ( buf1.mtext0 = x | buf1.mtext0 = X ) /若是退出标志,则给2个客户端都发退出信息 strcpy( buf1.mtext, x ); buf1.mtype = 3L; msgsnd( msgid, &buf1, LEN, 0 ); buf1.mtype = 4L; msgsnd( msgid, &buf1, LEN, 0 ); break; buf1.mtype = 4L;msgsnd( msgid, &buf1, LEN, 0 ); /将客户端1的消息转发给客户端2 msgrcv( msgid, &buf2, LEN, 2L, 0 ); /接受客户端2的消息printf( Receive client2 message: %sn, buf2.mtext ); /打印收到的消息if ( buf2.mtext0 = x | buf2.mtext0 = X )/若是退出标志,则给2个客户端发退出信息 strcpy( buf2.mtext, x ); buf2.mtype = 3L; msgsnd( msgid, &buf2, LEN, 0 ); buf2.mtype = 4L; msgsnd( msgid, &buf2, LEN, 0 ); break; buf2.mtype = 3L; msgsnd( msgid, &buf2, LEN, 0 ); /将客户端2的消息转发给客户端1 sleep(1); /若退出,则先等待,以确保客户端程序退出 msgctl( msgid, IPC_RMID, NULL ); /删除消息队列,释放空间 exit(0);客户端1:#include #include #include #include #include #define KEY_MSG 0x101#define MSGSIZE 64typedef struct long mtype; char mtextMSGSIZE; msgbuf;#define LEN sizeof(msgbuf) sizeof(long)int main() int msgid; msgbuf buf1, buf2; msgid = msgget( KEY_MS
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- XX学校智慧校园应用方案
- 汽车配件采购与维修方案
- 采矿工程师职业技能提升总结
- 文化艺术团体团建活动合同
- 2024-2025学年高中地理选择性必修1中图中华地图版(单元测试)试题合集
- 10KV铁塔施工应急预案方案
- 幼儿园新入园免疫接种制度解析
- 体育科研实验室安全应急预案
- 交通运输服务岗位招聘面试题与参考回答(某世界500强集团)2025年
- 离婚协议书与心理咨询的重要性
- 学前儿童听说游戏活动(学前儿童语言教育活动课件)
- 环境艺术设计发展现状分析报告
- 培训机构校长竞聘
- 企业微信指导手册管理员版
- 孙子兵法中的思维智慧智慧树知到期末考试答案2024年
- 临床试验方案偏离处理流程
- 乳腺癌一病一品
- JTT327-2016 公路桥梁伸缩装置通用技术条件
- 定量化学分析实验室的整理、整顿、清扫
- 酒精戒断综合症的护理
- 《美容营养学》课件-第八章 第二节 美胸丰胸营养与膳食
评论
0/150
提交评论