linux进程间通信快速入门【二】:共享内存编程(mmap、XSI、POSIX)

文章目录

  • mmap内存共享映射
  • XSI共享内存
  • POSIX共享内存
  • 参考

使用文件或管道进行进程间通信会有很多局限性,比如效率问题以及数据处理使用文件描述符而不如内存地址访问方便,于是多个进程以共享内存的方式进行通信就成了很自然要实现的IPC方案。
LInux给我们提供了三种共享内存的解决方案:

mmap内存共享映射。
XSI共享内存。
POSIX共享内存。

mmap内存共享映射

mmap可以将一个文件映射到内存中,在程序里就可以直接使用内存地址对文件内容进行访问,这可以让程序对文件访问更方便。
API:

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);

Linux产生子进程的系统调用是fork,根据fork的语义以及其实现,我们知道新产生的进程在内存地址空间上跟父进程是完全一致的。所以Linux的mmap实现了一种可以在父子进程之间共享内存地址的方式,其使用方法是:
step1:父进程将flags参数设置MAP_SHARED方式通过mmap申请一段内存。内存可以映射某个具体文件,也可以不映射具体文件(fd置为-1,flag设置为MAP_ANONYMOUS)。
step2:父进程调用fork产生子进程。之后在父子进程内都可以访问到mmap所返回的地址,就可以共享内存了。
示例:并发100个进程写共享内存

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100int do_child(int *count)
{int interval;/* critical section */interval = *count;interval++;usleep(1);*count = interval;/* critical section */exit(0);
}int main()
{pid_t pid;int count;int *shm_p;// 开辟一个int大小的共享内存 可读可写 Share changes Don't use a file.shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}// 对该共享内存内容清零*shm_p = 0;// fork子进程,在子进程进行取数、++、置数操作for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(shm_p);}}// 等待所有子进程生命周期结束for (count=0;count<COUNT;count++) {wait(NULL);}// 打印内容printf("shm_p: %d\n", *shm_p);// 回收共享内存munmap(shm_p, sizeof(int));exit(0);
}

这个例子中,我们在子进程中为了延长临界区(critical section)处理的时间,使用了一个中间变量进行数值交换,并且还使用了usleep加强了一下racing的效果。结果如下:

[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./racing_mmap.cpp -o racing_mmap
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 37
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 44
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 41

这段共享内存的使用是有竞争条件存在的,从文件锁的例子我们知道,进程间通信绝不仅仅是通信这么简单,还需要处理类似这样的临界区代码。在这里,我们也可以使用文件锁进行处理,但是共享内存使用文件锁未免显得太不协调了。除了不方便以及效率低下以外,文件锁还不能够进行更高级的进程控制。所以,我们在此需要引入更高级的进程同步控制原语来实现相关功能,这就是信号量(semaphore)的作用。这里信号量不是重点,将在后面的系列文章中进行探讨。

应该注意,mmap方式的共享内存只能在通过fork产生的父子进程间通信,因为除此之外的其它进程无法得到共享内存段的地址
接下来再看看mmap开辟的内存位于哪里吧:

/** @Author: your name* @Date: 2022-03-17 19:00:57* @LastEditTime: 2022-03-17 19:00:58* @LastEditors: Please set LastEditors* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: /SocketTest/LocalSocketDemo/mmap.cpp*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100
#define MEMSIZE 1024*1024*1023*1int main()
{pid_t pid;int count;void *shm_p;shm_p = mmap(NULL, MEMSIZE, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}bzero(shm_p, MEMSIZE);sleep(3000);munmap(shm_p, MEMSIZE);exit(0);
}

结果如下:

[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./mmap.cpp -o mmap
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# free -gtotal        used        free      shared  buff/cache   available
Mem:             15           8           3           0           2           6
Swap:             0           0           0
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./mmap &
[1] 23994
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# free -gtotal        used        free      shared  buff/cache   available
Mem:             15           8           2           1           3           5
Swap:             0           0           0

我们开辟了一个G内存,Centos的环境中mmap的共享内存会记录到buff/cache中。

XSI共享内存

为了满足多个无关进程共享内存的需求,Linux提供了更具通用性的共享内存手段,XSI共享内存就是这样一种实现。
XSI是X/Open组织对UNIX定义的一套接口标准(X/Open System Interface)。由于UNIX系统的历史悠久,在不同时间点的不同厂商和标准化组织定义过一些列标准,而目前比较通用的标准实际上是POSIX。我们还会经常遇到的标准还包括SUS(Single UNIX Specification)标准,它们大概的关系是,SUS是POSIX标准的超集,定义了部分额外附加的接口,这些接口扩展了基本的POSIX规范。相应的系统接口全集被称为XSI标准,除此之外XSI还定义了实现必须支持的POSIX的哪些可选部分才能认为是遵循XSI的。它们包括文件同步,存储映射文件,存储保护及线程接口。只有遵循XSI标准的实现才能称为UNIX操作系统。
XSI共享内存在Linux底层的实现实际上跟mmap没有什么本质不同,只是在使用方法上有所区别。其使用的相关方法为:

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);int shmctl(int shmid, int cmd, struct shmid_ds *buf);#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);int shmdt(const void *shmaddr);

在一个操作系统内,如何让两个不相关(没有父子关系)的进程可以共享一个内存段?系统中是否有现成的解决方案呢?
当然有,就是文件。我们知道,文件的设计就可以让无关的进程可以进行数据交换。文件采用路径和文件名作为系统全局的一个标识符,但是每个进程打开这个文件之后,在进程内部都有一个“文件描述符”去指向文件。此时进程通过fork打开的子进程可以继承父进程的文件描述符,但是无关进程依然可以通过系统全局的文件名用open系统调用再次打开同一个文件,以便进行进程间通信。
实际上对于XSI的共享内存,其key的作用就类似文件的文件名,shmget返回的int类型的shmid就类似文件描述符,注意只是“类似”,而并非是同样的实现。这意味着,我们在进程中不能用select、poll、epoll这样的方法去控制一个XSI共享内存,因为它并不是“文件描述符”。对于一个XSI的共享内存,其key是系统全局唯一的,这就方便其他进程使用同样的key,打开同样一段共享内存,以便进行进程间通信。而使用fork产生的子进程,则可以直接通过shmid访问到相关共享内存段。这就是key的本质:系统中对XSI共享内存的全局唯一表示符。
那么key是如何产生的呢?

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

一个key是通过ftok函数,使用一个pathname和一个proj_jd产生的。就是说,在一个可能会使用共享内存的项目组中,大家可以约定一个文件名和一个项目的proj_id,来在同一个系统中确定一段共享内存的key。ftok并不会去创建文件,所以必须指定一个存在并且进程可以访问的pathname路径。这里还要指出的一点是,ftok实际上并不是根据文件的文件路径和文件名(pathname)产生key的,在实现上,它使用的是指定文件的inode编号和文件所在设备的设备编号。所以,不要以为你是用了不同的文件名就一定会得到不同的key,因为不同的文件名是可以指向相同inode编号的文件的(硬连接)。也不要认为你是用了相同的文件名就一定可以得到相同的key,在一个系统上,同一个文件名会被删除重建的几率是很大的,这种行为很有可能导致文件的inode变化。所以一个ftok的执行会隐含stat系统调用也就不难理解了。
key作为全局唯一标识不仅仅体现在XSI的共享内存中,XSI标准的其他进程间通信机制(信号量数组和消息队列)也使用这一命名方式。
示例:多进程并发写,会有竞争

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>#define COUNT 100
#define PATHNAME "/etc/passwd"int do_child(int proj_id)
{int interval;int *shm_p, shm_id;key_t shm_key;/* 使用ftok产生shmkey */if ((shm_key = ftok(PATHNAME, proj_id)) == -1) {perror("ftok()");exit(1);}/* 在子进程中使用shmget取到已经在父进程中创建好的共享内存id,注意shmget的第三个参数的使用。 */shm_id = shmget(shm_key, sizeof(int), 0);if (shm_id < 0) {perror("shmget()");exit(1);}/* 使用shmat将相关共享内存段映射到本进程的内存地址。 */shm_p = (int *)shmat(shm_id, NULL, 0);if ((void *)shm_p == (void *)-1) {perror("shmat()");exit(1);}/* critical section */interval = *shm_p;interval++;usleep(1);*shm_p = interval;/* critical section *//* 使用shmdt解除本进程内对共享内存的地址映射,本操作不会删除共享内存。 */if (shmdt(shm_p) < 0) {perror("shmdt()");exit(1);}exit(0);
}int main()
{pid_t pid;int count;int *shm_p;int shm_id, proj_id;key_t shm_key;proj_id = 1234;/* 使用约定好的文件路径和proj_id产生shm_key。 */if ((shm_key = ftok(PATHNAME, proj_id)) == -1) {perror("ftok()");exit(1);}/* 使用shm_key创建一个共享内存,如果系统中已经存在此共享内存则报错退出,创建出来的共享内存权限为0600。 */shm_id = shmget(shm_key, sizeof(int), IPC_CREAT|IPC_EXCL|0600);if (shm_id < 0) {perror("shmget()");exit(1);}/* 将创建好的共享内存映射进父进程的地址以便访问。 */shm_p = (int *)shmat(shm_id, NULL, 0);if ((void *)shm_p == (void *)-1) {perror("shmat()");exit(1);}/* 共享内存赋值为0。 */*shm_p = 0;/*  打开100个子进程并发读写共享内存。 */for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(proj_id);}}/* 等待所有子进程执行完毕。 */for (count=0;count<COUNT;count++) {wait(NULL);}/* 显示当前共享内存的值。 */printf("shm_p: %d\n", *shm_p);/* 解除共享内存地质映射。 */if (shmdt(shm_p) < 0) {perror("shmdt()");exit(1);}/* 删除共享内存。 */if (shmctl(shm_id, IPC_RMID, NULL) < 0) {perror("shmctl()");exit(1);}exit(0);
}

XSI共享内存跟mmap在实现上并没有本质区别。而之所以引入key和shmid的概念,也主要是为了在非父子关系的进程之间可以共享内存。根据上面的例子可以看到,使用shmget可以根据key创建共享内存,并返回一个shmid。它的第二个参数size用来指定共享内存段的长度,第三个参数指定创建的标志,可以支持的标志为:IPC_CREAT、IPC_EXCL。从Linux 2.6之后,还引入了支持大页的共享内存,标志为:SHM_HUGETLB、SHM_HUGE_2MB等参数。shmget除了可以创建一个新的共享内存以外,还可以访问一个已经存在的共享内存,此时可以将shmflg置为0,不加任何标识打开

当获得shmid之后,就可以使用shmat来进行地址映射。shmat之后,通过访问返回的当前进程的虚拟地址就可以访问到共享内存段了。当然,在使用之后要记得使用shmdt解除映射,否则对于长期运行的程序可能造成虚拟内存地址泄漏,导致没有可用地址可用。shmdt并不能删除共享内存段,而只是解除共享内存和进程虚拟地址的映射,只要shmid对应的共享内存还存在,就仍然可以继续使用shmat映射使用。想要删除一个共享内存需要使用shmctlIPC_RMID指令处理。也可以在命令行中使用ipcrm删除指定的共享内存id或key。
注意点:

共享内存由于其特性,与进程中的其他内存段在使用习惯上有些不同。一般进程对栈空间分配可以自动回收,而堆空间通过malloc申请,free回收。这些内存在回收之后就可以认为是不存在了。但是共享内存不同,用shmdt之后,实际上其占用的内存还在,并仍然可以使用shmat映射使用。如果不是用shmctl或ipcrm命令删除的话,那么它将一直保留直到系统被关闭。当然,文件如果不删除,下次重启依旧还在,因为它放在硬盘上,而共享内存下次重启就没了,因为它毕竟还是内存。

跟mmap的共享内存一样,XSI的共享内存在free现实中也会占用shared和buff/cache的消耗。实际上,在内核底层实现上,两种内存共享都是使用的tmpfs方式实现的,所以它们实际上的内存使用都是一致的。

POSIX共享内存

XSI共享内存是历史比较悠久,也比较经典的共享内存手段。它几乎代表了共享内存的默认定义,当我们说有共享内存的时候,一般意味着使用了XSI的共享内存。但是这种共享内存也存在一切缺点,最受病垢的地方莫过于他提供的key+projid的命名方式不够UNIX,没有遵循一切皆文件的设计理念。
如果共享内存可以用文件描述符的方式提供给程序访问,毫无疑问可以在Linux上跟select、poll、epoll这样的IO异步事件驱动机制配合使用,做到一些更高级的功能。于是,遵循一切皆文件理念的POSIX标准的进程间通信机制应运而生。

POSIX共享内存实际上毫无新意,它本质上就是mmap对文件的共享方式映射,只不过映射的是tmpfs文件系统上的文件。

什么是tmpfs?Linux提供一种“临时”文件系统叫做tmpfs,它可以将内存的一部分空间拿来当做文件系统使用,使内存空间可以当做目录文件来用。Linux提供的POSIX共享内存,实际上就是在/dev/shm下创建一个文件,并将其mmap之后映射其内存地址即可。我们通过它给定的一套参数就能猜到它的主要函数shm_open无非就是open系统调用的一个封装。大家可以通过man shm_overview来查看相关操作的方法。
POSIX共享内存的使用相关方法如下:

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */int shm_open(const char *name, int oflag, mode_t mode);int shm_unlink(const char *name);

使用shm_open可以创建或者访问一个已经创建的共享内存。上面说过,实际上POSIX共享内存就是在/dev/shm目录中的的一个tmpfs格式的文件,所以shm_open无非就是open系统调用的封装,所以起函数使用的参数几乎一样。其返回的也是一个标准的我呢间描述符。

shm_unlink也一样是unlink调用的封装,用来删除文件名和文件的映射关系。在这就能看出POSIX共享内存和XSI的区别了,一个是使用文件名作为全局标识,另一个是使用key。

映射共享内存地址使用mmap,解除映射使用munmap。使用ftruncate设置共享内存大小,实际上就是对tmpfs的文件进行指定长度的截断。使用fchmod、fchown、fstat等系统调用修改和查看相关共享内存的属性。close调用关闭共享内存的描述符。实际上,这都是标准的文件操作。
下面看一下具体示例:
示例:多进程读写,有竞争

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100
#define SHMPATH "shm"int do_child(char * shmpath)
{int interval, shmfd, ret;int *shm_p;/* 使用shm_open访问一个已经创建的POSIX共享内存 */shmfd = shm_open(shmpath, O_RDWR, 0600);if (shmfd < 0) {perror("shm_open()");exit(1);}/* 使用mmap将对应的tmpfs文件映射到本进程内存 */shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, shmfd, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}/* critical section */interval = *shm_p;interval++;usleep(1);*shm_p = interval;/* critical section */munmap(shm_p, sizeof(int));close(shmfd);exit(0);
}int main()
{pid_t pid;int count, shmfd, ret;int *shm_p;/* 创建一个POSIX共享内存 */shmfd = shm_open(SHMPATH, O_RDWR|O_CREAT|O_TRUNC, 0600);if (shmfd < 0) {perror("shm_open()");exit(1);}/* 使用ftruncate设置共享内存段大小 */ret = ftruncate(shmfd, sizeof(int));if (ret < 0) {perror("ftruncate()");exit(1);}/* 使用mmap将对应的tmpfs文件映射到本进程内存 */shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, shmfd, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}*shm_p = 0;for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(SHMPATH);}}for (count=0;count<COUNT;count++) {wait(NULL);}printf("shm_p: %d\n", *shm_p);munmap(shm_p, sizeof(int));close(shmfd);//sleep(3000);shm_unlink(SHMPATH);exit(0);
}
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./racing_posix_shm.cpp -o racing_posix_shm
./racing_posix_shm.cpp: In function ‘int main()’:
./racing_posix_shm.cpp:80:20: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]do_child(SHMPATH);^
/tmp/ccduro4X.o: In function `do_child(char*)':
racing_posix_shm.cpp:(.text+0x1e): undefined reference to `shm_open'
/tmp/ccduro4X.o: In function `main':
racing_posix_shm.cpp:(.text+0xe0): undefined reference to `shm_open'
racing_posix_shm.cpp:(.text+0x215): undefined reference to `shm_unlink'
collect2: error: ld returned 1 exit status

编译执行这个程序需要指定一个额外rt的库,可以使用如下命令进行编译:

g++ ./racing_posix_shm.cpp -lrt -o racing_posix_shm

编译好可以看到,正好是在编译好之后,dev/shm文件路径被创建

root@VM-90-225-centos /dev]
...
drwxrwxrwt  2 root root          40 Mar 18 15:17 shm
...

解释:

shm_open的SHMPATH参数是一个路径,这个路径默认放在系统的/dev/shm目录下。这是shm_open已经封装好的,保证了文件一定会使用tmpfs。
shm_open实际上就是open系统调用的封装。我们当然完全可以使用open的方式模拟这个方法。
使用ftruncate方法来设置“共享内存”的大小。其实就是更改文件的长度。
要以共享方式做mmap映射,并且指定文件描述符为shmfd。
shm_unlink实际上就是unlink系统调用的封装。如果不做unlink操作,那么文件会一直存在于/dev/shm目录下,以供其它进程使用。
关闭共享内存描述符直接使用close。

其本质上就是个tmpfs文件。那么从这个角度说,mmap匿名共享内存、XSI共享内存和POSIX共享内存在内核实现本质上其实都是tmpfs。如果我们去查看POSIX共享内存的free空间占用的话,结果将跟mmap和XSI共享内存一样占用shared和buff/cache.

参考

https://zorrozou.github.io/docs/books/linuxjin-cheng-jian-tong-4fe1-gong-xiang-nei-cun.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROBOTS.TXT屏蔽笔记、代码、示例大全

自己网站的ROBOTS.TXT屏蔽的记录&#xff0c;以及一些代码和示例&#xff1a; 屏蔽后台目录&#xff0c;为了安全&#xff0c;做双层管理后台目录/a/xxxx/&#xff0c;蜘蛛屏蔽/a/&#xff0c;既不透露后台路径&#xff0c;也屏蔽蜘蛛爬后台目录 缓存&#xff0c;阻止蜘蛛爬静态…

五大主流浏览器 HTML5 和 CSS3 兼容性比较

转眼又已过去了一年&#xff0c;在这一年里&#xff0c;Firefox 和 Chrome 在拼升级&#xff0c;版本号不断飙升&#xff1b;IE10 随着 Windows 8 在去年10月底正式发布&#xff0c;在 JavaScript 性能和对 HTML5 和 CSS3 的支持方面让人眼前一亮。这篇文章给大家带来《五大主流…

Ubuntu下将Sublime Text设置为默认编辑器

转自将Sublime Text 2设置为默认编辑器 修改defaults.list 编辑/etc/gnome/default.list文件&#xff0c;将其中的所有gedit.desktop替换为sublime_text.desktop。 sublime_text.desktop在/opt/sublime_text目录下&#xff0c;使用ls -al *sublime*命令查看具体文件名。 转载于…

python获取最近N天工作日列表、节假日列表

# 获取最近两周工作日列表、节假日列表 import datetime import chinese_calendar import time import pandas as pd# 将时间戳转换成格式化日期 def timestamp_to_str(timestampNone, format%Y-%m-%d %H:%M:%S):if timestamp:time_tuple time.localtime(timestamp) # 把时间…

保存页面的浏览记录

我的设计思想是将用户的浏览记录保存到cookie里面&#xff0c;然后根据情况处理。cookie里面的数据格式是json格式&#xff0c;方便根据自己的需要添加或者修改属性。引用了3个js文件,下载地址如下。 https://github.com/carhartl/jquery-cookie/blob/master/jquery.cookie.js …

开窍小老虎,一步一个脚印之 初识汇编(一)

最近一直浸淫在计算机编程中无法自拔。哲学 认识论中讲过。人类的求知的过程是由两次飞跃。第一是从感性认识到理性认识&#xff1b;第二是从理性认识到实践。这段话对有些人是适用的。我就是其中的一名。在知乎上求助问题“学计算机要懂汇编吗&#xff1f;”&#xff0c;地下有…

python脚本 请求数量达到上限,http请求重试

由于在内网发送http请求同一个token会限制次数&#xff0c;所以很容易达到网关流量上限。 业务中使用了多线程并发&#xff0c;一个线程发起一次http请求&#xff0c;得到正确结果后返回。这里采用的策略是&#xff0c;如果解析出来达到流量上限&#xff0c;那么该线程休眠一段…

shell 字符串操作

string"abcABC123ABCabc" 字符串长度: echo ${#string} #15 echo expr length $string #15 索引 用法&#xff1a;expr index $string $substring expr index $string "ABC" #4 提取子串 用法&#xff1a;${string:position} echo ${string:3} #A…

Linux 之目录 -鸟哥的Linux私房菜

因为利用 Linux 来开发产品或 distributions 的社群/公司与个人实在太多了, 如果每个人都用自己的想 法来配置档案放置的目录,那么将可能造成很多管理上的困扰。 你能想象,你进入一个企业之后,所 接触到的 Linux 目录配置方法竟然跟你以前学的完全不同吗? 很难想象吧~所以,后来…

python脚本:向表中插入新数据,删除表中最旧的数据

一张表存储历史数据&#xff0c;最多存储HISTORY_TABLE_MAX_ROWS条数据&#xff0c;当表中数据未达到HISTORY_TABLE_MAX_ROWS&#xff0c;直接插入&#xff1b;如果达到的话需要保证插入新数据的时候将最旧的数据删除 这里使用先update最新数据&#xff0c;然后再重新update全表…

精通 VC++ 实效编程280例 - 02 菜单和光标

菜单和关闭时重要的 Windows 资源之一。SDK 中&#xff0c;用 HCURSOR 和 HMENU 分别表示菜单和光标的句柄。MFC 中&#xff0c;CMenu 类封装了菜单的功能。 23 动态添加和删除菜单项 添加菜单项可以调用 CMenu::AppendMenu 或 CMenu::InserMenu 函数&#xff0c;删除菜单项可以…

POJ 1860: Currency Exchange 【SPFA】

套汇问题&#xff0c;从源点做SPFA&#xff0c;如果有一个点入队次数大于v次&#xff08;v表示点的个数&#xff09;则图中存在负权回路&#xff0c;能够套汇&#xff0c;如果不存在负权回路&#xff0c;则判断下源点到自身的最长路是否大于自身&#xff0c;使用SPFA时松弛操作…

python脚本:判断字符是否为中文

# 判断字符是否为中文 def is_chinese(ch):if u\u4e00 < ch < u\u9fff:return Trueelse:return False

Android 广播 Broadcast学习

Android Broadcast 广播 进程内本地广播 如果你是在你的应用之内使用广播&#xff0c;即不需要跨进程&#xff0c;考虑使用LocalBroadcastManager &#xff0c;这样更有效率&#xff08;因为不需要跨进程通信&#xff09;&#xff0c;并且你不用考虑一些其他应用可以发送或接收…

python:将时间戳转换成格式化日期

import time # 将时间戳转换成格式化日期 def timestamp_to_str(timestampNone, format%Y-%m-%d %H:%M:%S):if timestamp:time_tuple time.localtime(timestamp) # 把时间戳转换成时间元祖result time.strftime(format, time_tuple) # 把时间元祖转换成格式化好的时间retur…

WebApp 里Meta标签大全

1.先说说mate标签里的viewport&#xff1a; viewport即可视区域&#xff0c;对于桌面浏览器而言&#xff0c;viewport指的就是除去所有工具栏、状态栏、滚动条等等之后用于看网页的区域。对于传统WEB页面来说&#xff0c;980的宽度在iphone上显示是很正常的&#xff0c;也是满屏…

python:封装CRUD操作

# 封装数据库操作 def SELECT(db, cursor, sql):try:# 执行SQL语句db.ping(reconnectTrue)cursor.execute(sql)# 获取所有记录列表results cursor.fetchall()logging.debug("select commit")except:logging.error(sql)logging.error("select 语句执行出错"…

我的osu游戏程序设计(oo)

osu是一款社区元素为主旨的音乐游戏,由澳大利亚人Dean Herbert (peppy)独立制作并运行. 游戏的方法简单,就是 1. 圈圈(Circle)&#xff1a;圈圈(Circle) 50。没打中显示X,并减少生命值。圈中序号的最后一个的300、100会显示为激300、喝100。2.滑条(Slider) : 在开始端点击按住不…

影像数据库调研

参考Paul Graham比较各种编程语言的方法&#xff0c;我们比较各种数据库的特点如下&#xff1a; Oracle: 我们需要企业级数据库。 MySQL: Oracle不开源。 PostgreSQL: MySQL的功能不够多。 SQLite: 你可以把我嵌入到任何地方。这样&#xff0c;4种数据库够大家用了。 MongoDB: …

linux进程间通信快速入门【三】:信号量(XSI、POSIX以及PV原语)

文章目录XSIsemgetsemop、semtimedopsemctl基于共享内存demo修改XSI信号量的限制PV原语PV控制并发进程数POSIX信号量使用posix命名信号量使用posix匿名信号量参考在前两篇文章中我们使用的racingdemo都没有对临界区代码进行加锁&#xff0c;这里我们介绍以下信号量的使用。Linu…