---
# System prepended metadata

title: L2_IPC
tags: [NetProg]

---

# L2_IPC
###### tags: `NetProg`
:::info
:page_with_curl: website: https://hackmd.io/@wbjfRtFvQ96uXiHjQ0AvgA/Hyah4KD1t#/
:::
## IPC (Interprocess Communication)
### Methods
- File locking
- Pipes
- FIFOs
- Message queues (system V)
- Semaphores
- Shared memory
- 不侷限於同一個機器上，不同的ysstem machine上都可以溝通（可能需要經過不同的kernel)
## File and Record Locking
e.g. Printer (shared resource)
- When using, must lock the resource
- The user needs to request for a uniqle ID for the sequence line-up
- During the locking, it can read the file for its content
- It uses the file and use the file
- The sequence number for ID increase 1 after the file fininshs using
e.g.
```c=
#define	SEQFILE	"seqno"		/* filename */
#define	MAXBUFF	   100

main()
{
    // fd: file tanle
    // n: number
    // this can be modified by anyone
    int	fd, i, n, pid, seqno;
    
    // use buffer to store the file content
    char	buff[MAXBUFF + 1];
	pid = getpid();
	if ( (fd = open(SEQFILE, 2)) < 0)
		err_sys("can't open %s", SEQFILE);
	for (i = 0; i < 20; i++) {
		my_lock(fd);			/* lock the file */
		lseek(fd, 0L, 0);		/* rewind before read */
        
        // read the file and store it into buffer, n is the number of the characters
		if ( (n = read(fd, buff, MAXBUFF)) <= 0)
			err_sys("read error");
		buff[n] = '\0';		/* null terminate for sscanf */

        // read the content inside buffer
		if ( (n = sscanf(buff, "%d\n", &seqno)) != 1)
			err_sys("sscanf error");
		printf("pid = %d, seq# = %d\n", pid, seqno);

        // add the shared content
		seqno++;		/* increment the sequence number */

        //store the shared content into fd
		sprintf(buff, "%03d\n", seqno);
		n = strlen(buff);
		lseek(fd, 0L, 0);		/* rewind before write */
		if (write(fd, buff, n) != n)
			err_sys("write error");
		my_unlock(fd);			/* unlock the file */
	}
}

// method A: no lock
// if the locking do nothing, the thread would be a mess
my_lock(fd)
int	fd;
{
	return;
}


my_unlock(fd)
int	fd;
{
	return;
}

// method B: FreeBSD locking
// it report to the system, saying it cannot be locked
// it waits till it can lock up
// But it cannot garantee the sequence for the different process
my_lock(fd)
int	fd;
{
	if(flock(fd, LOCK_EX == -1))
		err_sys("can't LOCK_EX");
}


my_unlock(fd)
int	fd;
{
	if(flock(fd, LOCK_UN== -1))
		err_sys("can't LOCK_UN");
}
```
## Pipe
- 一塊記憶體空間 在kernel裡面
- 堆到裡面後像水管一樣一個一個把他吐出來
- ```writefd```, ```readfd```
- 做出一個pipe會產生PR, PW分別放進去FD table, 並且放在pipe指標記憶體內，對應到不同fd指標。程式內部就會看對應動作做不同指示
e.g. pipe[0] (pr) = fd[3], pipe[1] (pw) = fd[4]
    when I want to write 'helloword', I called ```pipe[1]```
e.g.
```bash=
% cat f | grep "we"
```
- cat stdout要改成放到pipe write
- grep stdin要改成pipe read
```c=
main()
{
    // pipefd: pipe的兩個指標，頭尾的pointer
	int     pipefd[2], n;
	char        buff[100];

	if (pipe(pipefd) < 0)
		err_sys("pipe error");
	printf("read fd = %d, write fd = %d\n", pipefd[0], pipefd[1]);
	if (write(pipefd[1], "hello world\n", 12) != 12)
		err_sys("write error");

	if (read(pipefd[0], buff, BUFFSIZE) != BUFFSIZE)
		err_sys("read error");

	write(1, buff, n) /* fd 1 = stdout */
	exit(0);
}

/*
 * Output:
     * hello world
     * read fd = 3, write fd = 4
 * 
 * /
```
- printf: library
- read/write: system call
- 所有的sys call都是要來跟kernel做事的戶動要求
- 收到sys call 就會***馬上***到kernel並且印到console
    - 很有問題！ 因為這樣會打斷其他的東西
- printf的library會把要印的東西放到buffer，如果之後又有叫到printf就會繼續放在buffer直到(buffer滿了/ 強制fflush/ 已經exit)的時候再把它印出來
### Creation
- The parent, child process should use the same pipe
- Problem: 兩個會同時讀寫
- Solution: 2 way pipes
    - Parent --> Child: path1
    - Child --> Parent: path2
    - 想辦法讓他們讀寫會是不一樣的
    - But by default, they should be the same
    - Add one more pipe, duplicate the process, and the other edges for the connection should be closed (because the process should have its own pointers and being able to be deleted)
    - Related to Proj. 1
e.g.
![](https://i.imgur.com/sLTXXZr.png)
- Client gives filename to Server
- Server read the name and gives file content
```c=
main() {

    // 1. build 2 pipes
    // pipe1 --> fd[3]: R, fd[4]: W
    // pipe2 --> fd[5]: R, fd[6]: W
	int	childpid, pipe1[2], pipe2[2];
	if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
		err_sys("can't create pipes");
        
    // 2. fork child, naturally have pointers to 2 pipes
	if (childpid = fork() < 0)
		err_sys("can't fork");
	} else if (childpid > 0) {	/* parent */
        
        // 3. Close the un-needed pointers for client: pipe1-R, pipe2-W
		close(pipe1[0]);  close(pipe2[1]);
        
        // 4. Actions for client
		client(pipe2[0], pipe1[1]);
		while (wait((int *) 0) != childpid)	/* wait for child */
			;
		close(pipe1[1]);  close(pipe2[0]);
		exit(0);
	} else {					/* child */
    
        // 3. Close the un-needed pointers for server: pipe1-W, pipe2-R
		close(pipe1[1]); close(pipe2[0]);
        
        // 4. Actions for server
		server(pipe1[0], pipe2[1]);
		close(pipe1[0]);  close(pipe2[1]);
		exit(0);
	}
}

/* Client */
// readfd: pipe2[0], fd[5]
// writefd: pipe1[1], fd[4]
client(int readfd, int writefd)
{
	char	buff[MAXBUFF];
	int	n;

	// Read the filename from standard input, write it to the IPC descriptor. 
    // read from fd[5]
	if (fgets(buff, MAXBUFF, stdin) == NULL) err_sys(“client: filename read error”);

	n = strlen(buff);
	if (buff[n-1] == ‘\n’) n--; /* ignore newline from fgets() */
    // write to pipe1 with fd[4]
	if (write(writefd, buff, n) != n)  err_sys("client: filename write error");

	// Read the data from the IPC descriptor and write to standard output.
	while ( (n = read(readfd, buff, MAXBUFF)) > 0)
	if (write(1 /* stdout*/, buff, n) != n)	err_sys("client: data write error");
	if (n < 0) err_sys("client: data read error");
}


/* Server */
// readfd:  pipe1[0], fd[3]
// writefd:  pipe2[1], fd[6]
server(int readfd, int writefd)
{
	char	buff[MAXBUFF],  errmesg[256], *sys_err_str();
	int	n, fd;

	// Read the filename from the IPC descriptor.
    // read from fd[3]
	if ( (n = read(readfd, buff, MAXBUFF-1)) <= 0) err_sys("server: filename read error");
	buff[n] = '\0';		/* null terminate filename */

	if ( (fd = open(buff, 0)) < 0) {
		// Error.  Format an error message and send it back to the client.
		sprintf(errmesg, ": can't open, %s\n", sys_err_str());
		strcat(buff, errmesg);
		n = strlen(buff);
        
        // write to fd[6]
		if (write(writefd, buff, n) != n) err_sys("server: errmesg write error");
	} else {
		// Read the data from the file and write to the IPC descriptor.
		while ( (n = read(fd, buff, MAXBUFF)) > 0) 
		       if (write(writefd, buff, n) != n) err_sys("server: data write error");
		if (n < 0) err_sys("server: read error");
	}
}

```
### Unix Shell Pipe
```bash=
% who | sort ... | lpr ...
# output of who --> input of sort
# output of sort 0--> input of lpr
# Independent child processes, the i.o combined by pipe
```
- In a program, it can be done more simply without pipe
- But it is inflexible
- With ```pipe```, it can be done by combining dynamically
- SOP:
```
1. pipe
2. fork
3. close
4. dup stdin/ out 到原生stdin/ out
```
### SIGPIPE
- 很多人fork的時候會變成同一個memory address 有很多人指向
- 最好能關就關，要不然當要關掉某個位置時可能會造成不對的指向
- 如果指到已經關掉的部分，就會```SIGPIPE```
## FIFO
- pipe必須要parent process去開
- fifo一般檔案系統就可以獨立出來
- 不是parent child關西也可以溝通
### On 2 Hosts
- 如果是不同的kernel space, 跨機器也沒辦法用fifo, 因為就算名義上有共享，他其實還是一個虛擬檔名指向到同一個kernel
- 外部的硬碟其實只是一個空間，跟實際的運作沒有關係。
- 