共享内存和管道的C语言实现

共享内存

共享内存C语言API

int shmget(key_t key, int size, int shmflg),开辟或使用一块共享内存。
void *shmat(int shmid, const void *shmaddr, int shmflg), 将参数shmid所指向的共享内存与当前进程连接。当使用某共享内存时,需要先使用shmat,达成连接。
int shmdt(const void *shmaddr),将先前用shmat连接的共享内存与当前进程解除连接。参数shmaddr为shmat返回的共享内存的地址。
int shmctl(int shmid, int cmd, struct shmid_ds *buf),控制内存的操作。当cmd为IPC_RMID时,删除shmid所指的共享内存。

单个进程对共享内存操作

以下代码分为四个部分:
1、创建一块共享内存,获取它的id。
2、根据获取的id和当前进程的内存地址建立映射关系;写入字符串“hello,word!”;断开映射关系。
3、重新建立映射关系,读取数据并打印。
4、删除共享内存。

#include 
#include 
#include 
#include 

int main() {
    //创建
    int shmid;
    shmid = shmget(IPC_PRIVATE, 1000, IPC_CREAT | 0600);
    if (shmid < 0) {
        perror("shmget error");
        exit(1);
    }
    printf("create shared memory OK. shmid=%d\n", shmid);

    //写数据
    char * shmaddr = (char *)shmat(shmid, NULL, 0);
    if ((int )shmaddr == -1) {
        perror("shmat error.\n");
        exit(1);
    }
    strcpy(shmaddr, "hello, world!");
    shmdt(shmaddr);

    //读数据
    shmaddr = (char *)shmat(shmid, NULL, 0);
    if ((int )shmaddr == -1) {
        perror("shmat error.\n");
        exit(1);
    }
    printf("%s\n", shmaddr);
    shmdt(shmaddr);
    
    //删除
    shmctl(shmid, IPC_RMID, NULL);
}

其他进程如何获取shmid

方法1:
命令行:main函数的入口包含两个参数,argc和argv,argc表示了argv中参数的个数,argv为字符串数组,它的有效长度由argc决定。一般argc默认为1,argv[0]为程序运行的全路径名 。如果该程序是在DOS命令行中执行,那么可以添加参数,比如在全路径名之后跟若干个字符串作为参数,这些参数都会添加在argv中。如果预先获取了共享内存的id,那么就可以把id作为参数传入另一个进程的main函数,然后解析参数。

int main(int argc, char *argv[])
{
	int shmid;
	char *shmaddr;
	if (argc != 2) {
		perror("argc error/n");
		exit(1);
	}
	shmid = atoi(argv[1]);
}

方法2:
借助其他进程间通信方式传递shmid:比如sokcet,消息队列等。这样做的意义是,传递shmid之后就可以持续地使用共享内存资源,共享内存的速度也是所有方式中最快的。

两个进程操作同一块共享内存

进程1:创建共享内存并且写数据。

int main(int argc, char * argv[]) {
    printf("%s\n",argv[0]);
    
    //创建
    int shmid;
    shmid = shmget(IPC_PRIVATE, 1000, IPC_CREAT | 0600);
    printf("shmid:%d\n",shmid);
    if (shmid < 0) {
        perror("shmget error");
        exit(1);
    }
    printf("create shared memory OK. shmid=%d\n", shmid);

    //写数据
    char * shmaddr = (char *)shmat(shmid, NULL, 0);
    if ((int )shmaddr == -1) {
        perror("shmat error.\n");
        exit(1);
    }
    strcpy(shmaddr, "hello, world!");
    shmdt(shmaddr);
    
    return 0;
}

进程2:读取共享内存中的数据并输出。

int main(int argc, char * argv[]) {
    
    int shmid;
    char *shmaddr;
    shmid = 589854;
    shmaddr = (char *)shmat(shmid, NULL, 0);
    if ((int )shmaddr == -1) {
        perror("shmat error.\n");
        exit(1);
    }
    printf("%s\n", shmaddr);
    shmdt(shmaddr);
        
    return 0;
}

分析: 这里我设定共享内存的大小为100。先运行进程1,输出shmid为589854。然后运行进程2,并通过这个id去取数据。
输出:
进程1输出:
共享内存和管道的C语言实现_第1张图片
进程2输出:
在这里插入图片描述

管道

原理

如下图:管道一般用于父进程和子进程之间通信,子进程由父进程调用fork来获取。fd是进程描述符,它是一个长度为2的数组,fd[0]控制读,fd[1]控制写,创建pipe以及之后对pipe的操作都需要把它作为参数。
共享内存和管道的C语言实现_第2张图片

pipe实现子进程写,父进程读

#include 
#include 
#include 
#include 

int main()
{
    int fd[2];//创建进程描述符
    char out_put[strlen("hello world")];//创建一个读取的缓冲区,这里长度为hello world
    char str[] = "hello world";//需要写的内容
    int pipe_result = pipe(fd);//创建管道
    //错误处理
    if (pipe_result == -1) { 
        printf("pipe error");
        exit(0);
    }
    int fpid = fork();//创建子进程
    //父进程返回子进程pid,子进程返回0
    if (fpid == 0) {
        close(fd[0]);//子进程关闭读端
        write(fd[1],str,strlen(str));//子进程写
    }else {
        close(fd[1]);//父进程关闭写端
        read(fd[0],out_put,strlen(str));//父进程读
        printf("%s",out_put);//输出
    }
    return 0;
}

关于fork()函数: 这个函数的特点是调用一次会返回两次,一次是父进程返回,一次是子进程返回。父进程返回的是子进程的pid,子进程返回的是0。如果需要思考其中的原理,那么就是父进程和子进程之间通过链表的数据结构来实现,或者说父进程的结构体中保存着子进程的地址,所以父进程会返回子进程的pid(这是进程的标识符),子进程因为没有子进程,所以返回0。于是在这个函数调用前只有一个进程在执行,调用后就有两个进程在执行了。
pid参数: 因为进程是资源分配的最小单位,所以父进程到子进程虽然几乎完全一样的复制了一份资源,但是父进程和子进程的pid是不同的,于是fork()之后可以通过判断pid来让父进程和子进程执行不同的代码。

你可能感兴趣的:(操作系统)