前言:为了应付作业网上找的,看得懂但是不会写就很无奈orz,干脆就多加了写注解,并且修改了一下,这个是参考博客
一、设计内容
实现一个模拟shell:编写三个不同的程序:cmd1.c, cmd2.c, cmd3.c,每个程序输出一句话,分别编译成可执行文件cmd1, cmd2, cmd3。然后再编写一个程序,模拟shell程序的功能,能根据用户输入的字符串(表示相应的命令名),去为相应的命令创建子进程并让它去执行相应的程序,而父进程则等待子进程的结束,然后再等待接收下一条命令。如果接收到的命令为exit,则父进程结束,如果接收到无效命令,则显示”command not found”,继续等待。
实现一个管道通信程序:由父进程创建一个管道,然后再创建3个子进程,并由这三个子进程用管道与父进程之间进行通信:子进程发送信息,父进程等三个子进程全部发完消息后再接收信息。通信的具体内容可根据自己的需要随意设计,要求能够实验阻塞型读写过程的各种情况,并要求实现进程间对管道的互斥访问。运行程序,观察各种情况下,进程实际读写的字节数以及进程阻塞唤醒情况。
利用linux的消息队列通信机制实现两个线程间的通信:编写程序创建两个线程:sender线程和receive线程,其中sender运行函数sender(),他创建一个消息队列,然后,循环等待用户通过终端输入一串字符,将这串字符通过消息队列发送给receiver线程,直到用户输入”exit”为止;最后,它向receiver线程发送消息”end”,并且等待receiver的应答,直到应答消息后,将接收到的应答消息显示在终端上,删除相关消息队列,结束程序运行。receiver线程运行receive(),它通过消息队列接收来自sender的消息,将消息显示在终端屏幕,直到接收到”end”的消息后它向sender发送一个应答消息”over”,结束程序运行。使用无名信号量实现两个线程之间的同步与互斥。
利用linux的共享内存通信机制实现两个进程间的通信:编写程序sender,它创建一个共享内存,然后等待用户通过终端输入一串字符,并将这串字符通过共享内存发送给receiver,最后,等待receiver应答,等到应答消息后,它接收到的应答消息显示在终端屏幕上,删除共享内存,结束程序运行。编写receiver程序,它通过共享内存接收来自sender的消息,将消息显示在终端屏幕上,然后再通过该共享内存向sender发送一个应答消息”over”,结束程序的运行。使用有名信号量或System V信号量实现两个进程对共享内存的互斥使用。
二、代码实现
(1)实现一个模拟shell(感觉这个实现的很假orz)
1-1 编写cmd1.c cmd2.c cmd3.c,内容随意
//cmd1.c
#include
int main()
{
printf("this is the cmd1111111\n");
return 0;
}
//cmd2.c
#include
int main()
{
printf("this is the cmd222222\n");
return 0;
}
//cmd3.c
#include
int main()
{
printf("this is the cmd333333\n");
return 0;
}
1-2 编写Makefile(clear下面的三个rm的前面是tab,不是单纯空行)
all: myshell cmd1 cmd2 cmd3
.PHONY : clean
myshell.o : myshell.c
clean :
rm cmd1 cmd2 cmd3
rm myshell
rm *.o
1-3 编写myshell.c
#include
#include
#include
#include
#include
#include
#define CMD_COLLECTION_LEN 4 //命令数组的长度(有哪几个命令)
//command index
#define INVALID_COMMAND -1 //无效命令返回-1
#define EXIT 0
#define CMD_1 1
#define CMD_2 2
#define CMD_3 3
//bool
#define TRUE 1
char *cmdStr [CMD_COLLECTION_LEN ]= {"exit","cmd1","cmd2","cmd3"};
//对比所有命令参数,如果有一样的,就返回对应数字,用于后面执行
int getCmdIndex(char *cmd)
{
int i;
for(i=0;i
{
if (strcmp(cmd,cmdStr[i])==0)
{
return i;
}
}
return -1;
}
/*
创建子进程,这里使用了execl,后面的l表示list,即参数列表。
第一参数为path(要执行的文件路径),最后一个参数必须是NULL,
中间的为要传送的参数
*/
void myFork(int cmdIndex)
{
pid_t pid;
if((pid = fork())<0)
{
printf("创建子进程错误\n");
exit(0);
}
else if (pid == 0)
{
int execl_status = -1;
printf("子进程正在运行\n");
switch(cmdIndex)
{
case CMD_1:
execl_status = execl("./cmd1","cmd1",NULL);
break;
case CMD_2:
execl_status = execl("./cmd2","cmd2",NULL);
break;
case CMD_3:
execl_status = execl("./cmd3","cmd3",NULL);
break;
default:
printf("无此命令!!!\n");
break;
}
if(execl_status<0)
{
printf("创建错误\n");
exit(0);
}
printf("运行完毕!\n");
exit(0);
}
else{
return;
}
}
//运行cmd
void runCMD(int cmdIndex)
{
switch(cmdIndex)
{
case INVALID_COMMAND:
printf("Command Not Found \n"); //没有找到该命令
break;
case EXIT: //exit命令返回0
exit(0);
break;
default:
myFork(cmdIndex); //创建子进程运行
break;
}
}
int main()
{
pid_t pid;
char cmdStr[30]; //命令数组(最长30)
int cmdIndex; //用于显示运行哪个数据
while(TRUE)
{
printf("\n输入命令\n>>:");
scanf("%s",cmdStr);
cmdIndex = getCmdIndex(cmdStr);
runCMD(cmdIndex); //根据数字运行不同的cmd
wait(0);
}
}
1-4 编译:
make
1-5 运行截图示例:
(2)实现一个管道通信程序
2-1 编写pipe_communication.c文件
#include
#include
#include
#include
#include
#define READ 0 //filedes[0]用于读
#define WRITE 1 //filedes[1]用于写
int main() {
/*
函数原型:pipe(int filedes[2])
参数含义:filedes[0]对应管道读端,filedes[1]对应管道写端
功能:pipe在内存缓冲区中创建一个管道,并将读写该管道的一对文件描述符保存在filedes所指数组中
返回值:成功返回0,失败返回-1
*/
int filedes[2];
pid_t pid1,pid2,pid3;//pid_t本质就是int
char buf[256]; //用作read的缓冲区,保存读取的字符
pipe(filedes); //创建无名管道
if((pid1 = fork()) == -1) { //创建子进程
printf("fork error(pid1)!\n");
exit(1);
}
if(pid1 == 0) {
sleep(1); //挂起一秒
printf("正在产生子进程pid1:%d\n",getpid());
//子进程向父进程写数据,关闭管道的读端
close(filedes[READ]);
write(filedes[WRITE], "pid111111\n", strlen("pid111111\n"));
exit(0);
}
if ((pid2 = fork()) == -1) {
printf("fork error(pid2)\n");
exit(1);
}
if (pid2 == 0) {
sleep(1);
printf("正在产生子进程pid2:%d\n",getpid());
close(filedes[READ]);
write(filedes[WRITE], "pid222222\n", strlen("pid222222\n"));
exit(0);
}
if ((pid3 = fork()) == -1) {
printf("fork error(pid3)\n");
exit(1);
}
if (pid3 == 0) {
sleep(1);
printf("正在产生子进程pid3:%d\n",getpid());
close(filedes[READ]);
write(filedes[WRITE], "pid333333\n", strlen("pid333333\n"));
exit(0);
}
else {
//waitpid()会暂时停止目前进程的执行,直到有信号来或者子进程结束
pid1 = waitpid(pid1, NULL, WUNTRACED);
pid2 = waitpid(pid2, NULL, WUNTRACED);
pid3 = waitpid(pid3, NULL, WUNTRACED);
printf("main pid: %d\n",getpid());
printf("wait pid: %d %d %d 返回信息\n",pid1,pid2,pid3);
/*父进程从管道读取子进程写的数据,关闭管道的写端*/
close(filedes[WRITE]);
//read():读取的数据保存在缓冲区buf
read(filedes[READ], buf, sizeof(buf));
printf("3个子进程传输的数据为:\n%s\n", buf);
}
return 0;
}
2-2 Makefile
pipe_communication : pipe_communication.o
.PHONY : clean
clean:
rm *.o
rm pipe_communication
2-3 make编译
make
2-4 运行
(3)利用linux的消息队列通信机制实现两个线程间的通信
3.1 代码(message_queue.c)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TRUE 1
#define BUF_SIZE 255 //缓冲大小
//S_IRUSR|S_IWUSR:允许文件创建者读取|写入(感觉就是赋予权限)
#define PERM S_IRUSR|S_IWUSR
#define KEY_NUM 1000
typedef struct msgbuf msgbuf;
//消息缓冲区结构体
struct msgbuf
{
long mtype; //消息类型
char mtext[BUF_SIZE + 1]; //256,消息正文
};
//sem_t 信号量的数据类型,本质是个长整型的数
sem_t full;
sem_t empty;
sem_t mutex;
//pthread_t 声明线程,类似于pid_t
pthread_t write_pid;
pthread_t read_pid;
/*
IPC对象键值,每个IPC对象都关联着一个唯一的长整型的键值,
不同进程通过相同相同的键值可访问到同一个IPC对象。
若为0,创建一个新的消息队列,若大于0(通常通过ftok()生成的)
*/
key_t key;
//messageid
int msgid;
struct msgbuf msg;
//初始化
void Init()
{
/*
函数:sem_init(sem_t *sem,int pshared,unsigned int value)
参数:sem表示一个信号量,pashared(0-信号量被进程内的线程共享,非0-进程之间共享),value信号量初始值
*/
sem_init(&full,0,0);
sem_init(&empty,0,1);
sem_init(&mutex,0,1);
key = KEY_NUM;//给键值赋值
//创建消息队列
/*
函数:msgget(key_t key,int smgflag)。新教材p107
参数:key(消息队列键值,具体看上面)。msgflg(对消息队列的访问权限和控制命令的组合)
功能:如果参数msgflag为IPC_CREATE,则semget()新创建一个消息队列并返回其标识符,
或返回具有相同键值的已存在的消息队列的标识符
返回值:成功返回消息队列的标识符,失败返回-1
*/
if((msgid = msgget(key,PERM|IPC_CREAT)) == -1)
{
fprintf(stderr, "Create Message Queue Error %s\n",strerror(errno) );
exit(EXIT_FAILURE);
}
}
//读取信息
void * ReadProcess(void *arg)
{
msgbuf msg;
//init msg
msg.mtype = 1;
while(TRUE)
{
//sem_wait阻塞进程,直到信号量>=0,解除阻塞后sem值-1,表示公共资源使用后减少
sem_wait(&full);
sem_wait(&mutex);
//从消息队列获取内容
/*
函数:ssize_t msgrcv(int msqid,struct msgbuf *msgp,size_t msgsz,long msgtyp,int msgflg)
参数:msqid(消息队列的标识符),
msgp(用来存放接受到的消息内容的缓冲区指针)
msgsz(消息正文的长度),
msgtyp(接收的消息类型,0-接受消息队列中第一个消息,>0接收第一个类型为msgtyp的消息,<0接收第一个类型小于等于msgtyp的绝对值的消息)
msgflg(0-没有可接收的消息时,调用进程阻塞。其他略)
返回值:接收成功,返回实际接收到的消息正文的字节数,否则返回-1
*/
msgrcv(msgid,&msg,sizeof(msgbuf),1,0);//接收类型为1的消息(即mtype=1)
//如果接受到"end"
if(strcmp(msg.mtext,"end") == 0)
{
msg.mtype = 2;
strncpy(msg.mtext,"over",BUF_SIZE);
//msgsnd用于向标识符为msqid的消息队列发送一个消息(即发送over)
msgsnd(msgid,&msg,sizeof(msgbuf),0);
sem_post(&empty);
sem_post(&mutex);
break;
}
//print message
printf("Receive: %s\n\n",msg.mtext);
//sem_post增加信号量的值,当有线程阻塞在这个信号量时,该函数会使其中一个线程不在阻塞
sem_post(&empty);
sem_post(&mutex);
}
exit(EXIT_SUCCESS);
}
void * WriteProcess(void *arg)
{
char input[50];
msgbuf msg;
msg.mtype = 1;
while (TRUE)
{
sem_wait(&empty);
sem_wait(&mutex);
sleep(0.1);
printf("Sent: Please Input the message you want to send.\n");
scanf("%s",input);
if(strcmp(input,"exit") == 0)
{
strncpy(msg.mtext,"end",BUF_SIZE);
msgsnd(msgid,&msg,sizeof(msgbuf),0);
sem_post(&full);
sem_post(&mutex);
break; //输出exit后,转化为end,然后跳出while循环
}
strncpy(msg.mtext,input,BUF_SIZE);
msgsnd(msgid,&msg,sizeof(msgbuf),0);
printf("Sent: %s\n",msg.mtext );
//semaphore
sem_post(&full);
sem_post(&mutex);
}
// Clear Node
memset(&msg,'\0',sizeof(msgbuf));
// Block ,waiting for msg with type = 2
msgrcv(msgid,&msg,sizeof(msgbuf),2,0);
printf("Sent:%s\n",msg.mtext );
//Remove Message Queue
if( msgctl (msgid,IPC_RMID,0) == -1)
{
fprintf(stderr, "Remove Message Queue Error%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
int main()
{
Init();
pthread_create(&write_pid,NULL,WriteProcess,NULL);
pthread_create(&read_pid,NULL,ReadProcess,NULL);
//waiting for the thread end
pthread_join(write_pid,NULL);
pthread_join(read_pid,NULL);
printf("Main Function End...\n");
return 0;
}
3-2 Makefile
message_queue : message_queue.o
cc -pthread -o message_queue message_queue.o
.PHONY : clean
clean:
rm message_queue
rm *.o
3-3 运行
make
3-4 截图
(4)利用linux的共享内存通信机制实现两个进程间的通信
4-1 Makefile
all : init sender receiver
.PHONY : clean
init : init.o common.o
cc -pthread -o init init.o common.o
sender : sender.o common.o
cc -pthread -o sender sender.o common.o
receiver : receiver.o common.o
cc -pthread -o receiver receiver.o common.o
init.o : common.h
sender.o : common.h
receiver.o : common.h
clean :
rm init
rm receiver
rm sender
rm *.o
4-2 common.h(定义一些用到的头文件)
#ifndef _COMMON_H_
#define _COMMON_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const char *MUTEX_NAME= "mutex_shm";
static const char *FULL_NAME = "full_shm";
//static const char * PATH_NAME = "tmp/shmtemp";
//constant define
#define SHM_SIZE 1024 //输入的最大长度
#define KEY_NUM 1000
//返回共享内存的标识符
int GetShmId(key_t key);
void SemInit();
void SemDestroy();
void P(sem_t *sem);
void V(sem_t *sem);
#endif
4-3 common.c 一些公用的函数,如初始信号量等
#include "common.h"
/*
key_t GetKey(const char * pathname)
{
//int fd = open(pathname,O_CREAT,0666);
int fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
if(fd < 0)
{
perror("open file error");
return -1;
}
close(fd);
return ftok(pathname,0);
}
*/
int GetShmId(key_t key)
{
int shmid;
shmid = shmget(key,SHM_SIZE,IPC_CREAT|0666);
if(shmid < 0)
{
perror("Receiver: Shmget Error");
exit(EXIT_FAILURE);
}
return shmid;
}
/*
* create mutex + semaphore
* init those value
*/
void SemInit()
{
/*
* Funtion Prototype:
*
* sem_t *sem_open(const char *name, int oflag,
* mode_t mode, unsigned int value);
*
* name : MUTEX_NAME "mutex_shm"
* oflag : O_CREAT Create and initialize it if not exist
* mode_t : file perssion -rw-r--r--
* value : 1
*/
if((sem_open(MUTEX_NAME,O_CREAT,0644,1)) < 0)
{
perror("sem_open");
exit(EXIT_FAILURE);
}
if((sem_open(FULL_NAME,O_CREAT,0644,0)) < 0){
perror("sem_open");
exit(EXIT_FAILURE);
}
}
/*
* close and unlink semaphore that we crated
*/
void SemDestroy()
{
sem_t * mutexPtr = sem_open(MUTEX_NAME,O_CREAT);
sem_t * fullPtr= sem_open(FULL_NAME,O_CREAT);
/* Destroy mutex */
sem_close(mutexPtr); // int sem_close(sem_t *sem);
sem_unlink(MUTEX_NAME); // int sem_unlink(const char *name);
/* Destory full*/
sem_close(fullPtr);
sem_unlink(FULL_NAME);
}
void P(sem_t *semPtr)
{
sem_wait(semPtr); //int sem_wait(sem_t *sem);
}
void V(sem_t *semPtr)
{
sem_post(semPtr); //int sem_post(sem_t *sem);
}
4-4 sender.c (传送消息到内存区域)
#include "common.h"
//key
key_t key;
//shared memory
int shmid;
char * shmptr;
char input[SHM_SIZE];
//semaphore
sem_t * full;
sem_t * mutex;
//semaphore
void Init()
{
key = KEY_NUM; //init key
shmid = GetShmId(key); // init shared memory
shmptr = shmat(shmid,NULL,0); // attach segement to vitural ...?
//semaphore init
full = sem_open(FULL_NAME,O_CREAT);
mutex = sem_open(MUTEX_NAME,O_CREAT);
}
void SaveMessage()
{
P(mutex);
strcpy(shmptr,input);
V(mutex);
V(full);
}
int main(int argc, char const *argv[])
{
Init();
/*waiting for user to input message*/
scanf("%s",input); //input message from shell
// TODO input a whole line
SaveMessage();
printf("Sender: Process End\n");
return 0;
}
4-5 receiver.c(从内存获取消息)
#include "common.h"
//key
key_t key;
//shared memory
int shmid;
char * shmptr;
char result[SHM_SIZE];
//semaphore
sem_t * full;
sem_t * mutex;
//semaphore
void Init()
{
key = KEY_NUM; //init key
shmid = GetShmId(key); // init shared memory
shmptr = shmat(shmid,NULL,0); // attach segement to vitural ...?
//semaphore init
full = sem_open(FULL_NAME,O_CREAT);
mutex = sem_open(MUTEX_NAME,O_CREAT);
}
void ReadMessage()
{
P(full);
P(mutex);
strcpy(result,shmptr);
V(mutex);
}
int main(int argc, char const *argv[])
{
Init();
/*waiting for user to input message*/
ReadMessage();
printf("Receiver : message is %s\n",result);
SemDestroy();
printf("Receiver : Process End \n");
return 0;
}
4-6 init.c(初始化)
#include "common.h"
int main(int argc, char const *argv[])
{
key_t key;
int semid; //semaphore id
int shmid; //shared memory id
/* Create key*/
key = KEY_NUM;
/* Initialize Semaphore*/
SemInit();
/* TODO Initialize Shared Memory*/
GetShmId(key);
printf("End of initialize\n");
return 0;
}
4-7 编译测试
make