一、进程间通信

       linux下面提供了多种进程间通信的方法, 管道、信号、信号量、消息队列、共享内存、套接字等。下面我们分别
介绍管道、信号量、消息队列、共享内存。
       信号和套接字在后续介绍。
 
1、管道
    管道又分为无名管道、命名管道。   无名管道用于父子进程间通信, 而命名管道则可以用于同一计算机上运行的
两个进程间的通信。管道可以用类似“水管”的原理来理解。
 
1)无名管道
    要在父子进程进程间使用管道进行通信,那么先需要创建管道, 在linux中使用 pipe()创建管道。其原型如下:
PIPE(2)                    Linux Programmer’s Manual                   PIPE(2)
NAMEpipe - create pipe
SYNOPSIS#include <unistd.h>int pipe(int filedes[2]);  //参数为一个长度为2 的整型数组的数组首地址, 为输出参数,

    返回值:
               成功创建管道返回0; 失败则返回-1; 
    
    管道分为两端,一端用来写,另一端则用来读(可以想象为水管,位置高的一端进水,位置低的一端将水流出去) 。
pipe()函数的输出参数 filedes[0] 用于读取数据, filedes[1] 用于写入数据。
    管道的操作和普通文件的操作一样, 但是要注意,读的时候要将写端关闭,写的时候要将读端关闭。  
 
Exp:  pipe.c   首先测试从父进程给子进程写数据。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc,char* argv[])
{int fd_pipe[2];pid_t pid;char buf[32];//创建管道if( pipe(fd_pipe) ){perror("create pipe");exit(1);}pid=fork();if( 0==pid ){/*close(fd_pipe[1]); //关闭写端*/read(fd_pipe[0], buf,sizeof(buf));printf("in child process read data from pipe.\n");printf("the data read from pipe is:%s\n",buf);exit(0);}/*close(fd_pipe[0]); //关闭读端*/write(fd_pipe[1], "pipe test",sizeof("pipe test"));sleep(1);return 0;
}

    程序执行情况如下:
[root@localhost ipc]# gcc main.c
[root@localhost ipc]# ./a.out
in child process read data from pipe.
the data read from pipe is:pipe test

    利用系统调用 read()、write()进行操作的时候管道默认是阻塞的,如果管道没有数据可读,那么read( )函数就
阻塞,直到有数据读才返回。
   Exp:  pipe.c  子进程写入数据到管道,父进程从管道读取数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>#define CHAR  "pipe test from child to parent\n"int main(int argc,char* argv[])
{int fd_pipe[2];pid_t pid;char buf[32];//创建管道if( pipe(fd_pipe) ){perror("create pipe");exit(1);}pid=fork();if( 0==pid ){close(fd_pipe[0]);write(fd_pipe[1],CHAR,sizeof(CHAR));exit(0);} close(fd_pipe[1]);read(fd_pipe[0],buf,sizeof(buf));printf("data from child is: %s",buf);return 0;
}

    执行结果如下:
[root@localhost ipc]# gcc main.c
[root@localhost ipc]# ./a.out
data from child is: pipe test from child to parent

2)命名管道
    命名管道用于系统中两个进程之间通信;命名管道可用于系统中两个没有亲缘关系的进程进行通信(也可以用于父
子进程间的通信) 。
    要使用命名管道,则需要创建命名管道,用函数 mkfifo () 创建命名管道。其原型如下:
MKFIFO(3)                  Linux Programmer’s Manual                 MKFIFO(3)
NAMEmkfifo - make a FIFO special file (a named pipe)
SYNOPSIS#include <sys/types.h>#include <sys/stat.h>int mkfifo( const char *pathname,   //生成的管道特殊文件的位置和文件名mode_t mode);   //管道特殊文件的访问权限

返回值:    
       成功创建管道文件返回0, 失败返回 -1.
    
    创建号管道特殊文件后,就可以和访问普通文件一样访问管道特殊文件。
 
Exp:  测试命名管道    pipe-w.c  创建命令管道并向管道写入数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include  <sys/stat.h>#define CHAR  "pipe named\n"int main(int argc,char* argv[])
{int fd;int ret;//创建管道ret=mkfifo("./fifo-pipe",0666);if(ret){perror("mkfifo: fifo-pipe");exit(0);}fd=open("./fifo-pipe",O_WRONLY);write(fd,CHAR,sizeof(CHAR));close(fd);return 0;
}

    pipe-r.c 打开pipe-w.c 文件,并且从命名管道读取数据:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>int main(int argc,char* argv[])
{int fd;int ret;char buf[32];//打开命名管道文件fd=open("./fifo-pipe",O_RDONLY);if(-1 == fd){perror("open fifo-pipe");exit(1);}ret=read(fd,buf,sizeof(buf));if(ret<0){perror("read fifo-pipe");exit(1);}printf("the data read from fifo pipe:%s\n",buf);close(fd);return 0;
}

程序的执行结果如下:
    pipe-w.c 生成wp ,  pipe-r.c 生成rp
[root@localhost pipe]# ll                           //查看没有 fifo-pipe 的命名管道文件
总计 28
-rw-r--r-- 1 root root  560 12-11 16:57 pipe_fork.c
-rw-r--r-- 1 root root  500 12-11 17:15 pipe-r.c
-rw-r--r-- 1 root root  405 12-11 17:17 pipe-w.c
-rwxr-xr-x 1 root root 5359 12-11 17:15 rp
-rwxr-xr-x 1 root root 5296 12-11 17:17 wp
[root@localhost pipe]# ./wp &      //wp运行,并且进入后台
[1] 29409
[root@localhost pipe]# jobs
[1]+  Running                 ./wp &      //wp在后台运行, 等待命名管道的数据被读取, 即wp  阻塞
[root@localhost pipe]# ./rp            //rp 读取管道数据,
the data read from fifo pipe:pipe named     //数据读取成功

[1]+  Done                    ./wp                //管道中的数据被读取完后,wp不再阻塞,返回
[root@localhost pipe]# jobs
[root@localhost pipe]# ll
总计 28
prw-r--r-- 1 root root    0 12-11 17:23 fifo-pipe           //生成一个命名管道文件
-rw-r--r-- 1 root root  560 12-11 16:57 pipe_fork.c
-rw-r--r-- 1 root root  500 12-11 17:15 pipe-r.c
-rw-r--r-- 1 root root  405 12-11 17:17 pipe-w.c
-rwxr-xr-x 1 root root 5359 12-11 17:15 rp
-rwxr-xr-x 1 root root 5296 12-11 17:17 wp
[root@localhost pipe]# 

    要点:
            在进程操作管道的时候, write 和 read 都是阻塞的; 如果写的数据没有被读取走,那么就会写的进程就会
在 write 函数阻塞;  如果读数据的时候,管道没有数据,那么就会等待管道里面别写入数据,进程在read 函数阻塞。
 
 
2、消息队列
    消息队列也是linux下进程间通信的一种方式, 如果要使用消息队列在进程间进行通信,必须创建一个消息队列
或者打开一个已经存在的消息队列。
    要打开一个已经存在的消息队列,或者创建一个新的消息队列,则必须先获取一个关于消息队列的IPC键值;通过函
数 ftok( )获取消息队列的IPC键值。
    ftok 的原型如下:
FTOK(3)                    Linux Programmer’s Manual                   FTOK(3)
NAMEftok - convert a pathname and a project identifier to a System V IPC key//由一个特定的工程号和文件生成一个特定的IPC键值,
SYNOPSIS# include <sys/types.h># include <sys/ipc.h>key_t ftok(const char *pathname,    //文件名int proj_id);   //工程号

返回值:
            成功返回 IPC键值, 失败返回-1.
    要点:
            如果文件名和工程号一致,内核保证在任何进程中都将得到同样的 IPC 键值。
 
    有了消息队列的IPC键值后,就是创建或者打开消息队列, 通过 msgget( ) 创建或打开一个消息队列, 其原型如下:
MSGGET(2)                  Linux Programmer’s Manual                 MSGGET(2)
NAMEmsgget - get a message queue identifierSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget( key_t key,      //IPC 键值int msgflg);   //打开或者创建标志, 可以取值 IPC_CREAT

    返回值:
            如果成功,返回消息队列的ID号, 失败返回 -1。
 
    创建了消息队列或者打开已经消息队列后,就需要完消息队列中添加消息,即发送消息; 发送消息通过函数 msgsnd( )
实现。
    当发送完消息后,就可以从消息队列中获取消息,从消息队列中读取消息用函数 msgrcv( )实现。
    原型如下:
MSGOP(2)                   Linux Programmer’s Manual                  MSGOP(2)
NAMEmsgop - message operations
SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgsnd(int msqid,     //消息队列IDconst void *msgp,    //要发送的消息的消息结构体size_t msgsz,    //消息字符串的大小,或者消息结构的大小int msgflg);      // 消息标志
ssize_t msgrcv(int msqid,   //消息队列IDvoid *msgp,     //接受消息的消息结构体指针size_t msgsz,   //消息结构体的大小long msgtyp,  //指定要接收到消息的类型int msgflg);   //消息标志
    要发送或接收消息,还需要定义一个如下格式的结构体:struct msgbuf {long mtype;     /* message type, must be > 0 */     //消息类型, 这个值必须大于 0char mtext[1];  /* message data */    //要发送的消息数据, 字符数组长度可以根据实际需要定义
            };

    消息队列使用完后,需要删除消息队列,通过 msgctl 函数实现,这是一个与ioctl 函数类似的函数,其原型如下:
MSGCTL(2)                  Linux Programmer’s Manual                 MSGCTL(2)
NAMEmsgctl - message control operations
SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgctl(int msqid,     //消息队列IDint cmd,        //操作命令, 操作命令有很多,删除消息队列用 IPC_RMID 命令struct msqid_ds *buf);   //输出参数,通过这个结构体可获取消息队列的状态信息,如果不需要获取//消息队列的信息,那么就设置为NULL

    返回值: 
                成功删除(cmd=IPC_RMID)返回0 ,失败返回-1.
 
Exp: 发送消息的源文件:  msgsnd.c  
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>typedef struct
{long type;char data[128];
}msgbuf;int main(void)
{int ret;key_t key;int msgid;msgbuf msg={type: 1,data: "this is a message queue test.\n",};//获取键值key=ftok("./msgsnd.c",1);if(-1 == key){perror("ftok");exit(1);}//打开或创建一个消息队列msgid=msgget(key,IPC_CREAT);if(-1 == msgid ){perror("msgget");exit(2);}//发送消息ret=msgsnd(msgid,&msg,sizeof(msgbuf),0);if(-1 == ret){perror("msgsnd");}return 0;
}

    接收消息的源代码文件:  msgrcv.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>typedef struct
{long type;char data[128];
}msgbuf;int main(int argc,char* argv[])
{int ret;key_t key;int msgid;msgbuf msg;//获取键值key=ftok("./msgsnd.c",1);if(-1 == key ){perror("ftok");exit(1);}//打开消息队列msgid=msgget(key,0);if(-1 == msgid ){perror("msgget");exit(2);}//接收消息ret=msgrcv(msgid, &msg, sizeof(msgbuf),1,0);if(-1 == ret){perror("msgrcv");exit(3);}printf("the recive message is: %s",msg.data);//删除消息队列
    msgctl(msgid,IPC_RMID,NULL);return 0;
} 

测试结果如下:
[root@localhost msg]# gcc msgsnd.c  -o snd
[root@localhost msg]# gcc msgrcv.c -o rcv
[root@localhost msg]# ./snd
[root@localhost msg]# ./rcv
the recive message is: this is a message queue test.
[root@localhost msg]# 

3、信号量
    信号量主要用于两个进程间同步的,一般用于多进程间的同步操作。
    例如当两个进程同时要访问声卡的时候,那么就需要控制声卡先由那个进程操作,等第一个进程操作完后,其他进程
才能进行操作; 但是为什么我们可以同时用mplayer 还能同时使用kmplyer播放音乐呢? 从用户的角度来看,确实是这
样的,但是从硬件的角度来看,在某一时刻声卡就只能为一个应用层程序服务,当两个应用程序同时对声卡进行操作时就
会出现异常,为了防止这种异常,需要进行对两个进程进行控制,当有一个进程获取声卡的控制权后,另外的一个进程就
不能在同一时刻访问声卡,这就是互斥操作。(可以这样测试, 在windows的操作系统上安装VM虚拟机,打开windows
media player, 然后启动虚拟机(设置虚拟机在启动的时候自动挂载声卡设备),这时候声卡就会工作不正常,会出现
一小段时间的异常, 声音不正常,这就是出现两个应用程序同时使用声卡出现的异常)。
    信号量就是为解决类似的问题而设计的,信号量用来控制应用程序在同一时刻对某一系统资源的访问(这个系统资源
也称作临界资源,访问临界资源的代码,也称作临界区代码)。
    信号量用结构体 struct sembuf 描述,其定义如下:
/* semop system calls takes an array of these. */
struct sembuf {unsigned short  sem_num;    /* semaphore index in array */  //信号量集合中的信号量索引值,即表示信号量集合中第几个信号量short       sem_op;    /* semaphore operation */ //要对信号量进行的操作,=-1 表示信号量不可获取, =1 表示可以获取信号量short       sem_flg;   /* operation flags */      //信号量标志
};

    这里有一点需要说明:  通常对信号量进行操作分为 P操作、V操作,当设置 sem_op = -1 时表示进行P操作,
sem_op = 1 时表示要进行V操作。
 
    和消息队列一样,要使用信号量,首先需要获取一个用于信号量到IPC键值, 用 ftok( ) 函数获取。 获取到用于
信号量到IPC键值后,还需要创建或者打开一个已经存在的信号量,通过打开或创建信号量获取一个关于信号量的
信号量ID;然后通过对信号量ID进行操作,就可以使用信号量。
    通过semget()函数创建或打开一个信号量,并获取关于信号量的ID; 原型如下:
SEMGET(2)                  Linux Programmer’s Manual                 SEMGET(2)
NAMEsemget - get a semaphore set identifier   //获取一个信号集合的ID

SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget( key_t key,      //IPC键值int nsems,    //信号量集合中信号量的个数, 要创建的信号量到个数int semflg);   //信号量的标志, 同OPEN的打开标志类似

      在获取信号量集合ID后,需要对信号量进行一些设定(或者说信号量初始化),然后才能操作,通过函数 semctl( )对
信号量进行初始化操作; setctl( )的原型如下:
SEMCTL(2)                  Linux Programmer’s Manual                 SEMCTL(2)
NAMEsemctl - semaphore control operationsSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid,    //信号量集合ID int semnum,   //信号量集合中的信号量索引值int cmd,    //要对信号量进行的操作,可以使用的命名: IPC_SET、IPC_STAT、IPC_INFO、GETVAL、SETVAL.........);  //最后一个参数根据 操作的不同,可以传递,也可以不传递

    返回值:

            如果成功返回0 ,失败返回-1.(操作为 IPC_GETVAL, 成功返回信号量到值 )。
    SETVAL: 对信号量进行设置。 这时候,要传递4个参数, 最后一个可变参数,要定义一个类型如下:
           union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux specific) */};

    这是一个联合体类型,根据不同的操作命令,传递的值表示不同的意义。

 
    可以通过 semop( )函数来操作信号量,其原型如下:
SEMOP(2)                   Linux Programmer’s Manual                  SEMOP(2)NAMEsemop, semtimedop - semaphore operationsSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid,    //信号量集合IDstruct sembuf *sops,    //信号量结构体指针unsigned nsops);   //表示要操作的信号量个数int  semtimedop(int  semid,   //信号量集合IDstruct  sembuf *sops,   //信号量结构体指针unsigned nsops,  //表示要操作的信号量个数struct timespec  *timeout);  //表示超时等待时间,如果在超时时间内没有获取到可操作的信号量,就返回

Exp:  测试 信号量到代码,
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <stdlib.h>int main(void)
{int i;int j;int ret;int fd;   pid_t pid;key_t key;int semid;char buf[64];int size;struct sembuf sembuf;/*sembuf=(struct sembuf*)malloc(sizeof (struct sembuf));*///打开文件,用来进行操作fd=open("./test",O_RDWR | O_CREAT | O_TRUNC,0666);if(-1 == fd){perror("open");exit(1);}//IPC键值key=ftok("./main.c",1);if(-1 == key){perror("ftok");exit(2);}//获取信号量集合的IDsemid=semget(key,1,IPC_CREAT);if(-1 == semid ){perror("semget");exit(3);}//初始化信号量集合中的第一个信号量,设定信号量的值为0 , sem.sem_op = 0;ret=semctl(semid, 0, SETVAL, 1);pid=fork();if( pid==0 )   //----------------子进程------------
    {//信号量的P 操作, 即加锁信号量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;semop(semid,&sembuf,1);size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信号量的V操作,即解锁信号量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;semop(semid,&sembuf,1);exit(0);}//---------------子进程结束---------------------//-----------------------父进程---------------//信号量的P 操作, 即加锁信号量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;semop(semid,&sembuf,1);size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信号量的V操作,即解锁信号量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;semop(semid,&sembuf,1);semctl(semid,0,IPC_RMID);close(fd);return 0;
}

代码执行后生成的test文件内容如下:
pid=1114, ppid=714
pid=1114, ppid=714
pid=1114, ppid=714
pid=1114, ppid=714
pid=1114, ppid=714
pid=1115, ppid=1
pid=1115, ppid=1
pid=1115, ppid=1
pid=1115, ppid=1
pid=1115, ppid=1

如果将信号量的加锁和解锁取消, 代码如下:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <stdlib.h>int main(void)
{int i;int j;int ret;int fd;   pid_t pid;key_t key;int semid;char buf[64];int size;struct sembuf sembuf;/*sembuf=(struct sembuf*)malloc(sizeof (struct sembuf));*///打开文件,用来进行操作fd=open("./test",O_RDWR | O_CREAT | O_TRUNC,0666);if(-1 == fd){perror("open");exit(1);}//IPC键值key=ftok("./main.c",1);if(-1 == key){perror("ftok");exit(2);}//获取信号量集合的IDsemid=semget(key,1,IPC_CREAT);if(-1 == semid ){perror("semget");exit(3);}//初始化信号量集合中的第一个信号量,设定信号量的值为0 ret=semctl(semid, 0, SETVAL, 1);pid=fork();if( pid==0 )   //----------------子进程------------
    {//信号量的P 操作, 即加锁信号量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/   //取消信号量到作用
size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信号量的V操作,即解锁信号量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/    //取消信号量到作用
        exit(0);}//---------------子进程结束---------------------//-----------------------父进程---------------//信号量的P 操作, 即加锁信号量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/   //取消信号量到作用
size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信号量的V操作,即解锁信号量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/  //取消信号量到作用
semctl(semid,0,IPC_RMID);close(fd);return 0;
}

    生成的test文件内容如下:
ppiidd==22333387,,  ppppiidd==2731347ppiidd==22333378,,  ppppiidd==721343
7p
ipdi=d2=323373,8 ,p ppipdi=d7=1243
3p7i
dp=i2d3=3273,3 8p,p ipdp=i7d1=42
3p3i7d
=p2i3d3=72,3 3p8p,i dp=p7i1d4=
2337

    可以发现两个进程同时访问一个文件,而没有互斥机制的话,就会出现乱码。因此在访问临界资源的时候,就需要
采用互斥机制。
 
 
4、共享内存
    进程都具有自己的虚拟地址空间(即进程空间),进程A不能随意的访问进程B的进程空间; 内核提供了一种机制,
可以在物理内存中开辟一块存储空间,这块存储空间可供进程A或者进程B访问, 这样一块存储空间就是共享内存。
    要使用共享内存也需要获取一个IPC键值,通过  ftok()获取IPC键值。
    获取到IPC键值后,就需要向系统申请共享的存储空间,通过函数 shmget( ) 申请共享空间,并获取关于共享内存的
ID标识符。 shmget()的原型如下所示:
SHMGET(2)                  Linux Programmer’s Manual                 SHMGET(2)
NAMEshmget - allocates a shared memory segment
SYNOPSIS#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key,    //IPC 键值size_t size,   //要申请的内存空间的大小int shmflg);  //共享内存的空间打开标志  ,与 open 的打开标志类似,
    返回值:申请成功返回共享内存标识ID, 失败返回-1。

    申请成功后,还不能访问共享内存,因为访问内存需要知道内存的地址或者指针,所以就需要向系统申请返回
共享内存的地址或者指针。通过 shmmat( )向系统申请返回共享内存的首地址或者指针。其原型如下:
SHMOP(2)                   Linux Programmer’s Manual                  SHMOP(2)
NAMEshmop - shared memory operations
SYNOPSIS#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid,    //共享内存标志IDconst void *shmaddr,  //传递NULL,表示要系统分配存储缓冲区,传递地址表示指定地址int shmflg);   //打开标志,int shmdt(const void *shmaddr);   //删除共享内存

返回值:
        成功返回共享内存的首地址(虚拟地址),失败返回NULL。
 
        在成功返回共享内存首地址后,就可以向访问用malloc 分配的内存一样进行操作。
 
Exp:  申请共享内存,并往共享内存写的文件  shm-w.c 
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>#define SHM_SIZE 128int main(int argc,char* argv[])
{key_t key;int shm_id;char* shm_p;//获取IPC 键值key=ftok("./shm-w.c",1);if(-1 == key){perror("ftok");exit(1);}//申请共享内存空间,大小为 SHM_SIZEshm_id=shmget(key,SHM_SIZE,IPC_CREAT);if(-1 == shm_id ){perror("shmget");exit(2);}//将申请的共享内存映射到用户空间shm_p=shmat(shm_id,NULL,0); //
    if(NULL == shm_p ){perror("shmat");exit(3);}//将数据写入到共享内存   写入到数据可以在其他进程中读取memset(shm_p,0,SHM_SIZE);strcpy(shm_p, "this is a sheard memmory.\n"); //这个函数不安全,需要注意return 0;
}从共享内存中读取数据的文件  shm-r.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>#define SHM_SIZE 128int main(int argc,char* argv[])
{key_t key;int shm_id;char* shm_p;char  buf[SHM_SIZE];//获取IPC 键值key=ftok("./shm-w.c",1);if(-1 == key){perror("ftok");exit(1);}//申请共享内存空间,大小为 SHM_SIZEshm_id=shmget(key,SHM_SIZE,IPC_CREAT);if(-1 == shm_id ){perror("shmget");exit(2);}//将申请的共享内存映射到用户空间shm_p=shmat(shm_id,NULL,0); //
    if(NULL == shm_p ){perror("shmat");exit(3);}//从共享内存读取数据memset(buf,0,SHM_SIZE);strcpy(buf, shm_p); //这个函数不安全,需要注意printf("the data read from sheard memory is: %s",buf);shmdt(shm_p); //申请撤销共享内存return 0;
}

程序执行的效果如下:
[root@localhost shm]# gcc shm-w.c -o shmw
[root@localhost shm]# gcc shm-r.c -o shmr
[root@localhost shm]# ./shmw
[root@localhost shm]# ./shmr
the data read from sheard memory is: this is a sheard memmory.
[root@localhost shm]# 

  【Linux草鞋应用编程系列】_3_进程间通信

   本系列文章未完,待续。

   如果查看的过程中发现错误,请不吝指教,包括错别字、标点符号等。

     前篇:【linux草鞋应用编程系列】_2_ 环境变量和进程控制

 

转载于:https://www.cnblogs.com/volcanol/p/3473642.html

【linux草鞋应用编程系列】_3_ 进程间通信相关推荐

  1. 【linux草鞋应用编程系列】_4_ 应用程序多线程

    一.应用程序多线程 当一个计算机上具有多个CPU核心的时候,每个CPU核心都可以执行代码,此时如果使用单线程,那么这个线程只能在一个 CPU上运行,那么其他的CPU核心就处于空闲状态,浪费了系统资源: ...

  2. 【linux草鞋应用编程系列】_2_ 环境变量和进程控制

    一. 环境变量 应用程序在执行的时候,可能需要获取系统的环境变量,从而执行一些相应的操作. 在linux中有两种方法获取环境变量,分述如下. 1.通过main函数的参数获取环境变量 main函数的多种 ...

  3. Linux环境高级编程函数,Linux环境高级编程--出错处理(CLStatus)

    很多程序库对外提供若干类,每个方法出错时如何告知调用者是否出错,以及出错码(在Linux上在error.h中的全局errno就是保存我们Linux程序执行的出错码的)?方法很多,为了简化起见,函数将返 ...

  4. Linux内核模块编程系列1-极简内核模块编写

    1.准备工作 使用如下命令查看自己Linux的内核版本 uname -a 结果如下: Linux VM-73-203-debian 4.9.0-6-amd64 #1 SMP Debian 4.9.88 ...

  5. 视频教程-Kali Linux渗透测试全程课与脚本语言编程系列课程-渗透测试

    Kali Linux渗透测试全程课与脚本语言编程系列课程 本人有多年的服务器高级运维与开发经验,擅长计算机与服务器攻防及网络攻防技术!对网络安全领域有持续的关注和研究! 林晓炜 ¥899.00 立即订 ...

  6. 嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解

    在上一篇文章中,我们介绍了串口的一些基本知识.串口配置接口 termios结构体的概念,串口的配置参数有n多个,这里面不用都背下来,什么时候使用,翻看手册即可,但是有两个 参数是一定要理解的,就是VM ...

  7. 嵌入式Linux 串口编程系列3——通过VTIM、VMIN、select实现串口不定长数据接收功能

    上一篇文章中,我们详细分析了VTIM和VMIN的功能, <嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解> 也明白了这两个参数设计的初衷和使用方法,接下来 ...

  8. Linux下C编程实战

    2019独角兽企业重金招聘Python工程师标准>>> Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于 ...

  9. Linux驱动视频教程推荐,隆重推荐:linux驱动基础开发系列免费教程独家版本

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 相信大家经常网上闲逛,会经常碰到很多的linux免费教程之类得,但是今天我推荐的这个linux驱动基础开发系列免费教程可不是网上可以随意找到得.废话少说: ...

最新文章

  1. SCOI2009 最长距离
  2. 转载 一个渣硕iOS春招总结 | 掘金技术征文
  3. uniapp处理IOS底部横条安全区域
  4. 【新产品发布】《EVC8021 RS-232RS-485/422 隔离接口转换器》
  5. golang数据类型与MySQL数据类型的对应
  6. springMVC上传下载
  7. MySQL:从B树到B+树到索引再到存储引擎
  8. http协议报文体_Java面试中可能涉及到的通信协议类问题
  9. 数据结构——双向链表的实现
  10. [洛谷P5048][Ynoi2019模拟赛]Yuno loves sqrt technology III
  11. nodejs 本地php服务器,node.js创建本地服务器详解
  12. 聚划算的夜场新生意 “三叉戟”打通夜间消费命脉
  13. log4j deadlock
  14. 我是如何在自学编程9个月后找到工作的
  15. 自己制作的4X4光立方焊接时候出现的问题
  16. filenet java配置_连接到filenet的外部java应用程序服务器
  17. GitLab 创建项目组及将代码导入项目
  18. RPG Maker MZ如何导入dlc素材?
  19. 直击|支付宝还信用卡下月开始收费 每月2000免费额度
  20. git 新建分支并切换到该分支_Git 从master拉取代码创建新分支 并且再将修改合并到master...

热门文章

  1. SQL-SQLServer(926)
  2. jsonArray转换成List
  3. 仔细学习CSS(二)
  4. Vs2012 打开项目 自动关闭 并停止工作 解决方法
  5. 产品经理的必经之路:搭建属于自己的成长模型
  6. 【干货】从有道5亿用户看工具型产品的转型之路
  7. golang 开源代理
  8. pyDash : Linux 性能监测工具
  9. 世界卫生日:大数据解读抑郁症群体
  10. 简单编译安装Apache