C语言实现用户态线程库案例

轮子年年有人造,我们也来凑热闹,参考协程实现,大概有以下几种方法:

1)利用setjmp,longjmp

2)利用ucontext接口函数

3)汇编

(线程无非就是多了个抢占功能,由定时器触发,而非自愿让出运行权限)

因为我写的时候还没看到其他帖子,如果看到了,铁定会用最直观的ucontext接口写的(注意,在macOSX中已经标注为废除,头文件得换做sys/ucontext.h),结果就是我用了汇编来写,但是尽量不用汇编来写整个switch_to调度函数(这样有个明显的坏处,那就是用gas/nasm的标准汇编格式写的函数在macOSX下不能编译通过,这个与系统自带的编译工具有关),而用经量少的内嵌汇编来写。switch_to函数参考的是minix操作系统中任务切换函数实现的,用软件时钟器每隔1s发信号以激发switch_to函数切换任务。下面直接贴代码了,对外提供了类似pthread的接口(只有两个,分别是threadCreate和threadJoin)。现在的代码还非常的buggy,只能安全地支持在线程函数里头纯计算,其他的行为非常可能引发bus error和segmentation fault。(要更加严谨地研究用户态线程库,请去看gnu pth的实现代码)

 thread.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <time.h>

#define JMP(r)  asm volatile \
        (  "pushl %3\n\t" \
          "popfd\n\t" \
          "movl %2, %%ebp\n\t" \
          "movl %0, %%esp\n\t" \
          "jmp *%1\n\t" \
          : \
          : "m"(r._esp),"m"(r._eip),"m"(r._ebp),"m"(r._eflags) \
          : \
        )

#define SAVE()         asm volatile \
              (  "movl %%eax, %0\n\t" \
                "movl %%ecx, %1\n\t" \
                "movl %%edx, %2\n\t" \
                "movl %%ebx, %3\n\t" \
                  "movl %%esp, %4\n\t" \
                "movl %%ebp, %5\n\t" \
                "movl %%esi, %6\n\t" \
                "movl %%edi, %7\n\t" \
                "pushfd\n\t" \
                "movl (%%esp), %%eax\n\t" \
                "movl %%eax, %8\n\t" \
                "popfd\n\t" \
                : "=m"(_eax),"=m"(_ecx),"=m"(_edx),"=m"(_ebx) \
                ,"=m"(_esp),"=m"(_ebp) \
                , "=m"(_esi),"=m"(_edi),"=m"(_eflags) \
                : \
                : "%eax" \
              )

#define RESTORE(r)     asm volatile \
              (  "movl %0, %%eax\n\t" \
                "movl %1, %%ecx\n\t" \
                "movl %1, %%edx\n\t" \
                "movl %3, %%ebx\n\t" \
                "movl %4, %%esi\n\t" \
                "movl %5, %%edi\n\t" \
                : \
                :"m"(r._eax),"m"(r._ecx),"m"(r._edx),"m"(r._ebx) \
                , "m"(r._esi),"m"(r._edi) \
              )

typedef void Func(int);

/* __timer struct is the real Timer struct we use
 * id is unique to each timer
 * intersec is the inteval seconds to each signal forwarding the this Timer
 * sigactor is the handler for this Timer
 * next is a internal member used for linked list
 */
struct __timer
{
  void *next;
  unsigned int sec;
  unsigned int intersec;
  int id;
  Func *sigactor;
};

/* struct alarm is ugly for the compatibility with early struct.
 * I should have used unnamed member instead of __inner.
 */
typedef struct alarm *Timer;
struct alarm
{
  union{
    struct
    {
      Timer next;
      unsigned int sec;
    };
    struct __timer __inner;
  };
};

typedef struct list *Header;

struct list
{
  Timer head;
};

typedef struct __thread_table_regs Regs;
struct __thread_table_regs
{
  int _edi;
  int _esi;
  int _ebp;
  int _esp;
  int _ebx;
  int _edx;
  int _ecx;
  int _eax;
  int _eip;
  int _eflags;
};

typedef struct __ez_thread Thread_t;
struct __ez_thread
{
  Regs regs;
  int tid;
  sigset_t sigmask;
  unsigned int priority;
  int tick;
  int state;
  int errno;
  unsigned int stacktop;
  unsigned int stacksize;
  void *stack;
  void *retval;
  volatile int __reenter;
};

typedef struct __pnode pNode;
struct __pnode
{
  pNode *next;
  pNode *prev;
  Thread_t *data;
};

typedef struct __loopcursor Cursor;
struct __loopcursor
{
  int total;
  pNode *current;
};
typedef struct __stack *Stack_t;
struct __stack
{
  int __pad[4096];
};

void switch_to(int);

extern Header hdr_ptr;
extern Cursor live;
extern Cursor dead;
extern Thread_t pmain;

thread.c

/* MIT License

Copyright (c) 2017 Yuandong-Chen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

#include "thread.h"
/************************* Alarm facility *************************/

struct list linkedlist;
Header hdr_ptr = &linkedlist;

Timer mallocTimer(int id, Func *actor,unsigned int sec, unsigned int interval)
{
  Timer ret = (Timer)malloc(sizeof(struct alarm));
  assert(ret);
  ret->__inner.id = id;
  ret->__inner.sigactor = actor;
  ret->__inner.intersec = interval;
  ret->sec = sec;
  return ret;
}

/* find Timer in linked list which id is id.
 * return: return NULL if not found, -1 if it's header link,
 * otherwise prev which is the previous Timer member to this Timer
 */

Timer findTimerPrev(Header h, int id)
{
  assert(h);
  if(h->head == NULL)
    return NULL;

  Timer t = h->head;
  Timer prev = NULL;

  while(t)
  {
    if(t->__inner.id == id){
      if(prev == NULL)
        return (Timer)-1;
      else
        return prev;
    }
    prev = t;
    t = t->next;
  }

  return NULL;
}

/* delete Timer in linked list.
 * return: nothing, we ensure this t is deleted in the linked list.
 */

void delTimer(Header h, Timer t)
{
  assert(h);
  assert(t);
  Timer prevtodel = findTimerPrev(h, t->__inner.id);
  unsigned int base = 0;

  if(prevtodel)
  {
    if(prevtodel == (Timer)-1){

      unsigned int res = (h->head)->sec;
      if(res != 0)
      {
        base = res;
      }
      else
      {
        kill(getpid(),SIGALRM);
        return;
      }
      h->head = (h->head)->next;
      Timer tmp = (h->head);

      while(tmp){
        tmp->sec += base;
        tmp = tmp->next;
      }
      return;
    }
    else
    {

      base = (prevtodel->next)->sec;
      prevtodel->next = (prevtodel->next)->next;
      Timer tmp = (prevtodel->next);

      while(tmp){
        tmp->sec += base;
        tmp = tmp->next;
      }
      return;
    }
  }

  return;
}

/* append Timer in appropriate place in linked list.
 * the appropriate place means all timers in linked list are arranged
 * according their next alarm seconds.
 * The algorithm we use here is that the real left alarm seconds for this Timer
 * is the sum of all the sec member in Timer in linked list prev to this Timer
 * plus its sec member. For example, we add 3 Timers to the linked list,
 * whose sec are 4, 3, 2 respectively. Then the linked list looks like:
 * 2 (real sec = 2) --> 1 (real sec = 2+1 = 3) --> 1 (real sec = 2+1+1 = 4)
 * The advantage is obviously, we dont need to remember how many seconds passed.
 * We always fetch the header to respond the alarm signal and set next alarm sec
 * as the next timer in the linked list. (The real situation is a little bit more
 * complex, for example if upcoming timers' sec equals 0, we need to call their
 * handler right away all together in a certain sequence. If its intersec is not
 * zero, we need to append it to the linked list again as quick as possible)
 * note: delTimer also address this problem. If we delete any Timer, we need to
 * recalculate the secs after this timer in the linked list.(simply to add sec to
 * the next timer and delete this timer node)
 * return: only 0 if success, otherwise the hole process failed.
 */

int appendTimer(Header h, Timer t)
{
  assert(h);
  assert(t);
  delTimer(h, t);

  if(h->head == NULL)
  {
    h->head = t;
    return 0;
  }

  Timer tmp = h->head;
  Timer prev = NULL;
  unsigned int prevbase = 0;
  unsigned int base = 0;

  while(tmp)
  {
    prevbase = base;
    base += tmp->sec;
    if(t->sec < base){
      break;
    }
    else{
      prev = tmp;
      tmp = tmp->next;
    }

  }

  if(prev == NULL)
  {
    (h->head)->sec -= t->sec;
    t->next = h->head;
    h->head = t;
    return 0;
  }

  if(tmp == NULL)
    t->sec -=base;
  else
    t->sec -=prevbase;

  prev->next = t;
  t->next = tmp;
  if(tmp)
    tmp->sec -= t->sec;

  return 0;
}

/* pop header timer in linked list.
 * return: its hander
 */

Func* popTimer(Header h)
{
  assert(h);
  if(h->head == NULL)
    return (Func *)-1;
  Func *ret = (h->head)->__inner.sigactor;
  Timer todel = h->head;
  h->head = (h->head)->next;
  // if its intersec greater than 0, we append it right away to the linked list
  if(todel->__inner.intersec > 0)
  {
    todel->sec = todel->__inner.intersec;
    appendTimer(h, todel);
  }
  return ret;
}

void printList(Header h)
{
  assert(h);
  if(h->head == NULL)
    return;

  Timer tmp = h->head;

  while(tmp)
  {
    printf("timer[%d] = %u saved %u\n", tmp->__inner.id, tmp->sec, tmp->__inner.intersec);
    tmp = tmp->next;
  }
}

/* it's the real signal handler responding to every SIGALRM.
 */
void sig_alarm_internal(int signo)
{
  void funcWrapper(int signo, Func *func);

  if(hdr_ptr->head == NULL)
    return;

  Func *recv;
  if((recv = popTimer(hdr_ptr)) == (Func *)-1){
    funcWrapper(SIGALRM, recv);
  }
  else
  {
    // signal ourself if next timer's sec = 0
    if(hdr_ptr->head){
      ((hdr_ptr->head)->sec > 0?alarm((hdr_ptr->head)->sec):kill(getpid(), SIGALRM));
    }
    funcWrapper(SIGALRM, recv);
  }
}

/* Alarm function simulates native alarm function.
 * what if SIGALRM arrives when process is running in Alarm?
 * we just block the signal since there is no slow function in Alarm,
 * sig_alarm_internal will for sure address the signal very soon.
 */

unsigned int Alarm(Header h, Timer mtimer)
{
  sigset_t mask;
  sigset_t old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);

  unsigned int res = 0;
  Timer t;

  if((t = findTimerPrev(h, mtimer->__inner.id)) == NULL)
    goto LL;

  t = h->head;
  while(t)
  {
    res += t->sec; // it's not precise, we should use alarm(0) for the first sec.
            // However, its simple enough to implement.
    if(t->__inner.id == mtimer->__inner.id)
      break;

    t = t->next;
  }
LL:
  if(mtimer->sec == 0)
  {
    delTimer(h, mtimer);
    sigprocmask(SIG_SETMASK, &old, NULL);
    return res;
  }

  appendTimer(h, mtimer);
  if(mtimer->__inner.id == (h->head)->__inner.id)
    ((h->head)->sec > 0?alarm((h->head)->sec):kill(getpid(), SIGALRM));
  sigprocmask(SIG_SETMASK, &old, NULL);
  return res;
}

void initTimer()
{
  struct sigaction act;
  act.sa_handler = sig_alarm_internal;
  act.sa_flags = SA_RESTART|SA_NODEFER;
  sigemptyset(&act.sa_mask);
  sigaction(SIGALRM, &act, NULL);
}

void funcWrapper(int signo, Func *func)
{
  sigset_t mask;
  sigset_t old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_UNBLOCK, &mask, &old);
  func(signo);
  sigprocmask(SIG_SETMASK, &old, NULL);
}

/************************* Thread facility *************************/

Cursor live;
Cursor dead;
Thread_t pmain;

void initCursor(Cursor *cur)
{
  cur->total = 0;
  cur->current = NULL;
}

Thread_t *findThread(Cursor *cur, int tid)
{
  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);
  int counter = cur->total;
  if(counter == 0){
    sigprocmask(SIG_SETMASK, &old, NULL);
    return NULL;
  }

  int i;
  pNode *tmp = cur->current;
  for (int i = 0; i < counter; ++i)
  {
    if((tmp->data)->tid == tid){
      sigprocmask(SIG_SETMASK, &old, NULL);
      return tmp->data;
    }
    tmp = tmp->next;
  }
  sigprocmask(SIG_SETMASK, &old, NULL);
  return NULL;
}

int appendThread(Cursor *cur, Thread_t *pth)
{
  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);
  if(cur->total == 0)
  {
    //note this never freed for simple implementation
    cur->current = (pNode *)malloc(sizeof(pNode));
    assert(cur->current);
    (cur->current)->data = pth;
    (cur->current)->prev = cur->current;
    (cur->current)->next = cur->current;
    cur->total++;
    sigprocmask(SIG_SETMASK, &old, NULL);
    return 0;
  }
  else
  {
    #define MAXTHREADS 5
    if(cur->total > MAXTHREADS)
    {
      assert((cur->total == MAXTHREADS));
      sigprocmask(SIG_SETMASK, &old, NULL);
      return -1;
    }
    //freed at threadJoin for simple implementation
    pNode *tmp = malloc(sizeof(pNode));
    assert(tmp);
    tmp->data = pth;
    tmp->prev = cur->current;
    tmp->next = (cur->current)->next;
    ((cur->current)->next)->prev = tmp;
    (cur->current)->next = tmp;
    cur->total++;
    sigprocmask(SIG_SETMASK, &old, NULL);
    return 0;
  }
}

pNode *deleteThread(Cursor *cur, int tid)
{
  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);

  int counter = cur->total;
  int i;
  pNode *tmp = cur->current;
  for (int i = 0; i < counter; ++i)
  {
    if((tmp->data)->tid == tid){
      (tmp->prev)->next = tmp->next;
      (tmp->next)->prev = tmp->prev;
      if(tmp == cur->current)
      {
        cur->current = cur->current->next;
      }
      //free(tmp);
      cur->total--;
      assert(cur->total);
      sigprocmask(SIG_SETMASK, &old, NULL);
      return tmp;
    }
    tmp = tmp->next;
  }
  sigprocmask(SIG_SETMASK, &old, NULL);
  return NULL;
}

void printThread(Thread_t *pth)
{
  printf("pth tid: %d\n", pth->tid);
  printf("pth stack top: %x\n", pth->stacktop);
  printf("pth stack size: %u\n", pth->stacksize);
  printf("pth state: %d\n", pth->state);
  printf("pth errno: %d\n", pth->errno);
  printf("pth retval: %p\n", pth->retval);
  printf("pth sigmask: %u\n", pth->sigmask);
  printf("pth priority: %d\n", pth->priority);
  printf("pth tick: %d\n", pth->tick);
  printf("EFLAGS: %x\t", pth->regs._eflags);
  printf("EIP: %x\t", pth->regs._eip);
  printf("EAX: %x\t", pth->regs._eax);
  printf("ECX: %x\n", pth->regs._ecx);
  printf("EDX: %x\t", pth->regs._edx);
  printf("EBX: %x\t", pth->regs._ebx);
  printf("ESP: %x\t", pth->regs._esp);
  printf("EBP: %x\n", pth->regs._ebp);
  printf("ESI: %x\t", pth->regs._esi);
  printf("EDI: %x\n", pth->regs._edi);

}

void printLoop(Cursor *cur)
{
  int count = 0;
  pNode *tmp = cur->current;
  assert(tmp);
  do{
    printThread(tmp->data);
    tmp = tmp->next;
    count ++;
  }while(tmp != cur->current);
  printf("real total: %d\n", count);
  printf("total record:%d\n", cur->total);
  assert(count == cur->total);
}

int fetchTID()
{
  static int tid;
  return ++tid;
}

void real_entry(Thread_t *pth, void *(*start_rtn)(void *), void* args)
{
  //printf("in real entry: %p\n", start_rtn);

  pth->retval = (*start_rtn)(args);
  //deleteThread(&live, pth->tid);
  /* some clean job here */
  //free(pth->stack);
  //pth->stack = NULL;
  //pth->stacktop = 0;
  //pth->stacksize = 0;
  #define DETACHED 1
  deleteThread(&live, pth->tid);
  appendThread(&dead, pth);

  if(pth->state == DETACHED)
    threadJoin(pth, NULL);

  switch_to(-1);
}

int threadCreat(Thread_t **pth, void *(*start_rtn)(void *), void *arg)
{
  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);
  //freed at threadJoin for simple implementation
  *pth = malloc(sizeof(Thread_t));
  #define PTHREAD_STACK_MIN 4096
  //freed at threadJoin for simple implementation
  (*pth)->stack = malloc(PTHREAD_STACK_MIN);
  assert((*pth)->stack);
  (*pth)->stacktop = (((int)(*pth)->stack + PTHREAD_STACK_MIN)&(0xfffff000));
  (*pth)->stacksize = PTHREAD_STACK_MIN - (((int)(*pth)->stack + PTHREAD_STACK_MIN) - (*pth)->stacktop);
  (*pth)->state = 0; // 0 JOINABLE 1 DETACHED
  (*pth)->priority = 1; //one seconds
  (*pth)->tick = (*pth)->priority;
  (*pth)->tid = fetchTID();
  sigprocmask(0,NULL,&((*pth)->sigmask));
  /* set params */
  void *dest = (*pth)->stacktop - 12;
  memcpy(dest, pth, 4);
  dest += 4;
  memcpy(dest, &start_rtn, 4);
  dest += 4;
  memcpy(dest, &arg, 4);
  (*pth)->regs._eip = &real_entry;
  (*pth)->regs._esp = (*pth)->stacktop - 16;
  (*pth)->regs._edi = 0;
  (*pth)->regs._esi = 0;
  (*pth)->regs._ebp = 0;
  (*pth)->regs._eax = 0;
  (*pth)->regs._ebx = 0;
  (*pth)->regs._ecx = 0;
  (*pth)->regs._edx = 0;
  (*pth)->regs._eflags = 0;
  appendThread(&live, (*pth));
  sigprocmask(SIG_SETMASK, &old, NULL);
  return 0;
}

int threadJoin(Thread_t *pth, void **rval_ptr)
{

  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);
  Thread_t *find1, *find2;
  find1 = findThread(&live, pth->tid);
  find2 = findThread(&dead, pth->tid);

  if((find1 == NULL)&&(find2 == NULL)){
    sigprocmask(SIG_SETMASK, &old, NULL);
    return -1;
  }

  if(find2){
    if(rval_ptr != NULL)
      *rval_ptr = find2->retval;

    sigprocmask(SIG_SETMASK, &old, NULL);
    return 0;
  }
  sigprocmask(SIG_SETMASK, &old, NULL);
  while(1)
  {
    if((find2 = findThread(&dead, pth->tid))!= NULL){
      if(rval_ptr!= NULL)
        *rval_ptr = find2->retval;

      pNode *tmp = deleteThread(&dead, pth->tid);
      free(tmp);
      free((Stack_t)find2->stack);
      free(find2);
      return 0;
    }
  }
  return -1;
}

void init()
{
  initTimer();
  initCursor(&live);
  initCursor(&dead);
  appendThread(&live, &pmain);
  Alarm(hdr_ptr,mallocTimer(1, switch_to, 1, 1));
}

void switch_to(int signo)
{
  sigset_t mask,old;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  sigprocmask(SIG_BLOCK, &mask, &old);
  Regs regs;
  //printf("");
  if(signo == -1)
  {
    regs = live.current->data->regs;
    sigprocmask(SIG_SETMASK, &old, NULL);
    JMP(regs);
    assert(0);
  }

  int _edi;
  int _esi;
  int _ebp;
  int _esp;
  int _ebx;
  int _edx;
  int _ecx;
  int _eax;
  int _eip = &&_REENTERPOINT;
  int _eflags;
  live.current->data->__reenter = 0;
  /* save current context */
  SAVE();

  /* save context in current thread */
  live.current->data->regs._eflags = _eflags;
  live.current->data->regs._eip = _eip;
  live.current->data->regs._eax = _eax;
  live.current->data->regs._ecx = _ecx;
  live.current->data->regs._edx = _edx;
  live.current->data->regs._ebx = _ebx;
  live.current->data->regs._esp = _esp;
  live.current->data->regs._ebp = _ebp;
  live.current->data->regs._esi = _esi;
  live.current->data->regs._edi = _edi;

  if(!live.current->data->__reenter)
  {
    goto _END;
  }

_REENTERPOINT:
  regs = live.current->data->regs;

  if(live.current->data->__reenter){
    live.current->data->__reenter = 0;
    sigprocmask(SIG_SETMASK, &old, NULL);
    return;
  } 

_END:
  live.current->data->__reenter = 1;
  regs = live.current->next->data->regs;
  live.current = live.current->next;
  sigprocmask(SIG_SETMASK, &old, NULL);
  JMP(regs);
  assert(0);
}

/************************* Test *************************/
/**
 * Note: The implementation is really bugy, right now only support compute in thread.
 * Even standard I/O in the thread will cause I/O bus error or segmentation error because
 * all pthread-reentrant function is not guaranteed in our thread model.
 * (pthread_mutex_t cannot block thread in our model cause we modify eip directly)
 */
void *sum1tod(void *d)
{
  int i, k, j=0;

  for (i = 0; i <= (int)d; ++i)
  {
      /* code */
      j+=i;
  }
  return ((void *)j);
}

int main(int argc, char const *argv[])
{
  int res = 0;
  int i;
  init();
  Thread_t *tid1, *tid2;
  int *res1, *res2;
  threadCreat(&tid1, sum1tod, 100);
  threadCreat(&tid2, sum1tod, 100);
  for (i = 0; i <= 100; ++i){
    res+=i;
  }

  threadJoin(tid1, &res1);
  threadJoin(tid2, &res2);
  printf("parallel compute: %d = 5050 * 3\n", (int)res1+(int)res2+(int)res);
  return 0;
}

以上这篇C语言实现用户态线程库案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C语言实现用户态线程库案例

    轮子年年有人造,我们也来凑热闹,参考协程实现,大概有以下几种方法: 1)利用setjmp,longjmp 2)利用ucontext接口函数 3)汇编 (线程无非就是多了个抢占功能,由定时器触发,而非自愿让出运行权限) 因为我写的时候还没看到其他帖子,如果看到了,铁定会用最直观的ucontext接口写的(注意,在macOSX中已经标注为废除,头文件得换做sys/ucontext.h),结果就是我用了汇编来写,但是尽量不用汇编来写整个switch_to调度函数(这样有个明显的坏处,那就是用gas/n

  • 使用C语言实现字符串逆序操作案例

    编写一个函数 reverse_string(char * string) 实现:将参数字符串中的字符反向排列. 要求:不能使用C函数库中的字符串操作函数. 非递归实现: #include<stdio.h> //编写一个函数 reverse_string(char * string)(非递归实现) //实现:将参数字符串中的字符反向排列. //要求:不能使用C函数库中的字符串操作函数. //求字符串长度 int my_strlen(char* str) { int count = 0; whil

  • 纯c语言优雅地实现矩阵运算库的方法

    目录 1.一个优雅好用的c语言库必须满足哪些条件 2.实现一个矩阵运算库的几点思考 (1)采用预定义的数据类型,避免直接使用编译器定义的数据类型 (2)基于对象编程,定义矩阵对象 (3)除了特别编写的内存处理函数(使用栈链表保存.释放动态分配的内存地址),不允许任何函数直接分配和释放内存 (4)防御性编程,对输入参数做有效性检查,并返回错误号 (5)注意编程细节的打磨 3.完整c程序 参考资料 编程既是技术输出也是艺术创作.鉴赏高手写的程序,往往让人眼前一亮,他们思路.逻辑清晰,所呈现的代码简洁

  • 详解C++11中的线程库

    目录 一.线程库的介绍 1.1. 使用时的注意点 1.2. 线程函数参数 1.3. join与detach 二.原子性操作库 2.1. atomic 2.2. 锁 三.使用lambda表达式创建多个线程 四.条件变量 一.线程库的介绍 在C++11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口,这使得代码的可移植性比较差.C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念.要使用标

  • 深入解析C++11 lambda表达式/包装器/线程库

    目录 零.前言 一.lambda表达式 1.lambda的引入 2.lambda表达式语法 3.捕获列表说明 4.函数对象与lambda表达式 二.包装器 1.function包装器 2.bind 概念: 三.线程库 1.线程的概念及使用 2.线程函数参数 3.原子性操作库(atomic) 4.lock_guard与unique_lock 1.mutex的种类 2.lock_guard 3.unique_lock 5.两个线程交替打印奇数偶数 零.前言 本章是讲解学习C++11语法新特性的第三篇

  • C语言编程中借助pthreads库进行多线程编程的示例

    运行之前需要做一些配置: 1.下载PTHREAD的WINDOWS开发包 pthreads-w32-2-4-0-release.exe(任何一个版本均可)    http://sourceware.org/pthreads-win32/ ,解压到一个目录. 2.找到include和lib文件夹,下面分别把它们添加到VC++6.0的头文件路径和静态链接库路径下面:    a).Tools->Options,选择Directory页面,然后在Show directories for:中选择Includ

  • 详解Linux用户态与内核态通信的几种方式

    Linux 用户态和内核态由于 CPU 权限的限制,通信并不像想象中的使用进程间通信方式那么简单,今天这篇文章就来看看 Linux 用户态和内核态究竟有哪些通信方式. 我们平常在写代码时,一般是在用户空间,通过系统调用函数来访问内核空间,这是最常用的一种用户态和内核态通信的方式.(关于 Linux 用户态和内核态可以参考 xx) 除此之外,还有以下四种方式: procfs(/proc) sysctl(/proc/sys) sysfs(/sys) netlink 套接口 procfs(/proc)

  • python爬虫线程池案例详解(梨视频短视频爬取)

    python爬虫-梨视频短视频爬取(线程池) 示例代码 import requests from lxml import etree import random from multiprocessing.dummy import Pool # 多进程要传的方法,多进程pool.map()传的第二个参数是一个迭代器对象 # 而传的get_video方法也要有一个迭代器参数 def get_video(dic): headers = { 'User-Agent':'Mozilla/5.0 (Wind

  • Python基础之标准库和常用的第三方库案例教程

    Python基础:标准库和常用的第三方库 Python的标准库有: 名称 作用 datetime 为日期和时间处理同时提供了简单和复杂的方法. zlib 直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile. random 提供了生成随机数的工具. math 为浮点运算提供了对底层C函数库的访问. sys 工具脚本经常调用命令行参数.这些命令行参数以链表形式存储于 sys 模块的 argv 变量. glob 提供了一个函数用于从目录通配符搜索中生成文

  • R语言与多元线性回归分析计算案例

    目录 计算实例 分析 模型的进一步分析 计算实例 例 6.9 某大型牙膏制造企业为了更好地拓展产品市场,有效地管理库存,公司董事会要求销售部门根据市场调查,找出公司生产的牙膏销售量与销售价格,广告投入等之间的关系,从而预测出在不同价格和广告费用下销售量.为此,销售部门的研究人员收集了过去30个销售周期(每个销售周期为4周)公司生产的牙膏的销售量.销售价格.投入的广告费用,以及周期其他厂家生产同类牙膏的市场平均销售价格,如表6.4所示. 试根据这些数据建立一个数学模型,分析牙膏销售量与其他因素的关

随机推荐