岩川黑鬼 发表于 2016-1-1 18:35:17

[C氵] 非抢占式并发(1):p7简介

本帖最后由 岩川黑鬼 于 2016-1-2 17:39 编辑

看了抚子的boost协程介绍,觉得自己搞那套玩意儿还是太污。不过也姑且安利一下。

一般来说,开发者习于抢占式的并发。在高级语言语句和/或表达式的层面上,抢占式并发很可能不保证任何串行行为,因为它们的重调度触发很可能来自某个外部定时器。在这一前提下,原子化、临界、工人和池是常见的手法,actor风格的并发也稍稍流行。然而如果我们回顾对并发这话题进行探讨的历史,会发现还有CSP(Communicating Sequential Processes, 通信顺序进程)这样的观念。对Go有所了解的读者或许会想到goroutine, 不过单纯的迫真并发并非CSP的全部。

翻开Hoare本人对CSP的论述,他强调过trace这一概念——也即,并发成分产生的确定轨迹。何处串行、何处并发,应当予以演算推定。

实现并没法做得很好,不过我仍然尝试在C中再现一点goroutine. 这结果是p7.
p7提供用户态的并发。实际的并发由可定义数目的pthread(后称线程)搭载,同一线程上的用户线程(粗糙期间,以后称作协程)在没有被迫使重调度的情况下顺序执行。重调度发生的时机可能有:

[*]用户主动暂时放弃;
[*]阻塞在瘦自旋锁或读写自旋锁上;
[*]阻塞在用户指定的IO上;
[*]阻塞在协程间通信上。
同时,若线程上执行了创建协程的代码,会触发一次主动放弃。

这不是个高效的方案,也不是个框架。它多数时候只并发,但审慎的用户或许能够集中地编写代码,而不必在意消息的集散。
下面给出一个例子(stackdemo.c),它是一个粗糙的回显服务。只是简单例子,因此缓冲区是硬编码尺寸的。

#include    <stdio.h>
#include    "./net_common.h"
#include    <sys/stat.h>
#include    "../servcraft/p7/libp7.h"
#include    "../servcraft/p7/p7_root_alloc.h"
#include    "../servcraft/include/model_alloc.h"

void test_echo(void *arg) {
    intptr_t fd_conn_i64crap = (long long) arg;
    int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF);
    char msg;
    memset(msg, 0, sizeof(char) * 32);
    int ret = p7_iowrap_timed(recv, P7_IOMODE_READ, 30000, fd_conn, msg, sizeof(msg), 0);
    switch (ret) {
      case -1:
            printf("internal error\n");
            break;
      case -2:
            printf("p7 timed out\n");
            break;
      default:
            p7_iowrap(send, P7_IOMODE_WRITE, fd_conn, msg, strlen(msg), 0);
    }
    close(fd_conn);
}

void test_timeout(void *arg) {
    long long fd_conn_i64crap = (long long) arg;
    int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF);
    printf("Timed out: %d\n", fd_conn);
}

void test_echo_wrapper(void *arg) {
    p7_coro_concat(test_echo, arg, 2048);
}

void test_startup(void *unused) {
    printf("startup\n");
}

int main(int argc, char *argv[]) {
    __auto_type allocator = p7_root_alloc_get_allocator();
    allocator->allocator_.closure_ = malloc;
    allocator->deallocator_.closure_ = free;
    allocator->reallocator_.closure_ = realloc;
    p7_init(atoi(argv), test_startup, NULL);

    int fd_listen;

    if ((fd_listen = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
      perror("socket");
      exit(1);
    }

    struct sockaddr_in lserv;
    lserv.sin_family = AF_INET;
    lserv.sin_port = htons(8080);
    lserv.sin_addr.s_addr = inet_addr(argv);
    bzero(&(lserv.sin_zero), 8);

    if (bind(fd_listen, (struct sockaddr *) &lserv, sizeof(lserv)) == -1) {
      perror("bind");
      exit(1);
    }
   
    if (listen(fd_listen, 5) == -1) {
      perror("listen");
      exit(1);
    }

    while (1) {
      struct sockaddr_in rcli;
      socklen_t addrlen = sizeof(rcli);
      int fd_conn = p7_iowrap(accept, P7_IOMODE_READ, fd_listen, (struct sockaddr *) &rcli, &addrlen);
      intptr_t fd_conn_i64crap = fd_conn;
      p7_coro_create(test_echo_wrapper, (void *) fd_conn_i64crap, 512);
    }

    return 0;
}

头文件net_common.h是个胡乱拼凑的东西:

/* common header for net app */

#ifndef                NET_COMMON_H_
#define                NET_COMMON_H_

#include      <stdio.h>
#include      <stdlib.h>
#include      <string.h>

#include      <unistd.h>
#include      <sys/types.h>
#include      <sys/wait.h>
#include      <errno.h>
#include      <sys/stat.h>
#include      <sys/socket.h>
#include    <arpa/inet.h>
#include      <netinet/in.h>
#include      <netdb.h>

#include      <sys/mman.h>
#include      <sys/epoll.h>

#include      <signal.h>
#include      <fcntl.h>

#endif                // NET_COMMON_H_
(之前小耗指出这里有reserved name, 确实如此。去掉前缀的下划线。)

然后我试着编译它。

gcc -o stackdemo stackdemo.c -g -O2 -lp7
运行的时候给出线程数和IP地址两个参数。端口也是胡乱编码的8080。
它应当能并发地执行,连接建立之后对一次回显请求的响应要求在30秒内到达,否则视作超时而放弃连接。

这个回显服务做了如下的事情:

[*]启动p7并发,执行监听;
[*]accept一个连接,以这个连接上的fd为参数启动一个协程A; 协程A具有512B尺寸的栈,它以2KB栈尺寸启动另一个协程B, 并等待它结束;
[*]协程B试图按30kms超时recv一条消息,并把它发送回去;无论如何,适当地善后。

{:5_163:}至少它看上去还算简单……

帅气可爱魔理沙 发表于 2016-1-2 03:12:48

{:6_197:}
不明觉厉

iyzsong 发表于 2016-1-2 16:36:05

瞻仰留念{:6_194:}

岩川黑鬼 发表于 2016-1-2 17:37:28


_NET_COMMON_H_ 这 reserved name 是怎么回事?

是我傻逼了。那头文件我大二的时候写的,后来胡乱拷着本地用,几年都忘改header guard...

nadesico19 发表于 2016-1-6 08:51:48

终于有官方文档了,先马后读{:6_188:}

moecmks 发表于 2016-1-6 12:55:31

斯国一

岩川黑鬼 发表于 2016-1-9 14:36:00

nadesico19 发表于 2016-1-6 08:51
终于有官方文档了,先马后读

我懒成狗,文档一直没写……
哪天写个man吧_(:з」∠)_

cbke05 发表于 5 天前

2019最新最强防护神器,来自进口的防护射雾,射程高达10米之远,确保与对方保持足够的安全距离,也是女生、瘦弱人群必备首选;

2019最新最强防护神器见效快、一旦按下射到对方脸上,对方将立刻泪流满面咳嗽不止!

2019最新最强防护神器效果器、对方呼吸道如火,皮肤会刺痛等症状,难受至极点!

2019最新最强防护神器时间长、对方20分钟内无法恢复正常,给您足够的时间逃生!

2019最新最强防护神器易携带、外形似口红,可随身放置于口袋,手提包,衣服口袋中!

2019最新最强防护神器合法性、不会造成任何伤害,为你免去所有法律麻烦!

2019最新最强防护神器如此受到青睐并非空穴来风!它具有比拳脚棍棒更具威胁,挥手制敌,锐不可挡!对付铁器钢管易如反掌,以柔克刚,纵强悍之敌,亦难近身!随意挥手,射出强烈雾状气体,使对付失去反抗能力!

2019最新最强防护神器极具伪装性,比任何器械携带更方便,更隐蔽,出击更快捷!

2019最新最强防护神器让您告别一忍再忍、任人栽割、饱受欺凌的日子一去不复返!

2019最新最强防护神器使用人群:司机、驾驶员、出纳员、出差人员、上夜班者、外出经商者、旅游者、门卫、联防队员、巡罗人员、护村队及金荣系统配备 使用,老板、厂长、经理不可少,尤为现代女生首选防护自卫用品!

以质量求生存,我们追求高质量,我们不打价格战,我们只拼高品质!30天包退换,3年质保,我们保证,品质保障,无效退款!
热线:1717 7011 032    QQ:1224557444
页: [1]
查看完整版本: [C氵] 非抢占式并发(1):p7简介