linux C 编程 之 进程间通信(IPC)

多任务通信方法:

1.管道:pipe();
2.有名管道 : mkfifo()
3.信号:signal(),sigaction()
4.消息队列:msgget()
5.POSIX 无名信号 / SYSTEM V 有名信号:semaphore
6.SYSTEM V共享内存 / MMAP共享内存  
7.socket网络通信

进程间通信:

1.管道;(父子进程,线程)

int fd[2];
pipe(fd);//fd[0] 读,fd[1]写

open/unlink

2.有名管道FIFO:解决管道只能在父子进程中通信限制

int mkfifo(char*pathname,mode_t mode);
创建后就可像文件一样读写访问
read/write/close;

//file1.c

#include <sys/types.h>

#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

#define FIFO_SERVER "/tmp/fifoserver"

int main(int argc,char** argv)
//参数为即将写入的字节数
{
int fd;
char w_buf[4096*2];
int real_wnum;
memset(w_buf,0,4096*2);
if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
if(fd==-1)
if(errno==ENXIO)
printf("open error; no reading process\n");
     fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
//设置非阻塞标志
//fd=open(FIFO_SERVER,O_WRONLY,0);
//设置阻塞标志
real_wnum=write(fd,w_buf,2048);
if(real_wnum==-1)
{
if(errno==EAGAIN)
printf("write to fifo error; try later\n");
}
else 
printf("real write num is %d\n",real_wnum);
real_wnum=write(fd,w_buf,5000);
//5000用于测试写入字节大于4096时的非原子性
//real_wnum=write(fd,w_buf,4096);
//4096用于测试写入字节不大于4096时的原子性
if(real_wnum==-1)
if(errno==EAGAIN)
printf("try later\n");
}
//file2.c
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
int  main(int argc,char** argv)
{
char r_buf[4096*2];
int  fd;
int  r_size;
int  ret_size;
r_size=atoi(argv[1]);
printf("requred real read bytes %d\n",r_size);
memset(r_buf,0,sizeof(r_buf));
fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);
//fd=open(FIFO_SERVER,O_RDONLY,0);
//在此处可以把读程序编译成两个不同版本:阻塞版本及非阻塞版本
if(fd==-1)
{
printf("open %s for read error\n");
exit();
}
while(1)
{
memset(r_buf,0,sizeof(r_buf));
ret_size=read(fd,r_buf,r_size);
if(ret_size==-1)
if(errno==EAGAIN)
printf("no data avlaible\n");
printf("real read bytes %d\n",ret_size);
sleep(1);
}
pause();
unlink(FIFO_SERVER);
};

3.信号:

kill -l 列出系统所有信号(64个,前32个为不可靠信号,后32个为可靠信号)
发送信号:
kill(pid_t pid,int sig);
sigqueue(pid_t pid,int sig,const union sigval value);
raise(int sig)//给自己发送信号
alarm(sec);//产生SIGALRM信号,闹钟时间
setitimer(int which,struct itimerval*new_value,struct itimervar*old_value);
setitimer()比alarm功能强大,支持3种类型的定时器:
ITIMER_REAL:设定绝对时间;经过指定的时间后,内核将发送SIGALRM信号给本进程;
ITIMER_VIRTUAL:设定程序执行时间;经过指定的时间后,内核将发送SIGVTALRM信号给本进程;
ITIMER_PROF:设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间后,
内核将发送ITIMER_VIRTUAL信号给本进程;
Setitimer()第一个参数which指定定时器类型(上面三种之一);
第二个参数是结构itimerval的一个实例。第三个参数可不做处理。
  struct itimerval {
                struct timeval it_interval; /* next value */
                struct timeval it_value;    /* current value */
            };
            struct timeval {
                long tv_sec;                /* seconds */
                long tv_usec;               /* microseconds */
            };
Setitimer()调用成功返回0,否则返回-1。
abort()//产生SIGABRT 信号
安装:
signal(int sig,sighandler_t handler);
sigaction(int sig,struct sigaction*act,struct sigaction*oldact);
信号集:
sigemptyset(sigset_t *set);//初始化,清空信号集合
sigfillset(sigset_t*set);//填满64个信号
sigaddset(sigset_t*set,int sig);//添加信号到集合
sigdelset(sigset_t*set,int sig);//删除信号从集合中
sigismember(sigset_t*set,int sig);//是否在集合中
sigpending(sigset_t*set);
sigsuspend(sigset_t*mask);
(消息队列,信号量,共享内存均使用同一系统调用ipc(),下面介绍使用库函数)

4,消息队列   (内核对消息队列有限制:MSGMAX 8192 单个消息的最大大小,MSGMNB 16384 每个消息队列的总长度)

key_t ftok(char*pathname,int proj_id);
int msgget(key_t key,int msgflag)
key:键值名,命名消息队列
msgflag: IPC_CREAT 512创建 
IPC_PRIVATE 0 私有,只能被 单前进程访问

返回 >0队列标识符,-1失败

int msgsnd(int msqid,const void*msq_ptr,size_t msq_sz,int msqflag);
添加消息进队列
消息 msq_ptr需以一个long int message_type;作为首成员变量
msq_sz是消息长度,不含message_type的长度
msqflag:  IPC_NOWAIT 2048立刻返回,否则等待队列中有可用空间
  返回 0成功,-1失败
int msgrcv(int msqid,void*msq_ptr,size_t msg_sz,long int msqtype,int msgflg);
msq_ptr: 消息,需以一个long int message_type;作为首成员变量
msq_sz: 消息长度,不含 message_type长度
msqtype: 接收优先级。0:获取队列中的第一个,>0就获取相同消息类开型的第一个消息
<0
msqflag:  IPC_NOWAIT 立刻返回,否则等待队列中有可用消息
返回 :成功返回接收缓存区中的字节数。 -1 失败。

int msgctl(int msqid,int command,struct msqid_ds*buf);
command: IPC_RMID删除消息队列

例:
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
void msg_stat(int msgid,struct msqid_ds msg_info)
{
int reval;
sleep(1); //只是为了后面输出时间的方便
reval=msgctl(msgid,IPC_STAT,&msg_info);
if(reval==-1)
{
printf("get msg info error\n");
return;
}
printf("\n");
printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
printf("number of messages in queue is %d\n",msg_info.msg_qnum);
printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
//每个消息队列的容量(字节数)都有限制MSGMNB,值的大小因系统而异。在创建新的消息队列时,//msg_qbytes的缺省值就是MSGMNB
printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
printf("last msgsnd time is %s", ctime(&(msg_info.msg_stime)));
printf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime)));
printf("last change time is %s", ctime(&(msg_info.msg_ctime)));
printf("msg uid is %d\n",msg_info.msg_perm.uid);
printf("msg gid is %d\n",msg_info.msg_perm.gid);
};
int main()
{
int gflags,sflags,rflags;
key_t key;
int msgid;
int reval;
struct msgsbuf{
        int mtype;
        char mtext[1];
    }msg_sbuf;
struct msgmbuf{
int mtype;
char mtext[10];
    }msg_rbuf;
struct msqid_ds msg_ginfo,msg_sinfo;
char* msgpath="/unix/msgqueue";
key=ftok(msgpath,'a');
gflags=IPC_CREAT|IPC_EXCL;
msgid=msgget(key,gflags|00666);
if(msgid==-1)
{
printf("msg create error\n");
return;
}
//创建一个消息队列后,输出消息队列缺省属性
msg_stat(msgid,msg_ginfo);
sflags=IPC_NOWAIT;
msg_sbuf.mtype=10;
msg_sbuf.mtext[0]='a';
reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
if(reval==-1)
{
printf("message send error\n");
}
//发送一个消息后,输出消息队列属性
msg_stat(msgid,msg_ginfo);
rflags=IPC_NOWAIT|MSG_NOERROR;
reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);
if(reval==-1)
printf("read msg error\n");
else
printf("read from msg queue %d bytes\n",reval);
//从消息队列中读出消息后,输出消息队列属性
msg_stat(msgid,msg_ginfo);
msg_sinfo.msg_perm.uid=8;//just a try
msg_sinfo.msg_perm.gid=8;//
msg_sinfo.msg_qbytes=16388;
//此处验证超级用户可以更改消息队列的缺省msg_qbytes
//注意这里设置的值大于缺省值
reval=msgctl(msgid,IPC_SET,&msg_sinfo);
if(reval==-1)
{
printf("msg set info error\n");
return;
}
msg_stat(msgid,msg_ginfo);
//验证设置消息队列属性
reval=msgctl(msgid,IPC_RMID,NULL);//删除消息队列
if(reval==-1)
{
printf("unlink msg queue error\n");
return;
}
}

5,信号量/灯 semaphore

5.1. POSIX 的无名信号量的函数有以下几个:(父子进程,线程间)

#include <semaphore.h>;

int sem_init(sem_t *sem,int pshared,unsigned int value);

int sem_destroy(sem_t *sem);

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

int sem_post(sem_t *sem);

int  sem_getvalue(sem_t *sem);

int  sigprocmask(int how,sigset_t*set,sigset_t*oldset);

sem_init 创建一个信号灯,并初始化其值为value.pshared 决定了信号量能否在几个进程
间共享.由于目前Linux 还没有实现进程间共享信号灯,所以这个值只能够取0. 
sem_destroy 是用来删除信号灯的.
sem_wait 调用将阻塞进程,直到信号灯的值大于0.这个函数返回的时候自动的将信号灯的值的件一.
sem_post 和sem_wait 相反,是将信号灯的内容加一同时发出信号唤醒等待的进程..
sem_trywait 和sem_wait 相同,不过不阻塞的,当信号灯的值为0的时候返回EAGAIN,表示以后重试.

sem_getvalue 得到信号灯的值.

5.2,System V 有名信号量的函数主要有下面几个.

#include <sys/types.h>;
#include <sys/ipc.h>;
#include <sys/sem.h>;
key_t  ftok(char *pathname,char proj);
int  semget(key_t key,int nsems,int semflg);
int  semctl(int semid,int semnum,int cmd,union semun arg);
int  semop(int semid,struct sembuf *spos,int nspos);
struct sembuf {
short sem_num; /* 使用那一个信号 */
short sem_op; /* 进行什么操作 */
short sem_flg; /* 操作的标志 */
};
ftok 函数是根据pathname 和proj 来创建一个关键字.
semget 创建一个信号量.成功时返回信号的ID,
key 是一个关键字,可以是用ftok 创建的也可以是IPC_PRIVATE 表明由系统选用一个关键字. 
nsems 表明我们创建的信号个数.
semflg 是创建的权限标志,和我们创建一个文件的标志相同.
semctl 对信号量进行一系列的控制.
semid 是要操作的信号标志,
semnum 是信号的个数,
cmd 是操作的命令.经常用的两个值是:SETVAL(设置信号量的值)和IPC_RMID(删除信号灯).
arg 是一个给cmd 的参数.
semop 是对信号进行操作的函数.
semid 是信号标志,
spos 是一个操作数组表明要进行什么操作,
nspos 表明数组的个数. 
如果sem_op 大于0,那么操作将sem_op 加入到信号量的值中,并唤醒等待信号增加的进程. 如果为0,当信号量的值是0 的时候,函数返回,否则阻塞直到信号量的值为0. 如果小于0,函数判断信号量的值加上这个负值.如果结果为0 唤醒等待信号量为0 的进程,
如果小与0 函数阻塞.如果大于0,那么从信号量里面减去这个值并返回..
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define PERMS S_IRUSR|S_IWUSR

void init_semaphore_struct(struct sembuf *sem,int semnum,int semop,int semflg)
{
/* 初始话信号灯结构 */
sem->sem_num=semnum;
sem->sem_op=semop;
sem->sem_flg=semflg;
}
int del_semaphore(int semid)
{
/* 信号灯并不随程序的结束而被删除,如果我们没删除的话(将1 改为0)可以用ipcs 命令查看到信号灯,用ipcrm 可以删除信号灯的
*/
return semctl(semid,0,IPC_RMID);
}

int main(int argc,char **argv)
{
char buffer[MAX_CANON],*c;
int i,n;
int semid,semop_ret,status;
pid_t childpid;
struct sembuf semwait,semsignal;
if((argc!=2)||((n=atoi(argv[1]))<1))
{
fprintf(stderr,"Usage:%s number\n\a",argv[0]);
exit(1);
}
/* 使用IPC_PRIVATE 表示由系统选择一个关键字来创建 */
/* 创建以后信号灯的初始值为0 */
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)
{
fprintf(stderr,"[%d]:Acess Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* semwait 是要求资源的操作(-1) */
init_semaphore_struct(&semwait,0,-1,0);
/* semsignal 是释放资源的操作(+1) */
init_semaphore_struct(&semsignal,0,1,0);
/* 开始的时候有一个系统资源(一个标准错误输出)*/
if(semop(semid,&semsignal,1)==-1)
{
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* 创建一个进程链 */
for(i=0;i<n;i++)
if(childpid=fork()) break;
sprintf(buffer,"[i=%d]-->;[Process=%d]-->;[Parent=%d]-->;[Child=%d]\n",i,getpid(),getppid(),childpid);
c=buffer;
/* 这里要求资源,进入原子操作 */
while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
{
fprintf(stderr,"[%d]:Decrement Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
else{
while(*c!='\0')fputc(*c++,stderr);
/* 原子操作完成,赶快释放资源 */
while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
/* 不能够在其他进程反问信号灯的时候,我们删除了信号灯 */
while((wait(&status)==-1)&&(errno==EINTR));
/* 信号灯只能够被删除一次的 */
if(i==1)
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(0);
}
信号量的主要用途是保护临界资源(在一个时刻只被一个进程所拥有).
#define PERMS S_IRUSR|S_IWUSR
void init_semaphore_struct(struct sembuf *sem,int semnum,int semop,int semflg)
{
/* 初始话信号灯结构 */
sem->sem_num=semnum;
sem->sem_op=semop;
sem->sem_flg=semflg;
}
int del_semaphore(int semid)
{
/* 信号灯并不随程序的结束而被删除,如果我们没删除的话(将1 改为0)可以用ipcs 命令查看到信号灯,用ipcrm 可以删除信号灯的
*/
return semctl(semid,0,IPC_RMID);
}

int main(int argc,char **argv)
{
char buffer[MAX_CANON],*c;
int i,n;
int semid,semop_ret,status;
pid_t childpid;
struct sembuf semwait,semsignal;
if((argc!=2)||((n=atoi(argv[1]))<1))
{
fprintf(stderr,"Usage:%s number\n\a",argv[0]);
exit(1);
}
/* 使用IPC_PRIVATE 表示由系统选择一个关键字来创建 */
/* 创建以后信号灯的初始值为0 */
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)
{
fprintf(stderr,"[%d]:Acess Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* semwait 是要求资源的操作(-1) */
init_semaphore_struct(&semwait,0,-1,0);
/* semsignal 是释放资源的操作(+1) */
init_semaphore_struct(&semsignal,0,1,0);
/* 开始的时候有一个系统资源(一个标准错误输出)*/
if(semop(semid,&semsignal,1)==-1)
{
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* 创建一个进程链 */
for(i=0;i<n;i++)
if(childpid=fork()) break;
sprintf(buffer,"[i=%d]-->;[Process=%d]-->;[Parent=%d]-->;[Child=%d]\n",i,getpid(),getppid(),childpid);
c=buffer;
/* 这里要求资源,进入原子操作 */
while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
{
fprintf(stderr,"[%d]:Decrement Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
else{
while(*c!='\0')fputc(*c++,stderr);
/* 原子操作完成,赶快释放资源 */
while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
/* 不能够在其他进程反问信号灯的时候,我们删除了信号灯 */
while((wait(&status)==-1)&&(errno==EINTR));
/* 信号灯只能够被删除一次的 */
if(i==1)
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(0);
}
信号量的主要用途是保护临界资源(在一个时刻只被一个进程所拥有).
void init_semaphore_struct(struct sembuf *sem,int semnum,int semop,int semflg)
{
/* 初始话信号灯结构 */
sem->sem_num=semnum;
sem->sem_op=semop;
sem->sem_flg=semflg;
}
int del_semaphore(int semid)
{
/* 信号灯并不随程序的结束而被删除,如果我们没删除的话(将1 改为0)可以用ipcs 命令查看到信号灯,用ipcrm 可以删除信号灯的
*/
return semctl(semid,0,IPC_RMID);
}
int main(int argc,char **argv)
{
char buffer[MAX_CANON],*c;
int i,n;
int semid,semop_ret,status;
pid_t childpid;
struct sembuf semwait,semsignal;
if((argc!=2)||((n=atoi(argv[1]))<1))
{
fprintf(stderr,"Usage:%s number\n\a",argv[0]);
exit(1);
}
/* 使用IPC_PRIVATE 表示由系统选择一个关键字来创建 */
/* 创建以后信号灯的初始值为0 */
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)
{
fprintf(stderr,"[%d]:Acess Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* semwait 是要求资源的操作(-1) */
init_semaphore_struct(&semwait,0,-1,0);
/* semsignal 是释放资源的操作(+1) */
init_semaphore_struct(&semsignal,0,1,0);
/* 开始的时候有一个系统资源(一个标准错误输出)*/
if(semop(semid,&semsignal,1)==-1)
{
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* 创建一个进程链 */
for(i=0;i<n;i++)
if(childpid=fork()) break;
sprintf(buffer,"[i=%d]-->;[Process=%d]-->;[Parent=%d]-->;[Child=%d]\n",i,getpid(),getppid(),childpid);
c=buffer;
/* 这里要求资源,进入原子操作 */
while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
{
fprintf(stderr,"[%d]:Decrement Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
else{
while(*c!='\0')fputc(*c++,stderr);
/* 原子操作完成,赶快释放资源 */
while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
/* 不能够在其他进程反问信号灯的时候,我们删除了信号灯 */
while((wait(&status)==-1)&&(errno==EINTR));
/* 信号灯只能够被删除一次的 */
if(i==1)
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(0);
}
信号量的主要用途是保护临界资源(在一个时刻只被一个进程所拥有).
int main(int argc,char **argv)
{
char buffer[MAX_CANON],*c;
int i,n;
int semid,semop_ret,status;
pid_t childpid;
struct sembuf semwait,semsignal;
if((argc!=2)||((n=atoi(argv[1]))<1))
{
fprintf(stderr,"Usage:%s number\n\a",argv[0]);
exit(1);
}
/* 使用IPC_PRIVATE 表示由系统选择一个关键字来创建 */
/* 创建以后信号灯的初始值为0 */
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)
{
fprintf(stderr,"[%d]:Acess Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* semwait 是要求资源的操作(-1) */
init_semaphore_struct(&semwait,0,-1,0);
/* semsignal 是释放资源的操作(+1) */
init_semaphore_struct(&semsignal,0,1,0);
/* 开始的时候有一个系统资源(一个标准错误输出)*/
if(semop(semid,&semsignal,1)==-1)
{
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(1);
}
/* 创建一个进程链 */
for(i=0;i<n;i++)
if(childpid=fork()) break;
sprintf(buffer,"[i=%d]-->;[Process=%d]-->;[Parent=%d]-->;[Child=%d]\n",i,getpid(),getppid(),childpid);
c=buffer;
/* 这里要求资源,进入原子操作 */
while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
{
fprintf(stderr,"[%d]:Decrement Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
else{
while(*c!='\0')fputc(*c++,stderr);
/* 原子操作完成,赶快释放资源 */
while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR));
if(semop_ret==-1)
fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",getpid(),strerror(errno));
}
/* 不能够在其他进程反问信号灯的时候,我们删除了信号灯 */
while((wait(&status)==-1)&&(errno==EINTR));
/* 信号灯只能够被删除一次的 */
if(i==1)
if(del_semaphore(semid)==-1)
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",getpid(),strerror(errno));
exit(0);
}
信号量的主要用途是保护临界资源(在一个时刻只被一个进程所拥有).

6,共享内存

6.1 SystemV 共享内存:

#include <sys/types.h>;
#include <sys/ipc.h>;
#include <sys/shm.h>;
int  shmget(key_t key,int size,int shmflg);
void  *shmat(int shmid,const void *shmaddr,int shmflg);
int  shmdt(const void *shmaddr);
int  shmctl(int shmid,int cmd,struct shmid_ds *buf);
shmget 和shmctl 没有什么好解释的.
size 是共享内存的大小. 
shmat 是用来连接共享内存的.
shmdt 是用来断开共享内存的.
不要被共享内存词语吓倒,共享内存其实很容易实现和使用的.
shmaddr,shmflg 我们只要用0 代替就可以了.
在使用一个共享内存之前我们调用shmat 得到共享内存的开始地址,
使用结束以后我们使用shmdt 断开这个内存.
#include <stdio.h>;
#include <string.h>;
#include <errno.h>;
#include <unistd.h>;
#include <sys/stat.h>;
#include <sys/types.h>;
#include <sys/ipc.h>;
#include <sys/shm.h>;
#define PERM S_IRUSR|S_IWUSR
int main(int argc,char **argv)
{
int shmid;
char *p_addr,*c_addr;
if(argc!=2)
{
fprintf(stderr,"Usage:%s\n\a",argv[0]);
exit(1);
}
if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)
{
fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
exit(1);
}
if(fork())
{   //parent
p_addr=shmat(shmid,0,0);
memset(p_addr,'\0',1024);
strncpy(p_addr,argv[1],1024);
exit(0);
}
else
{   //child
c_addr=shmat(shmid,0,0);
printf("Client get %s",c_addr);
exit(0);
}
}
这个程序是父进程将参数写入到共享内存,然后子进程把内容读出来.
最后我们要使用ipcrm 释放资源的.先用ipcs 找出ID 然后用ipcrm shm ID 删除.

6.2  共享内存;

mmap(void*addr,size_t length,int prot,int flags);

munmap(void*addr,size_t length);

例1:不同进程间通过文件共享内存
/*-------------map_normalfile1.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int  age;
}people;
int main(int argc, char** argv) // map a normal file as shared mem:
{
  int fd,i;
  people *p_map;
  char temp;
  
  fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
  lseek(fd,sizeof(people)*5-1,SEEK_SET);
  write(fd,"",1);
  
  p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
        MAP_SHARED,fd,0 );
  close( fd );
  temp = 'a';
  for(i=0; i<10; i++)
  {
    temp += 1;
    memcpy( ( *(p_map+i) ).name, &temp,2 );
    ( *(p_map+i) ).age = 20+i;
  }
  printf(" initialize over \n ");
  sleep(10);
  munmap( p_map, sizeof(people)*10 );
  printf( "umap ok \n" );
}
/*-------------map_normalfile2.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
  char name[4];
  int  age;
}people;
main(int argc, char** argv)  // map a normal file as shared mem:
{
  int fd,i;
  people *p_map;
  fd=open( argv[1],O_CREAT|O_RDWR,00777 );
  p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
       MAP_SHARED,fd,0);
  for(i = 0;i<10;i++)
  {
printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
  }
  munmap( p_map,sizeof(people)*10 );
}
例2:父子进程通共匿名共享内存
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
  char name[4];
  int  age;
}people;
int main(int argc, char** argv)
{
  int i;
  people *p_map;
  char temp;
  p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
       MAP_SHARED|MAP_ANONYMOUS,-1,0);
  if(fork() == 0)
  {
    sleep(2);
    for(i = 0;i<5;i++)
      printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);
    (*p_map).age = 100;
    munmap(p_map,sizeof(people)*10); //实际上,进程终止时,会自动解除映射。
    exit();
  }
  temp = 'a';
  for(i = 0;i<5;i++)
  {
    temp += 1;
    memcpy((*(p_map+i)).name, &temp,2);
    (*(p_map+i)).age=20+i;
  }
  sleep(5);
  printf( "parent read: the first people,s age is %d\n",(*p_map).age );
  printf("umap\n");
  munmap( p_map,sizeof(people)*10 );
  printf( "umap ok\n" );
}
例2:父子进程通共匿名共享内存
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
  char name[4];
  int  age;
}people;
int main(int argc, char** argv)
{
  int i;
  people *p_map;
  char temp;
  p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
       MAP_SHARED|MAP_ANONYMOUS,-1,0);
  if(fork() == 0)
  {
    sleep(2);
    for(i = 0;i<5;i++)
      printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);
    (*p_map).age = 100;
    munmap(p_map,sizeof(people)*10); //实际上,进程终止时,会自动解除映射。
    exit();
  }
  temp = 'a';
  for(i = 0;i<5;i++)
  {
    temp += 1;
    memcpy((*(p_map+i)).name, &temp,2);
    (*(p_map+i)).age=20+i;
  }
  sleep(5);
  printf( "parent read: the first people,s age is %d\n",(*p_map).age );
  printf("umap\n");
  munmap( p_map,sizeof(people)*10 );
  printf( "umap ok\n" );
}

7,socket通信

int socket( int domain, int type, int ptotocol);
int bind( int sockfd, const struct sockaddr * my_addr, socklen_t my_addr_len)
int connect( int sockfd, const struct sockaddr * servaddr, socklen_t addrlen);
int accept( int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen);
read/write/close
int recv(int s, void *buf, size_t len, int flags)
int recvfrom(int s,  void *buf,  size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
int recvmsg(int s, struct msghdr *msg, int flags)
int send(int s,const void *msg, size_t len, int flags)
int sendto(int s, const void *msg, size_t len, int flags const struct sockaddr *to, socklen_t tolen)
int sendmsg(int s, const struct msghdr *msg, int flags)

你可能感兴趣的:(linux C 编程 之 进程间通信(IPC))