如何在 C 中异步调用函数并使其更改全局变量? 我想做什么我的尝试tl;博士

如何解决如何在 C 中异步调用函数并使其更改全局变量? 我想做什么我的尝试tl;博士

我想做什么

我正在尝试使自己成为 dwm 的模块化状态栏,例如 i3blocks。简而言之,状态栏将由称为块的基本单元组成,每个单元代表一个命令。
例如,在下面显示的状态栏中,由箭头“

Modular statusbar

这个想法是让它们中的每一个都以一定的间隔独立更新。

我可以同步更新块,但我不想这样做,因为我的状态栏中的某些脚本需要大约 5 秒的时间来执行,这会在此期间停止其他模块,我希望避免这种情况.这在我启动 PC 时非常明显,并且状态栏上大约 5 秒没有显示任何内容,因为一个模块需要很长时间才能执行。

我的尝试

我曾尝试使用 pthread.h,但我想放弃这种方法,因为每个模块使用一个线程似乎是个坏主意,甚至 i3blocks 也不使用它。 我使用 fork() 也只是发现我无法从子进程中修改父进程的全局变量。 我在网上研究,发现我可以使用管道让子进程与父进程通信,但我所有的努力都是徒劳的,因为使用 read() 读取管道会阻止父进程的控制流复制相同的顺序行为早点。

这是我要异步调用函数 getCommand 的代码:

void getCommands(int time) {
    int pipes[len(blocks)][2];
    for (int i = 0; i < len(blocks); i++) {
        if (pipe(pipes[i]) == -1) {
            printf("Pipe failed\n");
            exit(1);
        }

        const Block *current = blocks + i;
        if (time == 0 || (current->interval != 0 && time % current->interval == 0)) {
            if (fork() == 0) {
                getCommand(current,statusbar[i]);

                close(pipes[i][0]);
                write(pipes[i][1],statusbar[i],strlen(statusbar[i]));
                close(pipes[i][1]);
                exit(0);
            } else {
                close(pipes[i][1]);
                read(pipes[i][0],CMDLENGTH);
                close(pipes[i][0]);
            }
        }
    }
}

其余代码在我的 GitHub 存储库中:UtkarshVerma/dwmblocks

tl;博士

我想知道如何使用回调对函数进行异步调用。该函数也应该能够修改全局变量。

提前致谢。

这是我第一次用 C 编写软件,因此欢迎对代码提出任何建议。

更新

我想我终于取得了一些进展。这就是我正在做的事情。我在 fork 中执行命令,然后注意父级上的 SIGCHLD。在 SIGCHLD 处理程序中,我然后以非阻塞方式将所有管道读取到子叉,以检查缓冲区是否为非空。除了一个警告外,这非常有效。父级永远无法判断执行的命令是否有空白输出。我做了一个像 this 这样的 hacky 尝试,但我很好奇这是否可以更恰当地处理。

解决方法

我的建议是让您的应用程序基于不同的方法。

  1. 首先,限制信号的使用,它们是过时的 IPC 方法,它们可能会遇到同步问题。
  2. 如果您想从子进程收集数据,我认为 popen 就是您要搜索的内容。
  3. 正如其他同事所注意到的,我建议使用 selectpoll 来检查子输出是否准备就绪。

示例程序可能如下所示:

#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#include <stdbool.h>
#include <time.h>
#include <assert.h>

#define len(arr) (sizeof(arr) / sizeof(arr[0]))

typedef struct {
    char *command;
    unsigned int interval;
} Block;

const Block blocks[] = {
    {"ls",5},{"echo abc",1}
};

int main(int argc,const char * argv[])
{
   FILE* files[len(blocks)] = { NULL };
   int fds[len(blocks)] = { -1,-1 };
   int timeout[len(blocks)] = { 0 };
   fd_set set;
   int fdmax = -1;
   time_t previous = time(NULL);
   FD_ZERO(&set);

   while (true)
   {
      struct timeval tm = { 1,0 };
      fd_set tmp = set;
      int rc = select(fdmax+1,&tmp,NULL,&tm);
      //assert(rc >= 0); /* No error handling */

      /* Check what's the time now,the Monotonic clock would be better */
      time_t now = time(NULL);

      /* Child processes loop */
      for (int i = 0; i < len(blocks); ++i)
      {
         /* No matter if it's running or not,update counter */
         timeout[i] += now - previous;
         if (timeout[i]>=blocks[i].interval && fds[i]<0)
         {
            /* Timeout occured and the child is not running */
            files[i] = popen(blocks[i].command,"r");
            fds[i] = fileno(files[i]);
            assert(fds[i] >= 0);
            fcntl(fds[i],F_SETFL,O_NONBLOCK); /* Make sure to not block */
            
            FD_SET(fds[i],&set); /* Add it to descriptors set */
            fdmax = fdmax > fds[i] ? fdmax : fds[i];
            timeout[i] = 0; /* Start interval measurement */
         }
         else if (fds[i]>=0 && FD_ISSET(fds[i],&tmp))
         {
            /* Read the pipe and consume your output */
            char buffer[1024] = { 0 };
            rc = fread(&buffer[0],sizeof(char),sizeof(buffer)-1,files[i]);
            if (rc > 0)
               printf("%s",&buffer[0]); /* Do graphics stuff */
            else if (rc == 0)
            {
               /* Probably child has ended its job */
               /* but read more about it */
               rc = pclose(files[i]); /* Read exit code */
               fdmax = fdmax == fds[i] ? (fdmax-1) : fdmax;
               fds[i] = -1; /* Now wait for next round */
               FD_CLR(fds[i],&set);
               files[i] = NULL;
            }
         }
      }

      previous = now;
   }

   return 0;
}

更新: 此外,如果您真的需要信号来处理鼠标(我在您的代码中找到了它),您可以使用 signalfd 函数,它为信号处理创建 fd。这将限制 for 循环中的所有逻辑内容。

,

为每个模块创建一个数据结构,用管道连接到分叉的子节点,以易于解析的数据结构生成数据;例如,一个字母后跟数据。让孩子将更新写入管道,父母从读取端读取。

使用 fcntl(read_fd,O_NONBLOCK) 设置父描述符非阻塞。这样您就可以使用单个线程和 select()poll() 作为更新描述符(可读性),并且 read() 不会阻塞。

我个人会使用一个线程来处理子进程,以上述非阻塞方式读取它们的输出管道;数据结构受互斥锁保护(仅保留最短持续时间),每个模块的更新标志由读取线程设置(每当数据更新时),并由 GUI 线程清除(每当数据更新到图形用户界面)。

,

我使用 fork() 也只是发现我无法从子进程中修改父进程的全局变量。

足够公平,但您可以使用 shared 内存:

/* shm.c,compile with gcc -o shm shm.c */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

#define BUFSIZE 256

int main() {
  int protection       = PROT_READ | PROT_WRITE;
  int visibility       = MAP_SHARED | MAP_ANONYMOUS;
  /* use shared memory as a "hole in the wall" between parent and child: */
  void *service_hatch  = mmap(NULL,BUFSIZE,protection,visibility,-1,0);


  if (fork()) { /* parent */
    sleep(1); /* have an afternoon nap while child is cooking */
    printf("we got served: %s\n",(char *) service_hatch); 
  } else {      /* child */
    /*cook up a simple meal for parent: */ 
    strncpy(service_hatch,"French fries with mayonaise",BUFSIZE);
  }
}

运行此程序时,您将看到您能够从子进程中修改父进程的全局 *service_hatch 的内容:

$ ./shm
we got served: French fries with mayonaise

编辑:为了避免在写入/读取共享内存时父子之间的竞争,请使用信号量来仲裁对它的访问:

/* shm_sem.c,compile with gcc -o shm_sem shm_sem.c -lpthread */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <semaphore.h>

#define BUFSIZE 256

/* example of a very non-atomic (i.e. piecemeal) modification of a variable */
void slowly_cook(char *meal,char *destination) {
  do {
    *destination++ = *meal++;
    sleep(1);
  } while(*meal);
}       

int main() {
  int protection       = PROT_READ | PROT_WRITE;
  int visibility       = MAP_SHARED | MAP_ANONYMOUS;

  /* use shared memory as a "hole in the wall" between parent and child: */
  void *service_hatch  = mmap(NULL,0);
  
  /* use a semaphore,like  a red/green light on the outside of the hatch  */
  sem_t *service_hatch_indicator  =  mmap(NULL,sizeof(sem_t),0);
  sem_init(service_hatch_indicator,1,1);

  /* use it by locking and unlocking the semaphore around each critical section */
  #define CRITICAL_SECTION(sem,code)  {sem_wait(sem); code; sem_post(sem);}
  

  if (fork()) { /* parent */
    sleep(1); /* have an afternoon nap while child is cooking,then wait for the light  to turn green .... */
    CRITICAL_SECTION(service_hatch_indicator,printf("we finally got served: %s\n",(char *) service_hatch)); 
  } else {      /* child */
    /* cook up a simple meal for parent,take your time ...  */ 
    CRITICAL_SECTION(service_hatch_indicator,slowly_cook("French fries with mayonaise",service_hatch));
  }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res