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.