利用C语言编写一个微型命令解释程序minishell.c,该程序可接收并解释以下命令:
(1) dir 列出当前目录
(2) cop file1 file2 拷贝文件
(3) era filename 删除文件
(4) disp string 显示字符串
(5) end 结束,退出
要求:
(1)检查命令的合法性,如果有错误,显示出错信息,等待重新输入;
(2)命令前后有空格示为合法命令。
- gets() 读入字符串,直到回车结束,但是不包括回车
- strcspn(str1,str2) 返回str1开头连续不包含str2的字符串
- strncpy(str1,str2,n) 将str2字符串的前n个复制到str1中
- strcmp(str1,str2) 将str1和str2进行比较
- 返回值小于0,str1 < str2
- 等于零 str1 == str2
- 大于零 str1 > str2
- system(command) 执行command
#define true 1 #define false 0 #include实验代码#include #include int main(){ char cmdl[80]; char *scwt[] = {"exit","dir","time"}; static int cmdnum = 3; // 可用命令数 char cmd[80]; int j,n; while(true){ printf("Please input command:"); gets(cmdl); // 取命令行输入 n = strcspn(cmdl," ");// 取命令部分 if(n > 0 || strlen(cmdl) > 0){ strncpy(cmd,cmdl,n); // 构成字符串 cmd[n] = ' '; for(j = 0;j < cmdnum;j++){ if(strcmp(cmdl,scwt[j]) == 0) break; } if(j == 0) exit(0); if(j < cmdnum){ system(cmdl); continue; } printf("Bad command!n"); } } }
暂时先不放出来
实验内容
1、进程的创建
编写一段程序,使用系统调用fork()创建两个子进程。让父进程显示字符串‘Parent:’;两个子进程分别显示字符串‘Child1:’和‘Child2:’。多次运行此程序,观察屏幕显示的结果,并分析原因。
2、进程控制
修改已编写的程序,将输出多次重复的一句话,观察程序执行时在屏幕上显示的结果,并分析原因。
若在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察屏幕显示的结果,并分析原因。
fork 的测试
fork的功能描述:
- fork()通过复制进程来创建一个新进程。子进程类似于当前进程的副本,但是:
- 子进程有自己唯一的进程ID,而且这个进程的PID与任何进程的ID都不相同
- 子进程的父进程ID等于父进程的进程ID
- 返回值:
- 如果成功,在父进程中返回子进程的PID,在子进程中返回0
- 失败了,在父进程中返回-1,没有创建子进程
crescent_p@CP:~/Program/Experiment2$ gcc -o fork1 forkTest1.c crescent_p@CP:~/Program/Experiment2$ ./fork1 fork test fork test
#include#include int main(){ fork(); printf("fork testn"); return 0; }
分析:为什么输出了两次?
- 程序运行开始就创建了一个进程
- 当进程执行fork之后,创建了一个子进程
- 子进程的代码是父进程代码的副本
- 子进程的代码执行进度和父进程创建子进程时代码执行进度一致
测试二:
#include#include int main(){ pid_t pid; pid = fork(); // 父进程 if(pid > 0){ printf("I am fathern"); } if(pid == 0){ printf("I an sonn"); } if(pid < 0){ printf("fork errorn"); } printf("main overn"); return 0; }
分析为什么结果不一样:
- 为什么又会出现son又会出现father?
- 在父进程中,fork()返回子进程的PID,大于0,因此会执行第一个if
- 在子进程中,fork()返回子进程的PID,等于0,因此会执行第二个if
- 为什么两次执行这个可执行文件结果不一样?
- 进程的异步性,我们设父进程为p1,子进程为p2
- 第一个执行:
- p1执行完第一个if,就轮到p2上处理机运行
- 第二个执行:
- p1执行完,才轮到p2上处理机
测试三:
#include#include int main(){ printf("执行到fork函数之前其进程为 %d,其父进程为 %dn",getpid(),getppid()); sleep(1); fork(); printf("这个进程id为:%d,它的父进程为%dn",getpid(),getppid()); return 0; }
测试四:
#include实验代码#include int main(){ pid_t p1,p2; while((p1 = fork()) == -1); // 子进程 if(p1 == 0) { puts("b"); } else { puts("a"); } return 0; }
暂时先不放出来
测试 pipe():
#include#include #include #include int main(){ pid_t pid; int fd[2]; char buf[50],s[50]; // 创建了管道,半双工 pipe(fd); while((pid = fork()) == -1); // 子进程 if(pid == 0){ sprintf(buf,"Child is sending message!"); // fd[1] 写入管道 write(fd[1],buf,50); // exit 在退出前,还会把文件缓冲区中的内容写回文件 // 退出进程,其实是变成了一个僵尸进程 exit(0); }else{ // 进程一旦调用了wait,就立即阻塞自己 // 由wait自动分析是否当前进程的某个子进程已经退出 // 如果让它找到了这样一个已经变成僵尸的子进程 // wait就会收集这个子进程的信息,并把它彻底销毁后返回 // 如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。 wait(0); // fd[0] 读取管道,将管道中的内容读入到s中 read(fd[0],s,50); printf("%sn",s); exit(0); } return 0; }
一个管道
#include#include #include #include #include int main(){ // 通道数组 int fd[2]; pid_t pid; // 一定要先创建管道,否则fork出的子进程也会键管道 pipe(fd); while((pid = fork()) == -1); // 子进程 if(pid == 0){ char str[100] = {0}; printf("child process: input string:n"); scanf("%s",str); // 写入到管道 write(fd[1],str,strlen(str)); exit(0); // 父进程 }else{ wait(0); char buf[30] = {0}; // 从管道中读入到buf read(fd[0],buf,30); printf("parent process:n %sn",buf); exit(0); } return 0; }
双通道:
#include实验代码#include #include #include #include int main(){ // 两个管道 int fd1[2],fd2[2]; // 创建两个通道 pipe(fd1),pipe(fd2); pid_t pid; while((pid = fork()) != -1); // 子进程 if(pid == 0){ char read_buf1[100] = {0}; char write_buf1[100] = {0}; printf("child process write:n") scanf("%s",write_buf1); // 写入 write(fd1[1],write_buf1,strlen(write_buf1)); // 休眠 sleep(10); // 读入 read(fd2[0],read_buf1,100); printf("child process read:n %sn",read_buf1); exit(0); // 父进程 }else{ sleep(5); char read_buf2[100] = {0}; char write_buf2[100] = {0}; read(fd1[0],read_buf2,100); printf("parent process read:n %sn",read_buf2); printf("parent process write:n") scanf("%s",write_buf2); // 写入 write(fd2[1],write_buf2,strlen(write_buf2)); exit(0); } return 0; }
暂时先不放出来
#include实验代码#include #include #include #include #include int main(){ // 共享内存标识符 int shmid; // 创建共享内存 if((shmid = shmget((key_t)0x5005,1024,0666|IPC_CREAT)) == -1){ printf("shmat(0x5005) failedn"); return -1; } // 用于指向共享内存的指针 char *ptext = 0; // 将共享内存连接到当前进程的地址空间,由ptext指向它 ptext = (char *)shmat(shmid,0,0); // 操作本程序的ptext指针,就是操作共享内存 printf("写入前:%sn",ptext); sprintf(ptext,"本程序的进程号是:%d",getpid()); printf("写入后:%sn",ptext); // 把共享内存从当前进程中分离 shmdt(shmid); // 删除共享内存 if(shmctl(shmid,IPC_RMID,0) == -1){ printf("shmctl(0x5005) failedn"); return -1; } }
#include消息队列 实验预备内容和示例程序#include #include #include #include #include int main(){ // 共享内存标识符 int shmid; // 创建共享内存 if((shmid = shmget((key_t)0x5005,1024,0666|IPC_CREAT)) == -1){ printf("shmat(0x5005) failedn"); return -1; } // 用于指向共享内存的指针 char *ptext = 0; // 将共享内存连接到当前进程的地址空间,由ptext指向它 ptext = (char *)shmat(shmid,0,0); printf("Please enter the name of the contactn"); char name[50]; scanf("%s",name); printf("Please enter the contact's phone numbern"); long long tel; scanf("%lld",&tel); // 操作本程序的ptext指针,就是操作共享内存 // 发送消息到共享内存 sprintf(ptext,"联系人: %st联系电话: %lldn",name,tel); // 从共享内存读东西 printf("共享内存中读取数据:n%sn",ptext); // 把共享内存从当前进程中分离 shmdt(ptext); // 删除共享内存 if(shmctl(shmid,IPC_RMID,0) == -1){ printf("shmctl(0x5005) failedn"); return -1; } }
message.h
#ifndef MESSAGE_H
#define MESSAGE_H
struct mymsgbuf
{
long mtype;
char mtext[256];
};
#endif
msg_server.c
#include#include #include #include #include #include #include "message.h" int msqid = -1; // 软终端 void sig_handle(int signo){ if(msqid != -1) msgctl(msqid,IPC_RMID,NULL); // 删除 printf("server quit...n"); exit(0); } int main(){ struct mymsgbuf msgbuf; int left,right; char c; int length; if((msqid = msgget(999,0666)) != -1){ msgctl(msqid,IPC_RMID,NULL); // 删除 } if((msqid = msgget(999,IPC_CREAT|0666)) == -1 ){ printf("error:getmsgn"); exit(1); } signal(SIGINT,sig_handle); // 注册软中断 for(;;){ if(msgrcv(msqid,&msgbuf,256,1L,0) == -1){ printf("error:msgrcvn"); exit(1); } length = strlen(msgbuf.mtext); left = 0; right = length - 1; while (left < right) { c = msgbuf.mtext[left]; msgbuf.mtext[left] = msgbuf.mtext[right]; msgbuf.mtext[right] = c; left++; right--; } msgbuf.mtext[length] = ' '; msgbuf.mtype = 2; // 类型改成2 if(msgsnd(msqid,&msgbuf,256,0) == -1){ printf("error:msgsnd"); exit(1); } } msgctl(msqid,IPC_RMID,NULL); exit(0); }
msg_client.c
#include#include #include #include #include #include #include "message.h" int main(){ struct mymsgbuf msgbuf; int msqid; if((msqid = msgget(999,0666)) == -1){ printf("Server is not runningn"); exit(1); } printf("Input a line:"); scanf("%s",msgbuf.mtext); msgbuf.mtype = 1; if(msgsnd(msqid,&msgbuf,256,0)==-1){ printf("error:msgsndn"); exit(1); } if(msgrcv(msqid,&msgbuf,256,2L,0) == -1){ printf("error:msgrcvn"); exit(1); } printf("The reversed line is :%sn",msgbuf.mtext); exit(0); }
msgsend.c
#include#include #include #include #include #include #include #include // 消息类型,用枚举 enum MT{MI=1,MD,MS,MSTU,MEMP}; #define QUIT 0 #define MSG_MAX_LEN 1024 #define NAME_LEN 48 typedef struct Msg{ int msgtype; //message 的类型 char msg[MSG_MAX_LEN]; // message的内容 }Msg; typedef struct Stu{ int id; char name[NAME_LEN]; int s[3]; // 三门成绩 }Stu; typedef struct Emp { int id; char name[NAME_LEN]; float salary; }Emp; void menu(void){ printf("***消息队列发送测试界面***n"); printf("*** %d.int类型的数据n",MI); printf("*** %d.double类型的数据n",MD); printf("*** %d.字符串n",MS); printf("*** %d.学生信息n",MSTU); printf("*** %d.员工信息n",MEMP); printf("*** %d.退出n",QUIT); printf(">>>"); } size_t readInt(Msg *msg){ // 设置消息的类型 msg->msgtype = MI; printf("input integer:"); // 将消息读入到消息结构体的msg属性中 scanf("%d",(int*)msg->msg); return sizeof(int); } size_t readDouble(Msg *msg){ msg->msgtype = MD; printf("input double:"); scanf("%lf",(double*)msg->msg); return sizeof(double); } size_t readString(Msg *msg){ msg->msgtype = MS; printf("input string:"); // 读取换行 scanf("%*c"); gets(msg->msg); // gets不安全 // 加上 return strlen(msg->msg)+1; } size_t readStu(Msg *msg){ msg->msgtype = MSTU; // 强转 Stu *pstu = (Stu*)msg->msg; printf("input stu's id:"); scanf("%d",&pstu->id); printf("input stu's name:"); scanf("%s",pstu->name); printf("input three score:"); scanf("%d %d %d",&pstu->s[0],&pstu->s[1],&pstu->s[2]); return sizeof(Stu); } size_t readEmp(Msg *msg){ msg->msgtype = MEMP; Emp *pe = (Emp*)msg->msg; printf("input emp's id:"); scanf("%d",&pe->id); printf("input emp's name:"); scanf("%s",pe->name); printf("input emp's salary:"); scanf("%f",&pe->salary); return sizeof(Emp); } int main(){ key_t keyid = ftok(".",250); if(keyid == -1){ perror("ftok"); return -1; } // 创建消息队列 int msgid = msgget(keyid,IPC_CREAT|0666); if(msgid == -1){ perror("msgget"); return -1; } // 创建消息 Msg msg = {}; bool run = true; size_t msgsz = 0; while(run){ menu(); int opt = 0; scanf("%d",&opt); switch (opt) { case MI:msgsz = readInt(&msg);break; case MD:msgsz = readDouble(&msg);break; case MS:msgsz = readString(&msg);break; case MSTU:msgsz = readStu(&msg);break; case MEMP:msgsz = readEmp(&msg);break; case QUIT:run = false;break; default: printf("error operation!n"); } // msgsz是消息的长度,不包含消息类型的长度 int ret = msgsnd(msgid,&msg,msgsz,0); //0:阻塞 IPC_NOWAIT 不阻塞的 if(ret == -1){ perror("msgend"); run = false; } } return 0; }
msgrecive.c
#include实验代码#include #include #include #include #include #include #include // 消息类型,用枚举 enum MT{MI=1,MD,MS,MSTU,MEMP,FIRST,ABSL}; #define QUIT 0 #define MSG_MAX_LEN 1024 #define NAME_LEN 48 typedef struct Msg{ int msgtype; //message 的类型 char msg[MSG_MAX_LEN]; // message的内容 }Msg; typedef struct Stu{ int id; char name[NAME_LEN]; int s[3]; // 三门成绩 }Stu; typedef struct Emp { int id; char name[NAME_LEN]; float salary; }Emp; void menu(void){ printf("***消息队列接受测试界面***n"); printf("*** %d.int类型的数据n",MI); printf("*** %d.double类型的数据n",MD); printf("*** %d.字符串n",MS); printf("*** %d.学生信息n",MSTU); printf("*** %d.员工信息n",MEMP); printf("*** %d.接受第一条消息 n",FIRST); printf("*** %d.接受指定消息小于绝对值最小的消息 n",ABSL); printf("*** %d.退出n",QUIT); printf(">>>"); } void showMsg(Msg *msg){ switch (msg->msgtype) { case MI:printf("recv:%dn",*(int*)msg->msg);break; case MD:printf("recv:%fn",*(double*)msg->msg);break; case MS:printf("recv:%sn",msg->msg);break; case MSTU:{ Stu *ps = (Stu *)msg->msg; printf("recv:%d %s %d %d %dn",ps->id,ps->name,ps->s[0],ps->s[1],ps->s[2]); break; } case MEMP:{ Emp *pe = (Emp *)msg->msg; printf("recv:%d %s %fn",pe->id,pe->name,pe->salary); break; } } } int main(){ key_t keyid = ftok(".",250); if(keyid == -1){ perror("ftok"); return -1; } // 创建消息队列 int msgid = msgget(keyid,IPC_CREAT|0666); if(msgid == -1){ perror("msgget"); return -1; } // 创建消息 Msg msg = {}; bool run = true; size_t msgsz = 0; // 读到的消息类型 int msgtype = 0; while(run){ menu(); int opt = 0; scanf("%d",&opt); if(opt >= MI && opt <=MEMP){ msgtype = opt; }else if(opt == FIRST){ msgtype = 0; }else if(opt == ABSL){ printf("input masgtype(<0):"); scanf("%d",&msgtype); if(msgtype > 0) msgtype *= -1; }else if(opt == 0){ run = false; break; } int ret = msgrcv(msgid,&msg,MSG_MAX_LEN-1,msgtype,IPC_NOWAIT); if(ret == -1){ if(errno == EAGAIN){ printf("no this type message"); }else{ perror("msgend"); run = false; } }else{ // 显示消息内容 showMsg(&msg); } } return 0; }
暂时先不放出来



