系统调用
fork
pid=fork()
fork系统调用用来从一个已存在的进程中创建一个新进程称之为子进程,该子进程是父进程的完整复制,与父进程并发运行。
fork系统调用调用一次,返回两次,在父进程返回的为子进程的pid,在子进程中返回0.所以可通过返回值来判断父进程or子进程
使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等,而子进程所独有的只有它的进程号、计时器等。
fork的另一个特性是所有由父进程打开的文件描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。
clone
int clone (int ( *fn ) (void *arg) , void *stack , int flag , void *arg) ;
其中,fn是轻进程所执行的函数;stack是轻进程所使用的栈;flag是CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGNAND, CLONE_PID的组合;arg是调用过程的对应参数。
与fork()不同的是,克隆生成的子进程继续运行时不以调用处为起点,转而去调用以参数func所指定的函数,func又称为子函数。调用子函数时的参数由func_arg指定。经过转换,子函数可对改参数的含义自由解读,例如可以作为整型值(int),也可以视为指向结构的指针。
当函数func返回或者是调用exit()(或者_exit())之后,克隆产生的子进程就会终止。照例,父进程可以通过wait()一类函数来等待克隆子进程。
因为克隆产生的子进程可能共享父进程内存,所以它不能使用父进程的栈。相反,调用者必须分配一块大小适中的内存空间供子进程的栈使用,同时将这块内存的指针置于参数child_stack中。
Clone()的关键是flag的设定,CLONE_VM表示子进程共享父进程内存,CLONE_FS表示子进程共享父进程的文件系统,CLONE_SIGNAND表示子进程共享父进程的消息处理机制,CLONE_PID是指子进程继承父进程的id号。
pipe
ret_val=pipe(int fds[2]);
用于两个进程的单向通信:一个进程的标准输出成为另一个进程的标准输入。即一个进程向管道写入,另一个进程从管道中读取。
管道文件创建后,可以被fork创建的子进程共享。
sem_wait(sem_t* sem)和sem_post(sem_t *sem)
对信号量(sem_t类型)的操作,信号量用于进程间通信,也用于同一进程不同现线程间的通信,可保证两个或多个关键代码段不被开发调用,防止多个进程同时对共享资源进行操作。
初始化
int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
- sem为指向信号量结构的一个指针;
- pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;、
- value给出了信号量的初始值。
sem_wait(sem_t* sem)
int sem_wait(sem_t * sem);
使信号量-1,但永远会先等待该信号量>0时才开始做减法
sem_post(sem_t* sem)
int sem_post(sem_t *sem);
使信号量+1,当有线程阻塞在这个信号量上时,调用此函数会使其中一个线程不再阻塞,(从阻塞队列中挑选一个线程)选择机制由线程的调度策略决定。
pthread_mutex_lock(pthread_mutex_t *mutex)和pthread_mutex_unlock(pthread_mutex_t *mutex)
对互斥量(pthread_mutex_t类型)进行加锁or释放锁。
pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_lock(pthread_mutex_t *mutex);
锁住由mutex指定的mutex 对象。如果mutex已经被锁住,调用这个函数的线程阻塞直到mutex可用为止。这跟函数返回的时候参数mutex指定的mutex对象变成锁住状态, 同时该函数的调用线程成为该mutex对象的拥有者。
初始化函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
释放有参数mutex指定的mutex对象的锁。如果被释放取决于该Mutex对象的类型属性。如果有多个线程为了获得该mutex锁阻塞,调用pthread_mutex_unlock()将是该mutex可用,一定的调度策略将被用来决定哪个线程可以获得该mutex锁。
注:
pthread_cond_wait,先会解除当前线程的互斥锁,然后挂起线程,等待条件变量满足条件。一旦条件变量满足条件,则会给线程上锁,继续执行pthread_cond_wait。
shm
进程间通信
shmget
shmget 得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符
shmat
shmat(把共享内存区对象映射到调用进程的地址空间) 连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问
shmdt
shmdt(断开共享内存连接)与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
shmctl
shmctl(共享内存管理) 完成对共享内存的控制
msg
消息队列
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的
进程间通信 pipe/sem/shm/msg的特点与比较
pipe:
A)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
B)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
C)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
sem:
A)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
B)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
C)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
D)支持信号量组。
shm:
A)共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
B)因为多个进程可以同时操作,所以需要进行同步。
C)信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
msg:
A)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
B)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
C)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
对比:
1.无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错。
2.消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的复制需要额外消耗CPU的时间.不适宜于信息量大或操作频繁的场合。
3.共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。