探索Linux中的基础进程与线程概念(以多进程和管道为载体的进程间通信实践入门)
先上一个多进程的简单多进程实例,此实例将在同一个程序中创建两个进程:
/*
程序说明:
创建两个进程,进程一执行”ls -l“操作,进程二做sleep操作
主进程阻塞等待进程1结束,再等待进程2结束,程序结束
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t child1,child2;
child1 = fork(); //进程1
if(child1 == -1){
perror("child1 creat error:");
exit(1);
}else
if(child1 == 0){
if(execlp("ls","ls","-l",NULL) < 0){
perror("execlp error:");
exit(1);
}
exit(0);
}
child2 = fork(); //进程2
if(child2 == -1){
perror("child2 creat error:");
exit(1);
}else
if(child2 == 0){
sleep(3);
exit(0);
}
// waitpid(child1,NULL,0); 主进程
wait(0);
while(waitpid(child2,NULL,WNOHANG)==0){
printf("child2 not exit\n");
sleep(1);
}
exit(0);
}
创建多进程的时候需要注意,不要连续创建多个进程,否则上面创建的子进程将同时创建下面的进程,向下面这样:
child1 = fork();
child2 = fork();
通过下面代码可以看出子进程1同时也创建了进程2:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t child1,child2,child_temp;
child1 = fork();
child2 = fork();
if(child1 == -1){
perror("child1 creat error:");
exit(1);
}
if(child2 == -1){
perror("child2 creat error:");
exit(1);
}
if(child1 == 0){
sleep(2);
// exit(0);
wait(0);
}
if(child2 == 0){
sleep(3);
exit(0);
}
// waitpid(child1,NULL,0);
while(waitpid(child1,NULL,WNOHANG)==0){
printf("chil1 has not exit\n");
sleep(1);
}
while(waitpid(child2,NULL,WNOHANG)==0){
printf("child2 has not exit\n");
sleep(1);
}
exit(0);
}
很多时候,不同的进程之间需要交换数据,即相互通信。而进程之间的通行方式也有多种,为了满足进程间通信在不同场景下的需求:管道通信(pipe),信号(signal),信号量(semaphore),共享内存(shared memory),消息队列(message queue),套接字(socket)。
接下来就一一来探索。
管道分为无名管道和有名管道。
无名管道:
1、基于文件描述符,只能用于具有亲缘关系的进程之间的通信;
2、是一种单工的通信协议,具有固定的读断和写断;
3、无名管道不属于任何文件系统,并且只存在于内存中。
编程细解:主要使用pipe()函数,例程为一个父进程写进,子进程读取的一个进程间通信实例。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#define MaxDataLen 256
#define DelayUTime 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MaxDataLen];
const char data[] = "Pipe Test Program";
int real_read,real_write;
memset(buf,0,sizeof(buf));
if(pipe(pipe_fd)<0)
/*
pipe运行机制:
函数原型:int pipe(int pipefd[2]);
描述:pipe()用于创建一个单向数据通道,可以用于进程间通信;
使用数组pipefd返回两个文件描述符指管的两端;
pipefd[0]是指读的管道。pipefd[1]是指写的管道;
数据在已写入管道而未被读取时由内核提供缓冲。
*/
{
perror("creat pipe failed:");
/*
perror运行机制:
函数原型:void perror(const char *S);
描述:当一个系统调用失败时,它通常会返回-1并设置变量errno用于描述这个错误(这个值可以在errno.h中找到)\
perror函数会翻译这些值成为可阅读的信息。errno会在一个成功的系统调用会是库函数调用后变成undefined。
程序中存在一个全局的错误表sys_errlist[],可以被下标errno索引。
NOTES: sys_nerr & sys_errlist 在 <stdio.h> 中
SEE ALSO: err(),errno(),error(),strerror()
*/
exit(-1);
}
if((pid = fork()) == 0){ //子进程为读端
close(pipe_fd[1]);
usleep(DelayUTime);
if((real_read = read(pipe_fd[0],buf,MaxDataLen)) > 0){
printf("%d bytes:%s\n",real_read,buf);
}
close(pipe_fd[0]);
exit(0);
}else
if(pid > 0){ //父进程为写端
close(pipe_fd[0]);
if((real_write = write(pipe_fd[1],data,strlen(data))) != -1){
printf("wrote %d bytes:'%s'\n",real_write,data);
}
close(pipe_fd[1]);
wait(0);
exit(0);
}
}
有名管道:
1、它可以用于互不相关的两个进程间通信;
2、在文件系统中可见,但还是单工通信;
3、遵循先进先出规则。
编程细解:可以先使用 mkfifo 指令在磁盘中创建一个有名管道文件,然后再程序中打开;也可以在程序中使用mkfifo()函数创建有名管道。读写依旧使用read(),write()函数。
mkfifo Linux指令:
mkfifo [OPTION]... NAME...
描述:创建名为NAME的fifo文件;
options:
-m, --mode=MODE
设置文件权限位模式
-Z 设置SELinux安全上下文的默认类型
例:mkfifo -m 777 /tmp/temp
对于有名管道,我们使用一个写端程序和一个读端程序来看进程通信:
/*
fifo_write.c
写端程序
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fifo_fd;
char buf[] = "hello";
if((fifo_fd = open("/tmp/temp",O_WRONLY,0600)) < 0){
fifo_fd = mkfifo("/tmp/temp",0777); //如没有此管道文件,mkfifo()创建管道文件
}
if(write(fifo_fd,buf,strlen(buf)) > 0){
printf("Write '%s' to FIFO\n",buf);
}
close(fifo_fd);
exit(1);
}
/*
fifo_read.c
读端程序
*/
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fifo_fd;
char buf[256];
fifo_fd = open("/tmp/temp",O_RDONLY);
if(fifo_fd < 0){
perror("open temp failed:");
exit(-1);
}
while(1){
memset(buf,0,sizeof(buf));
if(read(fifo_fd,buf,256) > 0){
printf("Read '%s' from FIFO\n",buf);
}
}
close(fifo_fd);
exit(1);
}
这个程序调试需要用到两个窗口,两个程序分开编译,先打开写程序,再打开读程序。
上一篇: 透彻解析浏览器里的进程与线程工作原理
下一篇: 理解进程与线程的基本概念