进程之IPC通信二
4.共享内存
共享内存是进程间通信一种方式,多个进程共享一段内存,“ 共享内存 ”。由于多个进程共享了同一段内
存,这个段内存既是你的也是我的。也就是你往这个内存里面写入数据,实际上就相当于往我的内存里
面写入数据。比起其他 IPC 方式( pipe fifo message..
)少拷贝操作,相对而言共享内存的效率高于其
他的。
共享内存的生存期:随内核持续性
实现方式:
- 在内核中开辟一块共享内存,其他的进程通过 “ 映射 ” 方式,获取这个段共享内存的引用(指针)
- 进程
P1
可以映射这段内存,同时其他的进程(如:P2..Pn
)也可以映射这段内存,P1
往内存里面写入数据,实际就是往P2..Pn
进程中写入数据,反之亦然
System V
和POSIX
两套方法
4.1 System V
共享内存的API
System V
( msg/shm/sem )操作流程:
4.1.1 创建或者是打开一个IPC
设施
ftok
:创建或者是打开一个IPC
设施( msg/shm/sem
)也就是一个 System V IPC 对象的" 钥匙 "( KEY )
- 函数原型
#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
- 函数描述
创建或者是打开一个IPC设施(msg/shm/sem)
也就是一个System V IPC
对象的"钥 匙"(KEY )
-
参数
-
pathname: 一个在文件系统中路径名(这个路径必须要存在) 创建或打开System V 共享内存
-
shmget
:通过ftok
获取到的IPC
设施的钥匙来创建或打开一个System V
共享内存 映射/解映射- 映射:把内核或者设备的文件中的一段内存映射到进程的地址空间去,用进程的一个指针,去 访问这段 内存。
-
shmat
:通过拿到的共享内存 ID 映射共享内存
-
-
proj_id
:0 int- 类型整数,一般用工程的代号。
-
-
**返回值 ** @return
- 成功生成一个唯一的
System V
的IPC
设施的KEY
- 失败返回-1,同时
errno
被设置
- 成功生成一个唯一的
4.1.2 创建或打开System V 共享内存
shmget
:通过 ftok
获取到的 IPC
设施的钥匙来创建或打开一个 System V
共享内存
- 函数原型
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
-
函数描述:
shmget
用来在内核中创建或打开一个SystemV
的共享内存 -
参数
-
@key:
System V
的IPC
设施的KEY
,一般由ftok
返回 -
@size: 字节单位,指定共享内存区域的字节大小
-
@
shmflg
: 标志位- (1)创建:
IPC_CREAT
| 权限位 - ( 2)打开:0
- (1)创建:
-
参数 @return:
-
成功返回共享内存区域的ID号,这个ID就唯一标识了这个共享内存
-
失败返回-1,同时
errno
被设置
-
4.1.3 映射/解映射
映射:把内核或者设备的文件中的一段内存映射到进程的地址空间去,用进程的一个指针,去访问这段
内存。
shmat
:通过拿到的共享内存 ID 映射共享内存
- 映射函数原型
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
函数描述
- 用来映射一段System V的共享内存
-
参数
-
@shmid
: 要映射的那个共享内存的id号,一般由shmget
返回 -
@shmaddr
:指定要映射到的进程的哪个地址上去一般为NULL,由系统自个决定。
-
@
shmflg
:标志位:SHM_RDONLY
只读- 读写:0
-
-
返回值 @return:
- 成功返回映射成功的首地址
- 失败返回NULL,同时
errno
被设置。
-
解映射函数原型
#include <sys/types.h>#include <sys/shm.h>
int shmdt(const void *shmaddr);
- 函数描述:
- 解除一段共享内存的映射
- 参数
@shmaddr
需要解除映射的内存首地址。
- 返回值
- 成功返回
- 失败返回-1,同时
errno
被设置
4.1.4其他操作
shmctl
:对于共享内存的操作的。
- 函数原型
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
-
函数描述:
-
对于共享内存的操作
-
参数
-
@shmid
:- 需要进程控制操作的共享内存id号
-
@
cmd
:command
操作命令,不同的命令第三个参数是不一样的。-
IPC_RMID
:删除指定的共享内存区域
-
如果命令为
IPC_RMID
的话,第三个参数为:NULL
-
-
@buf
:- 由第二个参数来决定第三个参数
-
- 返回值 @return:
- 成功返0
- 失败返-1,同时
errno
被设置。
示例
主进程
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "System.h"
using namespace std;// 从进程
int main()
{cout << "从进程" << endl;// 申请IPC设施的keykey_t key = ftok(IPC_PATH, IPC_PROJ );// 获取主进程申请的 共享内存id号 大小是0,权限打开就可以int shm = shmget(key, 0 , 0);// 映射共享内存到用户空间int *p_shmat =(int *)shmat(shm, nullptr, 0);//发送数据while(1){cout << "请输入:";cin >> *p_shmat;//向共享内存输入内容if( *p_shmat == -1) break;}// 解除映射shmdt(p_shmat);// 不需要删除共享内存,主进程负责return 0;
}
从进程
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "System.h"
using namespace std;
//主进程
int main()
{cout << "主进程" << endl;// 申请IPC设施的keykey_t key = ftok(IPC_PATH, IPC_PROJ );// 申请一个 System V 共享内存id号int shm = shmget(key, sizeof(int), IPC_CREAT | 0777);// 映射共享内存到用户空间int *p_shmat =(int *)shmat(shm, nullptr, 0);int old = 0 ;while(1){if(*p_shmat == -1) break;if( *p_shmat != old ){cout << "来自从进程的内容:p_shmat < " << *p_shmat << " > "<< endl;old = *p_shmat;}}// 解除映射shmdt(p_shmat);//删除指定的共享内存区域shmctl(shm, IPC_RMID, nullptr);return 0;
}
5. 信号量
有两个以上的任务(进程/线程)并发的实体,去访问同一个共享资源(硬件上,软件上)的时候,那么 要保证访问的这个共享资源是有序访问,如果不是有序访问有可能造成不可预知后果。
// P1:
void readContent(){// 1.txtchar buffer[1024]={0};read(fd,buffer,1024);....
}// P2:
void writeContent(){// 1.txtchar buffer[1024]={"123"};write(fd,buffer,3);....}
有两个实例(任务),调用 readContent 函数,那么读出最后的值是多少。有可能是 123 也有可能是
其他值。
其他值不是我们想要的,所以我们要保证多个实例能够有序的去访问,就需要对共享资源进行某种保
护,以便实例可以有序的访问,避免竞争
分析:
- 并发---->竞争---->共享资源的非法访问 ---->程序行为异常…
解决方法:
- 能不能不用并发?
- 显然不行
- 在保留并发前提下,“ 避免竞争 ” ===> 访问共享资源的时候,严格串行!!!!
5…1 信号量机制
信号量是个什么玩意?
信号量的作用是什么?
为什么要用到信号量?
信号量是怎么达到目的的?
5.1.1 信号量是个什么玩意?
信号量(semaphore)是一种用于提供不同进程的间或一个进程内部不同线程间的同步的一种机制。
- 进程/线程:任务,并发的实体
- 同步:并发实体间,相互等待相互约束的,有序的,有条件的访问。
信号量就是为了保护共享资源,让共享资源有序的访问的一种机制。
信号量目标:为了保护共享资源,使其能够被有序访问。
信号量是我们程序界最高尚的一种东西,因为它不是为了自己存在而存在,是为了别人而存在的。(它
保护的对象,共享资源)“ 保镖 ”
5.1.2 什么时候使用信号量?
- 有保护对象的时候,才需要信号量
- 首先搞清楚,谁需要保护,保护谁?
- 一个被保护对象,需要一个信号量。
5.1.3 如何来保护?
保护 是指,让这个被保护对象( 共享资源 )有序的访问。如: 互斥 ”
“ 共享资源 ”:大家都访问的资源。
信号量机制,其实是程序员之间的一种**约定,**用来保护共享资源的。比如说进程A和进程B,都要访问一
个互斥设备,那么我们可以使用一个信号量来表示能不能访问该设备,然后每个进程访问该设备的时
候,先去访问信号量,如果能访问设备就把信号量设置为“ NO ”,访问完毕之后再将信号量设置为 “YES
”。
在访问共享资源的时候,先去判断,共享资源是否能够访问。
- 能访问:你就获取到了该信号量( 变成不可访问 ),则进入能访问之后的代码。
- 不能访问: wait 直到信号量变成:能访问。
访问共享资源的代码区域叫做:临界区
LOCK
上锁- 操作共享资源的代码
UnLOCK
解锁
5.1.4 信号量是如何实现的?
“ 信号量 ”:大家都可以访问的一个整数。
一个进程/线程可以在某个信号量上执行以下三种操作:
-
创建 ( create ) 一个信号量:这还要求调用者指定信号量的初始值。
-
初始值表示该信号量保护的共享资源,可以同时被多少个任务访问。
-
sem
–> 5 表示此刻有5个进程或者线程去同时访问它所保护的共享资源。 -
sem
--> 1 表示此刻有一个进程或者线程可以去访问它所保护的共享资源。- “ 互斥信号量 ”
-
-
等待( wait )一个信号量
-
该操作会测试这个信号量的值,如其值 <= 0,那么会等待( 阻塞 )一旦其值>0,这个时候,将它-1,并继续往下执行临界区代码。
-
其函数实现如下:
-
while(semaphore_value <= 0){// wait block .... 卡死在这里 }semaphore_value--; // 表示该进程/线程获取到了信号量 // 下面的代码就是对于获取到该信号了之后,对共享资源的操作。
-
-
上述操作必须是 “ 原子操作 ”:不允许有两个及以上的进程同时操作。
-
P操作:
proberen
( 尝试 )荷兰语- down/lock 上锁
-
-
释放一个信号量:**该操作将信号量的值+1,**其函数实现类型如下
-
semaphore_value++; // 原子操作
-
V操作:
verhogen ( 增加 )
荷兰语- up / unlock 解锁
-
信号量保护的目标是通过如下方式实现:
- 在临界区的前面加上一个:P操作
- 在临界区的后面加上一个:V操作
5.2 Linux内核信号量的具体实现
System V
信号量POSIX
信号量
5.2.1 System V semaphore
System V
信号量的大概流程
ftok
:获取System V IPC
设施对象的keysemget
:在内核中创建或打开一个System V
信号量- P/V操作
System V信号量
-
计数信号量集(计数信号量数组):
-
计数信号量:
-
该信号量的值可以是> 1 的值,它所保护共享资源允许多个任务同时访问它。
-
计数值 1 , 0 ===> 互斥信号量
-
-
互斥信号量:
- 该信号量的值要么是1,要么是0,它所保护的共享资源同一时刻只能允许一个任务访问。
为什么System V要把信号量弄成一个信号量集(信号量数组)呢?
P(S1 & S2)
的这种情况…
5.3 信号量的API
函数
SystemV IPC
信号量
semget
emget
:用来创建或打开一个**System V信号量
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);/*作用:用来创建或打开一个System V信号量@key:System V IPC设施的key,有ftok返回@nsems:要创建的信号量集中的信号量的数量如果我们不是创建而是打开一个已经存在的信号量集,此处参数可以为0,一旦创建完成一个信号量集,其信号量的个数就不能改变了。@semflg:标志位:创建:IPC_CREAT | 权限位打开:0@return:成功返回System V信号量集的id号失败返回-1,同时errno被设置
*/
注意:在一个新创建的信号量集中的信号量的值,是不确定的。所以要初始化
semctl
semctl :控制操作
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);/*作用:控制操作,设置或获取信号量集中某个或者某些信号量的值@semid:信号量集的id号,由semget返回@semnum:要操作的信号量集中的哪个信号量,就是信号量数组的下标,从0开始到nsems-1@cmd:command 命令,操作信号量集常用命令:GETVAL:获取第semnum哪个对应的信号量的值SETVAL:设置第semnum哪个对应的信号量的值GETALL:获取这个信号量集中的所有信号量的值SETALL:设置这个信号量集中的所有信号量的值IPC_RMID:删除这个信号量集...@...:针对不同的命令号,第四个参数不一样cmd == GETVAL第四个参数不需要cmd == SETVAL第四个参数表示位要设置的信号量的值cmd == GETALL第四个参数表示所有信号量的值,为:unsigned short vals[]...@return:根据命令不同,返回值的含义不一样如:如:GETVAL 返回信号量的值正常情况成功返回0,失败返回-1*/
semop
semop
: System V 信号量的 PV
操作
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);
/*作用:System V信号量的PV操作@semid:需要PV操作的信号量集的id号@sops:用来描述System V的PV操作struct sembuf{unsigned short sem_num; 需要操作的信号量下标short sem_op;>0:表示V操作 解锁=0:表示try 尝试是否会阻塞<0:P操作,上锁semval(信号量的值) == 原semval + sem_opshort sem_flg;0:默认,如果P操作做不了,则会阻塞:死等IPC_NOWAIT:非阻塞不等待如果是P操作做不了,直接走人,能获取就获取。SEM_UNDO:撤销为了防止进程带锁退出。if you set SEM_UNDO这个标志,内核会额外记录该进程对信号量的所有PV操
作,然后再该进程退出的时候,会还原操作。如:// 正常情况P V P V kill-1 +1 -1 +1 0// 带锁退出P V P kill-1 +1 -1 (+1)};int semtimedop(int semid, struct sembuf *sops, size_t nsops,const struct
timespec *timeout);/*作用:限时等待@semid:需要限时等待的信号量集的id@sops:用来描述System V的PV操作@nsops:第二个参数的数量struct sembuf 是对于信号量集的PV操作,但是信号量集中不一定只有一个信号量,所以
PV操作也不止一个。@timeout:超时时间:struct timespec{long tv_sec; // 秒数long tv_nsec;// 纳秒1s ==> 1000ms1ms ==> 1000us1us ==> 1000ns};如:struct timespec tv;tv.tv_sec = 5; // 五秒tv.tv_nsec = 0;*/
示例
System.h
文件 共享内存通信所需的宏
#ifndef __SHM_H__
#define __SHM_H__#define IPC_PATH "/home/wanfeng"
#define IPC_PROJ 200206#endif
主进程
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "System.h"
#include <unistd.h>
using namespace std;
//主进程
int main()
{cout << "主进程" << endl;// 申请IPC设施的keykey_t key = ftok(IPC_PATH, IPC_PROJ );//申请信号量int sem_id = semget(key, 2, IPC_CREAT | 0777 );// 初始化信号量int set_val1 = 1;int set_val2 = 0;semctl(sem_id, 0, SETVAL, set_val1);//(互斥锁)semctl(sem_id, 1, SETVAL, set_val2);//互斥信号锁// 申请一个 System V 共享内存id号int shm = shmget(key, sizeof(int), IPC_CREAT | 0777);// 映射共享内存到用户空间int *p_shmat =(int *)shmat(shm, nullptr, 0);while(1){// usleep(5000);//等待同步信号量struct sembuf sem_fp ;sem_fp.sem_num = 1;// 需要操作的信号量下标sem_fp.sem_op = - 1; // 上锁sem_fp.sem_flg = SEM_UNDO; //死锁的是时候撤销// 互斥锁上锁// semop的第二个参数struct sembuf *sops成员struct sembuf sem_op ;sem_op.sem_num = 0;// 需要操作的信号量下标sem_op.sem_op = -1; // 上锁sem_op.sem_flg = SEM_UNDO; //死锁的是时候撤销semop(sem_id, &sem_op, 1);if(*p_shmat == -1) {// 解锁并退出sem_op.sem_op = 1;semop(sem_id, &sem_op, 1);break;}cout << "来自从进程的内容:p_shmat < " << *p_shmat << " > "<< endl;// 解锁sem_op.sem_op = 1; semop(sem_id, &sem_op, 1);//解除同步信号量sem_fp.sem_op = 1; // semop(sem_id, &sem_fp, 1);}// 解除映射shmdt(p_shmat);// 删除信号量semctl(sem_id, 0, IPC_RMID, nullptr); //删除指定的共享内存区域shmctl(shm, IPC_RMID, nullptr);return 0;
}
从进程
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "System.h"
#include <sys/sem.h>
#include <unistd.h>
using namespace std;// 从进程
int main()
{cout << "从进程" << endl;// 申请IPC设施的keykey_t key = ftok(IPC_PATH, IPC_PROJ );//申请信号量int sem_id = semget(key, 2, 0);// 获取主进程申请的 共享内存id号 大小是0,权限打开就可以int shm = shmget(key, 0 , 0);// 映射共享内存到用户空间int *p_shmat =(int *)shmat(shm, nullptr, 0);//发送数据while(1){int input = 0;cout << "请输入:";cin >> input;// 上锁// semop的第二个参数struct sembuf *sops成员struct sembuf sem_op ;sem_op.sem_op = -1; // 上锁sem_op.sem_num = 0;// 需要操作的信号量下标sem_op.sem_flg = SEM_UNDO; //死锁的是时候撤销semop(sem_id, &sem_op, 1);*p_shmat = input;if( *p_shmat == -1) {// 解锁并退出 sem_op.sem_op = 1;semop(sem_id, &sem_op, 1);break;}// 解锁sem_op.sem_op = 1;semop(sem_id, &sem_op, 1);// 通知主进程struct sembuf sem_fp ;sem_fp.sem_num = 1;sem_fp.sem_op = 1; // // 需要操作的信号量下标sem_fp.sem_flg = SEM_UNDO; //死锁的是时候撤销semop(sem_id, &sem_fp, 1);// 等待主进程处理sem_fp.sem_op = -1; // semop(sem_id, &sem_fp, 1);// usleep(5000);}// 解除映射shmdt(p_shmat);// 不需要删除共享内存,主进程负责return 0;
}
5.4 POSIX
单个信号量
有名信号量:可以用于进程间或线程间 同步/互斥,在文件系统中有一个入口(有一个文件/inode),
信号量的对象(值)存在于内核中。既可以用于任意进程间,也可以用于线程间同步。
无名信号量:没有名字,无名信号量存在于内存中,无名信号量:基于内存的信号量
-
如果这段内存在一个 内核的共享内存中 进程可以访问,线程也可以访问—>既可以用于进程间的同
步/互斥,也可以用于线程间同步/互斥。
-
如果这段内存在一个 进程的地址空间 此时只用于线程间(线程内部)同步/互斥
POSIX
信号量:
- ``sem_t
描述
POSIX`信号量
5.4.1创建或打开一个POSIX
信号量
- 有名信号量
#include <fcntl.h> /* For O_* constants */#include <sys/stat.h> /* For mode constants */#include <semaphore.h>// 有名信号量
sem_t *sem_open(const char *name, int oflag);/*作用:创建或打开一个POSIX信号量@name:要创建或打开POSIX信号量在文件系统中的名字@oflag:1)打开:02)创建:O_CREAT*/sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);/*作用创建或打开一个POSIX信号量@name:要创建或打开POSIX信号量在文件系统中的名字@oflag:1)打开:02)创建:O_CREAT@mode:创建权限位,两种方式:S_IRUSR 宏定义形式...0664 八进制形式@value:指定创建的有名信号量的初始值@return:成功返回一个sem_t的指针,指向POSIX有名信号量失败返回SEM_FAILED,同时errno被设置
*/
注意:POSIX
的路径名只能在根目录下。
- 无名信号量
- 定义或分配一个无名信号量
sem_t
sem_t t1
; // 无名信号量sem_t *psem = malloc(sizeof(sem_t));
// 无名信号量
初始化无名信号量 sem_init
#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);/*作用:初始化一个无名信号量@sem:sem_t 类型的指针表示需要初始化的无名信号量的空间首地址、@pshared:该无名信号量的共享方式0:表示进程内部线程共享 // 出现重复数据的打印,是有可能的,因为互斥锁不起作
用的sem指向地址进程内部空间1:不同进程的共享 // 出现重复数据的打印,是不可能的,因为互斥锁要起作
用sem指向地址内核共享内存区域@value:表示无名信号量的初始值。 @return:成功返回0,失败返回-1,同时errno被设置*/
5.4.2 POSIX
信号量的P/V操作
- P操作
#include <semaphore.h>int sem_wait(sem_t *sem);/*作用:死等上锁@sem:指向一个POSIX信号量的空间地址需要上🔒的那个信号量@return:成功返回0,表示获取该信号量成功失败返回-1,表示该信号量获取失败
*/int sem_trywait(sem_t *sem);/*作用:尝试上锁,能够上锁就上锁,不能上锁就返回@sem:指向一个POSIX信号量的空间地址需要上🔒的那个信号量@return:成功返回0,表示获取该信号量成功失败返回-1,表示该信号量获取失败
*/int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);/*作用:限时上锁,超过绝对时间就返回@sem:指向一个POSIX信号量的空间地址需要上🔒的那个信号量@abs_timeout:绝对时间:绝对时间 = 当前时间 + 相对时间@return:成功返回0,表示获取该信号量成功失败返回-1,表示该信号量获取失败如:struct timespec ts;clock_gettime(CLOCK_REALTIME,&ts); // 获取绝对时间// 等待5s 30msts.tv_sec += 5;ts.tv_nsec += 30000000;if(ts.tv_nsec >= 1000000000){ts.tv_sec++;ts.tv_nsec-=1000000000;}int r = sem_timedwait(sem,&ts)*/
- V操作
#include <semaphore.h>int sem_post(sem_t *sem);/*
作用:
解锁
@sem:
需要解锁的信号量指针
@return:
成功返回0,失败返回-1,同时errno被设置
*/
5.4.3 POSIX
有名信号量的关闭和删除操作
sem_close
:关闭一个POSIX
有名信号量
sem_unlink
:删除一个POSIX
有名信号量
#include <semaphore.h>int sem_close(sem_t *sem);/*@sem:需要关闭的有名信号量的指针@return:成功返回0,失败返回-1,同时errno被设置
*/int sem_unlink(const char *name);/*@name:需要删除的有名信号量的路径名@return:成功返回0,失败返回-1,同时errno被设置*/
5.4.4 POSIX
无名信号量的销毁操作
sem_destroy
:销毁一个无名信号量
#include <semaphore.h>int sem_destroy(sem_t *sem);/*作用:销毁一个无名信号量@sem:需要销毁的无名信号量的指针@return:成功返回0,失败返回-1,同时errno被设置
*/
示例
主进程
6.exec函数簇
exec函数簇是一组函数,用于执行一个程序并替换当前进程的映像。这些函数属于unistd.h头文件中
定义的POSIX标准。当调用exec函数成功时,当前进程的代码段、数据段、堆栈等将被新程序的相应部
分替换,然后从新程序的入口点开始执行。进程ID不会改变,但进程的内存空间和属性将发生改变
6.1 exec函数API
execl
函数:
-
作用:用新的程序替换当前进程的内存空间。
-
参数:
-
pathname
:要执行的程序的路径。 -
arg
及后续可变参数:依次为新程序的命令行参数,以 NULL 指针结束。
-
execlp
函数:
- 作用:与
execl
类似,但会在环境变量PATH
中搜索可执行文件。 - 参数:
file
:要执行的程序的文件名。arg
及后续可变参数:同execl
。
execle
函数:
- 作用:功能同
execl
,但可以自己指定环境变量。 - 参数:
pathname:同
execl
。arg
及后续可变参数:同execl
。envp[]
:环境变量数组,以 NULL 指针结束。
execv
函数:
-
作用:与
execl
类似,但参数以数组形式传递。 -
参数:
-
pathname
:要执行的程序的路径。 -
argv[]
:包含命令行参数的字符指 针数组,以 NULL 指针结束。 -
envp[]
:环境变量数组,以 NULL 指针结束。
-
``execvp`函数:
- 作用:与
execv
类似,会在环境变量PATH
中搜索可执行文件。 - 参数:
file
:要执行的程序的文件名。argv[]
:同execv
。
execvpe
函数:
-
作用:与
execvp
类似,但可以自己指定环境变量。 -
参数:
file
:要执行的程序的文件名。argv[]
:同execv
。senvp[]
:环境变量数组,以 NULL 指针结束。
-
这些函数执行成功后不会返回,如果返回则表示执行失败
相关文章:
进程之IPC通信二
4.共享内存 共享内存是进程间通信一种方式,多个进程共享一段内存,“ 共享内存 ”。由于多个进程共享了同一段内 存,这个段内存既是你的也是我的。也就是你往这个内存里面写入数据,实际上就相当于往我的内存里 面写入数据。比起其…...
打造高效数据处理利器:用Python实现Excel文件智能合并工具
有时候,我们需要将多个Excel文件按照特定顺序合并成一个文件,这样可以更方便地进行后续的数据处理和分析。今天,我想分享一个使用Python开发的小工具,它可以帮助我们轻松实现Excel文件的智能合并。C:\pythoncode\new\xlsx_merger.…...
使用注解动态映射:根据实体List列表动态生成Excel文件
我们一般通过POI来生成对应的Excel文件,绝大多数情况是需要手动编写单元格内容,然后顺序填充值,今天我们将动态根据实体来生成Excel表头,同时自动填充内容。 文章目录 1. 定义注解2. 实体类应用注解3. 动态导出工具类 1. 定义注解…...
【ISP算法精粹】什么是global tone mapping和local tone mapping?
1. 简介 全局色调映射(Global Tone Mapping)和局部色调映射(Local Tone Mapping)是高动态范围(HDR)图像处理中的两种关键技术,用于将高动态范围图像的亮度值映射到标准动态范围(LDR…...
uniapp生成的app,关于跟其他设备通信的支持和限制
以下内容通过AI生成,这里做一下记录。 蓝牙 移动应用(App)通过蓝牙与其他设备通信,是通过分层协作实现的。 一、通信架构分层 应用层(App) 调用操作系统提供的蓝牙API(如Android的BluetoothA…...
C++ Pimpl(Pointer to Implementation)设计思想
一、C Pimpl(Pointer to Implementation)设计思想 1. 核心思想 Pimpl(Pointer to Implementation)是一种通过将类的实现细节隐藏在一个私有指针背后的设计模式,旨在实现接口与实现的解耦。其核心思想是: …...
便捷的Office批量转PDF工具
软件介绍 本文介绍的软件是一款能实现Office批量转换的工具,名为五五Excel word批量转PDF。 软件小巧 这款五五Excel word批量转PDF软件大小不到2M。 操作步骤一 使用该软件时,只需把软件和需要转换的Word或Excel文件放在同一个文件夹里。 操作步骤…...
2025年渗透测试面试题总结-华顺信安[实习]安全服务工程师(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 华顺信安[实习]安全服务工程师 1. 自我介绍 2. 红蓝队经验 3. Shiro漏洞知识体系 4. APP渗透测试方法…...
Mermaid 使用快速入门
使用AI 生成Mermaid 代码,导入Vscode, 安装Mermaid Preview 插件 --- title: config:theme: forest --- graph TDA[开始] --> B{条件判断}B -->|是| C[执行操作1]B -->|否| D[执行操作2] theme可改变主题,可选主题: default - 这是…...
Windows系统下MySQL 8.4.5压缩包安装详细教程
一、MySQL 8.4.5新特性概览 相较于旧版本,MySQL 8.4.5在性能与功能上实现了显著提升: 性能优化:官方测试显示,在高并发场景下,其读写性能较5.7版本提升近2倍,尤其在处理热点数据竞争问题时表现更为出色。…...
机器学习10-随机森林
随机森林学习笔记 一、随机森林简介 随机森林(Random Forest)是一种集成学习算法,基于决策树构建模型。它通过组合多个决策树的结果来提高模型的准确性和稳定性。随机森林的核心思想是利用“集成”的方式,将多个弱学习器组合成一…...
【缺陷】GaN和AlN中的掺杂特性
1997 年 5 月 9 日,北卡罗来纳州立大学的 P. Bogusławski 和 J. Bernholc 等人在《Physical Review B》期刊发表了题为《Doping properties of C, Si, and Ge impurities in GaN and AlN》的文章,基于量子分子动力学方法,研究了碳(C)、硅(Si)和锗(Ge)杂质在氮化镓(G…...
游戏引擎学习第298天:改进排序键 - 第1部分
关于向玩家展示多个房间层所需的两种 Z 值 我们在前一天基本完成了为渲染系统引入分层 Z 值的工作,但还没有完全完成所有细节。我们开始引入图形渲染中的分层概念,即在 Z 轴方向上拥有多个独立图层,每个图层内部再使用一个单独的 Z 值来实现…...
AI大模型从0到1记录学习 大模型技术之数学基础 day26
高等数学 导数 导数的概念 导数(derivative)是微积分中的一个概念。函数在某一点的导数是指这个函数在这一点附近的变化率(即函数在这一点的切线斜率)。导数的本质是通过极限的概念对函数进行局部的线性逼近。 当函数f的自变量在…...
计算机视觉与深度学习 | matlab实现ARIMA-WOA-CNN-LSTM时间序列预测(完整源码和数据)
以下是一个基于MATLAB的ARIMA-WOA-CNN-LSTM时间序列预测框架。由于完整代码较长,此处提供核心模块和实现思路,完整源码和数据可通过文末方式获取。 1. 数据准备(示例数据) 使用MATLAB内置的航空乘客数据集: % 加载数据 data = readtable(airline-passengers.csv); data …...
【C++】vector模拟实现
实现以下功能: 构造函数:a.无参默认构造 b.n个val构造 c.迭代器区间构造 d.initializer_list构造,类似于数组 e.拷贝构造 赋值重载 [ ]重载 实现reserve接口 实现size(),capacity()接口 实现push_bakc接口&#…...
门店管理五大痛点解析:如何用数字化系统实现高效运营
实体店竞争白热化,五大管理痛点正悄悄吞噬利润:客户资源流失、数据决策滞后、员工效率模糊、合同风险暗藏、利润核算混乱。本文直击核心问题,拆解数字化系统如何精准破局。 一、传统管理模式的五大核心痛点 1. 客户资源管理粗放化 老…...
双指针法高效解决「移除元素」问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 双指针法高效解决「移除元素」问题 双指针法高效解决「移除元素」问题一、问题描述二、解法解析:双指针法1. 核心思想2. 算法步骤3. 执行过程示例 三、关键点分析…...
【Linux笔记】——线程同步信号量与环形队列生产者消费者模型的实现(PV操作)
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:Linux 🌹往期回顾🌹:【Linux笔记】——线程同步条件变量与生产者消费者模型的实现 🔖流水不争,争的是滔滔不…...
武汉科技大学人工智能与演化计算实验室许志伟课题组参加第八届智能优化与调度学术会议
武汉科技大学人工智能与演化计算实验室许志伟课题组参加第八届智能优化与调度学术会议 2025年5月15日至18日,第八届智能优化与调度学术会议在青岛金沙滩希尔顿酒店隆重召开。本次会议由青岛科技大学、湖南省系统仿真学会联合主办,中国仿真学会智能仿真优…...
SparkContext介绍
目录 1. 集群管理接口2. RDD操作中枢3. 任务分发引擎4. 执行环境配置5. 性能监控枢纽 SparkContext是Apache Spark的核心组件,其作用可概括为以下五个关键维度: 1. 集群管理接口 作为与集群管理器(YARN/Mesos/Standalone)通信的唯…...
海外盲盒系统开发:重构全球消费体验的科技引擎
当盲盒文化席卷全球,海外盲盒系统开发已成为重构消费体验的核心赛道。数据显示,2025年全球盲盒市场规模突破120亿,东南亚市场年增长率达4540。我们开发的海外盲盒系统,以技术创新为驱动,打造覆盖全链路的全球化解决方案…...
IP风险画像技术:如何用20+维度数据构建网络安全护城河?
IP风险画像技术作为一种先进的网络安全手段,正逐步成为构建网络安全护城河的重要工具。通过多维度数据的分析和挖掘,IP风险画像技术能够精准识别网络中的潜在威胁,为企业和组织提供更加全面的安全保障。 IP风险画像技术基于IP地址的行为分析…...
Vitest下一代前端单元测试框架
什么是Vitest? Vitest是有vue官方主导开发的下一代测试框架,一个原生支持Vite的测试框架。非常快速。 Vitest 与 Jest 兼容,具有开箱即用的 ESM、Typescript 和 JSX 支持,并且由 esbuild 提供支持。它在测试过程中使用 Vite 开发…...
车道线检测:自动驾驶的“眼睛”
在自动驾驶技术的庞大体系中,车道线检测扮演着至关重要的角色,它就像是自动驾驶汽车的“眼睛”,帮助车辆感知道路边界,从而实现安全、准确的行驶。今天,我们就来深入探讨一下车道线检测的奥秘,看看它是如何…...
基于NLP技术的客户投诉与需求文本分类方法研究
目录 摘要 1. 引言 2. 文本分类基础 2.1 文本分类的定义与类型 2.2 文本分类的评价指标 3. 传统文本分类方法 3.1 基于TF-IDF和SVM的方法 3.2 基于主题模型和词向量的改进方法 4. 深度学习文本分类方法 4.1 TextCNN模型 4.2 BiLSTM模型 4.3 注意力机制与Transformer…...
HarmonyOS5云服务技术分享--自有账号对接AGC认证
✨ 手把手教你:用自有账号玩转华为AGC认证服务 ✨ Hi 各位开发者朋友~👋 今天咱们来聊聊如何将自家已有的账号系统与华为的AppGallery Connect(AGC)认证服务无缝对接。不用重复造轮子,还能享受AGC…...
算法与数据结构:位运算与快速幂
文章目录 位运算快速幂 位运算 在计算机的世界中,一切数字都是二进制的。类比于现实世界中我们所使用的十进制,二进制即为「逢二进一」的运算体系。 我们以 B、D 来分别标记二进制与十进制,例如 10D 表示十进制中的 10,而 10B 则…...
基于R语言的空间异质性数据分析技术
在自然和社会科学领域,存在大量与地理或空间相关的数据,这些数据通常具有显著的空间异质性。传统的统计学方法在处理这类数据时往往力不从心。基于R语言的一系列空间异质性数据分析方法,如地理加权回归(GWR)、地理加权…...
数据分析预备篇---Pandas的DataFrame的更多操作
DataFrame的统计方法 之前学习Series的时候接触了一些很实用的统计相关方法,包括max求最大值,min求最小值,sum求和,mean求平均值等。DataFrame有类似的方法,由于它是二维的,我们可以指定到底是沿着索引纵向操作,相当于沿着轴0(计算每列的统计量);还是说沿着列名横向操…...
精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制
精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制 在创业的黏性阶段,如何从海量的功能创意中筛选出真正能提升用户留存的关键改动?今天,我们结合《精益数据分析》中的“开发功能…...
Linux系统之traceroute命令详解:追踪网络路径的核心工具
Linux系统之traceroute命令详解:追踪网络路径的核心工具 一、traceroute介绍1. 基本介绍2. 核心原理 二、安装与基本使用1. 安装traceroute2. 基础语法 三、基本使用四、常用参数与场景五、典型应用场景1. 定位网络延迟问题2. 检测防火墙拦截3. 绕过DNS解析问题4. 对…...
通义灵码2.5智能体模式联合MCP:打造自动化菜品推荐平台,实现从需求到部署的全流程创新
一、项目背景与目标 随着人工智能技术的快速发展,AI 辅助开发工具正在改变传统的软件开发模式。通义灵码 2.5 版本的发布,通过 Qwen3 模型 和 编程智能体模式 的结合,实现了从需求描述到代码生成的全流程自动化。同时,其对 魔搭M…...
Spring的AOP在什么场景下会失效?
SpringAOP是通过动态代理实现的,所以想要AOP生效就是需要动态代理生效,并且可以提前调用到代理对象的方法。 什么情况下会不走动态代理呢? 首先就是类内部的调用,比如一些私有方法的调用,内部类调用,以及同…...
基于规则引擎与机器学习的智能Web应用防火墙设计与实现
基于规则引擎与机器学习的智能Web应用防火墙设计与实现 引言:智能防御的必然选择 在2023年OWASP最新报告中,传统Web应用防火墙(WAF)对新型API攻击的漏报率高达67%,而误报导致的正常业务拦截损失每年超过2.3亿美元。面…...
NODE-I916 I721模块化电脑发布,AI算力与超低功耗的完美平衡
在智能工业与边缘计算蓬勃发展的今天,企业对计算设备的性能与能效需求日益严苛。全新推出NODE-I916与NODE-I721模块化电脑,分别搭载英特尔 酷睿™ Ultra 平台与Alder Lake-N平台,以差异化CPU配置为核心,为AI推理、工业自动化及嵌入…...
2024年热门AI趋势及回顾
人工智能的崛起 2024 年可能会被铭记为人工智能不再是一种技术新奇事物,而是成为现实的一年。微软、Salesforce 和 Intuit 等巨头将人工智能融入主流企业解决方案;从文案写作到数据分析,专门的人工智能应用程序和服务如雨后春笋般涌现&#…...
Python + moviepy:根据图片或数据高效生成视频全流程详解
前言 在数据可视化、自媒体内容生产、学术汇报等领域,我们常常需要将一组图片或一段变动的数据,自动合成为视频文件。这样不仅能提升内容表现力,也极大节省了人工操作时间。Python作为数据处理和自动化领域的王者,其`moviepy`库为我们提供了灵活高效的视频生成方案。本文将…...
文档债务拖累交付速度?5大优化策略文档自动化
开发者在追求开发速度的过程中,往往会忽视文档的编写,如省略设计文档、代码注释或API文档等。这种做法往往导致在后期调试阶段需要花费三倍以上的时间来理解代码逻辑,进而形成所谓的文档债务,严重拖累交付速度并造成资源浪费。而积…...
java接口自动化初识
简介 了解什么是接口和为什么要做接口测试。并且知道接口自动化测试应该学习哪些技术以及接口自动化测试的落地过程。 一、什么是接口 在这里我举了一个比较生活化的例子,比如我们有一台笔记本,在笔记本的两端有很多插口。例如:USB插口。那…...
Wan2.1 文生视频 支持批量生成、参数化配置和多语言提示词管理
Wan2.1 文生视频 支持批量生成、参数化配置和多语言提示词管理 flyfish 设计 一个基于 Wan2.1 文本到视频模型的自动化视频生成系统。 文件关系图 script.py ├── 读取 → config.json │ ├── 模型配置 → 加载AI模型 │ ├── 生成参数 → 控制生成质量 │ └…...
高阶数据结构——AVL树的实现(详细解答)
目录 1.AVL的概念 2.AVL树的实现 2.1 AVL树的插入 2.1.1 平衡因子的更新 2.1.2 AVL树的插入 2.2 旋转 2.2.1 旋转的原则 2.2.2 右单旋 2.2.3 左单旋 2.2.4 先左后右双旋转 2.2.5 先右后左双旋转(先左后右双旋转模型的镜像) 2.2.6 代码总结 2…...
工作流引擎-01-Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎,支持现实世界的流程自动化需求
前言 大家好,我是老马。 最近想设计一款审批系统,于是了解一下关于流程引擎的知识。 下面是一些的流程引擎相关资料。 工作流引擎系列 工作流引擎-00-流程引擎概览 工作流引擎-01-Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎&#x…...
自定义geojson生成物体的样式
在上节我们学习了如何在cesium中导入geojson数据,本节我们来学习如何让它变得更加炫酷. // 加载GeoJSON数据 // 使用Cesium的GeoJsonDataSource加载指定URL的地理数据 Cesium.GeoJsonDataSource.load("https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json&quo…...
在tensorflow源码环境里,编译出独立的jni.so,避免依赖libtensorflowlite.so,从而实现apk体积最小化
需要在APP里使用tensorflow lite来运行PC端训练的model.tlite,又想apk的体积最小,尝试了如下方法: 1. 在gradle里配置 implementation("org.tensorflow:tensorflow-lite:2.16.1") 这样会引入tensorflow.jar,最终apk的…...
springboot框架 集成海康ISUP-SDK 并实现 协议透传给设备下发指令!
最近有一个需求 需要通过springboot框架 来和 海康的摄像头设备进行通信,就研究了一下 海康的官方ISUP-SDK 文档对接。这个sdk 主要实现了 第三方快速集成海康的设备。 海康的文档地址: https://open.hikvision.com/docs/docId?productId5cda567cf47ae8…...
【移动应用安全】Android系统安全与保护机制
Android系统安全与保护机制是一个多层次、多维度的防御体系,其安全架构与系统层级紧密耦合。以下是对各层级安全机制的扩展分析: Linux内核层(Linux Kernel)安全机制 强制访问控制(MAC) 通过SELinux&#…...
Spring Boot中如何使用RabbitMQ?
前面已经了解了怎么使用RabbitMQ的JDK原生客户端,现在我们来了解Spring Boot中如何使用RabbitMQ,在学习之前,先做好准备工作: 1. 添加依赖 在Spring Boot中使用RabbitMQ,需要使用如下依赖: <dependenc…...
kotlin 将一个list按条件分为两个list(partition )
前言 在安卓开发过程中,我们经常需要将一个列表按照特定条件拆分为两个子列表。随着对语言的深入理解,我发现了一些更高效、更简洁的实现方式,现在与大家分享。 实现 传统Java实现 假设我们有以下列表需要处理: List<Stri…...