Linux 高性能服务器编程——多进程编程
#include <sys/types.h>
#include <unistd.h>
pid_t fork( void );
- 该函数每次调用返回两次。
- 在父进程中返回的是子进程的PID,在子进程中返回的是0,由此判断当前进程是父进程还是子进程(返回值是0的为子进程)。
- fork函数复制当前进程,在内核进程表中创建一个新的进程表项。
- 子进程的许多属性被赋予了新的值,比如该进程的PPID 被设置成原进程的PID ,信号位图被清除(原进程设置的信号处理函数不再对新进程起作用)。
- 子进程的代码和父进程完全相同。
- 子进程复制父进程的大部分数据(堆数据,栈数据,静态数据)。
- 写时复制。即只有再任一进程(父进程或者子进程)对数据执行了写操作时,复制才会发生(先是缺页中断,然后操作系统给子进程分配内存并复制父进程的数据)。
- 父进程中打开的文件描述符等在子进程中默认打开,因此它们的引用变量均+1。
#include <unistd.h>
extern char** environ; int execl( const char* path, const char* arg, ... );
int execlp( const char* file, const char* arg, ... );
int execle( cost char* path, const char* arg, ... , char* const envp[] );
int execv( const char* path, char* const argv[] );
int execvp( const char* file, char* const argv[] );
int execve( const char* path, char* const envp[] );
- 在子进程结束之后,父进程读取其退出状态之前,该子进程处于僵尸态。
- 如果父进程退出或异常终止,而子进程继续运行,此时子进程的PPID(父进程PID)被设置成1,即init进程,即init进程接管了该进程,并等待它结束。在父进程退出之后,子进程退出之前,该子进程处于僵尸态。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait( int* stat_loc );
pid_t waitpid( pid_t pid, int* stat_loc, int options );

static void handle_child( int sig )
{
pid_t pid;
int stat;
while ( ( pid = waitpid( -1, &stat, WHOHANG )) > 0 )
{
/* 对结束的子进程进行善后处理 */
}
}

- 一种特殊的变量
- 只能取自然数值
- 只支持两种操作:等待(P,要进入临界区)和信号(V,要退出临界区)
含义:假设有信号量SV,对它的P、V操作含义如下
- P(SV),如果SV的值大于0,就将它减一,然后允许访问临界区;如果SV的值为0,则挂起进程的执行。
- V(SV),如果有其他进程因为等待SV而挂起,则唤醒之;如果没有,则将SV加一

#include <sys/sem.h>
int semget ( key_t key, int num_sems, int sem_flags );
#include <sys/sem.h>
int semop ( int sem_id, struct sembuf* sem_ops, size_t num_sem_ops );
struct sembuf
{
unsigned short int sem_num; // 信号量集中信号量的编号
short int sem_op; // 指定操作类型,可选正整数,0,负整数
short int sem_flg; // 影响操作行为,可选值IPC_NOWAIT(无论信号量操作是否成功,都立即返回),SEM_UNDO(当进程退出时,取消正在进行的semop操作)
};
#include <sys/sem.h>
int semctl ( int sem_id, int sem_num, int command, ... );
union semun
{
int val; //用于SETVAL命令
struct semid_ds *buf; //用于IPC_STAT和IPC_SET命令
unsigned short *array; //用于GETALL和SETALL命令
struct seminfo *__buf; //用于IPC_INFO命令
};
sem_ctl成功时的返回值取决于command参数,失败时返回-1并设置errno。
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h> union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
}; //op为-1时执行P操作, op为1时执行V操作
void pv(int sem_id, int op)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = op;
sem_b.sem_flg = SEM_UNDO; semop(sem_id, &sem_b, 1);
} int main(int argc, char *argv[])
{
int sem_id = semget(IPC_PRIVATE, 1, 0666); union semun sem_un;
sem_un.val = 1; semctl(sem_id, 0, SETVAL, sem_un); pid_t id = fork(); if (id < 0) {
fprintf(stderr, "fork failed.\n");
return 1;
}
else if (id == 0) {
printf("child try to get binary sem\n"); pv(sem_id, -1);
printf("child get the sem and would release it after 5 seconds\n");
sleep(5);
pv(sem_id, 1); exit(0);
}
else{
printf("parent try to get binary sem\n");
pv(sem_id, -1);
printf("parent get the sem and would release it after 3 seconds\n");
sleep(3);
pv(sem_id, 1);
} waitpid(id, NULL, 0); //删除信号量
semctl(sem_id, 0, IPC_RMID, sem_un); return 0;
}
共享内存
- 不涉及进程之间的任何数据传输。
- 必须用其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。
- 因此,共享内存通常和其他进程间通信方式一起使用
#include <sys/shm.h>
int shmget ( key_t key, size_t size, int shmflg );
- SHM_HUGETLB:类似于mmap的MAP_HUGETLB标志,系统将使用“大页面”来为共享内存分配空间。
- SHM_NORESERVE:类似于mmap的MAP_NORESERVE标志,不为共享内存保留交换分区(swap空间)。这样,当物理内存不足的时候,对该共享内存执行写的操作将触发SIGSEGV信号。
- 共享内存在创建之后,不能立即使用,需要先将它关联到进程的地址空间中。——shmat
- 使用完共享内存后,我们也需要把它从进程地址空间中分离。——shmdt
定义:
#include <sys/shm.h>
void shmat ( int shm_id, const void * shm_addr, int shmflg );
int shmdt ( const void* shm_addr );
- 如果shm_addr为NULL,则被关联的地址由操作系统选择。
- 如果shm_addr非空,并且SHM_RND标志未被设置,则共享内存被关联到addr指定的地址处。
- 如果shm_addr非空,并且SHM_RND标志被设置,则被关联的地址是[ shm_addr -(shm_addr % SHMLBA) ]。SHMLBA的含义是“段低端边界地址倍数”,它必须是内存页面大小的整数倍。现在的Linux 内核中,它等于一个内存页大小。SHM_RND 的含义是圆整,即将共享内存被关联的地址向下圆整到离shm_addr最近的SHMLBA 的整数倍地址处。
- SHM_RDONLY:进程仅能读取共享内存中的内容。若没有指定该标志,则进程可同时共享内存进行读写操作(当然,这需要在创建共享内存的时候指定其读写权限)。
- SHM_REMAP:如果地址shm_add已经被关联到一段共享内存上,则重新关联。
- SHM_EXEC:它指定对共享内存段的执行权限。对共享内存而言,执行权限实际上和读权限是一样的。
#include <sys/shm.h>
int shmctl ( int shm_id , int command, struct shmid_ds* buf );

#include <sys/mman.h>
#include <sys/stat.h>
#include <fnctl.h>
int shm_open ( const char* name, int oflag, mode_t mode );
- O_RDONLY:以只读方式打开共享内存对象。
- O_RDWR:以可读、可写方式打开共享内存对象。
- O_CREAT:如果共享内存不存在,则创建之。此时mode参数的最低9位将指定该共享内存对象的访问权限。共享内存对象被创建的时候,其初始长度为0.
- O_EXCL:和O_CREAT一起使用,如果由name指定的共享内存对象已经存在,则shm_open调用返回错误,否则就创建一个新的共享内存对象。
- O_TRUNC:如果共享内存已经存在,则把它截断,使其长度为0.
#include <sys/mman.h>
#include <sys/stat.h>
#include <fnctl.h>
int shm_unlink ( const char *name );
#include <sys/msg.h>
int msgget ( key_t key, int msgflg );
#include <sys/msg.h>;
int msgsnd ( int msgid, const void* msg_ptr, size_t msg_sz, int msgflg );
- msgid:由msgget函数返回的消息队列标识符。
- msg_ptr:指向一个准备发送的消息,消息必须被定义为如下的类型:
struct msgbuf
{
long mtype; // 消息类型
char mtext[512]; // 消息数据
};
- msgflg:控制msgsnd的行为。它通常仅支持IPC_NOWAIT标志,即以非阻塞的方式发送消息。默认情况下,发送消息时如果消息队列满了,则msgsnd函数将阻塞。若IPC_NOWAIT标志被指定,则msgsnd将立即返回并设置errno 为EAGAIN。
- 消息队列被移除。此时msgsnd调用将立即返回并设置errno为EIDRM。
- 程序接收到信号。此时msgsnd调用将立即返回并设置errno为EINTR。
#include <sys/msg.h>
int msgrcv ( int msqid, void* msg_ptr, size_t msg_sz, long int msgtype, int msgflg );
- msqid:由msgget调用返回的消息队列标识符。
- msg_ptr:用于存储接收的消息。
- msg_sz:指的是消息数据部分的长度。
- msgtype:指定接收何种类型的消息,可以如下几种方式:(1) msgtype 等于0 。读取消息队列中的第一个消息。 (2)msgtype大于0 。读取消息队列中第一个类型为msgtype的消息。 (3)msgtype 小于0 。读取消息队列中第一个类型值比msgtype的绝对值小的消息。
- msgflg:控制msgrcv 函数的行为。它可以是如下一些标志的按位或:(1)IPC_NOWAIT,如果消息队列中没有任何消息,则msgrcv调用立即返回并设置errno为ENOMSG。(2)MSG_EXCEPT,如果msgtype大于0,则接收消息队列中第一个非msgtype 类型的消息。 (3)MSG_NOERROR,如果消息数据部分的长度超过了msg_sz,就将它截断。
- 消息队列被移除。此时msgsnd调用将立即返回并设置errno为EIDRM。
- 程序接收到信号。此时msgsnd调用将立即返回并设置errno为EINTR。
#include <sys/msg.h>
int msgctl ( int msqid, int command, struct msqid_ds* buf );
- 三种System V IPC进程间通信方式都使用一个全局唯一的键值来描述一个共享资源。使用ipcs命令可以查看当前系统的共享资源实例。
- 要尽可能缩短僵尸进程的存在时间
- 简单了解一下fork和exec系系统调用
Linux 高性能服务器编程——多进程编程的更多相关文章
- Linux高性能server规划——多进程编程
多进程编程 多进程编程包含例如以下内容: 复制进程影映像的fork系统调用和替换进程映像的exec系列系统调用. 僵尸进程以及怎样避免僵尸进程 进程间通信(Inter-Process Communic ...
- Linux 高性能服务器编程——高性能服务器程序框架
问题聚焦: 核心章节. 服务器一般分为如下三个主要模块:I/O处理单元(四种I/O模型,两种高效事件处理模块),逻辑单元(两种高效并发模式,有效状态机)和存储单元(不讨论). 服务器模 ...
- Linux 高性能服务器编程——Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字(so ...
- linux高性能服务器编程
<Linux高性能服务器编程>:当当网.亚马逊 目录: 第一章:tcp/ip协议族 第二章:ip协议族 第三章:tcp协议详解 第四章:tcp/ip通信案例:访问Internet 第五章: ...
- linux高性能服务器编程 (一) --Tcp/Ip协议族
前言: 在学习swoole入门基础的过程中,遇到了很多知识瓶颈,比方说多进程.多线程.以及进程池和线程池等都有诸多的疑惑.之前也有学习相关知识,但只是单纯的知识面了解.而没有真正的学习他们的来龙去脉. ...
- Linux 高性能服务器编程——多线程编程
问题聚焦: 在简单地介绍线程的基本知识之后,主要讨论三个方面的内容: 1 创建线程和结束线程: 2 读取和设置线程属性: 3 线程同步方式:POSIX信号量,互斥锁和条件变量 ...
- Linux 高性能服务器编程——I/O复用
问题聚焦: 前篇提到了I/O处理单元的四种I/O模型. 本篇详细介绍实现这些I/O模型所用到的相关技术. 核心思想:I/O复用 使用情景: 客户端程序要同时处理多个socket ...
- Linux 高性能服务器编程——Linux服务器程序规范
问题聚焦: 除了网络通信外,服务器程序通常还必须考虑许多其他细节问题,这些细节问题涉及面逛且零碎,而且基本上是模板式的,所以称之为服务器程序规范. 工欲善其事,必先利其器,这篇主要来探 ...
- Linux 高性能服务器编程——TCP协议详解
问题聚焦: 本节从如下四个方面讨论TCP协议: TCP头部信息:指定通信的源端端口号.目的端端口号.管理TCP连接,控制两个方向的数据流 TCP状态转移过程:TCP连接的任意一 ...
随机推荐
- [LeetCode] Monotone Increasing Digits 单调递增数字
Given a non-negative integer N, find the largest number that is less than or equal to N with monoton ...
- Who do you want to be bad? (谁会是坏人?)人工智能机器小爱的问话
人工智能的语言理解一直是一个千古谜团. 正如人工智能机器小爱(A.L.I.C.E)的问话:“Who do you want to be bad ?(谁会是坏人?)” 纵观世界上的140多种语言,汉语是 ...
- 机器学习基石:05 Training versus Testing
train:A根据给定训练集D在H中选出g,使得Ein(g)约等于0: test:g在整个输入空间X上的表现要约等于在训练集D上的表现,使得Eout(g)约等于Ein(g). 如果|H|小,更易保证t ...
- ●UVa 11346 Probability
题链: https://vjudge.net/problem/UVA-11346题解: 连续概率,积分 由于对称性,我们只用考虑第一象限即可. 如果要使得面积大于S,即xy>S, 那么可以选取的 ...
- UVALive - 3942:Remember the Word
发现字典里面的单词数目多且长度短,可以用字典树保存 f[i]表示s[i~L]的分割方式,则有f[i]=∑f[i+len(word[j])] 其中word[j]为s[i~L]的前缀 注意字典树又叫前 ...
- 【BZOJ1483】【HNOI2009】梦幻布丁
题意:n个连续的点,有若干种颜色,每个颜色会因为某些操作变为另一种颜色,动态查询颜色段数. 解题思路:对每个颜色开一棵平衡树启发式合并应该是最裸的想法,但是我们有更优的! 考虑对每个颜色利用链表储存它 ...
- codeforces round #419 A. Karen and Morning
Karen is getting ready for a new school day! It is currently hh:mm, given in a 24-hour format. As yo ...
- poj 3693 后缀数组 重复次数最多的连续重复子串
Maximum repetition substring Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8669 Acc ...
- 有源汇上下界可行流(POJ2396)
题意:给出一个n*m的矩阵的每行和及每列和,还有一些格子的限制,求一组合法方案. 源点向行,汇点向列,连一条上下界均为和的边. 对于某格的限制,从它所在行向所在列连其上下界的边. 求有源汇上下界可行流 ...
- 【Python系列】HDF5文件介绍
一个HDF5文件是一种存放两类对象的容器:dataset和group. Dataset是类似于数组的数据集,而group是类似文件夹一样的容器,存放dataset和其他group.在使用h5py的时候 ...