总结《Linux高性能服务器编程》第13章
Linux下创建新进程的系统调用是fork:
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
使用exec系列系统调用==在子进程中执行其他程序==,即替换当前进程映像:
#include<unistd.h>
extern char**environ;
int execl(const char*path,const char*arg,...);
int execlp(const char*file,const char*arg,...);
int execle(const 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 argv[],char*const envp[]);
父进程一般需要跟踪子进程的退出状态,当子进程结束运行时,内核不会立即释放该进程的进程表表项,以满足父进程后续对该子进程退出信息的查询;
僵尸进程:父进程没有正确处理子进程的返回信息,子进程将停留在僵尸态,并占据内核资源;
处理僵尸进程的函数,在父进程中调用;
#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);
管道是进程间通信的常用手段,但只能用于有关联的两个进程间的通信;
管道能在父、子进程间传递数据,父进程和子进程必须有一个关闭fd[0],另一个关闭fd[1];

socketpair;当多个进程同时访问系统上的某个资源时,需要考虑进程的同步问题,以确保任一时刻只有一个进程可以拥有对资源(关键代码段或临界区)的独占式访问;
信号量是一种特殊的变量,它只能取自然数值并且只支持两种操作:==P(占有,进入临界区)==和 V(释放,退出临界区):
P(SV),如果SV的值大于0,就将它减1;如果SV的值为0,则挂起进程的执行;
V(SV),如果有其他进程因为等待SV而挂起,则唤醒之;如果没有,则将SV加1;
Linux信号量的API都定义在sys/sem.h头文件中,主要包含3个系统调用:semget、semop和semctl;
创建一个新的信号量集,或者获取一个已经存在的信号量集
#include<sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
与每个信号量关联的一些重要的内核变量
unsigned short semval;
/*信号量的值*/
unsigned short semzcnt;
/*等待信号量值变为0的进程数量*/
unsigned short semncnt;
/*等待信号量值增加的进程数量*/
pid_t sempid;
/*最后一次执行semop操作的进程ID*/
semop系统调用改变信号量的值,即执行P、V操作,实际上就是对以上内核变量的操作;
int semop(int sem_id,struct sembuf* sem_ops, size_t num_sem_ops);
sem_id参数是由semget调用返回的信号量集标识符;
sem_ops参数指向一个sembuf结构体类型的数组
struct sembuf{
//sem_num成员是信号量的编号,sem_op成员指定操作类型,其可选值为正整数、0和负整数
unsigned short int sem_num; short int sem_op;
short int sem_flg; //可选值是IPC_NOWAIT和SEM_UNDO
}
semop对数组sem_ops中的每个成员按照数组顺序依次执行原子操作,以避免别的进程在同一时刻按照不同的顺序对该信号集中的信号量执行semop操作导致的竞态条件;
semctl系统调用允许调用者对信号量进行直接控制
#include<sys/sem.h>
int semctl(int sem_id, int sem_num, int command,...);

shmget、shmat、shmdt和shmctl;shmget系统调用创建一段新的共享内存,或者获取一段已经存在的共享内存
#include<sys/shm.h>
int shmget(key_t key,size_t size,int shmflg);
共享内存被创建/获取之后,需要先将它关联到进程的地址空间中;
使用完共享内存之后需要将它从进程地址空间中分离;
#include<sys/shm.h>
void*shmat(int shm_id, const void* shm_addr, int shmflg);
int shmdt(const void* shm_addr);
shmctl系统调用控制共享内存的某些属性;
#include<sys/shm.h>
int shmctl(int shm_id,int command,struct shmid_ds*buf);

POSIX:可移植操作系统接口(Portable Operating System Interface of UNIX)
mmap函数可以实现父、子进程或无关进程之间的内存共享;
使用mmap需要先使用如下函数来创建或打开一个POSIX共享内存对象
#include<sys/mman.h>
#include<sys/stat.h>
#include<fcntl.h>
int shm_open(const char*name,int oflag,mode_t mode);
由shm_open创建的共享内存对象使用完之后也需要被删除;
#include<sys/mman.h>
#include<sys/stat.h>
#include<fcntl.h>
int shm_unlink(const char*name);
代码清单13-4 使用共享内存的聊天室服务器程序
msgget、msgsnd、msgrcv和msgctl;msgget系统调用创建一个消息队列,或者获取一个已有的消息队列
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
如果msgget用于创建消息队列,则与之关联的内核数据结构msqid_ds将被创建并初始化;
msgsnd系统调用把一条消息添加到消息队列中
#include<sys/msg.h>
int msgsnd(int msqid,const void*msg_ptr,size_t msg_sz,int msgflg);
msqid参数是由msgget调用返回的消息队列标识符;
msg_ptr参数指向一个准备发送的消息,消息必须被定义为如下类型:
struct msgbuf
{
long mtype;/*消息类型*/
char mtext[512];/*消息数据*/
};
默认情况下,发送消息时如果消息队列满了,则msgsnd将阻塞;
msgflg参数控制msgsnd的行为,它通常仅支持IPC_NOWAIT标志,即以非阻塞的方式发送消息;
msgrcv系统调用从消息队列中获取消息
#include<sys/msg·h>
int msgrcv(int msqid, void* msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
msg_ptr参数用于存储接收的消息,msg_sz参数指的是消息数据部分的长度;
msgtype参数指定接收何种类型的消息
参数msgflg控制msgrcv函数的行为:IPC_NOWAIT、MSG_EXCEPT、MSG_NOERROR;
msgrcv成功时返回0,失败则返回-1并设置errno,msgrcv成功时将修改内核数据结构msqid_ds的部分字段;
msgctl系统调用控制消息队列的某些属性
#include<sys/msg.h>
int msgctl(int msqid,int command,struct msqid_ds*buf);
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LburDwG2-1668428072940)(img/Linux高性能服务器编程/image-20221029155521449.png)]](https://1000bd.com/contentImg/2024/04/23/8bce1852d3401bba.png)
上述3种System V IPC进程间通信方式都使用一个全局唯一的键值(key)来描述一个共享资源;
Linux提供了ipcs命令,以观察当前系统上拥有哪些共享资源实例;
可以使用ipcrm命令来删除遗留在系统中的共享资源;
e.g. 代码清单13-5 在进程间传递文件描述符