1、Linux系统中进程的概念,以及创建进程
在Linux中主要提供了fork、vfork、clone三个进程创建方法。
#include <sys/types.h> #include <unistd.h>
int main(){ printf("process id is :%d \n",getpid());
pid_t pid = fork();
sleep(1);
if (pid == 0){ printf("我是子进程,id:%d\n",getpid()); } else if (pid > 0){ printf("我是父进程,id:%d\n",getpid()); } }
|
执行结果:
process id is :13078 我是子进程,id:13079 我是父进程,id:13078
|
为什么执行了两次了,这就是fork机制导致的
fork函数创建了一个新的进程,新进程(子进程)与原有的进程(父进程)一模一样。子进程和父进程使用相同的代码段;子进程拷贝了父进程的堆栈段和数据段。子进程一旦开始运行,它复制了父进程的一切数据,然后各自运行,相互之间没有影响。
线程与进程区别,以及通信方式
- 线程之间是共享进程资源的,实现线程通信的方式很多种,共享对象,消息队列,注意的是线程通信需要注意线程安全问题,在这里不详细介绍
- 进程通信基本是依靠系统提供的方式,在不同系统上有独特的进程通信实现方式,在Linux上大致分为这几种:
- 管道(无名管道Pipe,有名管道FIFO)
- 信号量
- Socket
- 共享内存
- mmap
参考:
2、实现进程通信
2.1 无名管道Pipe
管道概念
1、管道是操作提供的一块操作系统内存;
2、管道是Unix中最古老的进程间通信方式;
3、我们把一个进程连接到另一进程的数据流称为管道。
管道函数:int pipe(int pipefd[2])
实现代码:
#include <stdio.h> #include <sys/types.h> #include <unistd.h>
void test_pipe() { int fd[2]; pipe(fd);
pid_t pid = fork();
if (pid == 0) { write(fd[1],"hello",5); } else if (pid > 0) { char buf[12]; int ret = read(fd[0],buf,sizeof(buf)); if (ret > 0) { } printf("pipe read:%s\n",buf); } }
输出: pipe read:hello��$�
|
参考:
2.2 命名管道(FIFO)
FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。
FIFO是unix基础文件类型中的一种。但,FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。备进程可以打开这个文件进行 read/write,实际上是在读写内核通道,这样就实现了进程间通信。
创建管道
使用命令:mkfifo myfifo
使用函数:int mkfifo(const char *pathname, mode_t mode);
成功:0;失败:-1
原理:内核会针对fifo文件开辟一个缓冲区,操作FIFO文件,可以操作缓冲区,实现进程通信。一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件IO函数都可以用于FIFO。如:close、read、write、unlink等
创建打开FIFO并将文件中的字符串写入FIFO:
#include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> int main(int argc, char **argv) { if(argc != 2){ fprintf(stderr,"usage:%s srcfile\n",argv[0]); exit(EXIT_FAILURE); } int infd; infd = open(argv[1],O_RDONLY); if(infd == -1){ perror("open error"); exit(EXIT_FAILURE); } if(mkfifo("tmpfifo",0644) == -1){ perror("mkfifo error"); exit(EXIT_FAILURE); } int fd ; fd = open("tmpfifo",O_WRONLY); if(fd == -1){ perror("open error"); exit(EXIT_FAILURE); } char buf[1024*4]; int n = 0; while((n = read(infd,buf,1024*4))){ write(fd,buf,n); } close(infd); close(fd); printf("write success\n"); return 0; }
|
打开FIFO读取FIFO中的字符串,放入新文件中:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> int main(int argc, char **argv) { if(argc != 3){ fprintf(stderr,"usage:%s desfile\n",argv[0]); exit(EXIT_FAILURE); } int outfd; outfd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC); if(outfd == -1){ perror("open error"); exit(EXIT_FAILURE); } int fd ; fd = open(argv[2],O_RDONLY); if(fd == -1){ perror("open error"); exit(EXIT_FAILURE); } char buf[1024*4]; int n = 0; while((n = read(fd,buf,1024*4))){ write(outfd,buf,n); } close(fd); close(outfd); unlink(argv[2]); printf("read success\n"); return 0; }
|
2.3 信号量(SEMAPHORE)
Linux中信号和信号量是存在区别的:
- Linux内核的信号量用来操作系统进程间同步访问共享资源。
- signal,又简称为信号(软中断信号 )用来通知进程发生了异步事件。
具体信号量实现,参考:
2.4 共享内存(SHARE MEMORY)
2.5 内存映射(MAPPED MEMORY)
很多黑科技开源库都是基于mmap实现的,理解mmap是提升代码能力的必不可少的。
mmap函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
- addr : 映射地址,可以传NULL
- length:映射区的长度
- prot:
- PROT_EXEC pages may be executed.
- PROT_READ pages may be read.
- PROT_WRITE pages may be written.
- PROT_NONE pages may not be accessed.
- flags:
- MAP_SHARED 映射区是共享的,对内存的修改会影响到源文件
- MAP_PRIVATE 映射区是私有的
- fd:文件描述符,open打开一个文件
- offset:偏移量
- 返回值
- 成功:返回可用内存首地址
- 失败:返回MAP_FAILED
释放mmap内存函数原型:
int munmap(void *addr, size_t length);
|
- addr:传mmap的返回值
- length:mmap创建长度
- 返回值:
void test_mmap() { int fd = open("/tmp/mem.txt",O_RDWR|O_CREAT); printf("fd:%d\n",fd); char* mem = (char*)mmap(nullptr,8,PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0); if (mem == MAP_FAILED) { printf("error!"); return; } strcpy(mem,"hello"); munmap(mem,8); close(fd); }
|
2.6 消息队列(MESSAGE QUEUE)