Click here to Skip to main content
15,887,596 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Greetings,

I am self-studying OSTEP (Operating Systems Three Easy Pieces). It's an amazing book, and I'm learning a ton from it. Currently, I am working on the measurement homework at the end of Chapter 6 (Mechanism: Limited Direct Execution). I come from a C++ background, so, please, if there are better ways to write a piece of code in this program, I would appreciate your insights.

Homework (bottom of the pdf):
https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-mechanisms.pdf

Code:
C
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sched.h>
#include <unistd.h>

#define SIZE      7
#define READ_END  0
#define WRITE_END 1

static __inline__ uint64_t rdtsc(void)
{
    unsigned lo, hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return (((uint64_t)lo) | (((uint64_t)hi) << 32));
}

int main(void)
{
    int fd1[2], fd2[2];
    if(pipe(fd1) == -1 || pipe(fd2) == -1) {
        perror("pipe()");
        return EXIT_FAILURE;
    }

    pid_t pid = fork();

    int cpu = 0;
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(cpu, &mask);
    if(sched_setaffinity(0, sizeof(mask), &mask) == -1) {
        perror("sched_setaffinity()");
        return EXIT_FAILURE;
    }

    uint64_t start = rdtsc();

    switch(pid) {
    case -1:
        perror("fork()");
        return EXIT_FAILURE;

    case 0: {
        close(fd1[READ_END]);
        char ca[] = {"rodata"};
        ssize_t bytes = write(fd1[WRITE_END], ca, sizeof(ca));
        if(bytes == -1) {
            perror("write()");
            return EXIT_FAILURE;
        }
        close(fd1[WRITE_END]);

        bytes = read(fd2[READ_END], ca, sizeof(ca));
        if(bytes == -1) {
            perror("read()");
            return EXIT_FAILURE;
        }
        close(fd2[READ_END]);
        break;
    }

    default: {
        close(fd1[WRITE_END]);
        char ca[SIZE];
        ssize_t bytes = read(fd1[READ_END], ca, SIZE);
        if(bytes == -1) {
            perror("read()");
            return EXIT_FAILURE;
        }
        close(fd1[READ_END]);

        bytes = write(fd2[WRITE_END], ca, sizeof(ca));
        if(bytes == -1) {
            perror("read()");
            return EXIT_FAILURE;
        }
        close(fd2[WRITE_END]);
        break;
    }
    
    }

    uint64_t end = rdtsc();

    printf("Total clock cycles: %llu\n", (unsigned long long)(end - start));

    return EXIT_SUCCESS;
}


What I have tried:

I read the man page for the sched_setaffinity to better understand how to take advantage of just one core for the two processes to run on. My concern is whether the code does what it is suppose to do. And to do it correctly.
Posted
Updated 23-Mar-24 14:20pm
v2

1 solution

Are you intending for only the parent child process to have CPU affinity?

C++
if(sched_setaffinity(0, sizeof(mask), &mask) == -1) {
    perror("sched_setaffinity()");
    return EXIT_FAILURE;
}


If not you should probably change the if statement to:

C++
if(sched_setaffinity(getpid(), sizeof(mask), &mask) == -1) {
    perror("sched_setaffinity()");
    return EXIT_FAILURE;
}


I think this is what the example in the PDF intended (lock both processes to the same CPU core)

"One difficulty in measuring context-switch cost arises in systems
with more than one CPU; what you need to do on such a system is
ensure that your context-switching processes are located on the
same processor. Fortunately, most operating systems have calls to
bind a process to a particular processor; on Linux, for example, the
sched_setaffinity() call is what you’re looking for. By ensuring both
processes are on the same processor, you are making sure to measure
the cost of the OS stopping one process and restoring another on
the same CPU."
 
Share this answer
 
v5
Comments
PhaazeReborn 24-Mar-24 18:42pm    
Yes, locking both processes to the same CPU core. Also, wouldn't it work if the first argument is 0 as well? This is what the man page reads about it:

"sched_setaffinity() sets the CPU affinity mask of the thread whose ID is pid to the value specified by mask. If pid is zero, then the calling thread is used. The argument cpusetsize is the length (in bytes) of the data pointed to by mask. Normally this argument would be specified as sizeof(cpu_set_t)."
Sean Cundiff 24-Mar-24 19:18pm    
I see that in the man page. However, there is a program example where they are clearly using getpid() in both the child and parent sections of the switch statement:

switch (fork()) {
           case -1:            /* Error */
               err(EXIT_FAILURE, "fork");

           case 0:             /* Child */
               CPU_SET(childCPU, &set);

               if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
                   err(EXIT_FAILURE, "sched_setaffinity");

               for (unsigned int j = 0; j < nloops; j++)
                   getppid();

               exit(EXIT_SUCCESS);

           default:            /* Parent */
               CPU_SET(parentCPU, &set);

               if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
                   err(EXIT_FAILURE, "sched_setaffinity");

               for (unsigned int j = 0; j < nloops; j++)
                   getppid();

               wait(NULL);     /* Wait for child to terminate */
               exit(EXIT_SUCCESS);
           }
Sean Cundiff 24-Mar-24 19:58pm    
I did some testing and setting PID to 0 in the first argument [sched_setaffinity(0, sizeof(mask), &mask)] does force both the child and parent to run on the same core (in your code).

You can test this in your code by adding the following to your switch statements:

45     case 0: {
46         printf("CHILD CORE:%d:",sched_getcpu());

65     default: {
66         printf("PARENT CORE:%d:",sched_getcpu());


If you comment out your sched_setaffinity block you'll see that they will run on different cores.


So you can, in fact, use 0 --or-- getpid() in your code to lock to the same core.

Your example appears correct for the end of chapter task.
PhaazeReborn 24-Mar-24 19:49pm    
You're right. I did not look that far down. In that case, thanks!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900