操作系统实验:Linux进程管理

实验内容

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

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

(3)利用Linux的消息队列通信机制实现两个线程间的通信
编写程序创建三个线程:sender1线程、sender2线程和receiver线程,三个线程的功能描述如下:
① sender1线程:运行函数sender1(),它创建一个消息队列,然后等待用户通过终端输入一串字符,并将这串字符通过消息队列发给receiver线程;可循环发送多个消息,直到用户输入“exit”为止,表示它不再发送消息,最后向receiver线程发送消息“end1”,并且等待receiver的应答(老师告知可以省略这步,所以代码没有体现,sender2同),等到应答消息后,将接收到的应答信息显示在终端屏幕上,结束线程的运行。
② sender2线程:运行函数sender2(),共享sender1创建的消息队列,等待用户通过终端输入一串字符,并将这串字符通过消息队列发送给receiver线程;可循环发送多个消息,直到用户输入“exit”为止,表示它不再发送消息,最后向receiver线程发送消息“end2”,并且等待receiver的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,结束线程的运行。
③ receiver线程:运行函数receive(),它通过消息队列接收来自sender1和sender2两个线程的消息,将消息显示在终端屏幕上,当收到内容为“end1”的消息时,就向sender1发送一个应答消息“over1”;当收到内容为“end2”的消息时,就向sender2发送一个应答消息“over2”;消息接受完成后删除消息队列,结束线程的运行。选择合适的信号量机制实现三个线程之间的同步和互斥。

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

代码实现

1、实现一个模拟的shell

cmd1.c

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

cmd2.c

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

cmd3.c

#include<stdio.h>
int main()
{
    printf("this is the cmd3!\n");
    return 0;
}

Linux中的shell是什么意思?
答:Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。

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,中间的为要传送的参数列表。

当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。
因为调用exec并不创建新进程,所以前后的进程ID并未改变。
exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。
*/
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);
    }
    return 0;
}

Makefile

all: myshell cmd1 cmd2 cmd3
.PHONY : clean
myshell.o : myshell.c
clean : 
	rm cmd1 cmd2 cmd3
	rm myshell
	rm *.o

运行命令

(1)make
(2)./myshell

执行效果

在这里插入图片描述

2、实现一个管道通信程序

pipe.c

#include <sys/sem.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/wait.h>

#define READ 0  //描述符
#define WRITE 1
#define MAX_SIZE 16384
/*16KB, 当管道进行写入操作的时候,如果写入的数据小于128K则是非原子的,
如果大于128K字节,缓冲区的数据将被连续地写入*/
 
int main(int argc, char** argv){
    int fd[2];
    ssize_t status;
    int flag=0;
    char buf[MAX_SIZE], str[MAX_SIZE];
    pid_t pid1,pid2,pid3;
    //定义三个互斥信号量指针
	sem_t *write_mutex;
	sem_t *read_mutex1;
	sem_t *read_mutex2;
	//创建有名信号量
	write_mutex = sem_open("pipe_test_wm", O_CREAT | O_RDWR, 0666, 0);
	read_mutex1 = sem_open("pipe_test_rm_1", O_CREAT | O_RDWR, 0666, 0);
	read_mutex2 = sem_open("pipe_test_rm_2", O_CREAT | O_RDWR, 0666, 0);
    status = pipe(fd);  
    /*返回值:成功,返回0,否则返回-1。
    参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道,fd[1]:写管道。*/
    if(status < 0){
        fprintf(stderr, "create Pipe Error %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    status = 0;
    //创建子进程1, 测试管道默认大小
	if((pid1 = fork()) == -1){
		printf("child process 1 fork() failed.\n");
		exit(1); //异常退出
	}
	if (pid1 == 0) {
		printf("\n>> child process 1 [pid=%d, ppid=%d]:\n", getpid(), getppid());
		int count = 0;
		close(fd[READ]);  //关闭读端口
		//F_GETFL,获取pipefd[1]的文件状态标志
		int flags = fcntl(fd[WRITE], F_GETFL);
		//F_SETFL,设置pipefd[1]的文件状态标志 非阻塞方式
		fcntl(fd[WRITE], F_SETFL, flags | O_NONBLOCK);
		while (!flag) {
			//write把buf指针所指内存的16384个字节写入管道中,返回实际写入的字节数
			status = write(fd[WRITE], buf, MAX_SIZE);
			//管道已满,不能写入
			if (status == -1) {
				flag = 1;
			} else {
				count++; //记录写入的次数
				printf("child process 1 write %ldB.\n", status);
			}
		}
		//输出管道的默认大小
		printf("pipe size = %dKB.\n\n", (count * MAX_SIZE) / 1024);
		exit(0);
	}

    status = 0;
    //创建子进程2
	if((pid2 = fork()) == -1){
		printf("child process 2 fork() failed.\n");
		exit(1); //异常退出
	}
	if (pid2 == 0) {
		sem_wait(write_mutex);
		close(fd[READ]);
		printf(">> child process 2 [pid=%d, ppid=%d]:\n", getpid(), getppid());
		status = write(fd[WRITE], "This is child process 2.\n", strlen("This is child process 2.\n"));
		printf("child process 2 write %ldB.\n\n", status);
		sem_post(write_mutex);
		sem_post(read_mutex1);
		exit(0);
	}

    status = 0;
	//创建子进程3
	if((pid3 = fork()) == -1){
		printf("child process 3 fork() failed.\n");
		exit(1); //异常退出
	}
	if (pid3 == 0) {
		sem_wait(write_mutex);
		close(fd[READ]);
		printf(">> child process 3 [pid=%d, ppid=%d]:\n", getpid(), getppid());
		status = write(fd[WRITE], "This is child process 3.\n", strlen("This is child process 3.\n"));
		printf("child process 3 write %ldB.\n\n", status);
		sem_post(write_mutex);
		sem_post(read_mutex2);
		exit(0);
	}

	//父进程
	wait(0);
	close(fd[WRITE]);
	printf(">> father process [pid=%d]:\n", getpid());
	int flags = fcntl(fd[READ], F_GETFL);
	fcntl(fd[READ], F_SETFL, flags | O_NONBLOCK);
	while (!flag) {
		//read把管道中的MAX_SIZE个字节读到str指针所指的内存中
		status = read(fd[READ], str, MAX_SIZE);
		if (status == -1) {
			flag = 1;
		} else {
			//输出读出的字节数
			printf("father process read %ldB.\n", status);
		}
	}
	printf("\n");
	sem_post(write_mutex);
	//子进程23全部写完后开始读
	sem_wait(read_mutex1);
	sem_wait(read_mutex2);
	printf(">> father process [pid=%d]:\n", getpid());
	status = read(fd[READ], str, MAX_SIZE);
	//输出子进程23写入的内容
	printf("father process read %ldB.\n", status);
	for (int i = 0; i < status; i++) {
	    printf("%c", str[i]);
	}
	printf("\n");

	//关闭信号量
	sem_close(write_mutex);
	sem_close(read_mutex1);
	sem_close(read_mutex2);
	//从系统中删除信号量
	sem_unlink("pipe_test_wm");
	sem_unlink("pipe_test_rm_1");
	sem_unlink("pipe_test_rm_2");

    return 0;
}

Makefile

.PHONY : clean
pipe : pipe.o
	cc -pthread -o pipe pipe.o
clean: 
	rm pipe
	rm *.o

运行命令

(1)make
(2)./pipe

执行效果

在这里插入图片描述

3、利用Linux的消息队列通信机制实现三个线程间的通信

message_queue.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<fcntl.h>
#include<unistd.h>

#define MSG_MAX 100
 
 /*函数的msgp位是指向消息缓冲区的指针,
 此位置用来暂时存储发送和接收的消息,
 是一个用户可定义的通用结构*/
struct my_msgbuf{
    long int mtype;
    char mtext[MSG_MAX];
};
 
 
void *sender1(){
    int n;
    struct my_msgbuf message;
    char buf[MSG_MAX];
    //创建有名信号量
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender1_over = sem_open("sender1_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1_over = sem_open("receive1_over", O_CREAT | O_RDWR, 0666, 0);
    /*获取关联的键为"8088"的消息队列的标识,
    以及消息队列的建立标志(key指定的消息队列已存在IPC_CREAT)和存取权限*/
    int msqid = msgget((key_t)8088, 0666 | IPC_CREAT);
    if( msqid == -1){
        printf("create failed");
        exit(-1);
    }
    while(1){
        sem_wait(mutex);
        printf("sender1线程等待用户输入字符:");
        scanf("%s", buf);
        //printf("\n");
        message.mtype = 1;
        if(strcmp(buf,"exit") == 0){
            strcpy(message.mtext,"end1");
            /*将一个新的消息写入队列(消息队列识别码, 消息缓冲区指针, 消息的大小, 忽略)*/
            n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
            sem_wait(receive1_over);
            /*从消息队列中读取消息(消息队列识别码, 消息缓冲区指针, 消息的大小, 消息类型, 忽略)
            消息类型大于0,则返回其类型为mtype的第一个消息。*/
            n = msgrcv(msqid, (void *)&message, MSG_MAX, 2, 0); 
            printf("%s\n", message.mtext);
 
            sem_post(sender1_over);
            sem_post(mutex);
            sleep(1);
            return 0;
        }else{
            strcpy(message.mtext,buf);
            n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
            sem_post(mutex);
            sleep(1);
        }
    }
}

void *sender2(){
    int n;
    struct my_msgbuf message;
    char buf[MSG_MAX];
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2_over = sem_open("sender2_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive2_over = sem_open("receive2_over", O_CREAT | O_RDWR, 0666, 0);
    int msqid = msgget((key_t)8088, 0666 | IPC_CREAT);
    if( msqid == -1){
        printf("create failed");
        exit(-1);
    }
    while(1){
        sem_wait(mutex);
        printf("sender2线程等待用户输入字符:");
        scanf("%s", buf);
        //printf("\n");
        message.mtype = 1;
        if(strcmp(buf,"exit") == 0){
            strcpy(message.mtext,"end2");
            n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
            sem_wait(receive2_over);
            n = msgrcv(msqid, (void *)&message, MSG_MAX, 3, 0);
            printf("%s\n", message.mtext);
            sem_post(sender2_over);
            sem_post(mutex);
            sleep(1);
            return 0;
        }else{
            strcpy(message.mtext,buf);
            n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
            sem_post(mutex);
            sleep(1);
        }
    }
}
 
void *receive(){
    int n;
    int over1=0;
    int over2=0;
    struct my_msgbuf message;
    char buf[MSG_MAX];
    //创建有名信号量
    sem_t *sender1_over = sem_open("sender1_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1_over = sem_open("receive1_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2_over = sem_open("sender2_over", O_CREAT | O_RDWR, 0644, 0);
    sem_t *receive2_over = sem_open("receive2_over", O_CREAT | O_RDWR, 0666, 0);
    //获取消息队列
    int msqid = msgget((key_t)8088, 0666 | IPC_CREAT);
    if( msqid == -1){
        printf("create failed");
        exit(-1);
    }
    while(1){
        n = msgrcv(msqid, (void *)&message, MSG_MAX, 0, 0);
        if(n > 0){
            printf("\nReceiver线程收到内容“%s”\n", message.mtext);
            if(strcmp(message.mtext,"end1") == 0){
                message.mtype = 2;
                strcpy(message.mtext,"over1");
                n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
                if(n == 0){  //当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
                    sem_post(receive1_over);
                    sem_wait(sender1_over);
                }
                over1 = 1;
            }else if(strcmp(message.mtext,"end2") == 0){
                message.mtype = 3;
                strcpy(message.mtext,"over2");
                n = msgsnd(msqid, (void *)&message, MSG_MAX, 0);
                if(n == 0){
                    sem_post(receive2_over);
                    sem_wait(sender2_over);
                }
                over2 = 1;
            }
        }
        if(over1==1 && over2==1){
        	//获取和设置消息队列的属性, IPC_RMID:把一个段标记为删除 
            msgctl(msqid, IPC_RMID, 0);
            exit(0);
        }
    }
}

int main(){
    int msqid = msgget((key_t)8088, 0666 | IPC_CREAT);
    msgctl(msqid, IPC_RMID, 0);
    sem_unlink("mutex");
    sem_unlink("sender1_over");
    sem_unlink("sender2_over");
    sem_unlink("receive1_over");
    sem_unlink("receive2_over");
 
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender1_over = sem_open("sender1_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1_over = sem_open("receive1_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2_over = sem_open("sender2_over", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive2_over = sem_open("receive2_over", O_CREAT | O_RDWR, 0666, 0);
 
    pthread_t pt1,pt2,pt3;  //线程id
    pthread_create(&pt1, NULL, sender1, NULL);  //创建线程,调用sender1
    pthread_create(&pt2, NULL, sender2, NULL);  //创建线程,调用sender2
    pthread_create(&pt3, NULL, receive, NULL);  //创建线程,调用receive
    sem_post(mutex);
    pthread_join(pt1, NULL);  //等待线程退出
    pthread_join(pt2, NULL);  //等待线程退出
    pthread_join(pt3, NULL);  //等待线程退出
    return 0;
}

Makefile

.PHONY : clean
message_queue : message_queue.o
	cc -pthread -o message message_queue.o
clean:
	rm message_queue
	rm *.o

运行命令

(1)make
(2)./message

执行效果

在这里插入图片描述

4、利用Linux的共享内存通信机制实现两个进程间的通信

sender.c

//为消息发送程序
#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";

#define INPUT_SIZE 1024 //输入的最大长度
#define KEY_NUM 8848

void P(sem_t *semPtr){
    sem_wait(semPtr);
}

void V(sem_t *semPtr){
    sem_post(semPtr);
}
 
int main(int argc, char** argv){
    key_t key = KEY_NUM;  //为共享内存段命名
    char input[INPUT_SIZE];
    char reply[INPUT_SIZE];
    int shmid;
    char* shmptr;

    //创建共享内存
    shmid = shmget(key, INPUT_SIZE, IPC_CREAT | 0666);
    if(shmid < 0)
    {
        perror("Receiver: Shmget Error");
        exit(EXIT_FAILURE);
    }

    //启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间
    shmptr = shmat(shmid, NULL, 0);
    sem_t* mutex = sem_open(MUTEX_NAME,O_CREAT);  //共享内存只能同时一个程序访问
    sem_t* full = sem_open(FULL_NAME,O_CREAT);  //共享内存的消息数量

    printf("请输入一串字符:");
    scanf("%s",input);
    P(mutex);
    strcpy(shmptr,input);
    V(mutex);
    V(full);
    printf("消息已发送给receiver!\n");

    //把共享内存从当前进程中分离
    if(shmdt(shmptr) == -1){
        fprintf(stderr, "shmdt failed\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

receiver.c

//为消息接收程序
#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";

#define RESULT_SIZE 1024 
#define KEY_NUM 8848

void P(sem_t *semPtr){
    sem_wait(semPtr);
}

void V(sem_t *semPtr){
    sem_post(semPtr);
}
 
int main(int argc, char** argv){
    key_t key = KEY_NUM;
    char result[RESULT_SIZE];
    int shmid;
    char* shmptr;

    shmid = shmget(key, RESULT_SIZE, IPC_CREAT | 0666);
    if(shmid < 0)
    {
        perror("Receiver: Shmget Error");
        exit(EXIT_FAILURE);
    }
    shmptr = shmat(shmid, NULL, 0);
    sem_t* mutex = sem_open(MUTEX_NAME,O_CREAT);
    sem_t* full = sem_open(FULL_NAME,O_CREAT);

    P(full);
    P(mutex);                       
    strcpy(result,shmptr);
    V(mutex);
    printf("接收到来自sender的消息是: %s\n",result);

    printf("over\n");

    //把共享内存从当前进程中分离
    if(shmdt(shmptr) == -1){
        fprintf(stderr, "shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    shmctl(shmid, IPC_RMID, NULL);  //控制共享内存, IPC_RMID表示删除共享内存段

    sem_t * mutexPtr = sem_open(MUTEX_NAME,O_CREAT); 
    sem_t * fullPtr= sem_open(FULL_NAME,O_CREAT);
    sem_close(mutexPtr);
    sem_unlink(MUTEX_NAME);
    sem_close(fullPtr);
    sem_unlink(FULL_NAME);

    return 0;
}

Makefile

all : sender receiver
.PHONY : clean
sender : sender.o
	cc -pthread -o sender sender.o
receiver : receiver.o
	cc -pthread -o receiver receiver.o
clean : 
	rm  receiver
	rm  sender 
	rm  *.o

运行命令

(1)make
(2)./sender
(3)./receiver

执行效果

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

征服所有不服

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值