Страницы

Поиск по вопросам

вторник, 17 декабря 2019 г.

C++ Как в Linux мониторить usb устройства?

#cpp #linux #gtk


У меня есть графическое GTK приложение и мне нужно получать информацию об подключенных
USB девайсах, используя libusb я могу получить то что нужно но как оформить сам мониторинг,
правильно ли запускать в отдельном потоке вечный цикл и обновлять поток UI? Мне интересно
каким образом работают такие системы, какие есть модели управления потоками, возможно
есть какие-нибудь механизмы взаимодействия с системой так как программа работает в
user space, system calls? 


  Можно ли чтобы Linux пробудил мой поток когда подключили новый девайс?

    


Ответы

Ответ 1



Для начала поставить библиотеку libudev sudo apt-get install libudev-dev Код отсюда #include #include #include int main() { struct udev *udev; struct udev_device *dev; struct udev_monitor *mon; int fd; int status; /* create udev object */ udev = udev_new(); if (!udev) { fprintf(stderr, "Can't create udev\n"); return 1; } //группы сообщений //(1)kernel - посылает событие к udevd в момент подключения устройства для его инициализации //(2)udev - посылает событие в userspace программам, используемым libudev mon = udev_monitor_new_from_netlink(udev, "udev"); //Можно также добавлять подсистемы pci, usb, scsi, scsi_host, scsi_generic //Типы устройств usb_device, usb_interface, scsi_host, scsi_device, disk, partition udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk"); //флешки udev_monitor_enable_receiving(mon); fd = udev_monitor_get_fd(mon); printf("Start...\n"); fflush(stdout); while (1) { fd_set fds; struct timeval tv; int ret; FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 0; tv.tv_usec = 0; ret = select(fd+1, &fds, NULL, NULL, &tv); if (ret > 0 && FD_ISSET(fd, &fds)) { dev = udev_monitor_receive_device(mon); if (dev) { printf("I: ACTION=%s\n", udev_device_get_action(dev)); printf("I: DEVNAME=%s\n", udev_device_get_sysname(dev)); printf("I: DEVPATH=%s\n", udev_device_get_devpath(dev)); printf("I: MACADDR=%s\n", udev_device_get_sysattr_value(dev, "address")); printf("---\n"); fflush(stdout); /* free dev */ udev_device_unref(dev); } } /* 500 milliseconds */ usleep(500*1000); } /* free udev */ udev_unref(udev); return 0; } Событие uevent, которое udevd получает от драйвера ядра - это GROUP_KERNEL (1), а уведомление программ из пространства пользователя /lib/udev programs or others - это GROUP_UDEV (2). Workflow отсюда По поводу взаимодействия, я бы форкул цикл ожидания событий монитора и при инициализации нового устройства посылал родительскому процессу сигналы (сигналы реального времени, чтоб устройство не потерять при подключении, т.к. эти сигналы ставятся в очередь). Поправьте если не прав. #include #include #include #include #include #include void task() { printf("Create thread and join my func"); auto func = []() { for(auto i = 0 ; i<3 ; ++i) { sleep(1); printf("Hard calculations: %d ", i); } fflush(stdout); }; std::thread t(func); t.join(); } int main() { struct udev *udev; struct udev_device *dev; struct udev_monitor *mon; int fd; int status; /* create udev object */ udev = udev_new(); if (!udev) { fprintf(stderr, "Can't create udev\n"); return 1; } pid_t pid = fork(); switch (pid) { case -1: exit(EXIT_FAILURE); /*Ошибко*/ case 0: { /*Выполнение дочернего процесса*/ //группы сообщений //kernel - посылает событие к udevd в момент подключения устройства для его инициализации //udev - посылает событие в userspace программам, используемым libudev mon = udev_monitor_new_from_netlink(udev, "udev"); //Можно также добавлять подсистемы pci, usb, scsi, scsi_host, scsi_generic //Типы устройств usb_device, usb_interface, scsi_host, scsi_device, disk, partition udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk"); //флешки udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device"); //флешки udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_interface"); //флешки udev_monitor_enable_receiving(mon); fd = udev_monitor_get_fd(mon); printf("Start...\n"); fflush(stdout); while (1) { fd_set fds; int ret; FD_ZERO(&fds); FD_SET(fd, &fds); ret = select(fd+1, &fds, nullptr, nullptr, nullptr); if (ret > 0 && FD_ISSET(fd, &fds)) { dev = udev_monitor_receive_device(mon); if (dev) { printf("I: ACTION=%s\n", udev_device_get_action(dev)); printf("I: DEVNAME=%s\n", udev_device_get_sysname(dev)); printf("I: DEVPATH=%s\n", udev_device_get_devpath(dev)); printf("I: MACADDR=%s\n", udev_device_get_sysattr_value(dev, "address")); printf("---\n"); fflush(stdout); if(strncmp(udev_device_get_action(dev), "add", 3) == 0) { union sigval value; value.sival_int = 1; sigqueue(getppid(), SIGRTMIN, value); } /* free dev */ udev_device_unref(dev); } } /* 500 milliseconds */ usleep(500*1000); } /* free udev */ udev_unref(udev); } default: { /*Продолжение выполнения родительского процесса*/ int ex; int sig; siginfo_t si; sigset_t allSigs; sigfillset(&allSigs); if(sigprocmask(SIG_SETMASK, &allSigs, NULL) == -1) { printf("fail\n"); exit(EXIT_FAILURE); } while(1) { sig = sigwaitinfo(&allSigs, &si); printf("sig %s\n",strsignal(sig)); if(SIGINT == sig || SIGTERM == sig) { kill(pid, SIGTERM); exit(EXIT_SUCCESS); } if(SIGRTMIN == sig) { task(); /*Запускаем функцию, где реализуем отдельный поток*/ } } } } //switch return 0; }

Комментариев нет:

Отправить комментарий