I am trying to write to and then read from a file descriptor opened using shm_open. It works as I expect on Linux, but not on macOS (specifically macOS Monterey 12.5 21G72).
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char * argv[]) {
int fd = shm_open("/example", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (fd < 0) {
printf("shm_open() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
const char *buf = "hello world";
unsigned long len = strlen(buf);
ssize_t ret = write(fd, buf, len);
if (ret < 0) {
printf("write() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
ret = lseek(fd, 0, SEEK_SET);
if (ret < 0) {
printf("lseek() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
char *newbuf = calloc(len + 1, 1);
ret = read(fd, newbuf, len);
if (ret < 0) {
printf("read() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
printf("read: %s\n", newbuf);
return 0;
}
On Linux the output is what I would expect:
$ cc main.c
$ ./a.out
read: hello world
On macOS I get this:
$ cc main.c
$ ./a.out
write() failed Device not configured (6)
Under Linux, the POSIX shared memory is typically backed by a tmpfs file system mounted on /dev/shm:
$ cat /proc/mounts | grep /dev/shm
tmpfs /dev/shm tmpfs rw,nosuid,nodev,inode64 0 0
The name passed to shm_open() is the name of the file entry corresponding to the shared memory area:
$ gcc main.c -lrt
$ ./a.out
read: hello world
$ ls -l /dev/shm
total 4
-rw------- 1 xxx xxx 11 sept. 17 08:53 example
The above file system is typically mounted at startup through /etc/fstab or by systemd.
Under MacOS, this manual says that there is no visible entry in the file system for the shared memory segments:
There is no visible entry in the file system for the created object in this implementation.
So, the low level implementation is different than the one in Linux. You may only be able to access the shared memory by adding a call to ftruncate() to set the size of the memory segment and use mmap() to map the content into the process address space as it is the way we normally use the shared memory. Any process wanting to access the area, will do the same except that only one should specify O_CREAT to shm_open() and call ftruncate() to create/resize the object:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char * argv[]) {
int flags = O_RDWR;
// Pass some parameter to trigger the creation of the object
if (argc > 1) {
flags |= O_CREAT;
}
int fd = shm_open("/example", flags, S_IRUSR|S_IWUSR);
if (fd < 0) {
printf("shm_open() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
const char *buf = "hello world";
unsigned long len = strlen(buf);
if (argc > 1) {
ssize_t ret = ftruncate(fd, len + 1);
if (ret < 0) {
printf("ftruncate() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
}
char *newbuf = (char *)mmap(NULL, len + 1, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (newbuf == MAP_FAILED) {
printf("mmap() failed %s (%d)\n", strerror(errno), errno);
return 1;
}
if (argc > 1) {
memcpy(newbuf, buf, len + 1);
}
printf("read: %s\n", newbuf);
return 0;
}
Example of execution under Linux:
$ gcc main.c -lrt
$ ls -l /dev/shm
total 0
$ ./a.out
shm_open() failed No such file or directory (2)
$ ls -l /dev/shm
total 0
$ ./a.out creat
read: hello world
$ ls -l /dev/shm
total 4
-rw------- 1 xxx xxx 12 sept. 17 09:36 example
$ ./a.out
read: hello world
Additional information
MacOS is derived from BSD. The manual of the latter clearly specifies that operations like read() or write() on the resulting file descriptor return in error:
The result of using open(2), read(2), or write(2) on a shared memory object, or on the descriptor returned by shm_open(), is undefined. It is also undefined whether the shared memory object itself, or its contents, persist across reboots.
In FreeBSD, read(2) and write(2) on a shared memory object will fail with EOPNOTSUPP and neither shared memory objects nor their contents persist across reboots.
Related
I have built a IPC prgram with FIFO(named piped).
A very interesting problem is that my written message may be lost.
the following is the code snippet.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
char buffer[2000] = {0};
strcpy(buffer, "abc");
char *write_path = "test-123";
mkfifo(write_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int dummy = open(write_path, O_RDONLY | O_NONBLOCK);
int fd = open(write_path, O_WRONLY | O_NONBLOCK);
int bytes = write(fd, buffer, 2000);
printf("write_path:%d %s %d %d\n", bytes, write_path, dummy, fd);
close(fd);
close(dummy);
}
how to reproduce?
ubuntu 1804
gcc main.c -o main
./main
cat < test-123
it will be pending. I think, it should output abc.
You open the FIFO in nonblocking mode, which in means I/O functions can fail with (-1 and errno set to) EAGAIN or EWOULDBLOCK, instead of blocking (waiting) for the function to complete. In Linux, open descriptors to a FIFO have the same semantics as pipes.
If you bothered to check for errors (printing strerror(errno) whenever open() or write() returns -1 indicating an error), you'd already know the reason why this fails. As is, the code shown does not even reproduce the problem. The following code,
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char *msg = "The Example Message.\n";
const size_t msg_len = strlen(msg);
int rfd, wfd;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *mypath = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", mypath);
fprintf(stderr, " %s FIFO\n", mypath);
fprintf(stderr, "\n");
fprintf(stderr, "This program tests opening and writing a short message to the FIFO.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
rfd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (rfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for reading: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
wfd = open(argv[1], O_WRONLY | O_NONBLOCK);
if (wfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for writing: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
ssize_t n = write(wfd, msg, msg_len);
if (n == -1) {
fprintf(stderr, "%s: Cannot write to FIFO: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
} else
if (n != (ssize_t)msg_len) {
fprintf(stderr, "%s: Wrote only %zd of %zu bytes to the FIFO.\n", argv[1], n, msg_len);
return EXIT_FAILURE;
} else {
printf("Success!\n");
}
close(wfd);
close(rfd);
return EXIT_SUCCESS;
}
compiles (gcc -Wall -Wextra -O2 source.c -o binary) cleanly, and implements the shown code (but with error checking, and using a command line parameter for the name of the FIFO to be opened). It does not verify the named file is a FIFO, though. If run on a non-existent FIFO, it complains "FIFO: Cannot open FIFO for reading: No such file or directory."
If you create the FIFO (mkfifo test-fifo) and run the test program, there are no errors; the only output is "Success!".
If you create the FIFO, and repeatedly write data to it but never read from it, at some point the kernel buffer for the FIFO becomes full, and running the test program will report "test-fifo: Cannot write to FIFO: Resource temporarily unavailable." (which means that write() returned -1 with errno==EWOULDBLOCK or errno==EAGAIN).
You can simulate this by running e.g. bash -c 'exec 4<>test-fifo ; dd if=/dev/zero of=test-fifo bs=1 oflag=nonblock status=progress', which opens the test-fifo FIFO read-write (which in Linux always succeeds) to descriptor 4, then runs dd to fill it with zeroes, using Bash (sub-)shell. On my system, a FIFO can hold 65536 bytes (64k). After filling the FIFO like this, running the test program (./binary test-fifo) will fail as described.
Hopefully, you'll see the light and the importance and usefulness of error checking. It is NOT something you should consider as "I'll add them in later when/if I have time"; they are also an important development tool.
My code is
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char parent[] = "parent";
char child[] = "child";
char *shmem = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
char *shmem_child = "NOT CHANGE";
memcpy(shmem, parent, sizeof(parent));
int pid = fork();
if (pid == 0) {
char child_new[] = "new child";
printf("Child read: %s\n", shmem);
memcpy(shmem, child, sizeof(child));
printf("Child wrote: %s\n", shmem);
shmem_child = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memcpy(shmem_child, child_new, sizeof(child_new));
printf("Child create: %s\n", shmem_child);
} else {
printf("Parent read: %s\n", shmem);
sleep(1);
printf("After 1s, parent read: %s\n", shmem);
printf("After 1s, parent read shmem_child: %s\n", shmem_child);
}
}
And the output is
Parent read: parent
Child read: parent
Child wrote: child
Child create: new child
After 1s, parent read: child
After 1s, parent read shmem_child: NOT CHANGE
As you can see, the shared memory(shmem) created before fork works, but the shared memory(shmem_child) created inside child does not work.
Am I doing something wrong? How can I create shared memory inside child so that parent and even brothers(other children of same parent) can access?
Anonymous shared memory stays shared across a fork().
So, both the parent and the child(ren) should access the same memory area, shmem.
You cannot create anonymous shared memory in a child process, and have it magically appear in the parent process. Anonymous shared memory must be created in the parent; then it will be accessible to all children.
You can create non-anonymous shared memory, via e.g. shm_open(). The creator ftruncate()s it to appropriate length, and all processes memory-map the descriptor. You do need to remember to remove the shared memory when no longer needed, via shm_unlink().
Here is a simple (tested and verified) example of anonymous shared memory between a parent and a child:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
char message[256];
} shared_mem;
static size_t page_aligned(const size_t size)
{
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
}
int child_process(shared_mem *shared)
{
printf("Child: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
snprintf(shared->message, sizeof shared->message, "Child");
printf("Child: changed shared memory to \"%s\".\n", shared->message);
fflush(stdout);
return EXIT_SUCCESS;
}
int main(void)
{
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
pid_t child, p;
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, (off_t)0);
if (shared == MAP_FAILED) {
fprintf(stderr, "Cannot map %zu bytes of shared memory: %s.\n", size, strerror(errno));
return EXIT_FAILURE;
}
snprintf(shared->message, sizeof shared->message, "Parent");
printf("Parent: set shared memory to \"%s\".\n", shared->message);
fflush(stdout);
/* Create the child process. */
child = fork();
if (!child) {
/* This is the child process. */
return child_process(shared);
} else
if (child == -1) {
fprintf(stderr, "Cannot create a child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* This is the parent process. */
/* Wait for the child to exit. */
do {
p = waitpid(child, NULL, 0);
} while (p == -1 && errno == EINTR);
if (p == -1) {
fprintf(stderr, "Cannot reap child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Describe the shared memory, */
printf("Parent: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
/* and tear it down. Done. */
munmap(shared, size);
return EXIT_SUCCESS;
}
Save it as e.g. example.c, then compile and run it via e.g.
gcc -Wall -Wextra -O2 example1.c -o ex1
./ex1
It will output
Parent: set shared memory to "Parent".
Child: shared memory contains "Parent".
Child: changed shared memory to "Child".
Parent: shared memory contains "Child".
showing that this indeed works.
To create shared memory after fork(), or between unrelated processes, all processes have to agree on the name. For POSIX shared memory objects (that you obtain a descriptor to using shm_open(), the name must start with a slash.
Note that I used mode 0600, which corresponds to (decimal 384) -rw-------, i.e. only accessible to processes running as the same user.
Consider the following example:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
typedef struct {
pid_t changer;
time_t when;
char message[256];
} shared_mem;
static size_t page_aligned(const size_t size)
{
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
}
enum {
ACTION_NONE = 0,
ACTION_CREATE = (1<<0),
ACTION_REMOVE = (1<<1),
ACTION_MODIFY = (1<<2),
};
int main(int argc, char *argv[])
{
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
const char *name;
time_t now;
const char *message = NULL;
int action = ACTION_NONE;
int arg, shm_fd;
if (argc < 2 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s /NAME [ +CREATE ] [ MESSAGE ] [ -REMOVE ]\n", argv0);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
/* Grab and check name */
name = argv[1];
if (name[0] != '/' || name[1] == '\0') {
fprintf(stderr, "%s: Shared memory name must begin with a slash.\n", argv[1]);
return EXIT_FAILURE;
}
/* Check other command-line parameters. */
for (arg = 2; arg < argc; arg++) {
if (argv[arg][0] == '+') {
action |= ACTION_CREATE;
if (argv[arg][1] != '\0') {
message = argv[arg] + 1;
action |= ACTION_MODIFY;
}
} else
if (argv[arg][0] == '-') {
action |= ACTION_REMOVE;
if (argv[arg][1] != '\0') {
message = argv[arg] + 1;
action |= ACTION_MODIFY;
}
} else
if (argv[arg][0] != '\0') {
if (message) {
fprintf(stderr, "%s: Can only set one message (already setting '%s').\n", argv[arg], message);
return EXIT_FAILURE;
}
message = argv[arg];
action |= ACTION_MODIFY;
}
}
if (action & ACTION_CREATE) {
/* Create the shared memory object. */
shm_fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (shm_fd == -1) {
fprintf(stderr, "%s: Cannot create shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
}
/* Resize it to desired size. */
if (ftruncate(shm_fd, (off_t)size) == -1) {
fprintf(stderr, "%s: Cannot resize shared memory object to %zu bytes: %s.\n", name, size, strerror(errno));
close(shm_fd);
shm_unlink(name);
return EXIT_FAILURE;
}
} else {
/* Open an existing shared memory object. */
shm_fd = shm_open(name, O_RDWR, 0600);
if (shm_fd == -1) {
fprintf(stderr, "%s: Cannot open shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
}
}
/* Map the shared memory object. */
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, shm_fd, (off_t)0);
if (shared == MAP_FAILED) {
fprintf(stderr, "%s: Cannot map %zu bytes of shared memory object: %s.\n", name, size, strerror(errno));
close(shm_fd);
if (action & (ACTION_CREATE | ACTION_REMOVE))
shm_unlink(name);
return EXIT_FAILURE;
}
/* The shared memory object descriptor is no longer needed. */
if (close(shm_fd) == -1) {
fprintf(stderr, "Warning: Error closing shared memory object: %s.\n", strerror(errno));
}
/* Current time in UTC */
now = time(NULL);
/* If we created it, we need to initialize it too. */
if (action & ACTION_CREATE) {
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "Initialized");
}
/* Show contents. */
printf("Shared memory was last changed %ld seconds ago by process %ld to '%s'.\n",
(long)(now - shared->when), (long)(shared->changer), shared->message);
fflush(stdout);
/* Modify contents. */
if (action & ACTION_MODIFY) {
printf("Changing shared memory contents into '%s'.\n", message);
fflush(stdout);
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "%s", message);
}
/* Unmap shared memory object. */
munmap(shared, size);
/* Remove shared memory? */
if (action & ACTION_REMOVE) {
if (shm_unlink(name) == -1) {
fprintf(stderr, "Warning: %s: Cannot remove shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
} else {
printf("%s: Shared memory object removed successfully.\n", name);
fflush(stdout);
}
}
/* All done. */
return EXIT_SUCCESS;
}
Save it as e.g. example2.c, and compile it using e.g.
gcc -Wall -Wextra -O2 example2.c -lrt -o ex2
Open up multiple windows. In one, run
./ex2 /myshared +
to create the shared memory; and in others, run
./ex2 /myshared newmessage
When you are done, remember to remove the shared memory object using
./ex2 /myshared -
I am trying to figure out how to correctly use tee. In my application, tee always returns EINVAL for some reason. I was getting desperate, and tried to run the example application listed in the man page of tee (for example: https://linux.die.net/man/2/tee), only to find out that even that example code always fails with: tee: Invalid argument, for example when using it as follows: cat tee.c | ./tee tee.log. Any idea why this might happen?
The example code from die.net:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
int
main(int argc, char *argv[])
{
int fd;
int len, slen;
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
/*
* tee stdin to stdout.
*/
len = tee(STDIN_FILENO, STDOUT_FILENO,
INT_MAX, SPLICE_F_NONBLOCK);
if (len < 0) {
if (errno == EAGAIN)
continue;
perror("tee");
exit(EXIT_FAILURE);
} else
if (len == 0)
break;
/*
* Consume stdin by splicing it to a file.
*/
while (len > 0) {
slen = splice(STDIN_FILENO, NULL, fd, NULL,
len, SPLICE_F_MOVE);
if (slen < 0) {
perror("splice");
break;
}
len -= slen;
}
} while (1);
close(fd);
exit(EXIT_SUCCESS);
}
tee requires a pipe for both file descriptors, fd_in and fd_out.
Your invocation does not supply a pipe for the second file descriptor, but a file descriptor referring to a TTY. Note also that the example in the manpage specifically uses a trailing | cat:
The example below implements a basic tee(1) program using the tee() system call.
Here is an example of its use:
$ date |./a.out out.log | cat
Tue Oct 28 10:06:00 CET 2014
$ cat out.log
Tue Oct 28 10:06:00 CET 2014
Not using a pipe file descriptor for the second (or first, for that matter) argument would qualify for EINVAL:
EINVAL fd_in or fd_out does not refer to a pipe; or fd_in and fd_out refer to
the same pipe.
I want to create a file and the file has no write permission like this.
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
main()
{
int fd;
fd=open("xxx",O_CREAT|O_WRONLY|O_APPEND,S_IRUSR);
if(fd==-1)
return 0;
else
if(write(fd,"created",7)==-1)
return 0;
if(close(fd)==-1)
return 0;
printf("OK");
}
I ran executable file and notice that the file xxx could be written to. Of cource, when I ran executable after first time, it worked like I expect when it could't be written to.
But how can I make the file xxx even can't be written to in first time (when it is created).
You should remove:
O_WRONLY
And it should look like this:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void){
int fd;
fd=open("test.sh",O_CREAT|O_APPEND,S_IRUSR);
if(fd==-1)
return 0;
else
if(write(fd,"created",7)==-1)
return 0;
if(close(fd)==-1)
return 0;
printf("OK");
return 0;
}
And if you try to write that file, you will get:
[ Error writing test.sh: Permission denied ]
Anyway, on my Linux machine if I wanna check that file, I will do it like this:
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
void fileCheck(const char *fileName){
if(!access(fileName, F_OK )){
printf("The File %s\t Found\n",fileName);
}else{
printf("The File %s\t not Found\n",fileName);
exit(1);
}
if(!access(fileName, R_OK )){
printf("The File %s\t can be read\n",fileName);
}else{
printf("The File %s\t cannot be read\n",fileName);
}
if(!access( fileName, W_OK )){
printf("The File %s\t it can be Edited\n",fileName);
}else{
printf("The File %s\t it cannot be Edited\n",fileName);
}
if(!access( fileName, X_OK )){
printf("The File %s\t is an Executable\n",fileName);
}else{
printf("The File %s\t is not an Executable\n",fileName);
}
}
int main (void){
char *fileName = "test.sh";
fileCheck(fileName);
return 0;
}
And the output says:
The File test.sh Found
The File test.sh can be read
The File test.sh it cannot be Edited
The File test.sh is not an Executable
I can understand author's intent. He want to test the usage of S_IRUSR. I will explain why it doesn't work at first time.
At first let us resolve your question. you can just remove O_WRONLY and O_APPEND flag, and add O_RDONLY flag or not. Codes are following:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main() {
int fd;
fd = open("xxx", O_CREAT|O_RDONLY, S_IRUSR);
// or
// fd = open("xxx", O_CREAT, S_IRUSR);
if (fd == -1) {
printf("open failed\n");
return 0;
} else {
if (write(fd, "created", 7) == -1) {
printf("write failed\n");
return 0;
}
}
if (close(fd) == -1) {
printf("close failed\n");
return 0;
}
printf("OK\n");
}
then execute the program whenever, it will always says "write failed", and "xxx" is empty.
$ ls
test.c
$ make test
cc test.c -o test
$ ls
test test.c
$ ./test
write failed
$ ls
test test.c xxx
$ cat xxx
$ ./test
write failed
you said:
Of cource, when I ran executable after first time, it worked like I expect when it could't be written to.
in fact, if use your code, ran after first time, it is not couldn't be written to, it is not be open.
$ ls
test test.c
$ ./test
OK
$ ls
test test.c xxx
$ cat xxx
created
$ ./test
open failed
Then let us face the more important question, why S_ISUSR doens't work at first time. let us run 'man 2 open', it says:
Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only file may well return a read/write file descriptor.
It means S_ISUSR only works after first time. Even S_ISUSR is read only, but in fact still return read/write permission, the final permission depends on the flags, if the flag is O_WRONLY, it can only write when create, if the flag is O_RDONLY, it can only read when create.
let's do a test:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main() {
int fd;
char buf[10];
fd = open("xxx", O_CREAT|O_WRONLY, S_IRUSR);
if (fd == -1) {
printf("open failed\n");
return 0;
} else {
if (write(fd, "created", 7) == -1) {
printf("write failed\n");
return 0;
}
}
if (read(fd, buf, 7) == -1) {
printf("read failed\n");
return 0;
} else {
printf(buf);
}
if (close(fd) == -1) {
printf("close failed\n");
return 0;
}
printf("OK\n");
}
and result is expected, at first time, when file create, if has write permission and has no read permission, after first time, it open failed:
$ ls
test.c
$ make test
cc test.c -o test
$ ./test
read failed
$ ./test
open failed
$ cat xxx
created
And I can understand why they design the mode parameter only work after first time. You think, if S_IRUSR works when file create, then the file is always empty and read only, it has no sense.
I have two programs to write and read a FIFO. One is read(O_RDONLY) a FIFO. Another is write data into this FIFO. This is code:
Read: The executable file is named read.
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
int main(int argc, char *argv[])
{
int fd, nread;
const char *pipe_file = "/tmp/test_fifo";
char buf[0x100] ;
fd = open(pipe_file, O_RDONLY);
while(1) {
printf("\n"); /* */
memset(buf, 0, 100);
nread = read(fd, buf, 0x100);
printf("data is %s", buf);
sleep(1);
}
return 0;
}
Write: The executable file is named write.
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
int main(int argc, char *argv[])
{
int fd;
int num = 1234;
char *s = "changzhi";
const char *pipe_file = "/tmp/test_fifo";
fd = open(pipe_file, O_WRONLY);
write(fd, s, strlen(s));
return 0;
}
The FIFO named /tmp/test_fifo is already exists. When I run read to read FIFO and run write to write the FIFO, everything goes ok. But, the read can not read data when there is no printf("\n"); in read code. I don not know why. Could someone help me ? Thanks a lot!
Your second printf() is buffered until the \n is written. The read is not blocked :-)
Rewrite your loop:
while(1) {
memset(buf, 0, 100);
nread = read(fd, buf, 0x100);
printf("data is %s\n", buf);
sleep(1);
}
With cleanly compiling code under stringent compilation options:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror write.c -o write
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror read.c -o read
$
the code basically works for me. I've added error checking. The slightly odd argv[argc-argc] prints argv[0] but avoids a warning (error because of -Werror) about argc being unused.
read.c
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd, nread;
const char *pipe_file = "/tmp/test_fifo";
char buf[0x100];
if ((fd = open(pipe_file, O_RDONLY)) < 0)
{
fprintf(stderr, "%s: Failed to open FIFO %s\n", argv[argc-argc], pipe_file);
return 1;
}
printf("%s: open successful\n", argv[0]);
while(1) {
printf("\n"); /* */
memset(buf, 0, 100);
nread = read(fd, buf, 0x100-1);
if (nread <= 0)
break;
printf("data is %s\n", buf);
sleep(1);
}
return 0;
}
write.c
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char *s = "changzhi";
const char *pipe_file = "/tmp/test_fifo";
if ((fd = open(pipe_file, O_WRONLY)) < 0)
{
fprintf(stderr, "%s: failed to open FIFO %s\n", argv[argc-argc], pipe_file);
return 1;
}
printf("%s: open successful\n", argv[0]);
if (write(fd, s, strlen(s)) <= 0)
{
fprintf(stderr, "%s: failed to open FIFO %s\n", argv[argc-argc], pipe_file);
return 1;
}
printf("%s: write successful\n", argv[0]);
return 0;
}
Sample output
$ ./read & ./write
[1] 85542
./write: open successful
./write: write successful
./read: open successful
data is changzhi
$
[1]+ Done ./read
$ ./write & ./read
[1] 85544
./write: open successful
./read: open successful
./write: write successful
data is changzhi
[1]+ Done ./write
$
The programs report an error sensibly if the FIFO does not exist.