473,396 Members | 1,760 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Memory mapped, forks and unnamed semaphores- doesn't always give the same output

Hi there,

I have an assignment to invert a ppm image, with a header of 4 lines (P6\nwidth\nheight\ndepth\n),and we have to do a shared memory version and a memory mapped file (MMF) too.
This version is basically equal to the SM version, whick worker, but with mmf, but the final image although is inverted it shows a small black line and a grey two, in the end, even the file has the same size ...
The more stranger is that yesterdar the output was correct and now I run and it's not. I think it's a problem with children not actually exit from their function, but I've tried so many changes and no success.

I'll leave you the code instead of the links, perhaps it's easier to see:

Expand|Select|Wrap|Line Numbers
  1. //###################################### MMF_INVERTER.C (main) #########################################
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <sys/ipc.h>
  7. #include <sys/shm.h>
  8. #include <sys/fcntl.h>
  9. #include <semaphore.h>
  10. #include <sys/types.h>
  11. #include <sys/wait.h>
  12. #include <sys/mman.h>
  13. #include <sys/stat.h>
  14. #include <errno.h>
  15. #include <signal.h>
  16.  
  17. #include "timeprofiler.h"
  18. #include "ppmtools.h"
  19.  
  20. #define    FILE_MODE    (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
  21.  
  22. //Global vars:
  23. int shmids[2];
  24. //################# On shared memory goes #####################
  25. //To share unnamed semaphores between processes, they must be allocated in a shared memory.
  26. mem_struct *sh_mm;
  27. int *total_lines, *processed_lines, *next_line, *buf_vars;
  28. //###################################################################
  29. //unnamed semaphores
  30. sem_t *mutex1, *mutex2, *mutex3, *sem_remaining_lines;
  31. pid_t *workersPID;
  32. header *h;
  33. char *data; //mmf that will hold the image
  34. int last_index;
  35.  
  36. int main(int argc, char *argv[]) {
  37.     int i, fdout,id;
  38.     pixel *row;
  39.     double start, stop, startms, stopms;
  40.     struct stat sbuf;
  41.  
  42.     if (argc < 3) {
  43.         printf("Incorrect usage.\nPlease use \"./invert input_filename.ppm output_filename.ppm\"\n");
  44.         return -1;
  45.     }
  46.  
  47.  
  48.     printf("Opening input file [%s]\n", argv[1]);
  49.     FILE *fpin = fopen(argv[1], "r");
  50.     if (fpin == NULL) {
  51.         printf("Could not open input file\n");
  52.         return -1;
  53.     }
  54.  
  55.     printf("Opening output file [%s]\n", argv[2]);
  56.     FILE *fpout = fopen(argv[2], "w");
  57.     if (fpout == NULL) {
  58.         printf("Could not open output file\n");
  59.         return -1;
  60.     }
  61.  
  62.     printf("Getting header\n");
  63.     h = getImageHeader(fpin);
  64.     if (h == NULL) {
  65.         printf("Error getting header from file\n");
  66.         return -1;
  67.     }
  68.     printf("Got file Header: %s - %u x %u - %u\n", h->type, h->width, h->height, h->depth);
  69.  
  70.     init();
  71.  
  72.     //start timer
  73.     start = getCurrentTimeMicro();
  74.     startms = getCurrentTimeMili();
  75.     printf("Saving header to output file\n");
  76.     if (writeImageHeader(h, fpout) == -1) {
  77.         printf("Could not write to output file\n");
  78.         return -1;
  79.     }
  80.  
  81.     last_index = (int)ftell(fpout);
  82.     //printf("offset after header= %d\n",last_index);
  83.  
  84.     //alloc mem space for one row (width * size of one pixel struct)
  85.     row = malloc(h->width * sizeof (pixel));
  86.  
  87.     /*Create a copy of the original image to the output file, which will be inverted*/
  88.     printf("Starting work\n");
  89.     for (i = 0; i < h->height; i++) {
  90.         printf("Reading row... ");
  91.         if (getImageRow(h->width, row, fpin) == -1) {
  92.             printf("Error while reading row\n");
  93.         }
  94.         printf("Got row %d || ", (i + 1));
  95.  
  96.         printf("Saving row... ");
  97.         if (writeRow(h->width, row, fpout) == -1) {
  98.             printf("Error while reading row\n");
  99.         }
  100.         printf("Done\n");
  101.     }
  102.  
  103.     /*Open file descriptor of the ouput file.
  104.      * O_RDWR -  Read and Write operations both permitted
  105.      * O_CREAT - Create file if it doesn't already exist
  106.      * O_TRUNC - Delete existing contents of file*/
  107.     if ((fdout = open(argv[2], O_RDWR, FILE_MODE)) < 0) {
  108.         fprintf(stderr, "Can't create %s for writing\n", argv[2]);
  109.         exit(1);
  110.     }
  111.  
  112.     /*Get size of the output file*/
  113.     if (fstat(fdout, &sbuf) == -1) {
  114.         perror("Stat error ---------->\n");
  115.         exit(1);
  116.     }
  117.     //printf("Size of output file: %d\n",(int)sbuf.st_size);
  118.  
  119.     /*Maps output file to memory*/
  120.     if ((data = mmap((caddr_t) 0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) (-1)) {
  121.         perror("Error mmaping");
  122.         exit(EXIT_FAILURE);
  123.     }
  124.     //printf("header last index: %u\n",last_index);
  125.  
  126.     sem_wait(mutex1);
  127.     *total_lines = h->height;
  128.     *next_line = last_index;
  129.     sem_post(mutex1);
  130.  
  131.     //Creates workers
  132.     workersPID = (pid_t*) malloc(sizeof (pid_t) *((NUM_WORKERS)));
  133.     for (i = 0; i < NUM_WORKERS; i++) {
  134.         id = fork();
  135.         if (id == -1) {
  136.             printf("Error creating worker number %d\n", i);
  137.             exit(EXIT_FAILURE);
  138.         } else if (id == 0) {
  139.             workersPID[i] = getpid();
  140.             worker(i);
  141.         }
  142.     }
  143.     sem_wait(mutex2); //if unblocks, it means the inversion is finished
  144.  
  145.     printf("Cleaning up...\n");
  146.     //clean up row
  147.     free(row);
  148.     //clean up header
  149.     free(h);
  150.  
  151.     printf("Closing file pointers.\n");
  152.     fclose(fpin);
  153.     fclose(fpout);
  154.     //stop timer
  155.     stop = getCurrentTimeMicro();
  156.     stopms = getCurrentTimeMili();
  157.  
  158.     for (i = 0; i < NUM_WORKERS; i++) {
  159.         if (workersPID[i]) {
  160.             kill(workersPID[i], SIGTERM); //TODO: sigkill or sigterm?
  161.             waitpid(workersPID[i], NULL, 0);
  162.         }
  163.     }
  164.  
  165.     //unmmaps output file from memory
  166.     if((munmap(data, sbuf.st_size)==-1))
  167.     printf("munmap failed! Errno returned: %s\n",strerror(errno));
  168.     close(fdout);
  169.     terminate();
  170.  
  171.     printTimeElapsed(start, stop, "microseconds");
  172.     printTimeElapsed(startms, stopms, "miliseconds");
  173.     printf("Done!\n");
  174.  
  175.     return 0;
  176. }
  177.  
  178. void init() {
  179.  
  180.     //unnamed semaphores go, in this case, to shared memory (is it REALLY necessary or as globlal vars it'll work?)
  181.     if ((shmids[0] = shmget(IPC_PRIVATE, sizeof (mem_struct), IPC_CREAT | 0700)) == -1) {
  182.         printf("shmget to allocate mem_Struct for semaphores failed. Errno returned %s\n", strerror(errno));
  183.         exit(EXIT_FAILURE);
  184.     }
  185.     sh_mm = (mem_struct*) shmat(shmids[0], NULL, 0);
  186.     if (sem_init(&sh_mm->mutex1, 1, 1) == -1) {
  187.         printf("Error initializing semaphore mutex1.Errno returned: %s\n", strerror(errno));
  188.         exit(EXIT_FAILURE);
  189.     }
  190.     mutex1 = &sh_mm->mutex1;
  191.  
  192.     if (sem_init(&sh_mm->mutex2, 1, 0) == -1) {
  193.         printf("Error initializing semaphore mutex2.Errno returned: %s\n", strerror(errno));
  194.         exit(EXIT_FAILURE);
  195.     }
  196.     mutex2 = &sh_mm->mutex2;
  197.  
  198.     if (sem_init(&sh_mm->mutex3, 1, 1) == -1) {
  199.         printf("Error initializing semaphore mutex3.Errno returned: %s\n", strerror(errno));
  200.         exit(EXIT_FAILURE);
  201.     }
  202.     mutex3 = &sh_mm->mutex3;
  203.  
  204.     if (sem_init(&sh_mm->sem_remaining_lines, 1, h->height) == -1) {
  205.         printf("Error initializing semaphore sem_remaining_lines.Errno returned: %s\n", strerror(errno));
  206.         exit(EXIT_FAILURE);
  207.     }
  208.     sem_remaining_lines = &sh_mm->sem_remaining_lines;
  209.  
  210.  
  211.     /*Shared Memory segment for 3 integers -  MUST be like this*/
  212.     if ((shmids[1] = shmget(IPC_PRIVATE, 3 * sizeof (int), IPC_CREAT | 0700)) == -1) {
  213.         printf("shmget to allocate the 3 integers failed. Errno returned;  %s\n", strerror(errno));
  214.         exit(EXIT_FAILURE);
  215.     }
  216.     buf_vars = (int*) shmat(shmids[1], NULL, 0);
  217.     total_lines = &buf_vars[0];
  218.     processed_lines = &buf_vars[1];
  219.     next_line = &buf_vars[2];
  220.  
  221.  
  222. }
  223.  
  224. /*Worker process*/
  225. void worker(int i) {
  226.     int cur_index = 0;
  227.     //pixel *row;
  228.     //Block all signals, except SIGINT and SIGKILL which are handled
  229.     sigset_t block_ctrlc;
  230.     sigfillset(&block_ctrlc);
  231.     sigdelset(&block_ctrlc, SIGINT);
  232.     sigdelset(&block_ctrlc, SIGTERM);
  233.     sigprocmask(SIG_BLOCK, &block_ctrlc, NULL);
  234.     signal(SIGINT, handle_signal);
  235.     signal(SIGTERM, handle_signal);
  236.  
  237.     while (sem_wait(sem_remaining_lines) != -1) { //if there are still lines to read, go on
  238.         sem_wait(mutex3);
  239.         cur_index = *next_line; //current image's line
  240.         *next_line += 3 * h->width; //refreshs line for the next worker
  241.         sem_post(mutex3);
  242.         printf("WORKER %d Inverting row... \n",i);
  243.         invertRowOnMMF(3*h->width,data,cur_index);
  244.         printf("Done || \n");
  245.         sem_wait(mutex1);
  246.         *processed_lines += 1; //increases the number of inverted lines
  247.         if (*processed_lines == *total_lines) { //check if it reaches last line
  248.             sem_post(mutex2); //if so, wakes the master telling that is ready
  249.         }
  250.         sem_post(mutex1);
  251.     }
  252. }
  253.  
  254. void handle_signal(int signum) {
  255.     if (signum == SIGINT)
  256.         signal(SIGINT, handle_signal);
  257.     else
  258.         signal(SIGTERM, handle_signal);
  259.     exit(0);
  260. }
  261.  
  262. void terminate() {
  263.     int i;
  264.     /*An unnamed semaphore should be destroyed with sem_destroy()
  265.     before  the memory  in  which it is located is deallocated*/
  266.     sem_destroy(mutex1);
  267.     sem_destroy(mutex2);
  268.     sem_destroy(mutex3);
  269.     sem_destroy(sem_remaining_lines);
  270.  
  271.     //cleans up shared memory = removes shared memory segments
  272.     for (i = 0; i < 2 ;i++) {
  273.         shmctl(shmids[i], IPC_RMID, NULL);
  274.     }
  275.  
  276. }
  277.  
  278.  
and now the other .c e .h needed:

Expand|Select|Wrap|Line Numbers
  1. #include <sys/types.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <time.h>
  6. #include <sys/wait.h>
  7. #include <math.h>
  8.  
  9. #include "ppmtools.h"
  10.  
  11. #define READ_BUFFER_LEN 16
  12.  
  13. char* readType(FILE *fp) {
  14.     char buf[READ_BUFFER_LEN];
  15.     char *t;
  16.  
  17.     //get line of the file into read buffer -> type of ppm
  18.     t = fgets(buf, READ_BUFFER_LEN, fp);
  19.     //check if it reads something and if it is a P6 type
  20.     if (t == NULL) {
  21.         printf("could not read type from file\n");
  22.         return "";
  23.     }
  24.  
  25.     if (strncmp(buf, "P6\n", 3) != 0) {
  26.         printf("type is not P6\n");
  27.         return "";
  28.     } else {
  29.         printf("set type as P6\n");
  30.         //set type as P6
  31.         return "P6";
  32.     }
  33. }
  34.  
  35. unsigned int readNextHeaderValue(FILE *fp) {
  36.     char buf[READ_BUFFER_LEN];
  37.     char *t;
  38.     int r;
  39.     int value;
  40.  
  41.     printf("read next header value\n");
  42.     //load next line
  43.     t = fgets(buf, READ_BUFFER_LEN, fp);
  44.     if (t == NULL) {
  45.         printf("Error reading from file");
  46.         return 0;
  47.     }
  48.  
  49.     //read value as unsigned int
  50.     r = sscanf(buf, "%u", &value);
  51.  
  52.     //check if the read was successful
  53.     if (r != 1) {
  54.         printf("Could not read width value\n");
  55.         return 0;
  56.     } else {
  57.         return value;
  58.     }
  59. }
  60.  
  61. header* getImageHeader(FILE *fp) {
  62.     //alloc header
  63.     header* h = malloc(sizeof (header));
  64.  
  65.     //check if file pointer is null
  66.     if (fp == NULL) {
  67.         //file pointer is null
  68.         return NULL;
  69.     }
  70.  
  71.     //get type
  72.     char* type = readType(fp);
  73.     if (strcmp(type, "") != 0) {
  74.         //write type
  75.         strcpy(h->type, type);
  76.     } else {
  77.         //error reading type
  78.         return NULL;
  79.     }
  80.  
  81.     h->width = readNextHeaderValue(fp);
  82.     h->height = readNextHeaderValue(fp);
  83.     h->depth = readNextHeaderValue(fp);
  84.  
  85.     //check for successful read and valid color range
  86.     if (h->depth != 255 || h->width == 0 || h->height == 0) {
  87.         //printf("error reading values\n");
  88.         return NULL;
  89.     }
  90.  
  91.     return h;
  92. }
  93. //NOTE:
  94. // The program fails if the first byte of the image is equal to 32 (ascii for space char). because
  95. // the fscanf in eats the space (ascii 32) and the image is read with one byte less
  96.  
  97. int getImageRow(int pixelNo, pixel* row, FILE *fp) {
  98.  
  99.     int i;
  100.     byte b;
  101.     //reading line
  102.  
  103.     for (i = 0; i < pixelNo; i++) {
  104.         //set pixel values
  105.         if (fread(&row[i], sizeof (b), 3 * sizeof (b), fp) < 1)
  106.             return -1;
  107.     }
  108.  
  109.     return pixelNo;
  110. }
  111.  
  112. void invertRow(int pixelNo, pixel* row) {
  113.     pixel aux;
  114.     int i;
  115.  
  116.     //for each pixel
  117.     for (i = 0; i < pixelNo / 2; i++) {
  118.         //exchange pixel [i] with the pixel at the end minus [i]
  119.         aux = row[i];
  120.         row[i] = row[pixelNo - i - 1];
  121.         row[pixelNo - i - 1] = aux;
  122.     }
  123. }
  124.  
  125. void invertRowOnMMF(int width, char *data, int offset) {
  126.     char bytes1[3];
  127.     char bytes2[3];
  128.     int i, j, end_offset;
  129.  
  130.     //for each pixel
  131.     for (i = offset, j = 0; j < width / 2; i += 3, j += 3) {
  132.         end_offset= offset+width;
  133.         //exchange pixel [i] with the pixel at the end minus [i]
  134.         bytes1[0] = data[i]; //red
  135.         bytes1[1] = data[i + 1]; //green
  136.         bytes1[2] = data[i + 2]; //blue
  137.  
  138.         bytes2[0] = data[end_offset - j- 3]; //red
  139.         bytes2[1] = data[end_offset - j - 2]; //green
  140.         bytes2[2] = data[end_offset - j - 1]; //blue
  141.  
  142.         data[i] =     bytes2[0]; //red
  143.         data[i + 1] = bytes2[1]; //green
  144.         data[i + 2] = bytes2[2]; //blue
  145.  
  146.         data[end_offset - j - 3] = bytes1[0]; //red
  147.         data[end_offset - j - 2] = bytes1[1]; //green
  148.         data[end_offset - j - 1] = bytes1[2]; //blue
  149.     }
  150. }
  151.  
  152. int writeImageHeader(header* h, FILE* fp) {
  153.     //write header fields with newline between them
  154.     if (fprintf(fp, "%s\n%u\n%u\n%u\n", h->type, h->width, h->height, h->depth) < 1) {
  155.         return -1;
  156.     }
  157.  
  158.     return 0;
  159. }
  160.  
  161. int writeRow(int pixelNo, pixel* row, FILE* fp) {
  162.     int i, r;
  163.  
  164.     //for each pixel
  165.     for (i = 0; i < pixelNo; i++) {
  166.         //write one byte per RGB channel in pixel
  167.         r = fwrite(&row[i], sizeof (byte), 3 * sizeof (byte), fp);
  168.  
  169.         //check for errors
  170.         if (r < 1) {
  171.             return -1;
  172.         }
  173.     }
  174.  
  175.     return 0;
  176. }
  177.  
the header file:
Expand|Select|Wrap|Line Numbers
  1. //###################################### PPMTOOLS.H #######################################
  2. #include <semaphore.h>
  3.  
  4. #define NUM_WORKERS 2
  5.  
  6.  
  7. //this is a convenient typedef to better read the code.
  8. //it defines "byte" as a unsigned 8bit int.
  9. typedef u_int8_t  byte;
  10.  
  11. //this struct represents each pixel.
  12. //should be used to handle each pixel and maintain their correct colors.
  13. typedef struct pixel_t {
  14.     byte red;
  15.     byte green;
  16.     byte blue;
  17. }pixel;
  18.  
  19. //this is the structure used do allocate the SH_MM to the image file
  20. typedef struct {
  21.     pixel *pixel_data;
  22. } image_struct;
  23.  
  24.  
  25. typedef struct{
  26.     sem_t mutex1, mutex2, mutex3, sem_remaining_lines;
  27. }mem_struct;
  28. //this struct represents the ppm file header.
  29. //it stores the file info and will be copied to the output file.
  30. typedef struct header_t {
  31.     char type[3];
  32.     unsigned int width;
  33.     unsigned int height;
  34.     unsigned int depth;
  35. }header;
  36.  
  37. //reads a line and extracts the type of ppm file.
  38. //returns "P6" on success or "" on fail or other type of ppm file.
  39. char* readType(FILE *fp);
  40.  
  41. //reads a line and extracts the unsigned int value of that line.
  42. //returns 0 on fail and a value greater than 0 ond success.
  43. //used to read width, height and depth from the ppm file.
  44. unsigned int readNextHeaderValue(FILE *fp);
  45.  
  46. //reads file and extracts the header.
  47. //returns a header struct on success or NULL on failure.
  48. header* getImageHeader(FILE *fp);
  49.  
  50. //reads a complete row of the image based on the number of pixels given.
  51. //user should give an initialized pixel array to be filled.
  52. //returns number of pixels read on success and -1 on failure.
  53. int getImageRow(int pixelNo, pixel* row, FILE *fp);
  54.  
  55. //inverts the pixels in the given row.
  56. void invertRow(int pixelNo, pixel* row);
  57.  
  58. //writes the image header to file.
  59. //returns 0 on success and -1 on failure.
  60. int writeImageHeader(header* h, FILE* fp);
  61.  
  62. //writes a row of pixels to a file.
  63. //returns 0 on success and -1 on failure
  64. int writeRow(int pixelNo, pixel* row, FILE* fp);
  65.  
  66. //initiates sems and allocates shared memory
  67. void init();
  68. void worker(int i);
  69. void handle_signal(int signum);
  70. //cleans everything
  71. void terminate();
  72. void invertRowOnMMF(int width, char *data, int offset);
  73.  
and the makefile:
Expand|Select|Wrap|Line Numbers
  1. FLAGS = -Wall -g -lrt
  2. CC = gcc
  3. EXEC2 = MMF_inverter
  4. OBJS2 = ppmtools.o MMF_inverter.o
  5.  
  6. all: ${EXEC2} clean
  7.  
  8. clean:
  9.     rm *o
  10.  
  11. ${EXEC2}:    ${OBJS2}
  12.     ${CC} ${FLAGS} ${OBJS2} -o $@    
  13.  
  14. .c.o:
  15.     ${CC} ${FLAGS} $< -c
  16.  
  17.  
  18. # # # # # # # # #
  19.  
  20. ppmtools.o: ppmtools.c
  21. MMF_inverter.o: MMF_inverter.c
  22.  
Nov 23 '10 #1
0 1093

Sign in to post your reply or Sign up for a free account.

Similar topics

1
by: Srijit Kumar Bhadra | last post by:
Hello, I see that it is possible to use mmapfile.pyd of win32all. The same is mentioned in http://www.python.org/windows/win32/#mmapfile. Unfortunately I could not trace any example using...
1
by: Peter Nolan | last post by:
Hi All, I have some software that currently loads database tables into sorted arrays in memory to be binary searched by processes. (Long story as to why it does this..) Each process must either...
2
by: vikas | last post by:
I have following structure in c++. typedef struct MMF_result_struct { int action; char text; int cols,rows; int month,day,year; } MMF_result; Now this structure is shared between C++ and C#...
3
by: wakun | last post by:
Hi there, I am seeking a fastest way to load a BIG string and parse it as a given format. I have a extern function which return a (char *)string in BIG size. Now, I am going to parse it with a...
1
by: Carl Mackey | last post by:
hi, i'm new to this list and new to python as well. i have a question on the memory mapped file ability python has. when i use a mmap on a file, will it copy the whole thing to ram or just...
13
by: Samshayam | last post by:
I have come across the application of placement new in memory mapped i/o in a number of books.I am not able to understand it completely, may be becaues of my lack of knowledge with memory mapped...
1
by: Chris Brooks | last post by:
Hi, I would like to read directly from a tar file into memory so I can manipulate a file (quickly) and write its changes out to another file. I thought I could do something like: ...
1
by: manjuk | last post by:
Hi there, I have couple of questions. 1) Are Shared Memory & Memory Mapped Files same? 2) Is there a library/code that implements either of them? With which I can just pass object reference (of...
3
by: Frank Chang | last post by:
Good afternoon, I have written a C++ class for Windows and Linux that creates a memory-mapped view for an file of arbitrary size n. The code for the class constructor is in the www.pastebin.com url...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.