LuMing's blog

  • Home

  • Tags

  • Archives

进程通信-信号量

Posted on 2018-09-29 | Edited on 2019-04-18

语法

#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。

via: http://man7.org/linux/man-pages/man2/semget.2.html

进程通信的实现--管道

Posted on 2018-09-23 | Edited on 2019-04-18

语法


  #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中按位或运算来获得不同的功能:

O_CLOEXEC

为两个文件描述符设置close-on-exec (FD_CLOEXEC)标志。在 open 中查看相同标志的描述,了解它为什么非常有用。

O_DIRECT( 自从 Linux 3.4 )

创建一个在 packet 模式下执行 I/O 的管道。每次对管道的 write 都被当作分开的包处理,并且每次从管道中 read 都会依次读取一个包。注意以下几点:

  • 写入超过 PIPE_BUF 字节(查看 pipe(7))将会分为多个包。常数 PIPE_BUF定义在 <limits.h>中。
  • 如果 read 确定的缓冲区大小比下一个包小,这时,包中需要的字节被读取,超出的字节被丢弃。指定缓冲区大小为 PIPE_BUF 就足以读取尽可能大的包。
  • 不支持 0 长度的包。(read 指定缓冲区大小为 0 是一个空操作,返回 0。)
    老版本内核不支持这个标志将会通过 EINVAL 错误指出。
    自从 Linux 4.5,可以通过 fcntl 改变管道文件描述符的 O_DIRECT 设置。

O_NONBLOCK

为两个新开的文件描述符设置 O_NONBLOCK 文件状态标志。使用这个标志节省额外的 fcntl 调用以达到同样的效果。

返回值

成功,返回 0。错误,返回 -1 并且设置 errno。
在 Linux 上(和其他的系统),pipe()不在失败时修改pipefd。在 POSIX.1-2016 中添加了规范这一行为的要求。Linux 规定的 pipe2() 系统调用同样在失败时不修改 pipefd。

错误

  • EFAULT 无效 pipefd
  • EINVAL(pipe2())flags 值非法
  • EMFILE 达到之前进程所限制的打开文件描述符的数目
  • ENFILE 达到系统所限制的打开文件描述符的数目
  • ENFILE 管道空间达到用户强行限制的内存,或者调用者没有权限。查看pipe(7)

版本

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

c 中的骚操作

Posted on 2018-09-22 | Edited on 2019-04-18
  1. 不使用 sizeof 得到变量大小:

     double a = 0;
     double b = 0;
         printf("%d",(void*)&b-(void*)&a);
    

    注意:不可用于 c++。c++ 中不允许对 void * 进行算数运算。

  2. 不使用 sizeof 得到机器位数:

     和之前一样,定义两个指针变量。再得到指针变量的大小。
     如果是 8 字节,则为 64 位机器。
     如果是 4 字节,则位 32 位机器。
     如果是 2 字节,则位 16 位机器。
    

i++

Posted on 2018-09-22 | Edited on 2019-04-18

关于 i++ 操作总会在一些意想不到的地方给程序员带来各种各样的麻烦。例如:多次执行count = count ++ 后,为什么 count 的值不变? 要深入理解这个操作,就需要从更深层次的汇编来解释它了。

1. 准备工作

在 Linux 64 位机器下编写 c++ 代码:

int main()
{
        int i = 0;
        i++;
        cout << i;
}

使用 g++ -g 1.cpp 对其进行编译并加入调试信息。

2. 分析汇编指令

使用 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。很简单,没啥问题。

3.复杂情况

当然了,单步执行 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>
}

可以看到,仅仅增加了一个循环,汇编代码就变得非常有意思了。一步步分析。

1. 定义变量
movl   $0x1,-0x8(%rbp)     //分配变量 i 的空间
movl   $0x0,-0x4(%rbp)     //分配变量 count 的空间

可以看到,先定义的变量被分到了低地值区域。

2. 开始循环
movl   $0x0,-0x8(%rbp)
jmp    400785 <main+0x2f>

接着是 for 循环中的第一条语句,即 i = 0,然后跳至循环主体 <main+0x2f>。

3. 循环体
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;

3. 从运算符角度看

感谢这篇文章 。其实,产生上面汇编代码的根本原因在于 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 代理上网

Posted on 2018-09-21 | Edited on 2019-04-18

局域网内有多台电脑,其中只有一台能够上网,怎样通过 squid 软件搭建代理服务器让多台电脑上网呢?

1. 安装 squid

sudo apt-get install squid

2.修改配置文件

打开/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

3. 连接代理服务器

Ubuntu 14.04 下,打开【系统设置】-【网络】-【网络代理】。设置【方法】为【手动】。在【HTTP代理】那里填上代理服务器的 IP 地址。端口设置为【3128(默认)】。最后,点击应用到整个系统。

4. 测试

经过测试,百度、淘宝、京东等国内网站均遭到拒绝访问。而像,leetcode、stackoverflow、dappradar 等均正常访问。

5.后记

关于不能访问有些网站的事情,调查了一下,是因为 squid 不能正常代理 https。百度了许多,最后发现直接使用 tinyporxy作为代理,将 http、https 的代理都设置为 tinyproxy 的 8888 就能上网了。哎,折腾这么多不如找对工具呀~

安装 JAVA

Posted on 2018-09-20 | Edited on 2019-04-18

1.下载 JDK

访问jdk 10 下载页面。根据机器的位数与系统选择响应的安装包。

2.解压

tar -xvf [下载的.tar.gz]

3.移动到合适的位置

个人推荐将解压后的文件放在如下目录里,注意要用sudo:

sudo mkdir /usr/lib/jvm
sudo mv jdk-10.0.2/ /usr/lib/jvm/

4.配置环境变量

打开 home 下的 .profile 文件,添加如下内容:

#set jdk env
export JAVA_HOME=/usr/lib/jvm/jdk-10.0.1
export PATH=$JAVA_HOME/bin:$PATH

5. 检查是否安装成功

执行:

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)

即成功安装。

如何判断点在多边形内部?

Posted on 2018-09-20 | Edited on 2019-04-18

引射线法

从目标点出发,看这条射线与多边形所有边的交点数目,如果是奇数个。说明在多边形内部,偶数个,在多边形外部。

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);                                                                     //若有奇数个点交点,在内部

}

process_space

Posted on 2018-09-16 | Edited on 2019-04-18

看一百遍这张图片

http

Posted on 2018-09-05 | Edited on 2019-04-18

GET

用户请求一个网页,返回相应 h5 页面。

POST

用户提交表单例如账号密码等,可在浏览器调试页面的NetWork中查看,一般为加密数据。

动态网站

Posted on 2018-09-05 | Edited on 2019-04-18

实现一个动态站点,所需要的工具语言有

1. Spring 框架 
2. vue
3. Mysql

Spring 框架用于接收 Http 请求,并做出响应。

Vue 是前端框架,本身基于 JavaScript,用作渲染页面,传递数据。
MySql 存储数据。

在前后端分离的设计中,数据以 Json 的形式传递在前后端之间传递。具体表现形式为,在前端 Vue 框架中定义的变量(null/{}/[]) 以 Json 的形式传递给后端,后端代码中,用一个类封装好 Json 的属性并生成标准的get,set方法。这样,传递过去后 Spring 框架就能自动根据属性调用类的构造函数,形成后端的一个对象。

通过 Mydao 与 Mysql 进行交互。简单的一个动态站点就是这样做起来的。

1234…6

LuMing

57 posts
3 categories
11 tags
GitHub E-Mail
© 2020 LuMing
Powered by Hexo v3.9.0
|
Theme – NexT.Muse v6.3.0