一、设计内容

  1. 实现一个模拟shell:编写三个不同的程序:cmd1.c, cmd2.c, cmd3.c,每个程序输出一句话,分别编译成可执行文件cmd1, cmd2, cmd3。然后再编写一个程序,模拟shell程序的功能,能根据用户输入的字符串(表示相应的命令名),去为相应的命令创建子进程并让它去执行相应的程序,而父进程则等待子进程的结束,然后再等待接收下一条命令。如果接收到的命令为exit,则父进程结束,如果接收到无效命令,则显示”command not found”,继续等待。

  2. 实现一个管道通信程序:由父进程创建一个管道,然后再创建3个子进程,并由这三个子进程用管道与父进程之间进行通信:子进程发送信息,父进程等三个子进程全部发完消息后再接收信息。通信的具体内容可根据自己的需要随意设计,要求能够实验阻塞型读写过程的各种情况,并要求实现进程间对管道的互斥访问。运行程序,观察各种情况下,进程实际读写的字节数以及进程阻塞唤醒情况。

  3. 利用linux的消息队列通信机制实现两个线程间的通信:编写程序创建两个线程:sender线程和receive线程,其中sender运行函数sender(),他创建一个消息队列,然后,循环等待用户通过终端输入一串字符,将这串字符通过消息队列发送给receiver线程,直到用户输入”exit”为止;最后,它向receiver线程发送消息”end”,并且等待receiver的应答,直到应答消息后,将接收到的应答消息显示在终端上,删除相关消息队列,结束程序运行。receiver线程运行receive(),它通过消息队列接收来自sender的消息,将消息显示在终端屏幕,直到接收到”end”的消息后它向sender发送一个应答消息”over”,结束程序运行。使用无名信号量实现两个线程之间的同步与互斥。

  4. 利用linux的共享内存通信机制实现两个进程间的通信:编写程序sender,它创建一个共享内存,然后等待用户通过终端输入一串字符,并将这串字符通过共享内存发送给receiver,最后,等待receiver应答,等到应答消息后,它接收到的应答消息显示在终端屏幕上,删除共享内存,结束程序运行。编写receiver程序,它通过共享内存接收来自sender的消息,将消息显示在终端屏幕上,然后再通过该共享内存向sender发送一个应答消息”over”,结束程序的运行。使用有名信号量或System V信号量实现两个进程对共享内存的互斥使用。


二、代码实现

(1)实现一个模拟shell

1-1 编写cmd1.c cmd2.c cmd3.c,可自己设计

//cmd1.c
#include<stdio.h>
int main()
{
        printf("this is the cmd1111111\n");
        return 0;
}

//cmd2.c
#include<stdio.h>
int main()
{
        printf("this is the cmd222222\n");
        return 0;
}

//cmd3.c
#include<stdio.h>
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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#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<CMD_COLLECTION_LEN;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

1-5 运行截图示例:
![在这里插入图片描述](https://img-blog.csdn.net/20181022103145242?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZpY1RyZWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)


(2)实现一个管道通信程序

2-1 编写pipe_communication.c文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

#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 <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>  
#include <semaphore.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

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
这里写图片描述

Logo

更多推荐