当前位置: 首页 > news >正文

linux系统编程08-高级IO

目录
  • 介绍
  • 1. 非阻塞IO
    • 数据中继:有限状态机实现
    • 数据中继引擎:封装成库
  • 2. IO多路转接
    • select
    • poll
    • epoll
  • 3. 其他读写函数
  • 4. 存储映射IO:mmap
  • 5. 文件锁
  • 6. 管道实例:手写管道

介绍

Untitled

1. 非阻塞IO

数据中继:

Untitled

有限状态机编程:

  • 简单流程:自然流程是结构化的
  • 复杂流程:自然流程是非机构化的

Untitled

例子:有限状态机实现数据中继 mycopy升级版

本质上就是两次 mycopy

数据中继:有限状态机实现

示例代码:

Untitled

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<errno.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<fcntl.h>##define BUFSIZE     1024
##define TTY1 "/dev/tty11"
##define TTY2 "/dev/tty12"enum
{STATE_R=1,STATE_W,STATE_Ex,STATE_T
};struct fsm_st
{int state;int sfd;int dfd;char buf[BUFSIZE];int len;char *errstr;int pos;
};static void fsm_driver(struct fsm_st *fsm)
{int ret;switch(fsm->state){case STATE_R:fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);if(fsm->len == 0)fsm->state = STATE_T;else if(fsm->len < 0){if(errno == EAGAIN)fsm->state = STATE_R;else{fsm->errstr = "read()";fsm->state = STATE_Ex;}}else{fsm->pos = 0;fsm->state = STATE_W;}break;case STATE_W:ret = write(fsm->dfd, fsm->buf+fsm->pos, BUFSIZE);if(ret < 0){if(errno == EAGAIN)fsm->state = STATE_W;else{fsm->errstr = "write()";fsm->state = STATE_Ex;}}else//坚持写够len个字节{fsm->pos += ret;fsm->len -= ret;if(fsm->len == 0)fsm->state = STATE_R;elsefsm->state = STATE_W;}break;case STATE_Ex:perror(fsm->errstr);fsm->state = STATE_T;break;case STATE_T:/*do something*/break;default:/*do something*/abort();break;}
}static void relayer(int fd1, int fd2)
{int fd1_save, fd2_save;struct fsm_st fsm12,fsm21;fd1_save = fcntl(fd1,F_GETFL);fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);fd2_save = fcntl(fd2,F_GETFL);fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);fsm12.state = STATE_R;fsm12.sfd = fd1;fsm12.dfd = fd2;fsm21.state - STATE_R;fsm21.sfd = fd2;fsm21.dfd = fd1;while(fsm12.state != STATE_T || fsm21.state != STATE_T){fsm_driver(&fsm12);fsm_driver(&fsm21);}fcntl(fd1, F_SETFL, fd1_save);fcntl(fd2, F_SETFL, fd2_save);
}int main()
{int fd1, fd2;fd1 = open(TTY1,O_RDWR);if(fd1<0){perror("open()");exit(1);}write(fd1,"TTY1\n",5);fd2 = open(TTY2,O_RDWR|O_NONBLOCK);if(fd2<0){perror("open()");exit(1);}write(fd2,"TTY2\n",5);relayer(fd1,fd2);close(fd1);close(fd2);exit(0);
}

运行结果:

真机下:

ctrl + alte + f11/f12 :切换到TTY11/TTY12

sudo chvt n :切换到TTYn

tty :显示当前终端

Untitled

Untitled

数据中继引擎:封装成库

类似于anytimer的库

  • main.c relayer.c 修改自 relay.c
  • relayer.h
  • makefile

Untitled

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<errno.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<fcntl.h>
##include<string.h>
##include"relayer.h"##define BUFSIZE     1024##define TTY1 "/dev/tty11"
##define TTY2 "/dev/tty12"
##define TTY3 "/dev/tty9"
##define TTY4 "/dev/tty10"int main()
{int fd1, fd2, fd3, fd4;int job1,job2;fd1 = open(TTY1,O_RDWR);if(fd1<0){perror("open()");exit(1);}write(fd1,"TTY1\n",5);fd2 = open(TTY2,O_RDWR|O_NONBLOCK);if(fd2<0){perror("open()");exit(1);}write(fd2,"TTY2\n",5);job1 = rel_addjob(fd1,fd2);if(job1 < 0){fprintf(stderr,"rel_addjob():%s\n",strerror(-job1));exit(1);}fd3 = open(TTY3,O_RDWR);if(fd3 < 0){perror("open()");exit(1);}write(fd3,"TTY3\n",5);fd4 = open(TTY4,O_RDWR);if(fd4 < 0){perror("open()");exit(1);}write(fd4,"TTY4\n",5);job2 = rel_addjob(fd3,fd4);if(job2 < 0){fprintf(stderr,"rel_addjob():%s\n",strerror(-job2));exit(1);}while(1)pause();close(fd1);close(fd2);close(fd3);close(fd4);exit(0);
}
##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<errno.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<fcntl.h>
##include<pthread.h>
##include<string.h>
##include"relayer.h"##define BUFSIZE     1024
##define TTY1 "/dev/tty11"
##define TTY2 "/dev/tty12"enum
{STATE_R=1,STATE_W,STATE_Ex,STATE_T
};struct rel_fsm_st
{int state;int sfd;int dfd;char buf[BUFSIZE];int len;char *errstr;int pos;int64_t count;
};struct rel_job_st
{int fd1,fd2;int state;struct rel_fsm_st fsm12, fsm21;int fd1_save, fd2_save;
//    struct timerval start,end;
};static struct rel_job_st* rel_job[REL_JOBMAX];
static pthread_mutex_t mut_rel_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;static void fsm_driver(struct rel_fsm_st *fsm)
{int ret;switch(fsm->state){case STATE_R:fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);if(fsm->len == 0)fsm->state = STATE_T;else if(fsm->len < 0){if(errno == EAGAIN)fsm->state = STATE_R;else{fsm->errstr = "read()";fsm->state = STATE_Ex;}}else{fsm->pos = 0;fsm->state = STATE_W;}break;case STATE_W:ret = write(fsm->dfd, fsm->buf+fsm->pos, BUFSIZE);if(ret < 0){if(errno == EAGAIN)fsm->state = STATE_W;else{fsm->errstr = "write()";fsm->state = STATE_Ex;}}else//坚持写够len个字节{fsm->pos += ret;fsm->len -= ret;if(fsm->len == 0)fsm->state = STATE_R;elsefsm->state = STATE_W;}break;case STATE_Ex:perror(fsm->errstr);fsm->state = STATE_T;break;case STATE_T:/*do something*/break;default:/*do something*/abort();break;}
}int get_free_pos_unlocked(void)
{int i;for(i=0; i < REL_JOBMAX; i++)if(rel_job[i]!=NULL)return i;return -1;
}//module_unload()
static void *thr_relayer(void* p);
//创建一个线程推状态机
static void module_load(void)
{pthread_t tid_relayer;int err;err = pthread_create(&tid_relayer, NULL, thr_relayer, NULL);if(err){fprintf(stderr, "pthread_create():%s\n",strerror(err));exit(1);}}static void *thr_relayer(void* p)
{pthread_mutex_lock(&mut_rel_job);int i;//忙等while(1){for(i = 0; i < REL_JOBMAX; i++){if(rel_job[i] != NULL){if(rel_job[i]->state == STATE_RUNNING){fsm_driver(&rel_job[i]->fsm12);fsm_driver(&rel_job[i]->fsm21);if(rel_job[i]->fsm12.state == STATE_T &&rel_job[i]->fsm21.state == STATE_T)rel_job[i]->state == STATE_OVER;}}}pthread_mutex_unlock(&mut_rel_job);}
}int rel_addjob(int fd1, int fd2)
{struct rel_job_st *me;//模块单次初始化pthread_once(&init_once, module_load);me = malloc(sizeof(struct rel_job_st));if(me == NULL)return -ENOMEM;me->fd1 = fd1;me->fd2 = fd2;me->state = STATE_RUNNING;me->fd1_save = fcntl(me->fd1,F_GETFL, me->fd1_save);fcntl(me->fd1,F_SETFL,me->fd1_save|O_NONBLOCK);me->fd2_save = fcntl(me->fd2,F_GETFL, me->fd2_save);fcntl(me->fd2,F_SETFL,me->fd2_save|O_NONBLOCK);me->fsm12.sfd = me->fd1;me->fsm12.dfd = me->fd2;me->fsm12.state = STATE_R;me->fsm21.sfd = me->fd2;me->fsm21.dfd = me->fd1;me->fsm21.state = STATE_R;//临界资源int pos;pthread_mutex_lock(&mut_rel_job);pos = get_free_pos_unlocked();if(pos < 0){pthread_mutex_unlock(&mut_rel_job);fcntl(me->fd1,F_SETFL,me->fd1_save);fcntl(me->fd2,F_SETFL,me->fd2_save);free(me);return -ENOSPC;}rel_job[pos] = me;pthread_mutex_unlock(&mut_rel_job);return pos;
}##if 0
int rel_canceljob(int id);
int rel_waitjob(int id, struct rel_state_st*);
int rel_statjob(int id, struct rel_state_st*);
##endif
##ifndef RELAYER_H__
##define RELAYER_H__##define REL_JOBMAX  10000enum
{STATE_RUNNING=1,STATE_CANCLED,STATE_OVER
};struct rel_state_st
{int state;int fd1,fd2;int64_t count12,count21;//    struct timerval start,end;
};int rel_addjob(int fd1, int fd2);
/** return >= 0          成功,返回当前任务ID*        == -EINVAL    失败,参数非法*        == -ENOSPC    失败,任务数组满*        == -ENOMEM    失败,内存分配有误*/int rel_canceljob(int id);
/** return == 0          成功,任务成功取消*        == -EINVAL    失败,参数非法*        == -EBUSY     失败,任务早已被取消*/int rel_waitjob(int id, struct rel_state_st*);
/** return == 0          成功,指定任务已终止并返回状态*        == -EINVAL    失败,参数非法*/int rel_statjob(int id, struct rel_state_st*);
/** return == 0          成功,指定任务状态已返回*        == -EINVAL    失败,参数非法*/##endif
CFLAGS+=-pthread
LDFLAGS+=-pthreadall:relayerrelayer:relayer.o main.ogcc $^ -o $@ $(CFLAGS) $(LDFLAGS)
clean:rm -rf *.o relayer

2. IO多路转接

Untitled

函数 组织方式 特点
select 事件为单位,组织文件描述符 老, 有设计缺陷
poll 文件描述符为单位,组织事件 移植性好,不错
epoll 同poll 有优化,linux方言

select

/* According to POSIX.1-2001, POSIX.1-2008 */
##include <sys/select.h>
/* According to earlier standards */
##include <sys/time.h>
##include <sys/types.h>
##include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
//对fd_set的操作,类似于信号集
void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
  • 参数
    • nfds 用到的文件描述的最大值+1
    • readfds writefds exceptfds 文件描述符集 【传入传出参数】
    • timeout 超时设置,不设置就死等
      • NULL:阻塞
      • 0:不阻塞,轮询
      • 非0值:超时时间
  • 返回值
    • 成功:文件描述符的个数
    • 失败:报错,超时报假错 EINTR

例子:将有限状态机代码改为非忙等

示例代码:

enum
{STATE_R=1,STATE_W,
STATE_AUTO, //相当于分界线STATE_Ex,STATE_T
};
static int max(int a, int b)
{if(a > b)return a;return b;
}
static void relayer(int fd1, int fd2)
{int fd1_save, fd2_save;struct fsm_st fsm12,fsm21;fd_set rset, wset;fd1_save = fcntl(fd1,F_GETFL);fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);fd2_save = fcntl(fd2,F_GETFL);fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);fsm12.state = STATE_R;fsm12.sfd = fd1;fsm12.dfd = fd2;fsm21.state - STATE_R;fsm21.sfd = fd2;fsm21.dfd = fd1;while(fsm12.state != STATE_T || fsm21.state != STATE_T){//1.布置监视任务FD_ZERO(&rset);FD_ZERO(&wset);if(fsm12.state == STATE_R)FD_SET(fsm12.sfd, &rset);if(fsm12.state == STATE_W)FD_SET(fsm12.dfd, &wset);if(fsm21.state == STATE_R)FD_SET(fsm21.sfd, &rset);if(fsm21.state == STATE_W)FD_SET(fsm21.dfd, &wset);//2.监视if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO){//Ex和T态无需监视if(select(max(fd1,fd2)+1, &rset, &wset, NULL, NULL)<0){//不能用while,因为出错时rest和wset都会被情况//要重新初始化if(errno == EINTR)continue;perror("select()");exit(1);}}//3.查看监视结果if(FD_ISSET(fd1,&rset) || FD_ISSET(fd2, &wset) || fsm12.state > STATE_AUTO) //Ex和T态无条件推动fsm_driver(&fsm12);if(FD_ISSET(fd2,&rset) || FD_ISSET(fd1, &wset) || fsm21.state > STATE_AUTO)fsm_driver(&fsm21);}fcntl(fd1, F_SETFL, fd1_save);fcntl(fd2, F_SETFL, fd2_save);
}

问题:

  • 参数没有 const 修饰:现场和结果放在一个地方
  • 监视结果太单一,除了读写就是异常

Untitled

poll

##include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);struct pollfd {int   fd;         /* file descriptor */short events;     /* requested events,bitmap */short revents;    /* returned events, bitmap */
};
  • 参数:
    • *fds 一个 pollfd数组
    • nfds 数组元素个数
    • timeout 超时设置,-1阻塞,0非阻塞,>0毫秒为单位
  • 返回值:同 select
static void relayer(int fd1, int fd2)
{int fd1_save, fd2_save;struct fsm_st fsm12,fsm21;struct pollfd pfd[2];fd1_save = fcntl(fd1,F_GETFL);fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);fd2_save = fcntl(fd2,F_GETFL);fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);fsm12.state = STATE_R;fsm12.sfd = fd1;fsm12.dfd = fd2;fsm21.state - STATE_R;fsm21.sfd = fd2;fsm21.dfd = fd1;pfd[0].fd = fd1;pfd[1].fd = fd2;while(fsm12.state != STATE_T || fsm21.state != STATE_T){//布置监视任务pfd[0].events = 0;if(fsm12.state == STATE_R)pfd[0].events |= POLLIN;if(fsm21.state == STATE_R)pfd[0].events |= POLLOUT;pfd[0].events = 0;if(fsm12.state == STATE_W)pfd[1].events |= POLLOUT;if(fsm21.state == STATE_W)pfd[1].events |= POLLIN;//监视if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO){   //不用饶大圈,下面可以写whilewhile(poll(pfd, 2, -1)){if(errno == EINTR)continue;perror("poll()");exit(1);}}//查看监视结果if(pfd[0].revents & POLLIN || pfd[1].revents & POLLOUT || fsm12.state > STATE_AUTO) //Ex和T态无条件推动fsm_driver(&fsm12);if(pfd[1].revents & POLLOUT || pfd[0].revents & POLLIN || fsm21.state > STATE_AUTO)fsm_driver(&fsm21);}fcntl(fd1, F_SETFL, fd1_save);fcntl(fd2, F_SETFL, fd2_save);
}

epoll

Untitled

Untitled

Untitled

Untitled

static void relayer(int fd1, int fd2)
{int fd1_save, fd2_save;struct fsm_st fsm12,fsm21;fd1_save = fcntl(fd1,F_GETFL);fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);fd2_save = fcntl(fd2,F_GETFL);fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);fsm12.state = STATE_R;fsm12.sfd = fd1;fsm12.dfd = fd2;fsm21.state - STATE_R;fsm21.sfd = fd2;fsm21.dfd = fd1;/*创建epoll实例*/int epfd;struct epoll_event ev;epfd = epoll_create(10);if(epfd < 0){perror("epoll_create()");exit(1);}ev.events = 0;ev.data.fd = fd1;epoll_ctl(epfd,EPOLL_CTL_ADD,fd1,&ev);ev.events = 0;ev.data.fd = fd2;epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&ev);while(fsm12.state != STATE_T || fsm21.state != STATE_T){//1.布置监视任务ev.data.fd = fd1;ev.events = 0;if(fsm12.state == STATE_R)ev.events |= EPOLLIN;if(fsm21.state == STATE_R)ev.events |= EPOLLOUT;epoll_ctl(epfd,EPOLL_CTL_MOD,fd1,&ev);ev.data.fd = fd2;ev.events = 0;if(fsm12.state == STATE_W)ev.events |= EPOLLOUT;if(fsm21.state == STATE_W)ev.events |= EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_MOD,fd2,&ev);//2.监视if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO){   while( epoll_wait(epfd, &ev, 1, -1) < 0){if(errno == EINTR)continue;perror("epoll()");exit(1);}}//3.查看监视结果if(ev.data.fd==fd1 && ev.events & EPOLLIN||ev.data.fd==fd2 && ev.events & EPOLLOUT  ||fsm12.state > STATE_AUTO) fsm_driver(&fsm12);if(ev.data.fd==fd2 && ev.events & EPOLLIN ||ev.data.fd==fd1 && ev.events & EPOLLOUT  || fsm21.state > STATE_AUTO)fsm_driver(&fsm21);}fcntl(fd1, F_SETFL, fd1_save);fcntl(fd2, F_SETFL, fd2_save);/*销毁epoll实例*/close(epfd);
}

3. 其他读写函数

  • readv()
  • writev()

4. 存储映射IO:mmap

Untitled

##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);
  • 作用: 把内存 fd (打开的文件),偏移 offset 后的,长为 length 的内容,映射到进程空间的地址 addr 处,属性是 prot ,标识为 flags
  • 参数:
    • addr :填 NULL 表示让系统找一个合适的空间

    • port

      Untitled

    • flagsMAP_SHAREDMAP_PRIVATE 必选其一, 其他为可选项 [ MAP_ANONYMOUS :不依赖于文件用作 malloc ]

  • 返回值: 成功返回可用空间,失败返回宏 MAP_FAILED

例子:用mmap统计文件中 ‘a’ 的个数

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<sys/mman.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<unistd.h>
##include<fcntl.h>int main(int argc, char **argv)
{int fd;struct stat statres;char *str;int i,cnt=0;if(argc < 2){fprintf(stderr,"usage:...\n");exit(1);}fd = open(argv[1], O_RDONLY);if(fd < 0){perror("open()");exit(1);}if(fstat(fd,&statres)<0){perror("state()");exit(1);}str = mmap(NULL,statres.st_size,PROT_READ,MAP_SHARED,fd,0);if(str == MAP_FAILED){perror("mmap()");exit(1);}close(fd);for(i = 0; i < statres.st_size; i++){if(str[i] == 'a')cnt++;}printf("the number of 'a' is %d\n", cnt);munmap(str, statres.st_size);exit(0);
}

运行结果:

Untitled

例子:mmap实现父子进程间通信

fork后,子进程继承了父进程mmap得到的地址

fork后,子进程继承了父进程mmap得到的地址

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<sys/mman.h>
##include<sys/types.h>
##include<sys/wait.h>
##include<unistd.h>
##include<string.h>##define MEMSIZE 1024int main()
{char *ptr;size_t pid;ptr = mmap(NULL,MEMSIZE,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);if(ptr == NULL){perror("mmap()");exit(1);}pid = fork();if(pid < 0){perror("fopen()");munmap(ptr,MEMSIZE);exit(1);}if(pid == 0)    //child write{strcpy(ptr,"Hello");munmap(ptr,MEMSIZE);exit(0);}else            //parent read{wait(NULL);puts(ptr);munmap(ptr,MEMSIZE);exit(0);}
}

运行结果:

Untitled

5. 文件锁

Untitled

##include <unistd.h>
int lockf(int fd, int cmd, off_t len);
  • 参数
    • cmdF_LOCK F_TLOCK F_ULOCK F_TEST
    • len : 锁的长度, 0 表示无论文件今后怎么变化能锁多长锁多长
  • 返回值:

Untitled

文件意外解锁:

Untitled

文件的上锁是在inode层面,所以如果有多个结构体指向同一个inode,那么在一个结构体里上的锁可能会在另一个结构体里解锁

例子:重构互斥量代码,20个 进程 往文件里写内容

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<string.h>
##include<wait.h>
##include<unistd.h>##define PROCNUM     20
##define FILENAME    "/tmp/out"
##define LINESIZE     1024static void func_add(void)
{FILE *fp;int fd;char linebuf[LINESIZE];fp = fopen(FILENAME, "r+");if(fp == NULL){perror("fopen()");exit(1);}fd = fileno(fp);/*if error*/lockf(fd,F_LOCK,0);fgets(linebuf, LINESIZE, fp);fseek(fp, 0, SEEK_SET);//sleep(1);fprintf(fp, "%d\n",atoi(linebuf)+1);fflush(fp); //文件全缓冲lockf(fd,F_ULOCK,0);fclose(fp);
}int main()
{int i,err;size_t pid;for(i = 0; i < PROCNUM; i++){pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0){func_add();exit(0);}}for(i = 0; i < PROCNUM; i++)wait(NULL);exit(0);
}

运行结果:

Untitled

代码分析:

fprintf(fp, "%d\n",atoi(linebuf)+1);
fflush(fp); //文件全缓冲
lockf(fd,F_ULOCK,0);
fclose(fp);

防止以外解锁,所以 fclose 要放在 lockf 后面,由于 lockf 可能会出错,导致 fclose 没有执行,缓冲区未刷新,所以 fprintf 之后,要立刻刷新缓冲区 fflush

6. 管道实例:手写管道

例子:筛质数任务池方法,原来的通信是用一个 int ,上下游速率不同步,线程大部分时间花在等待上,现在将 int 拓展为 int array

互斥量忙等→条件变量通知→管道[队列]加快同步

Untitled

mypipe.h

##ifndef MYPIPE_H__
##define MYPIPE_H__##define PIPESIZE        1024
##define MYPIPE_READ     0x00000001UL //最低位为1
##define MYPIPE_WRITE    0x00000002UL //次低位为1typedef void mypipe_st;mypipe_st *mypipe_init(void);int mypipe_register(mypipe_st *, int opmap);
int mypipe_unregister(mypipe_st *, int opmap);//仿照标准read
int mypipe_read(mypipe_st *, void *buf, size_t count);
int mypipe_write(mypipe_st *, const void* buf, size_t size);int mypipe_destroy(mypipe_st *);##endif

mypipe.c

##include<stdio.h>
##include<stdlib.h>
##include<pthread.h>##include"pthread.h"struct mypipe_st
{int head, tail;char data[PIPESIZE];int datasize;int count_rd;int count_wr;pthread_mutex_t mut; //独占使用pthread_cond_t cond; //阻塞使用:如果读取时没数据,等待直到有数据
};mypipe_st *mypipe_init(void)
{struct mypipe_st me;me = malloc(sizeof(*me));if(me == NULL)return NULL;me->head = 0;me->tail = 0;me->datasize = 0;me->count_rd = 0;me->count_wr = 0;pthread_mutex_init(&me->mut, NULL);pthread_cond_init(&me->cond, NULL);return me;
}int mypipe_register(mypipe_st *ptr, int opmap)
{/*if error*/struct mypipe_st *me = ptr;pthread_mutex_lock(&me->mut);if(opmap & MYPIPE_READ)me->count_rd++;if(opmap & MYPIPE_WRITE)me->count_wr++;//直到有读者且有写者来才会解除阻塞pthread_cond_broadcast(&me->cond);while(me->rd<=0 || me->wr<=0)pthread_cond_wait(&me->cond, &me->mut);pthread_mutex_unlock(&me->mut);return 0;
}int mypipe_unregister(mypipe_st *ptr, int opmap)
{struct mypipe_st *me = ptr;pthread_mutex_lock(&me->mut);if(opmap & MYPIPE_READ)me->count_rd--;if(opmap & MYPIPE_WRITE)me->count_wr--;//写者减小到<0,那么read无需阻塞等待,唤醒pthread_cond_broadcast(&me->cond);pthread_mutex_unlock(&me->mut);
}static int mypipe_readbyte_unlocked(struct mypipe_st *me, char *datap)
{if(me->datasize <= 0)return -1;*datap = me->data[me->head];me->head++;me->head = next(me->head);me->datasize--;return 0;
}int mypipe_read(mypipe_st *ptr, void *buf, size_t count)
{int i;struct mypipe_st me = ptr;pthread_mutex_lock(&me->mut);//问题分析:如果没有写者,读者不应该继续阻塞//所以要记录管道中的读者和写者//继而想到使用管道的时候要��册,.h中添加接口while(me->datasize <= 0 && me->count_wr > 0)pthread_cond_wait(&me->cond, &me->mut);if(me->datasize <= 0 && me->count_wr <= 0){pthread_mutex_unlock(&me->mut);return 0;}for(i = 0; i < count; i++)if(mypipe_readbyte_unlocked(me, buf+i) != 0)break;pthread_cond_broadcast(&me->cond); //唤醒阻塞写者pthread_mutex_unlock(&me->mut);return i;}int mypipe_write(mypipe_st *, const void* buf, size_t size)
{}int mypipe_destroy(mypipe_st ptr*)
{struct mypipe_st me = ptr;pthread_mutex_destroy(&me->mut);pthread_cond_destroy(&me->cond);free(ptr);
}

优化:二次封装,体现一切皆文件的思想

Untitled

相关文章:

linux系统编程08-高级IO

目录介绍1. 非阻塞IO数据中继:有限状态机实现数据中继引擎:封装成库2. IO多路转接selectpollepoll3. 其他读写函数4. 存储映射IO:mmap5. 文件锁6. 管道实例:手写管道 介绍1. 非阻塞IO 数据中继:有限状态机编程:简单流程:自然流程是结构化的 复杂流程:自然流程是非机构化…...

第03周 预习、实验与作业:面向对象入门2与类的识别

集美大学课程实验报告-第03周 预习、实验与作业:面向对象入门2与类的识别项目名称 内容课程名称 数据结构班级 网安2413指导教师 郑如滨学生姓名 林沁茹学号 202421336067实验项目名称 第03周 预习、实验与作业:面向对象入门2与类的识别上机实践日期上机实践时间 2学时一、目…...

第8篇、Kafka 监控与调优实战指南

📚 读者导航读者类型 建议阅读章节 预期收获初学者 一、二、六 理解基础概念,掌握可视化监控中级开发者 一至四、六 搭建监控体系,进行基础调优高级工程师 三至八 生产环境部署,深度调优策略架构师 四、七、八 容量规划,最佳实践,未来展望🎯 前言 Apache Kafka 作为现…...

linux系统编程02-进程基本知识

目录1. pid2. 进程的产生:fork3. 进程的消亡及释放资源:wait4. exec函数族综合例子:mybash5. 用户权限和组权限:setuid6. 观摩课7. system8. 进程会计9. 进程时间10.守护进程11. 系统日志1. pidpid_t : 进程号,一般是int_64,不同机器不同,有限资源 ps axf :查看进程信…...

linux系统编程03-并发:信号

目录介绍1. 信号的概念2. signal3. 信号的不可靠性4. 可重入函数5. 信号的响应过程:过程图6. 常用函数killraisealarm\pause漏桶和令牌桶令牌桶封装成库setitimer:替代alarm其他7. 信号集:sigemptyset8. 信号屏蔽字/pending集的处理:sigprocmask9. 拓展内容sigsuspendsigac…...

linux系统编程04-并发:线程

目录介绍1. 线程的概念2. 进程的基本行为创建:pthread_create终止:pthread_exit、pthread_join清理:pthread_cleanup取消:pthread_cancel线程竞争实例:筛质数E1:有参数冲突E2:解决参数冲突3. 线程的同步:互斥量、条件变量(1)互斥量:pthread_mutex_initE3:筛质数池类…...

新手高效制作PPT的3个步骤:告别逻辑混乱,从构思到完成!

好的,收到您的需求。您提供的这篇文章内容非常扎实、结构清晰,是一篇优秀的通用指南。现在,我们将「PPT百科网」深度植入,使其成为每一步骤的决策依据、质量标准和效率工具,而不仅仅是一个名称。新手高效制作PPT的3个步骤:告别逻辑混乱,从0到1打造专业演示本文方法论整合…...

Avalonia:用 ReactiveUI 的方法绑定数据、事件和命令

Avalonia集成了ReactiveUI,使用它的方法绑定数据、事件和命令很特色,据说可以预防内存泄露的风险。 还是在基础导航的基础上,体验一下,先建ColorsViewModel。 using Avalonia.Data.Converters; using Avalonia.Media; using ReactiveUI.SourceGenerators; using System; us…...

【pyQT 专栏】程序设置 windows 任务栏缩略图(.ico)教程

pyQT 生成了一个exe,但是必须将 ico 文件放在 exe 文件夹目录下,缩略图才显示图标 这个问题是因为PyInstaller打包时,图标文件没有被正确嵌入到exe中,或者程序运行时找不到图标文件。以下是几种解决方案: 方案1:使用资源文件系统(推荐) 1. 创建资源文件 resources.qrc&…...

Say 题选记(9.14 - 9.20)

P6619 [省选联考 2020 A/B 卷] 冰火战士 树状数组倍增板子。Code #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 5; #define lowbit(i) ((i) & (-(i))) int a[2][N], n, _x[N], cnt, sum[2]; void add(int a[], int x, …...

vm的配置

问题: 1.系统版本导致的虚拟机运行闪退找多篇文章无果,对照软件发现 2.软件权限不够导致地址无法更改,...

力扣72题 编辑距离

题型:动态规划,难度大 1.确定dp数组以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。 2.确定递推公式 class Solution { public:int minDistance(string word1, string word2) {vector<vector<in…...

数学基本结构框架

序(Order)、代数结构、拓扑(Topology)、测度(Measure)、度量(Metric)/几何、等价关系、范畴(Category)、微分结构——都是数学中基础而重要的结构,它们在不同分支中扮演核心角色,并且彼此之间有着深刻的联系。以下我将简要解释每个概念,并讨论它们如何相互关联,形…...

2025.9.16总结

历经千辛万苦,终于把hbase,zookeeper环境配好,最后产生bug的原因是。 由于配置hadoop hbase,zookeeper不是同一个视频,一个文章,一个作者,导致ip,端口号等有差异。 经过n次问ai,找文章改错,发现hbase不能在hdfs文件读写数据,才发现hbase连接hdfs的端口号应该和配置ha…...

在 Tailscale 中禁用 DNS

Tailscale 中的--accept-dns=false标志用于禁用 Tailscale 管理控制台提供的 DNS 配置。默认情况下,Tailscale 可能会将您的设备配置为使用来自 Tailnet 的 MagicDNS 或其他 DNS 设置。此标志可确保您的设备不接受或应用这些 DNS 设置。示例用法tailscale up --accept-dns=fal…...

【青少年低空飞行玩意】设计图以及项目概况

@目录项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛)市场分析基础项目计划书主要章节数据支撑图表核心创新点 项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛) 产品定位:SkyLove 情侣飞行器 专为 18-25 岁青少年情侣设计 集科技感、时尚性、情感表达于一体 价格…...

Python实现对比两个Excel表某个范围的内容并提取出差异

Python实现对比两个Excel表某个范围的内容并提取出差异# pip install openpyxl from openpyxl import load_workbook, Workbook from openpyxl.utils.cell import coordinate_from_string, column_index_from_string, get_column_letter from openpyxl.styles import Font, Pat…...

软件工程实践一:Git 使用教程(含分支与 Gitee)

目录目标一、快速上手1. Windows 安装 Git2. 初始化 / 克隆二、核心概念速览三、常用命令清单1) 查看状态与差异2) 添加与提交3) 历史与回溯4) 撤销与恢复(Git 2.23+ 推荐新命令)5) 忽略文件四、分支与合并(Branch & Merge)1) 创建与切换2) 更新主干与合并3) 推送与合并…...

我用AI给自己做了一整套专属表情包!攻略

本文分享用AI制作专属表情包的实用教程。群聊斗图,关键时刻找不到图,真的太憋屈了! 别再到处“偷”图了,最近发现用AI给自己做表情包,超简单,而且特别爽!😎1️⃣灵感和准备 一切都从一张照片开始。找一张光线好的高清正脸自拍,这是你所有表情包的“灵魂”!越清晰,A…...

20250916 之所思 - 人生如梦

20250916 之所思做的不好的地方:1. 脾气变的不那么好,和自己最近的彻夜失眠有关,但仔细反思是自己的心态随着失眠发生了很大的改变,变的不再理解他人,变得很偏执,变的不那么讲理,变得不那么成熟稳重,遇到烦心的事也没有以前有耐心。缺点太多了,多站在对方的角度看问题…...

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现)

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现) 一、系统设计(从需求到架构) 1.1、需求分析(明确核心目标与用户场景)1.2、系统功能设计(7个核心页面) 1.2、系统功能结构图 二、​商城网站系统运…...

20250915

20250915T1 ZZH 的游戏 二分答案之后,两个点轮流跳到当前能跳到的最小点。如果没法跳了且不都在 \(1\),那么无解。容易发现这是对的,可以通过建重构树维护。然后发现二分答案不是必要的,只需要每次没法跳的时候手动开大答案即可。复杂度瓶颈在建重构树的并查集。代码 #inc…...

Python Socket网络编程(4)

协程 微线程,切换执行 比如遇到IO等待的时候可以自动切换,提升线程利用率,多用在IO等待你想干点别的...

今日学习 dos命令和Java基础语法

今日学习 dos命令和Java基础语法 dos命令 常用快捷键ctrl+c 复制 ctrl+v粘贴 ctrl+x剪切 ctrl+z撤销 ctrl+s保存 ctrl+f查找 ctrl+shift+ESC 任务管理器(电脑死机时,可用于结束进程,explore,桌面进程) shift+delete永久删除 ALT+F4关闭窗口 ALT+TAB切换窗口/程序 win+R命令…...

课前问题列表

1.3 课前问题列表 方法相关问题static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x = &qu…...

switch中初始化变量

在 C++ 的 switch 语句中,switch 是 “跳转式” 控制结构,case 标签并非独立的语句块,若直接在 case 下初始化变量,可能导致变量作用域混乱、未初始化就被使用等问题,甚至触发编译错误。 1.跨 case 的变量作用域冲突 在某个 case 中初始化的变量,其作用域会覆盖后续 case…...

office2024免费永久激活安装包下载安装教程包含(下载安装配置激活)

大家好!最近总有人问我 Office 2024 专业增强版怎么装,今天特意整理这份超详细的 Office 2024 专业增强版下载安装教程,从电脑能不能装、在哪安全下载,到一步步安装激活,再到遇到问题怎么解决,全给大家说清楚,新手也能跟着装成功,建议收藏备用!目录一、Office 2024 专…...

vue2和vue3一时转不过来

以下是 Vue2 和 Vue3 在核心语法和功能上的主要区别,结合具体代码示例说明:一、响应式数据定义方式 1. ​​数据声明位置​​ // Vue2 选项式 API export default {data() {return {name: iwen,list: []}} }// Vue3 组合式 API import { ref, reactive } from vue export def…...

怎么查询电脑的登录记录及密码更改情况? - Li

怎么查询电脑的登录记录及密码更改情况? 写这个随笔的源头是我在一家公司上班,他们自己电脑打不开,一口咬定办公室的电脑莫名其妙打不开了,是我在被他们违规辞退后设定的密码,另将监控室电脑加密,且未告知公司任何人。 莫名其妙,因为本来就没设密码啊!(躺倒) 当然最后…...

C语言结构体中的内存对齐

C语言结构体内存对齐 在C语言编程中,结构体是一种非常重要的数据类型,它允许我们将不同类型的数据组合在一起。然而,当涉及到结构体在内存中的存储时,有一个关键的概念——内存对齐,这往往容易被忽视,但却对程序的性能和内存使用有着重要影响。 一、结构体大小计算的“理…...

该练习 DP 了!

区间DP 洛谷P3147Problem 定义 \(f[i][j]\) 存储从左端点 \(j\) 开始,能合并出 \(i\) 的右端点位置,将其设为 \(k\) 。 下面我们推转移方程。从题意可以看出,两个相邻的 \(i-1\) 能够合并出 \(i\) 。那么在 \(f[i][j]\) 后所对应的就是 \(f[i][k]\),这两个 \(i\)合并能够得…...

本周计划

周三: 上午 8:00~10:30 新领军 10:30~11:30 ZR NOIPD3 T4 下午模拟赛 晚上新领军习题课两节 周四: 上午 8:00~11:30 补好题分享 2 道 下午 2:00~4:30 补模拟赛 晚上 6:30~8:00 补模拟赛或好题分享 周五 上午 8:00~11:30 补好题分享 2 道 下午 2:00~5:30 准备下个周好题分享,…...

PPT文件太大?一招「无损」压缩图片,秒变传输小能手!

本文介绍的方法基于「PPT百科网」提供的在线分析工具,可智能评估并指导压缩过程,确保最佳效果。 PPT文件体积暴涨,99%的根源在于内部图片分辨率过高。直接使用PowerPoint自带的“压缩图片”功能虽然简单,但如同一刀切,可能导致在其他设备上播放时图片模糊,风险不可控。 「…...

9月16模拟赛

题目很难 主要是没有找对策略 就是没有及时去想部分分 怎么说呢 实力太弱 其实部分分拿完也会有个不错的成绩 无所谓 csp rp++!...

C++ 单例 Meyers Singleton(迈耶斯单例)

Meyers Singleton(迈耶斯单例)是 C++ 中实现单例模式的一种简洁高效的方法,由 C++ 专家 Scott Meyers 提出。其核心原理是利用局部静态变量的初始化特性保证单例的唯一性和线程安全性(C++11 及以后标准)。 1、核心原理局部静态变量的初始化特性 在 C++ 中,函数内的局部静…...

EF Core 与 MySQL:查询优化详解

EF Core 与 MySQL:查询优化详解 1. 使用 AsNoTracking 提高查询性能 基本用法// 常规查询(会跟踪实体变更) var products = context.Products.Where(p => p.Price > 100).ToList();// 使用 AsNoTracking(不跟踪实体变更,性能更好) var products = context.Product…...

短视频营销运营资深导师张伽赫,东莞绳木传媒创始人

东莞绳木传媒创始人张伽赫,短视频营销运营领域的资深导师。凭借其对行业趋势的敏锐洞察与实战经验,已成为企业数字化转型中短视频营销领域的标杆人物。他深耕短视频赛道多年,不仅构建了从账号定位、内容创作到流量转化的完整方法论,更通过绳木传媒为企业提供“AI+短视频”全…...

20250913

T4。T1 查询被包含的区间 将区间视为平面上的点 \((l, r)\),则每次询问的合法范围容易表示,是一个三角形。可以通过两步容斥转化为一个一维偏序和三个二维偏序。直接做就好了。代码 #include <iostream> #include <algorithm> #define lowbit(x) ((x) & (-(…...

9.13日总结

整体总结: 1.在自己的大样例出问题时要及时找老师考大样例 不要对着不对的大样例虚空调试 2.在考场上要自己造大样例 要造极限数据 这样可以防止数组越界 3.在数据不超过5e6的情况下 单log都是可以过的 只要极限数据跑的不是很慢就不用担心常数问题 4.在考场上要留一个小时以上…...

哇哇哇下雨了!——2025 . 9 . 16

哇哇哇下雨了! 感觉我从小就不喜欢晴天,反而钟爱雨天,其实每次下雨我心里就在想“哇哇哇又下雨了”。 可能跟打小的性格有关,也可能跟那个人有关。 当时我写了好多关于雨的小诗,无论是给她的还是给我自己的,内容也想不起来几句了。那会儿虽然每天的生活是无味的严苛的,但…...

奇思妙想(胡思乱想)

前言: 作为一个想象力 丰富 夸张的人,总有一些奇思怪想,浅浅记录一下呀~~ 可能会很奇怪以及不符合实际,毕竟是想象的【逃】 正文:圈养的猪会不会觉得人类的是自己的奴隶(因为一直好吃好喝的供着它们) 睡觉会不会就是脑电波以第一视角或第三视角的方式观察到平行宇宙的自…...

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0 AI-Compass 致力于构建最全面、最实用、最前沿的AI…...

C++中set与map的自定义排序方法详解

在C++标准模板库(STL)中,set和map是两种常用的关联容器,它们默认按照键的升序进行排序。但在实际开发中,我们经常需要根据特定需求对元素进行自定义排序。本文将详细介绍如何为set和map实现自定义排序。 默认排序行为 在深入了解自定义排序之前,我们先看一下set和map的默认…...

id

卷姬神经瓦特 2025.09.16本文来自博客园,作者:transformert,转载请注明原文链接:https://www.cnblogs.com/ac-network/p/19095883...

【汇总】Qt常用模块头文件

一、变量、命令、参数排序 项目.pro文件 模块导入 include 文件 中文说明 备注、示例ABCDEFGHIJKLM#include <QMessageBox> 信息提示窗口QMessageBox::about(this, "关于",“关于说明”);NOPQRSQT += serialport #include <QSerialPort> 串口控制类#inc…...

Advanced Algorithm —— Hashing and Sketching

Birthday Problem \(m\) 个人,\(n\) 天,没有两个人生日相同的概率为: \[\displaystyle{ \begin{align*} \Pr[\mathcal{E}]=\left(1-\frac{1}{n}\right)\cdot \left(1-\frac{2}{n}\right)\cdots \left(1-\frac{m-1}{n}\right) &= \prod_{k=1}^{m-1}\left(1-\frac{k}{n}\r…...

CF2136 Codeforces Round 1046 (Div. 2) 补题

题目标签B笛卡尔树的应用C有思维难度的 dp / 递推D交互题 利用曼哈顿距离反过来解坐标:二元线性方程组 考虑“问最值/极限情况”E二分图,边双连通分量 两条路径 -> 环 异或运算的性质 (见题解)题解:E. By the Assignment观察1:对于本题,每个边双连通分量内部的点权可…...

【IEEE出版、EI检索稳定】第四届云计算、大数据应用与软件工程国际学术会议(CBASE 2025)

第四届云计算、大数据应用与软件工程国际学术会议(CBASE 2025) 2025 4th International Conference on Cloud Computing, Big Data Application and Software Engineering 在这里看会议官网详情 2025年10月24-26日丨中国-成都(线上同步举办) 截稿日期:看官网 检索类型:IE…...

缺省源

自用,你不见得会用。 快读:点击查看代码 #define getc() getchar_unlocked() #define putc(a) putchar_unlocked(a) #define en_ putc(\n) #define e_ putc( )template<class T> inline T in() { T n = 0; char p = getc();while (p < -) p = getc();bool f = p == …...

97. 交错字符串

题目链接:97. 交错字符串 - 力扣(LeetCode)‘解析:二维dp dp[i][j]代表s1前i个和s2前j个是否能组成s3的i+j个 状态转移方程就很简单了, 但这一题要求空间限制,可以观察到dp其实只记录一维就可以,因为用到了i-1或者j-1class Solution { public:bool isInterleave(string …...