GNU/Linux >> Belajar Linux >  >> Linux

bagaimana select() diperingatkan untuk fd menjadi siap?

Itu melaporkan bahwa itu sudah siap oleh kembali.

select menunggu acara yang biasanya di luar kendali program Anda. Intinya, dengan memanggil select , program Anda mengatakan "Saya tidak melakukan apa-apa sampai ..., harap tunda proses saya".

Kondisi yang Anda tentukan adalah serangkaian peristiwa, yang salah satunya akan membangunkan Anda.

Misalnya, jika Anda mengunduh sesuatu, loop Anda harus menunggu data baru tiba, waktu tunggu terjadi jika transfer terhenti, atau pengguna menyela, persis seperti select tidak.

Saat Anda memiliki banyak unduhan, data yang tiba di salah satu koneksi memicu aktivitas di program Anda (Anda perlu menulis data ke disk), jadi Anda akan memberikan daftar semua koneksi unduhan ke select dalam daftar deskriptor file untuk melihat "baca".

Saat Anda mengunggah data ke suatu tempat pada waktu yang sama, Anda kembali menggunakan select untuk melihat apakah koneksi saat ini menerima data. Jika pihak lain menggunakan dialup, data hanya akan dikenali secara perlahan, sehingga buffer pengiriman lokal Anda selalu penuh, dan upaya apa pun untuk menulis lebih banyak data akan diblokir hingga ruang buffer tersedia, atau gagal. Dengan meneruskan deskriptor file yang kami kirim ke select sebagai deskriptor "tulis", kami diberi tahu segera setelah ruang penyangga tersedia untuk pengiriman.

Ide umumnya adalah bahwa program Anda menjadi digerakkan oleh peristiwa , yaitu bereaksi terhadap peristiwa eksternal dari loop pesan umum daripada melakukan operasi berurutan. Anda memberi tahu kernel "ini adalah rangkaian peristiwa yang ingin saya lakukan sesuatu", dan kernel memberi Anda serangkaian peristiwa yang telah terjadi. Cukup umum untuk dua peristiwa yang terjadi secara bersamaan; misalnya, pengakuan TCP disertakan dalam paket data, ini dapat membuat fd yang sama dapat dibaca (data tersedia) dan dapat ditulisi (data yang diakui telah dihapus dari buffer pengiriman), jadi Anda harus siap untuk menangani semua kejadian sebelum memanggil select lagi.

Salah satu poin penting adalah select pada dasarnya memberi Anda janji bahwa satu permintaan read atau write tidak akan memblokir, tanpa membuat jaminan apa pun tentang panggilan itu sendiri. Misalnya, jika satu byte ruang buffer tersedia, Anda dapat mencoba menulis 10 byte, dan kernel akan kembali dan berkata "Saya telah menulis 1 byte", jadi Anda harus siap menangani kasus ini juga. Pendekatan tipikal adalah memiliki buffer "data untuk ditulis ke fd ini", dan selama tidak kosong, fd ditambahkan ke set tulis, dan peristiwa "dapat ditulisi" ditangani dengan mencoba menulis semua data yang saat ini ada di buffer. Jika buffer kosong setelahnya, baiklah, jika tidak, tunggu saja "dapat ditulisi" lagi.

Set "luar biasa" jarang digunakan - ini digunakan untuk protokol yang memiliki data out-of-band di mana transfer data mungkin diblokir, sementara data lain harus melalui. Jika program Anda saat ini tidak dapat menerima data dari deskriptor file "dapat dibaca" (misalnya, Anda sedang mengunduh, dan disk sudah penuh), Anda tidak ingin menyertakan deskriptor dalam kumpulan "dapat dibaca", karena Anda tidak dapat menangani peristiwa tersebut. dan select akan segera kembali jika dipanggil lagi. Jika penerima menyertakan fd di set "luar biasa", dan pengirim meminta tumpukan IP-nya untuk mengirim paket dengan data "mendesak", penerima kemudian dibangunkan, dan dapat memutuskan untuk membuang data yang tidak tertangani dan menyinkronkan ulang dengan pengirim . telnet protokol menggunakan ini, misalnya, untuk penanganan Ctrl-C. Kecuali jika Anda merancang protokol yang memerlukan fitur seperti itu, Anda dapat dengan mudah mengabaikannya tanpa membahayakan.

Contoh kode wajib:

#include <sys/types.h>
#include <sys/select.h>

#include <unistd.h>

#include <stdbool.h>

static inline int max(int lhs, int rhs) {
    if(lhs > rhs)
        return lhs;
    else
        return rhs;
}

void copy(int from, int to) {
    char buffer[10];
    int readp = 0;
    int writep = 0;
    bool eof = false;
    for(;;) {
        fd_set readfds, writefds;
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);

        int ravail, wavail;
        if(readp < writep) {
            ravail = writep - readp - 1;
            wavail = sizeof buffer - writep;
        }
        else {
            ravail = sizeof buffer - readp;
            wavail = readp - writep;
        }

        if(!eof && ravail)
            FD_SET(from, &readfds);
        if(wavail)
            FD_SET(to, &writefds);
        else if(eof)
            break;
        int rc = select(max(from,to)+1, &readfds, &writefds, NULL, NULL);
        if(rc == -1)
            break;
        if(FD_ISSET(from, &readfds))
        {
            ssize_t nread = read(from, &buffer[readp], ravail);
            if(nread < 1)
                eof = true;
            readp = readp + nread;
        }
        if(FD_ISSET(to, &writefds))
        {
            ssize_t nwritten = write(to, &buffer[writep], wavail);
            if(nwritten < 1)
                break;
            writep = writep + nwritten;
        }
        if(readp == sizeof buffer && writep != 0)
            readp = 0;
        if(writep == sizeof buffer)
            writep = 0;
    }
}

Kami mencoba membaca jika kami memiliki ruang buffer yang tersedia dan tidak ada akhir file atau kesalahan di sisi baca, dan kami mencoba menulis jika kami memiliki data di buffer; jika end-of-file tercapai dan buffer kosong, maka kita selesai.

Kode ini akan berperilaku jelas suboptimal (ini adalah kode contoh), tetapi Anda harus dapat melihat bahwa kernel dapat melakukan kurang dari yang kami minta untuk membaca dan menulis, dalam hal ini kami hanya kembali dan mengatakan "kapanpun kamu sudah siap", dan bahwa kami tidak pernah membaca atau menulis tanpa menanyakan apakah itu akan diblokir.


Dari halaman manual yang sama:

Saat keluar, set dimodifikasi untuk menunjukkan deskriptor file mana yang benar-benar berubah status.

Jadi gunakan FD_ISSET() pada set yang diteruskan ke select untuk menentukan FD mana yang telah siap.


Linux
  1. Mengapa Data Penting Dan Bagaimana Cara Melindunginya

  2. Cara Mengakses phpMyAdmin

  3. Bagaimana cara menginstal Duplicity di Ubuntu?

  1. Cara Menginstal Ubuntu di VirtualBox

  2. Bagaimana Pipeline Membatasi Penggunaan Memori?

  3. Cara Mencegah Serangan Cyber

  1. Cara Menginstal CentOS 7

  2. Bagaimana rm bekerja? Apa yang rm lakukan?

  3. Cara menghasilkan data netflow di linux