进程间的7种通信方式如下:
管道pipe: 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
命名管道FIFO: 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列MessageQueue: 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory: 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
信号量Semaphore: 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
套接字Socket: 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
从一个进程连接数据流到另一个进程时,就使用管道,通常是把一个进程的输出通过管道连接到另一个进程的输入。
该函数的原型为int pipe(int file_descritor[2]),其参数是一个由两个整数类型的文件描述符组成的数组的指针,该函数在数组中填上两个新的文件描述符后返回0,如果失败返回-1.两个文件描述符以一种特殊的方式连接起来,写到file_descriptor[1]的数据都可以从file_descriptor[0]读回来。数据基于先进先出的原则进行处理。
管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道 读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件 的I/O函数都可以用于管道,如close、read、write等等。
如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0; 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现 有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
注:只有在管道的读端存在时,向管道中写入数据才有意义。
read(fd,buf,nbyte)
功能:从fd所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。
write(fd,buf,nbyte)
功能:把nbyte个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。如文件加锁,暂停写入,直至开锁。
sprintf(str, format )
功能:根据参数format 字符串来转换并格式化数据,然后将结果复制到参数str所指的字符串数组,直到出现字符结束(‘\0’)为止。
#include
#include
#include
#include
#include
#include
int main(void)
{
pid_t pid1;
int fields[2];
char buffer[80];
char s[100];
char ss[100];
if(pipe(fields)!=0){
fprintf(stderr,"Createpipe error:%s\n\a",strerror(errno));
exit(1);
}
if((pid1=fork())<0)printf("fork child error!\n");
/* 子进程写入数据 */
if(pid1==0){
printf("fork child,child is sending a message !\n");
char s[]="hello!\n";
write(fields[1],s,sizeof(s));
exit(0)
}
/* 父进程读取数据 */
else
{
printf("parent read start !\n");
read(fields[0],buffer,80);
printf("parent receive the message:%s",buffer);
}
exit (0);
}
特点:
mkfifo
的形式创建匿名管道:mkfifo myPipe
echo “hello ” > myPipe
cat < myPipe
int mkfifo(const char * pathname,mode_t mode);
程序源码//读进程
#include
#include
#include
#include
#include
#include
#include
#include
#defineFIFO_PATH "myfifofile"
int main(){
int fd;
char cont_r[255];
#创建命名管道
if(mkfifo(FIFO_PATH,0666)<0 && errno!=EEXIST)
{
perror("create fifo failed");
return-1;
}
else {
printf("create fifo success\n");
#打开文件进行读操作
fd =open(FIFO_PATH,O_CREAT|O_RDONLY,0666);
if(fd>0)
{
while(1){
read(fd,cont_r,255);
printf("read:%s\n",cont_r);
}
close(fd);
}else
perror("open failed");
}
return0;
}
//写进程
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_PATH "myfifofile"
int main(){
int fd;
char cont_w[] = "hello sundy";
if(mkfifo(FIFO_PATH,0666)<0&& errno!=EEXIST)
{
perror("create fifo failed");
return-1;
}
else
{
printf("create fifo success\n");
fd =open(FIFO_PATH,O_CREAT|O_WRONLY,0666);
if(fd>0)
{
while(1){
write(fd,cont_w,strlen(cont_w));
printf("write success\n");
sleep(2);
}
close(fd);
}else
perror("open failed");
}
return0;
}
特点
int msgget(key_t key,int flags);
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,int msgflg);
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
程序源码
// 写进程
#include
#include
#include
// 消息队列数据结构
typedef struct mesg_buffer{
long mesg_type;
char mesg_text[100];
} message;
int main()
{
key_t key;
int msgid;
// ftok to generate unique key
key = ftok("progfile", 65);
// msgget creates a message queue
// and returns identifier
msgid = msgget(key, 0666| IPC_CREAT);
message.mesg_type = 1;
printf("Write Data : ");
gets(message.mesg_text);
// msgsnd to send message
msgsnd(msgid, &message, sizeof(message), 0);
// display the message
printf("Data send is : %s \n", message.mesg_text);
return0;
}
// 读进程
#include
#include
#include
// structure for message queue
typedef struct mesg_buffer{
long mesg_type;
char mesg_text[100];
} message;
intmain()
{
key_t key;
int msgid;
// ftok to generate unique key
key = ftok("progfile", 65);
// msgget creates a message queue // and returns identifier
msgid = msgget(key, 0666| IPC_CREAT);
// msgrcv to receive message
msgrcv(msgid, &message, sizeof(message), 1, 0);
// display the message
printf("Data Received is : %s \n", message.mesg_text);
// to destroy the message queue
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
特点:
shmget:申请共享内存
shmat:建立用户进程空间到共享内存的映射
shmdt:解除映射关系
shmctl:回收共享内存空间
#include
#include
#include
int main()
{
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
// shmat to attach to shared memory
char *str = (char*) shmat(shmid,(void*)0,0);
gets(str);
printf("Data written in memory: %s\n",str);
//detach from shared memory
shmdt(str);
return0;
}
//读进程
#include
#include
#include
intmain()
{
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
// shmat to attach to shared memory
char *str = (char*) shmat(shmid,(void*)0,0);
printf("Data read from memory: %s\n",str);
//detach from shared memory
shmdt(str);
// destroy the shared memory
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
信号是linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应的采取一些行动。可作为进程间传递消息的一种方式,信号可以被生成、捕获、响应或忽略。信号在signal.h中定义,信号的名称都以SIG开头如:SIGALRM 超时警告;SIGINT:终端中断。 如果进程接收到这些信号中的一个,但是事先没有安排捕获它,进程将会立刻终止。
#include
#include
#include
#include
#include
static int alarm_fired = 0;
/*该函数用来模拟闹钟*
void ding(intsig){
alarm_fired =1;
}
/*main函数中告诉子进程在等待5秒后发送SIGALRM信号给它的父进程*/
int main(){
pid_tpid;
printf("alarm start\n");
pid = fork(); /*创建子进程*/
switch(pid)
{
case -1:
perror("fork failed");
exit(1);
case 0:
sleep(5); /*子进程休眠5秒*/
kill(getppid(), SIGALRM); /*子进程在5秒后将SIGALRM信号传递给父进程*/
exit(0);
}
/*父进程通过一个signal调用捕获SIGALRM信号的工作,等待该信号的到来*/
printf("waitting for alarm to go on\n");
(void) signal(SIGALRM, ding);
pause();
if(alarm_fired)
printf("ding!\n");
printf("done\n");
exit(0);
}
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}
#include
#include
#include
#include
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
// struct seminfo *buff;
};
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
/* 如果程序第一个被调用,也就是调用时含有一个参数,使得argc>1,此时就调用set_semvalue初始化信号量,并将op_char设置为x*/
if (argc > 1)
{
if (!set_semvalue())
{
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X';
sleep(2);
}
/*进入和离开临界区10次,每次循环开始的时候首先调用semaphore_p函数,它在程序将进入临界区域时设置信号量以等待进入*/
for (i = 0; i < 10; i++)
{
if (!semaphore_p())
exit(EXIT_FAILURE);
printf("%c", op_char);
fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);
fflush(stdout);
/*进入临界区域后,调用semaphore_v将信号量设置为可用,然后等待一段随机的时间,再进入下一次循环*/
if (!semaphore_v())
exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n", getpid());
if (argc > 1)
{
sleep(10);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
/*该函数用来将semctl调用的command参数设置为SETVAL来初始化信号量*/
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
return0;
return (1);
}
/*通过调用semctl调用的command设置为IPC_RMID来删除信号量ID*/
static void del_semvalue(void)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore");
}
/*对信号量执行减1操作*/
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
return (0);
}
return (1);
}
/*对信号量执行加1操作*/
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return (0);
}
return (1);
}
#include
#include
#include
#include
#include
#include
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
// struct seminfo *buff;
};
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
int id = fork();
if (id < 0)
{
perror("fork failed\n");
return -1;
}
else if (id > 0)
{
if (!set_semvalue())
{
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X';
sleep(2);
}
for (i = 0; i < 10; i++)
{
if (!semaphore_p())
exit(EXIT_FAILURE);
printf("%c", op_char);
fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);
fflush(stdout);
if (!semaphore_v())
exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n", getpid());
if (id > 0)
{
sleep(10);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
return0;
return (1);
}
static void del_semvalue(void)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore");
}
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
return (0);
}
return (1);
}
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return (0);
}
return (1);
}
socket即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
程序源码
//服务器
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server_sockfd = -1;
int client_sockfd = -1;
int client_len = 0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
//创建流套接字
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
//设置服务器接收的连接地址和监听的端口
server_addr.sin_family = AF_INET;
//指定网络套接字
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//接受所有IP地址的连接
server_addr.sin_port = htons(9736);//绑定到9736端口
//绑定(命名)套接字
bind(server_sockfd, (structsockaddr*)&server_addr, sizeof(server_addr));
//创建套接字队列,监听套接字
listen(server_sockfd, 5);
//忽略子进程停止或退出信号
signal(SIGCHLD, SIG_IGN);
while(1)
{
charch = ‘\0’;
client_len = sizeof(client_addr);
printf(“Server waiting\n”);
//接受连接,创建新的套接字
client_sockfd = accept(server_sockfd, (structsockaddr*)&client_addr, &client_len);
if(fork() == 0)
{
//子进程中,读取客户端发过来的信息,处理信息,再发送给客户端
read(client_sockfd, &ch, 1);
sleep(5);
ch++;
write(client_sockfd, &ch, 1);
close(client_sockfd);
exit(0);
}
else
{
//父进程中,关闭套接字
close(client_sockfd);
}
}
}
//客户端
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = -1;
int len = 0;
struct sockaddr_in address;
in tresult;
char ch = 'A';
//创建流套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//设置要连接的服务器的信息
address.sin_family = AF_INET;
//使用网络套接字
address.sin_addr.s_addr = inet_addr("127.0.0.1");
//服务器地址
address.sin_port = htons(9736);
//服务器所监听的端口
len = sizeof(address);
//连接到服务器
result = connect(sockfd, (structsockaddr*)&address, len);
if(result == -1)
{
perror("ops:client\n");
exit(1);
}
//发送请求给服务器 write(sockfd, &ch, 1);
//从服务器获取数据 read(sockfd, &ch, 1);
printf("char form server = %c\n", ch);
close(sockfd);
exit(0);
}