【linux服务器编程学习】9、多进程编程

多进程编程

多进程编程主要包括

  • 复制进程映像的fork和替换进程映像的exec系列
  • 进程间通信(IPC)的方式,包括管道、信号量、消息队列、共享内存。

fork

#include<sys/type.h>
#include<unistd.h>

pid_t fork(void);

linux下创建一个新进程。

该函数返回两次,在父进程中返回子进程的pid,在子进程中返回0,以此来判断进程是父进程还是子进程。
失败时返回-1并设置errno

fork函数复制当前进程,在内核进程表中创建一个新进程表项,很多属性与父进程相同,比如堆指针、栈指针、标志寄存器的值,但是pid和信号处理都不同了。

子进程的代码和父进程完全相同,还会复制父进程的数据,如堆数据、栈数据、静态数据,打开的文件描述符也是一样的,被引用计数,当父子进程都关闭时才完全关闭文件描述符。

exec系列

在子进程中执行其他程序,即替换当前进程映像,用到exec系列。

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[]);

path 参数表示你要启动程序的名称包括路径名。

arg 参数表示启动程序所带的参数,
一般第一个参数为要执行命令名,
不是带路径且 arg 必须以NULL 结束。

返回值:成功返回 0,失败返回 -11、带 l 的exec函数:execl、execlp、execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。
2、带 p 的 exec 函数:execlp、execvp,表示第一个参数 path 不用输入完整路径,只有给出命令名即可,它会在环境变量 PATH 当中查找命令
3、不带 l 的 exec 函数:execv、execvp 表示命令所需的参数以 char *arg[] 形式给出且 arg 最后一个元素必须是 NULL4、带 e 的 exec 函数:execle 表示,将环境变量传递给需要替换的进程

参考:exec系列函数(execl、execlp、execle、execv、execvp)使用

处理僵尸进程

对于多进程,父进程一般需要跟踪子进程的退出状态,当子进程结束运行时,内核不会立即释放其进程表项,让父进程后续对该子进程退出信息查询。在子进程结束运行,父进程读取其退出状态之前,称该子进程处于僵尸态。另一种让子进程处于僵尸态的情况是,父进程结束或异常终止,而子进程继续运行。此时子进程的ppid被设为1,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);

wait将阻塞进程,知道进程的某个子进程结束运行为止,返回结束运行子进程的pid,并将退出信息保存在stat_loc中。
waitpid只等待pid参数指定的子进程。如果pid取值为-1就和wait相同。

信号量

当多个进程同时访问系统上的某个资源,需要考虑进程的同步问题,确保任何时候只有一个进程可以拥有对资源的独占式访问。
信号量是一种特殊的变量,他只能取自然数值并且只支持等待和信号两种操作。
等待操作p:如果sv的值大于0就减1,如果为0,则挂起执行
信号操作v:如果进程因为其他等待被挂起,则唤醒,否则sv加1

创建信号量
int semget((key_t)key , int nsems , int flag)

key:是一个信号标识,唯一标识一个信号量集,
若多个进程使用同一个信号量集,要求在调用semget方法时的key值相同
nsems:在创建时使用,用来执行创建的信号量集中信号量的个数
flag:指定操作权限,同时可以通过设置IPC_CREAT,来指明本次需要创建的信号量集
返回值:失败返回-1,成功返回内核对象的ID值,用于后续的其他方法

改变信号量的值
int	semop(int semid,struct sembuf *buf,int buflength)
semid:semget方法返回的id值。
buf是一个sembuf类型的数组首地址
buflength:数组的元素个数
返回值:成功返回0,失败返回-1

struct sembuf{
    short sem_num;//除非使用一组信号量,否则它为0,对应信号集中的一个信号量
    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
                    //一个是+1,即V(发送信号)操作。
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
                    //并在进程没有释放该信号量而终止时,操作系统释放信号量


直接控制信号量
int semctl(int semid, int sem_num, int cmd, ...);

semid指定信号量集
sen_num 信号量集合中的下标,指定对哪个信号量进行操作,一般来说只有一个信号量,也就是为 0
cmd 指定操作类型,一般有如 GETVAL , IPC_RMID, SETVAL, SETVALL 等操作
arg用于设置或返回信号量信息

共享内存

共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。

当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。

#include<sys/shm.h>

创建共享内存
int shmget(key_t key, size_t size, int shmflg);
key,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名
size以字节为单位指定需要共享的内存容量
shmflg是权限标志,它的作用与open函数的mode参数一样,
如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。

函数成功时返回一个与key相关的共享内存标识符(非负整数),
用于后续的共享内存函数。调用失败返回-1.




启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。
void *shmat(int shm_id, const void *shm_addr, int shmflg);

shm_id是由shmget()函数返回的共享内存标识。
shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
shm_flg是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.



将共享内存从当前进程中分离。
注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。
int shmdt(const void *shmaddr)
shmaddr是shmat()函数返回的地址指针,调用成功时返回0,失败时返回-1.



用来控制共享内存
int shmctl(int shm_id, int command, struct shmid_ds *buf);
shm_id是shmget()函数返回的共享内存标识符。
command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段
buf是一个结构指针,它指向共享内存模式和访问权限的结构。

struct shmid_ds

{

    uid_t shm_perm.uid;

    uid_t shm_perm.gid;

    mode_t shm_perm.mode;

};

消息队列

消息队列是在两个进程之间传递二进制块数据的一种简单有效的方式。每个数据块都有特定类型,接收方可以根据类型有选择的接收数据。


#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>



创建和访问一个消息队列
int msgget(key_t key, int msgflg);
key:某个消息队列的名字
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1




消息队列的控制函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid:由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
返回值:成功返回0,失败返回-1
cmd取值如下:
	IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
	IPC_SET  在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
	IPC_RMID 删除消息队列




把一条消息添加到消息队列中
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
返回值:成功返回0;失败返回-1




是从一个消息队列接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备接收的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:它可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1

msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息 
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。

后记

人生大事,尽心尽力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值