进程的通信方式

进程的7种通信方式


# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,

而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
int main()
{
int pid;
int pipefd[2];
int ret;
char buf[]="Hello,young man!";
ret=pipe(pipefd);
pid=fork();
if(pid<0)
{
perror("Failed to create child process:");
return -1;
}
if(pid>0)
{
close(pipefd[0]);//父进程中关闭无关的读端
write(pipefd[1],buf,strlen(buf));
wait(NULL);
printf("Parent process exit!\n");
}
else
{
char receive_buf[100];
int count;
close(pipefd[1]);//子进程中关闭无关的写端
count=read(pipefd[0],receive_buf,100);
if(count>0)
{
receive_buf[count]='\0';
printf("Child process receive a string:%s\n",receive_buf);

}
printf("Child process exit!\n");
}
return 0;
}
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。


# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
因此,主要作为进程间以及同一进程内不同线程之间的同步手段。


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) */
};
/*********************
Function:
sem_id:
num:
value:
*********************/
int init_sem(int sem_id,int num,int value)
{
int ret=0;;
union semun sem_union;
sem_union.val=value;
//把标识号为sem_id的信号量集中第num个信号量的值初始化为sem_union.value
ret=semctl(sem_id,num,SETVAL,sem_union);
if(ret<0)
{
perror("Failed  to  init semaphore:");
}
return ret;
}


int sem_p(int sem_id,int num)
{
int ret=0;
struct sembuf  sem_buf;
sem_buf.sem_num=num;//要操作的信号量的编号
sem_buf.sem_op=-1;//对信号量进行p操作
sem_buf.sem_flg=SEM_UNDO;//当进程终止时会清空信号量
ret=semop(sem_id,&sem_buf,1);
if(ret<0)
{
perror("Failed  to p semaphore:");
}
return ret;
}
int sem_v(int sem_id,int num)
{
int ret=0;
struct sembuf  sem_buf;
sem_buf.sem_num=num;//要操作的信号量的编号
sem_buf.sem_op=1;//对信号量进行p操作
sem_buf.sem_flg=SEM_UNDO;//当进程终止时会清空信号量
ret=semop(sem_id,&sem_buf,1);
if(ret<0)
{
perror("Failed  to p semaphore:");
}
return ret;
}


int destroy_sem(int sem_id,int num)
{
int ret=0;
//把标识号为sem_id的信号量集中第num个信号量destroy
ret=semctl(sem_id,num,IPC_RMID);
if(ret<0)
{
perror("Failed  to  destroy semaphore:");
}
return ret;
}
///
# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。
消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为含有一个类型,
接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。
但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。


struct  message_buf
{
long msgType;
char buf[512];
};


int msgget(key_t key, int msgflg);
//msgflg = 066|IPC_CREAT


int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg); 
//注意 需要将msgType设置为1,表示发送的消息类型为1
//msgflg=0


int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg); 
//将msgtype=0,表示接收第一个类型的消息


int msgctl(int msgid, int command, struct msgid_ds *buf); 
command是将要采取的动作,它可以取3个值,
    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
    IPC_RMID:删除消息队列


///
# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
signal(SIGINT,handler);
void handler(void)
{
//
}


//
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,
这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,
它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,
如信号两,配合使用,来实现进程间的同步和通信。
<1>创建/打开共享内存
int shmget(key_t key, size_t size, int shmflg);
IPC_CREAT: 当用key所对应共享内存不存在时则创建它;
IPC_EXCL:当空享内存块存在则创建失败;
0666:共享内块的权限;


<2>映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmaddr若NULL,则表示由系统自动完成映射
//SHM_RDONLY  共享内存只读,默认0:共享内存可读写。
//返回值 :调用成功放回映射后的地址


<3>撤销共享内存映射 
int shmdt(const void *shmaddr);


<4>删除共享内存对象
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmctl(shmid,IPC_RMID,NULL);


//
信号量
和管道、FIFO或者共享内存不一样,信号灯主要用于同步或者互斥对共享资源的访问,
它的发明来源于火车运行系统中的"信号灯",利用信号灯可以实现"PV"操作这种进程间同步进制。
P操作时获得资源,将信号灯的值减1,如果结果不为负则执行完毕,进程获得资源,
否则进程睡眠以等待的进程释放;V操作则是释放资源,给信号灯的值加1, 
唤醒一个因执行P操作而等待的进程。


(1) 初始化信号量;
(2) 判断信号量是否可用,不可用则等待;
(3) 进入临界区使用临界资源;
(4) 临界资源使用完毕,如果有其它进程在等待,则唤醒其它进程
key_t ftok( char * fname, int id );


int semget(key_t key, int nsems, int semflg);
//nsems:信号量集所包含的信号量个数;
//semflag:通常为IPC_CREAT|0666


int semop(int semid, struct sembuf *sops, unsigned nsops);
//sops:指向用来描述做操作的struct sembuf变量的指针
struct  sembuf
{
unsigned short sem_num;  /*所操作的信号量编号 */
        short          sem_op;   /* 具体的操作,1:p操作,-1:v操作 */
        short          sem_flg;  /* 一般为SEM_UNDO */
};
//nsops:所操作的信号量个数


 int semctl(int semid, int semnum, int cmd, ...);
//semnum:所操作的信号量编号;
//cmd:  1、GETVAL  2、SETVAL  3、IPC_RMID






//
# 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,
它可用于不同及其间的进程通信。


int socket(int domain, int type, int protocol);
//domain:对于IPV4,为AF_INET
  type:选择套接字类型,当我们选择TCP时为SOCK_STREAM;选择UDP则为SOCK_DGRAM
  protocol:一般为0


int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
//一般用struct sockaddr_in 
server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(8888);


server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
//INADDR_ANY表示对本机上所有的网卡 都监听


 int listen(int sockfd, int backlog);
//backlog:处于等待状态的请求有多少


int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
//sockfd:从哪个套接字接收请求(注意侦听方的套接字)
//返回一个新的套接字描述符


ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//sockfd:发送方的套接字描述符!!!!
//flags:一般为0
//当该函数返回时并不表示缓冲区的中的数据已经发送到网络中,
仅仅是表示缓冲区的数据被复制到了socket的内部的发送缓冲区!!


ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//sockfd:  接收方的套接字!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值