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);
}
方法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输出:
进程2输出:
如下图:管道一般用于父进程和子进程之间通信,子进程由父进程调用fork来获取。fd是进程描述符,它是一个长度为2的数组,fd[0]控制读,fd[1]控制写,创建pipe以及之后对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来让父进程和子进程执行不同的代码。