У меня есть графическое GTK приложение и мне нужно получать информацию об подключенных USB девайсах, используя libusb я могу получить то что нужно но как оформить сам мониторинг, правильно ли запускать в отдельном потоке вечный цикл и обновлять поток UI? Мне интересно каким образом работают такие системы, какие есть модели управления потоками, возможно есть какие-нибудь механизмы взаимодействия с системой так как программа работает в user space, system calls?
Можно ли чтобы Linux пробудил мой поток когда подключили новый девайс?
Для начала поставить библиотеку libudev
sudo apt-get install libudev-dev
Код отсюда
#include <stdio.h>
#include <unistd.h>
#include <libudev.h>
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 <stdio.h>
#include <unistd.h>
#include <libudev.h>
#include <signal.h>
#include <thread>
#include <sys/wait.h>
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;
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как изменить свойство CSS display:none у data-share--audio--top на display:block на основе выбранной кнопки?