语法
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
描述
semget()
系统调用返回 System V 系统中的一个信号量标识符并给参数key
。
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
pipe()
创建一个单向的可用作进程间通信的数据通道。pipefd
数组用作返回管道两端的文件描述符。pipefd[0]
代表管道的读取端。pipefd[1]
用作写入端。数据被写入到由内核所缓冲的管道写入端。
如果 flags
为 0,那么pipe2()
就和pipe()
一样。下面这些变量可以在flags
中按位或运算来获得不同的功能:
为两个文件描述符设置close-on-exec (FD_CLOEXEC)
标志。在 open 中查看相同标志的描述,了解它为什么非常有用。
创建一个在 packet 模式下执行 I/O 的管道。每次对管道的 write 都被当作分开的包处理,并且每次从管道中 read 都会依次读取一个包。注意以下几点:
<limits.h>
中。为两个新开的文件描述符设置 O_NONBLOCK 文件状态标志。使用这个标志节省额外的 fcntl 调用以达到同样的效果。
成功,返回 0。错误,返回 -1 并且设置 errno。
在 Linux 上(和其他的系统),pipe()不在失败时修改pipefd。在 POSIX.1-2016 中添加了规范这一行为的要求。Linux 规定的 pipe2() 系统调用同样在失败时不修改 pipefd。
pipe2()在 Linux 2.6.27 中被添加进去;glibc 支持从 2.9 版本后开始。
pipe(): POSIX.1-2001,POSIX.1-2008。
pipe2() 是 Linux 特有。
下面的程序创建一个管道,然后 frok 去创建一个子进程。子进程继承了一套指向同一管道的文件描述符。fork 之后,每个进程关闭了管道不需要的文件描述符。父进程这时写入管道从命令行参数获取的字符串,然后子进程依次按字节读取这个字符串并显示在标准输出上。
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
via: http://man7.org/linux/man-pages/man2/pipe.2.html
译者:LuuMing
不使用 sizeof 得到变量大小:
double a = 0;
double b = 0;
printf("%d",(void*)&b-(void*)&a);
注意:不可用于 c++。c++ 中不允许对 void *
进行算数运算。
不使用 sizeof 得到机器位数:
和之前一样,定义两个指针变量。再得到指针变量的大小。
如果是 8 字节,则为 64 位机器。
如果是 4 字节,则位 32 位机器。
如果是 2 字节,则位 16 位机器。
关于 i++
操作总会在一些意想不到的地方给程序员带来各种各样的麻烦。例如:多次执行count = count ++
后,为什么 count
的值不变? 要深入理解这个操作,就需要从更深层次的汇编来解释它了。
在 Linux 64 位机器下编写 c++
代码:
int main()
{
int i = 0;
i++;
cout << i;
}
使用 g++ -g 1.cpp
对其进行编译并加入调试信息。
使用 objdum -S a.out
可以将汇编代码与调试信息打开。找到 main
函数开始的地方:
int main()
{
400756: 55 push %rbp
400757: 48 89 e5 mov %rsp,%rbp
40075a: 48 83 ec 10 sub $0x10,%rsp
int i = 0;
40075e: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
i++;
400765: 83 45 fc 01 addl $0x1,-0x4(%rbp)
cout << i;
400769: 8b 45 fc mov -0x4(%rbp),%eax
40076c: 89 c6 mov %eax,%esi
40076e: bf 60 10 60 00 mov $0x601060,%edi
400773: e8 88 fe ff ff callq 400600 <_ZNSolsEi@plt>
}
这些都是 AT&T 汇编,想要深入了解就得参考 GUN AS 手册。在此不赘述。可见,i++
操作被翻译为:
addl $0x1,-0x4(%rbp)
也就是,将 rpb-4 内存地址中的值加1。很简单,没啥问题。
当然了,单步执行 i++
操作翻译成汇编指令看起来很简单,但是当组合i++
操作在一些语句中,例如:
int main()
{
int i = 1;
int count = 0;
for(i = 0; i < 10; i++)
{
count = count++;
}
cout << count;
}
这时候的汇编指令又会是怎么样的?如法泡制,看一看汇编指令:
using namespace std;
int main()
{
400756: 55 push %rbp
400757: 48 89 e5 mov %rsp,%rbp
40075a: 48 83 ec 10 sub $0x10,%rsp
int i = 1;
40075e: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
int count = 0;
400765: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
for(i = 0; i < 10; i++)
40076c: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
400773: eb 10 jmp 400785 <main+0x2f>
{
count = count++;
400775: 8b 45 fc mov -0x4(%rbp),%eax
400778: 8d 50 01 lea 0x1(%rax),%edx
40077b: 89 55 fc mov %edx,-0x4(%rbp)
40077e: 89 45 fc mov %eax,-0x4(%rbp)
using namespace std;
int main()
{
int i = 1;
int count = 0;
for(i = 0; i < 10; i++)
400781: 83 45 f8 01 addl $0x1,-0x8(%rbp)
400785: 83 7d f8 09 cmpl $0x9,-0x8(%rbp)
400789: 7e ea jle 400775 <main+0x1f>
{
count = count++;
}
cout << count;
40078b: 8b 45 fc mov -0x4(%rbp),%eax
40078e: 89 c6 mov %eax,%esi
400790: bf 60 10 60 00 mov $0x601060,%edi
400795: e8 66 fe ff ff callq 400600 <_ZNSolsEi@plt>
}
可以看到,仅仅增加了一个循环,汇编代码就变得非常有意思了。一步步分析。
movl $0x1,-0x8(%rbp) //分配变量 i 的空间
movl $0x0,-0x4(%rbp) //分配变量 count 的空间
可以看到,先定义的变量被分到了低地值区域。
movl $0x0,-0x8(%rbp)
jmp 400785 <main+0x2f>
接着是 for 循环中的第一条语句,即 i = 0
,然后跳至循环主体 <main+0x2f>。
cmpl $0x9,-0x8(%rbp)
jle 400775 <main+0x1f>
上面两句用于判断循环是否结束,若未结束,继续执行:
mov -0x4(%rbp),%eax // 将 count 的地址放在 eax 中
lea 0x1(%rax),%edx // 从 eax 存放的地址取值加 1,放在 edx 中
mov %edx,-0x4(%rbp) // 将 edx 值赋给 count
mov %eax,-0x4(%rbp) // 将 eax 值赋给 count
这几句汇编就是 count = count++
了。可以看到,在最后一步中将 +1 的结果覆盖掉了。也就是说,i++ 操作需要将原内容拷贝一份。
因此,实际上count = count + 1
等价于:
int tmp1 = count;
int tmp2 = tmp + 1;
count = tmp2;
count = tmp1;
感谢这篇文章 。其实,产生上面汇编代码的根本原因在于 c++ 对++
操作的定义。
Operator Operator::operator++()
{
++value; //内部成员变量
return *this;
}
Operator Operator::operator++(int)
{
Operator temp;
temp.value=value;
value++;
return temp;
}
可以看到,++
操作返回的是原值。所以,count = count ++
可以看作:
int ori_count = count;
count = count + 1;
count = ori_count;
这样,就解开了 count
值不变之迷。
局域网内有多台电脑,其中只有一台能够上网,怎样通过 squid 软件搭建代理服务器让多台电脑上网呢?
sudo apt-get install squid
打开/etc/squid/squid.conf,更改
# And finally deny all other access to this proxy
http_access deny all
为
# And finally deny all other access to this proxy
http_access allow all
Ubuntu 14.04 下,打开【系统设置】-【网络】-【网络代理】。设置【方法】为【手动】。在【HTTP代理】那里填上代理服务器的 IP 地址。端口设置为【3128(默认)】。最后,点击应用到整个系统。
经过测试,百度、淘宝、京东等国内网站均遭到拒绝访问。而像,leetcode、stackoverflow、dappradar 等均正常访问。
关于不能访问有些网站的事情,调查了一下,是因为 squid 不能正常代理 https
。百度了许多,最后发现直接使用 tinyporxy
作为代理,将 http
、https
的代理都设置为 tinyproxy
的 8888
就能上网了。哎,折腾这么多不如找对工具呀~
访问jdk 10 下载页面。根据机器的位数与系统选择响应的安装包。
tar -xvf [下载的.tar.gz]
个人推荐将解压后的文件放在如下目录里,注意要用sudo
:
sudo mkdir /usr/lib/jvm
sudo mv jdk-10.0.2/ /usr/lib/jvm/
打开 home 下的 .profile
文件,添加如下内容:
#set jdk env
export JAVA_HOME=/usr/lib/jvm/jdk-10.0.1
export PATH=$JAVA_HOME/bin:$PATH
执行:
java --version
显示:
java 10.0.2 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
即成功安装。
从目标点出发,看这条射线与多边形所有边的交点数目,如果是奇数个。说明在多边形内部,偶数个,在多边形外部。
bool inside(vector<point> polygon, point target)
{
int crossing = 0;
for(int i = 1; i < polygon.size(); i++)
{
double slope = (polygon[i].y - polygon[i-1].y) / (polygon[i].x - polygon[ i-1 ].x); // 两点之间的斜率
bool cond1 = ( target.x >= polygon[i-1].x && target.x <= polygon[i].x); // target 在某边两端点之间
bool cond2 = ( target.x >= polygon[i].x && target.x <= polygon[i-1].x); // target 在某边两端点之间
bool above = (target.y < slope * ( target.x - polygon[i-1].x) + polygon[i-1].y); // y0 < ax0+b (a = slope)即在两线的下方,因此必有一点相交
if( (cond1 || cond2) && above) crossing++;
}
return (crossing % 2 != 0); //若有奇数个点交点,在内部
}
实现一个动态站点,所需要的工具语言有
1. Spring 框架
2. vue
3. Mysql
Spring 框架用于接收 Http 请求,并做出响应。
Vue 是前端框架,本身基于 JavaScript,用作渲染页面,传递数据。
MySql 存储数据。
在前后端分离的设计中,数据以 Json 的形式传递在前后端之间传递。具体表现形式为,在前端 Vue 框架中定义的变量(null/{}/[]) 以 Json 的形式传递给后端,后端代码中,用一个类封装好 Json 的属性并生成标准的get,set方法。这样,传递过去后 Spring 框架就能自动根据属性调用类的构造函数,形成后端的一个对象。
通过 Mydao 与 Mysql 进行交互。简单的一个动态站点就是这样做起来的。