На одном серваке который достался мне с новым заказчиком от неизвестного коллеги админа постоянно была какая то ерунда, периодами падал сервер с симптомами – диски на 100% и процессор на 100%, при этом процессор довольно неплохой “Xeon(R) CPU E5-1650 v2 @ 3.50GHz”
Долго ковырялся в этом сервере, грешил на все что можно, но ничего не подтверждалось, пока не обнаружил что директории с сессиями “заботливо” не чистились, никогда, вообще никогда с момента запуска сервака и доменов на нем. Мне не удалось на некоторых доменах даже посчитать чисто для интереса сколько там было файлов линейно в директории, хотя в одной я все же для интереса обнаружил 28 миллионов файлов, считало долго.
Встал остро вопрос как чистить такие каталоги, так как стандартными средствами линукса это не реально (ошибка “rm -f *: Argument list too long“). Наткнулся на доброго человека на хабре который поделился кодом низкоуровнего удаления файлов, хоть и долго, зато спокойно и без лишнего гемора.
Ниже идет код на С который нужно собрать на сервере и запустить, указав в качестве параметра каталог откуда удалять. В комментарии к коду идет пометка что нужно указать номер inode для текущего каталога и на уровень выше, это делается просто командой “ls -li”, в остальном достаточно тупого копипаста, файл я обозвал “lssss.c”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#define _GNU_SOURCE #include <dirent.h> /* Defines DT_* constants */ #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/syscall.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[]; }; #define BUF_SIZE 1024*30 int main(int argc, char *argv[]) { int fd, nread; char buf[BUF_SIZE]; struct linux_dirent *d; int bpos; int deleted; char d_type; char temp[100]; fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY); if (fd == -1) handle_error("open"); deleted = 0; nread = syscall(SYS_getdents, fd, buf, BUF_SIZE); if (nread == -1) handle_error("getdents"); if (nread != 0) { for (bpos = 0; bpos < nread;) { d = (struct linux_dirent *) (buf + bpos); d_type = *(buf + bpos + d->d_reclen - 1); if(d->d_ino && d->d_ino != 22332748 && d->d_ino != 22332761) { // тут я прописал inode самой директории и директории верхнего уровня, чтобы он не пытался удалять файлы "." и ".." - принимаю подсказки, как это сделать лучше sprintf(temp,"%s/%s", argv[1], (char *) d->d_name); remove(temp); deleted += 1; } bpos += d->d_reclen; } } printf("deleted %d\n", deleted); exit(EXIT_SUCCESS); } |
Код любезно стянул с хабра себе на заметку.
Далее на сервере компилируем код:
1 |
gcc lssss.c -o lssss |
Получившийся файл я прописал в cron для запуска раз в минуту:
1 |
* * * * * cd /var/www/domain.ru/data/; ./lssss mod-tmp/ |
Посмотрим через время как оно будет, но надеюсь что по чуть чуть вычистит и я смогу включить автоматическую чистку сессия в php.
В принципе можно так не извращаться, если директорию удалось открыть или в ней всего пару сотен тысяч файлов или 1-2 миллиона, в ней можно пустить команду:
1 |
for i in `ls -1`; do rm -f $i;done |
повторюсь – пускать только находясь в директории, если же не получается запускать в директории, можно попробовать пустить ее их другого места, только в параметрах указать пусть к каталогу:
1 |
for i in `ls -1 /path/to/dir/`; do rm -f $i;done |
для снижения нагрузкаи на диски можно добавть nice и ionice
1 коментар до “rm -f *: Argument list too long”