C/C++ Linux System Programming Session 13 User-space System Programming  – session 3
Outline Pipes & FIFOs SysV mechanisms POSIX mechanisms
IPC Mechanisms So Far Signals Exit status Fork Address space
Pipes Characteristics: Single reader – single writer (uni-diriectional) File descriptors (unnamed) POSIX and Linux restrictions Pipefs SIGPIPE: No readers PAGE_SIZE max (blocking write!!) int pipe(int pipefd[2]); int dup2(int oldfd, int newfd);
struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; prevfd = -1; for (lp = n->npipe.cmdlist; lp; lp = lp->next) { ... pip[1] = -1; if (lp->next) { if (pipe(pip) < 0) { ... } } if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { ... if (pip[1] >= 0) { close(pip[0]); } if (prevfd > 0) { dup2(prevfd, 0); close(prevfd); } if (pip[1] > 1) { dup2(pip[1], 1); close(pip[1]); } /* Execute */ /* never returns */ } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; close(pip[1]); }
FIFOs Characteristics Named pipes File system int mkfifo(const char *pathname, mode_t mode);
SysV Generic General Interface Get IPC_PRIVATE or key,  key_t ftok(const char *pathname, int proj_id); Flags: IPC_CREAT / IPC_EXCL ctl Specific Ops & control details System-wide (named) or private Owner / Creator (time) Explicit removal ipc()
SysV Semaphores Resource: Array of Semaphore Primitives int semget(key_t key, int nsems, int semflg); Multiple critical regions Multiple instances of a resource Initialize to a value (# of available instances) -ve to get resource, +ve to release 0 means block (unless IPC_NOWAIT)
Semaphore control int semctl(int semid, int semnum, int cmd, ...); /* union semun */ int val;  /* SETVAL */ struct semid_ds *buf;  /* IPC_STAT, IPC_SET */ unsigned short  *array;  /* GETALL, SETALL */ struct seminfo  *__buf;  /* IPC_INFO (Linux) */ Initialization: SETVAL / SETALL / IPC_SET Debugging: GETVAL / GETALL / INFO / STAT  Removal: IPC_RMID
Semaphore Ops int semop(int semid, struct sembuf *sops, unsigned nsops); int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout); struct sem_buf unsigned short sem_num;  short  sem_op;  short  sem_flg;  /* SEM_UNDO, IPC_NOWAIT */ “Undoable” operations
SysV Messages int msgget(key_t key, int msgflg); int msgctl(int msqid, int cmd, struct msqid_ds *buf); IPC_RMID, IPC_SET int msgsnd(int msqid, const void  msgp, size_t msgsz, int msgflg); struct msgbuf { long mtype;  /* > 0 */ char mtext[1]; }
Msg Receival ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); Msgtyp:  0 first > 0 (first of type) / MSG_EXCEPT < 0 (first less than |type|) Flags: MSG_NOERROR, IPC_NOWAIT, MSG_EXCEPT
SysV Shared Memory int shmget(key_t key, size_t size, int shmflg); /* SHM_HUGETLB, SHM_NORESERVE */ int shmctl(int shmid, int cmd, struct shmid_ds *buf); IPC_RMID: Mark for removal  void *shmat(int shmid, const void  shmaddr, int shmflg); /*  SHM_RDONLY SHM_REMAP */ int shmdt(const void *shmaddr);
static void ipcsyslog_init(void) { if (DEBUG) printf(&quot;shmget(%x, %d,...)\n&quot;, (int)KEY_ID, G.shm_size); G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644); if (G.shmid == -1) { bb_perror_msg_and_die(&quot;shmget&quot;); } G.shbuf = shmat(G.shmid, NULL, 0); if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */ bb_perror_msg_and_die(&quot;shmat&quot;); } memset(G.shbuf, 0, G.shm_size); G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1; /*G.shbuf->tail = 0;*/ // we'll trust the OS to set initial semval to 0 (let's hope) G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023); if (G.s_semid == -1) { if (errno == EEXIST) { G.s_semid = semget(KEY_ID, 2, 0); if (G.s_semid != -1) return; } bb_perror_msg_and_die(&quot;semget&quot;); } } static void log_to_shmem(const char *msg, int len) { int old_tail, new_tail; if (semop(G.s_semid, G.SMwdn, 3) == -1) { bb_perror_msg_and_die(&quot;SMwdn&quot;); } ... /* Circular buffer calculation */ memcpy(G.shbuf->data + old_tail, msg, k); if (semop(G.s_semid, G.SMwup, 1) == -1) { bb_perror_msg_and_die(&quot;SMwup&quot;); } } static void ipcsyslog_cleanup(void) { if (G.shmid != -1) { shmdt(G.shbuf); } if (G.shmid != -1) { shmctl(G.shmid, IPC_RMID, NULL); } if (G.s_semid != -1) { semctl(G.s_semid, 0, IPC_RMID, 0); } }
POSIX Generic Filesystem like Focus on the unified interface May be not as versatile (do you care?) Still some specifics Newer, e.g. mq_ in >= 2.6.6 Shm_ in 2.4 was on mounted /dev/shm
POSIX Semaphores sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); int sem_destroy(sem_t *sem); int sem_init(sem_t *sem, int pshared, unsigned int value);
Semaphore Ops int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); int sem_post(sem_t *sem); int sem_getvalue(sem_t *sem, int *sval);
POSIX Message Queues mqd_t mq_open(const char *name, int oflag); mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio); mqd_t mq_close(mqd_t mqdes); mqd_t mq_unlink(const char *name);
Mq Attributes mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr); mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr); mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); {long mq_flags; /* 0 or O_NONBLOCK */ long mq_maxmsg;  long mq_msgsize;  long mq_curmsgs;  };
POSIX Shared Memory int shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name); From then on, mapping
Memory Mapping for Shared memory void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); /* MAP_SHARED */ int munmap(void *start, size_t length); void *mremap(void *old_address, size_t old_size, size_t new_size, int flags);
POSIX shm example int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) { char fn[32]; int fd = -1; struct shm_marker *marker; pa_random(&m->id, sizeof(m->id)); segment_name(fn, sizeof(fn), m->id); if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) { ... } m->size = size + PA_ALIGN(sizeof(struct shm_marker)); if (ftruncate(fd, m->size) < 0) { ...  } if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { ... } marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size -   PA_ALIGN(sizeof(struct shm_marker))); pa_atomic_store(&marker->pid, (int) getpid()); pa_atomic_store(&marker->marker, SHM_MARKER); ... m->do_unlink = 1; } void pa_shm_free(pa_shm *m) { ... if (munmap(m->ptr, m->size) < 0) pa_log(&quot;munmap() failed: %s&quot;, pa_cstrerror(errno)); if (m->do_unlink) { char fn[32]; segment_name(fn, sizeof(fn), m->id); if (shm_unlink(fn) < 0) pa_log(&quot; shm_unlink(%s) failed: %s&quot;, fn, pa_cstrerror(errno)); } ... memset(m, 0, sizeof(*m)); } struct shm_marker { pa_atomic_t marker; /* 0xbeefcafe */ pa_atomic_t pid; void *_reserverd1; void *_reserverd2; void *_reserverd3; void *_reserverd4; }; static char *segment_name(char *fn, size_t l, unsigned id) { pa_snprintf(fn, l, &quot;/pulse-shm-%u&quot;, id); return fn; }
struct pa_semaphore { sem_t sem; }; pa_semaphore* pa_semaphore_new(unsigned value) { pa_semaphore *s; s = pa_xnew(pa_semaphore, 1); &s->sem, 0, value); return s; } void pa_semaphore_free(pa_semaphore *s) { sem_destroy(&s->sem) ; } void pa_semaphore_post(pa_semaphore *s) { sem_post(&s->sem) ; } void pa_semaphore_wait(pa_semaphore *s) { int ret; do { ret = sem_wait(&s->sem); } while (ret < 0 && errno == EINTR); } pa_mempool* pa_mempool_new(int shared) { pa_mempool *p; ... p = pa_xnew(pa_mempool, 1); p->semaphore = pa_semaphore_new(0); p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); ... if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) { } ... return p; } void pa_mempool_free(pa_mempool *p) { ... pa_shm_free(&p->memory); ... pa_semaphore_free(p->semaphore); pa_xfree(p); } static void memblock_wait(pa_memblock *b) { if (pa_atomic_load(&b->n_acquired) > 0) { pa_atomic_inc(&b->please_signal); while (pa_atomic_load(&b->n_acquired) > 0) pa_semaphore_wait(b->pool->semaphore); pa_atomic_dec(&b->please_signal); } } void pa_memblock_release(pa_memblock *b) { int r; r = pa_atomic_dec(&b->n_acquired); pa_assert(r >= 1); if (r == 1 && pa_atomic_load(&b->please_signal)) pa_semaphore_post(b->pool->semaphore); }

Sysprog 13

  • 1.
    C/C++ Linux SystemProgramming Session 13 User-space System Programming – session 3
  • 2.
    Outline Pipes &FIFOs SysV mechanisms POSIX mechanisms
  • 3.
    IPC Mechanisms SoFar Signals Exit status Fork Address space
  • 4.
    Pipes Characteristics: Singlereader – single writer (uni-diriectional) File descriptors (unnamed) POSIX and Linux restrictions Pipefs SIGPIPE: No readers PAGE_SIZE max (blocking write!!) int pipe(int pipefd[2]); int dup2(int oldfd, int newfd);
  • 5.
    struct job *jp;struct nodelist *lp; int pipelen; int prevfd; int pip[2]; prevfd = -1; for (lp = n->npipe.cmdlist; lp; lp = lp->next) { ... pip[1] = -1; if (lp->next) { if (pipe(pip) < 0) { ... } } if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { ... if (pip[1] >= 0) { close(pip[0]); } if (prevfd > 0) { dup2(prevfd, 0); close(prevfd); } if (pip[1] > 1) { dup2(pip[1], 1); close(pip[1]); } /* Execute */ /* never returns */ } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; close(pip[1]); }
  • 6.
    FIFOs Characteristics Namedpipes File system int mkfifo(const char *pathname, mode_t mode);
  • 7.
    SysV Generic GeneralInterface Get IPC_PRIVATE or key, key_t ftok(const char *pathname, int proj_id); Flags: IPC_CREAT / IPC_EXCL ctl Specific Ops & control details System-wide (named) or private Owner / Creator (time) Explicit removal ipc()
  • 8.
    SysV Semaphores Resource:Array of Semaphore Primitives int semget(key_t key, int nsems, int semflg); Multiple critical regions Multiple instances of a resource Initialize to a value (# of available instances) -ve to get resource, +ve to release 0 means block (unless IPC_NOWAIT)
  • 9.
    Semaphore control intsemctl(int semid, int semnum, int cmd, ...); /* union semun */ int val; /* SETVAL */ struct semid_ds *buf; /* IPC_STAT, IPC_SET */ unsigned short *array; /* GETALL, SETALL */ struct seminfo *__buf; /* IPC_INFO (Linux) */ Initialization: SETVAL / SETALL / IPC_SET Debugging: GETVAL / GETALL / INFO / STAT Removal: IPC_RMID
  • 10.
    Semaphore Ops intsemop(int semid, struct sembuf *sops, unsigned nsops); int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout); struct sem_buf unsigned short sem_num; short sem_op; short sem_flg; /* SEM_UNDO, IPC_NOWAIT */ “Undoable” operations
  • 11.
    SysV Messages intmsgget(key_t key, int msgflg); int msgctl(int msqid, int cmd, struct msqid_ds *buf); IPC_RMID, IPC_SET int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg); struct msgbuf { long mtype; /* > 0 */ char mtext[1]; }
  • 12.
    Msg Receival ssize_tmsgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); Msgtyp: 0 first > 0 (first of type) / MSG_EXCEPT < 0 (first less than |type|) Flags: MSG_NOERROR, IPC_NOWAIT, MSG_EXCEPT
  • 13.
    SysV Shared Memoryint shmget(key_t key, size_t size, int shmflg); /* SHM_HUGETLB, SHM_NORESERVE */ int shmctl(int shmid, int cmd, struct shmid_ds *buf); IPC_RMID: Mark for removal void *shmat(int shmid, const void shmaddr, int shmflg); /* SHM_RDONLY SHM_REMAP */ int shmdt(const void *shmaddr);
  • 14.
    static void ipcsyslog_init(void){ if (DEBUG) printf(&quot;shmget(%x, %d,...)\n&quot;, (int)KEY_ID, G.shm_size); G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644); if (G.shmid == -1) { bb_perror_msg_and_die(&quot;shmget&quot;); } G.shbuf = shmat(G.shmid, NULL, 0); if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */ bb_perror_msg_and_die(&quot;shmat&quot;); } memset(G.shbuf, 0, G.shm_size); G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1; /*G.shbuf->tail = 0;*/ // we'll trust the OS to set initial semval to 0 (let's hope) G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023); if (G.s_semid == -1) { if (errno == EEXIST) { G.s_semid = semget(KEY_ID, 2, 0); if (G.s_semid != -1) return; } bb_perror_msg_and_die(&quot;semget&quot;); } } static void log_to_shmem(const char *msg, int len) { int old_tail, new_tail; if (semop(G.s_semid, G.SMwdn, 3) == -1) { bb_perror_msg_and_die(&quot;SMwdn&quot;); } ... /* Circular buffer calculation */ memcpy(G.shbuf->data + old_tail, msg, k); if (semop(G.s_semid, G.SMwup, 1) == -1) { bb_perror_msg_and_die(&quot;SMwup&quot;); } } static void ipcsyslog_cleanup(void) { if (G.shmid != -1) { shmdt(G.shbuf); } if (G.shmid != -1) { shmctl(G.shmid, IPC_RMID, NULL); } if (G.s_semid != -1) { semctl(G.s_semid, 0, IPC_RMID, 0); } }
  • 15.
    POSIX Generic Filesystemlike Focus on the unified interface May be not as versatile (do you care?) Still some specifics Newer, e.g. mq_ in >= 2.6.6 Shm_ in 2.4 was on mounted /dev/shm
  • 16.
    POSIX Semaphores sem_t*sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); int sem_destroy(sem_t *sem); int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 17.
    Semaphore Ops intsem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); int sem_post(sem_t *sem); int sem_getvalue(sem_t *sem, int *sval);
  • 18.
    POSIX Message Queuesmqd_t mq_open(const char *name, int oflag); mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio); mqd_t mq_close(mqd_t mqdes); mqd_t mq_unlink(const char *name);
  • 19.
    Mq Attributes mqd_tmq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr); mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr); mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); {long mq_flags; /* 0 or O_NONBLOCK */ long mq_maxmsg; long mq_msgsize; long mq_curmsgs; };
  • 20.
    POSIX Shared Memoryint shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name); From then on, mapping
  • 21.
    Memory Mapping forShared memory void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); /* MAP_SHARED */ int munmap(void *start, size_t length); void *mremap(void *old_address, size_t old_size, size_t new_size, int flags);
  • 22.
    POSIX shm exampleint pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) { char fn[32]; int fd = -1; struct shm_marker *marker; pa_random(&m->id, sizeof(m->id)); segment_name(fn, sizeof(fn), m->id); if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) { ... } m->size = size + PA_ALIGN(sizeof(struct shm_marker)); if (ftruncate(fd, m->size) < 0) { ... } if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { ... } marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker))); pa_atomic_store(&marker->pid, (int) getpid()); pa_atomic_store(&marker->marker, SHM_MARKER); ... m->do_unlink = 1; } void pa_shm_free(pa_shm *m) { ... if (munmap(m->ptr, m->size) < 0) pa_log(&quot;munmap() failed: %s&quot;, pa_cstrerror(errno)); if (m->do_unlink) { char fn[32]; segment_name(fn, sizeof(fn), m->id); if (shm_unlink(fn) < 0) pa_log(&quot; shm_unlink(%s) failed: %s&quot;, fn, pa_cstrerror(errno)); } ... memset(m, 0, sizeof(*m)); } struct shm_marker { pa_atomic_t marker; /* 0xbeefcafe */ pa_atomic_t pid; void *_reserverd1; void *_reserverd2; void *_reserverd3; void *_reserverd4; }; static char *segment_name(char *fn, size_t l, unsigned id) { pa_snprintf(fn, l, &quot;/pulse-shm-%u&quot;, id); return fn; }
  • 23.
    struct pa_semaphore {sem_t sem; }; pa_semaphore* pa_semaphore_new(unsigned value) { pa_semaphore *s; s = pa_xnew(pa_semaphore, 1); &s->sem, 0, value); return s; } void pa_semaphore_free(pa_semaphore *s) { sem_destroy(&s->sem) ; } void pa_semaphore_post(pa_semaphore *s) { sem_post(&s->sem) ; } void pa_semaphore_wait(pa_semaphore *s) { int ret; do { ret = sem_wait(&s->sem); } while (ret < 0 && errno == EINTR); } pa_mempool* pa_mempool_new(int shared) { pa_mempool *p; ... p = pa_xnew(pa_mempool, 1); p->semaphore = pa_semaphore_new(0); p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); ... if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) { } ... return p; } void pa_mempool_free(pa_mempool *p) { ... pa_shm_free(&p->memory); ... pa_semaphore_free(p->semaphore); pa_xfree(p); } static void memblock_wait(pa_memblock *b) { if (pa_atomic_load(&b->n_acquired) > 0) { pa_atomic_inc(&b->please_signal); while (pa_atomic_load(&b->n_acquired) > 0) pa_semaphore_wait(b->pool->semaphore); pa_atomic_dec(&b->please_signal); } } void pa_memblock_release(pa_memblock *b) { int r; r = pa_atomic_dec(&b->n_acquired); pa_assert(r >= 1); if (r == 1 && pa_atomic_load(&b->please_signal)) pa_semaphore_post(b->pool->semaphore); }