Let me share quite interesting observation.

Environment

  • clang++: aarch64-linux-android34-clang++ of android NDK.
  • Run on android device

Think about following project

a.cpp

namespace {
    std::map<std::string, int> m0_;
}
static std::map<std::string, int> m1_;

void noticeFromConstructor(const char *name, int value) {
    static std::map<std::string, int> m2_;
    m0_[name]  = value; // (*1)
    m1_[name]  = value; // (*2)
    m2_[name]  = value; // (*3)
}

b.cpp

namespace {
__attribute__((constructor)) void notify() {
    noticeFromConstructor("b", 1);
}

c.cpp

__attribute__((constructor)) static void notify() {
    noticeFromConstructor("c", 2);
}

SIGSEGV is signalled at both (1) and (2) when it run - from both b.cpp and c.cpp - but, (*3) is ok. I think it's because m0_ and m1_ are not initialized yet, but m2_ is initialized.
I'm not sure that there is any explicit description regarding initialization order for this case.

'Language > C&C++' 카테고리의 다른 글

[C/C++] Small tip - Argument passed to main() function.  (0) 2015.10.19
[C/C++] Template with value  (0) 2015.04.28
[C/C++] sizeof array  (0) 2015.03.19
[Macro] '##' macro operator  (0) 2015.02.11
'malloc' and 'Segmentation fault'  (0) 2014.07.25

In general, main function is used as below.


int main(int argc, const char *argv[]) {

}


But, it can be expanded like below


int main(int argc, const char *argv[], const char **envp) {

        while (*envp) {

                printf("%s\n", *envp++);

        }

}


By this way, we can use environment variables easily.

=> This matches parameters of execle() and execvpe() at Linux.



'Language > C&C++' 카테고리의 다른 글

[C/C++] __attribute__((constructor)) and variable initialization in C++.  (0) 2024.01.23
[C/C++] Template with value  (0) 2015.04.28
[C/C++] sizeof array  (0) 2015.03.19
[Macro] '##' macro operator  (0) 2015.02.11
'malloc' and 'Segmentation fault'  (0) 2014.07.25

#include <stdio.h>


template<int N> class A {

public:

        A() { a = N;}

        int a;

};


int

main() {

        A<10> *a = new A<10>();

        printf("%d\n", a->a);

        return 0;

}


type이 아니라, 변수 값을 전달할 수도 있다. 이 경우 장점은?

compile time에 값이 정해지므로 compiler가 code를 optimize할 수 있다.



예.


[ a. cpp ]

#include <stdio.h>


template<int N> class A {

public:

        A(int n) { mN = n; }

        void speak() {

                if (N > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

        }

        int mN;

};


int

main() {

        A<10> *a = new A<10>(10);

        A<2> *b = new A<2>(2);

        a->speak();

        b->speak();

        return 0;

}


<< objdump >>

...

        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (N > 5)

                        printf("I'm BIG\n");

   c: bf 00 00 00 00       mov    $0x0,%edi

  11: e8 00 00 00 00       callq  16 <_ZN1AILi10EE5speakEv+0x16>

                else

                        printf("I'm SMALL\n");

        }

  16: c9                   leaveq 

  17: c3                   retq   


...


        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (N > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

   c: bf 00 00 00 00       mov    $0x0,%edi

  11: e8 00 00 00 00       callq  16 <_ZN1AILi2EE5speakEv+0x16>

        }

  16: c9                   leaveq 

  17: c3                   retq   


...






[ b. cpp ]

#include <stdio.h>


template<int N> class A {

public:

        A(int n) { mN = n; }

        void speak() {

                if (mN > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

        }

        int mN;

};


int

main() {

        A<10> *a = new A<10>(10);

        A<2> *b = new A<2>(2);

        a->speak();

        b->speak();

        return 0;

}



<< objdump >>

...

        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (mN > 5)

   c: 48 8b 45 f8           mov    -0x8(%rbp),%rax

  10: 8b 00                 mov    (%rax),%eax

  12: 83 f8 05             cmp    $0x5,%eax

  15: 7e 0c                 jle    23 <_ZN1AILi10EE5speakEv+0x23>

                        printf("I'm BIG\n");

  17: bf 00 00 00 00       mov    $0x0,%edi

  1c: e8 00 00 00 00       callq  21 <_ZN1AILi10EE5speakEv+0x21>

  21: eb 0a                 jmp    2d <_ZN1AILi10EE5speakEv+0x2d>

                else

                        printf("I'm SMALL\n");

  23: bf 00 00 00 00       mov    $0x0,%edi

  28: e8 00 00 00 00       callq  2d <_ZN1AILi10EE5speakEv+0x2d>

        }

  2d: c9                   leaveq 

  2e: c3                   retq   


...


        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (mN > 5)

   c: 48 8b 45 f8           mov    -0x8(%rbp),%rax

  10: 8b 00                 mov    (%rax),%eax

  12: 83 f8 05             cmp    $0x5,%eax

  15: 7e 0c                 jle    23 <_ZN1AILi2EE5speakEv+0x23>

                        printf("I'm BIG\n");

  17: bf 00 00 00 00       mov    $0x0,%edi

  1c: e8 00 00 00 00       callq  21 <_ZN1AILi2EE5speakEv+0x21>

  21: eb 0a                 jmp    2d <_ZN1AILi2EE5speakEv+0x2d>

                else

                        printf("I'm SMALL\n");

  23: bf 00 00 00 00       mov    $0x0,%edi

  28: e8 00 00 00 00       callq  2d <_ZN1AILi2EE5speakEv+0x2d>

        }

  2d: c9                   leaveq 

  2e: c3                   retq   

...




===============================================


위의 코드를 보면, template 의 value를 사용할 경우, 'if'문에서, 사용되지 않는 코드가 compile시 object에 반영되지 않고 삭제되어 버리는 것을 알 수 있다.

즉 compile time에 충분히 optimize되어 버린다. (if 문의 condition이 compile time에 결정될 수 있으므로...)

[[ Tested on GCC 4.x ]]


static void

test01_00(char buf[TEST01_SZ]) {

        printf("In func: %lu\n", sizeof(buf));

}


static void

test01(void) {

        char buf[TEST01_SZ];

        printf("In local: %lu\n", sizeof(buf));

        test01_00(buf);

}


int

main(int argc, const char *argv[]) {

        test01();

        return 0;

}


<< Run on 64bit linux >>

$ ./a.out

In local: 16   <== array size

In func: 8     <== pointer size


'sizeof(<array>)' works as expected in the scope where it is declared. But, array that is declared as parameter, is regarded same as 'pointer' (as you can see above).

I don't think this is defined in C specification.

So, I think it is just one of special characteristics of GCC.

Is it interesting,isn't it?


즉 array를 선언한 곳에서 sizeof(<array>)는 기대한 대로 동작하는데, argument로 넘긴 array의 경우, pointer와 동일하게 취급하는 것을 알 수 있다.

개인적인 생각으로, 이것이, C spec 사항일 것 같지는 않다.

그러니, 그냥 GCC 특성 중 하나라고 생각한다....

꽤나 재미있는 내용이라, 일단 적어 둔다. ^_^

'Language > C&C++' 카테고리의 다른 글

[C/C++] Small tip - Argument passed to main() function.  (0) 2015.10.19
[C/C++] Template with value  (0) 2015.04.28
[Macro] '##' macro operator  (0) 2015.02.11
'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25

* '##' macro operator (improved)


<token> ## <token>

: The ## operator takes two separate tokens and paste them together to form a single token.


즉:


#define test(x) aa ## _x

#define test(x) aa##_x


위의 두가지 모두 결과는 'aa_x' 로 같다.

('##' 사용시 중간에 공백은 token delimiter 의 역할을 하게 된다.)

'Language > C&C++' 카테고리의 다른 글

[C/C++] Template with value  (0) 2015.04.28
[C/C++] sizeof array  (0) 2015.03.19
'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25
[Linux] Using named pipe in linux (주의할 점)  (0) 2014.01.09

'Segmentation fault' is very familiar error for C developer.

Then, when this runtime error is issued?

At the moment of accessing not-allocated-memory?

This may be right, but this is NOT strict enough.

'Segmentation fault' is issued by kernel through signal to notify that,

"You - user process - try to access not-permitted memory area. And usually, this happens when it tries to access not-allocated-memory - for the user process (In case of ARM architecture, it is triggered by Data Abort cpu exception).


Let's see below code.

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, const char *argv[]) {
        int i = 0;
        char *s = malloc(1);
        s += 1;
        for (i = 0; i < 1024 * 1024 * 1024; i++) {
                printf("%d\n", i);
                s[i] = 4;
        }
        return 0;
}

Do you think when this process is stopped by 'Segmentation fault' signal?

"i == 0" ? (that is at the first moment of trying to access not-allocated-memory-by-malloc).


It, is totally up to malloc implementation.

But, at above case, this is definitely NO.

The reason is, kernel(linux) allocate memory for user process at least 1-page.

So, above code will run even if i > 1024 especially on the Linux machine.


And, there are various implementation(algorithm) of malloc.

So, we cannot expect exact value of i for above code.

Important thing to know here is, answer the question - "What exactly does 'Segmentation fault' mean?"


'Language > C&C++' 카테고리의 다른 글

[C/C++] sizeof array  (0) 2015.03.19
[Macro] '##' macro operator  (0) 2015.02.11
[GCC] Initialize array...  (0) 2014.07.25
[Linux] Using named pipe in linux (주의할 점)  (0) 2014.01.09
[c/c++] file copy using 'sendfile' on linux.  (0) 2013.12.17

See https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Designated-Inits.html


And here is one more tips.

Sometimes, you may want to initialize all array element with same value.

Then, you can do it as follows if you are using GCC.


static const int arr[10] = {[0 ... (sizeof(arr)/sizeof(arr[0]) - 1)] = 10};



일단 man page를 유심히 살펴보자.

FIFO(7)                             Linux Programmer's Manual                            FIFO(7)

NAME
       fifo - first-in first-out special file, named pipe

DESCRIPTION
       A  FIFO  special  file (a named pipe) is similar to a pipe, except that it is accessed as
       part of the file system.  It can be opened by multiple processes for reading or  writing.
       When  processes  are  exchanging data via the FIFO, the kernel passes all data internally
       without writing it to the file system.  Thus, the FIFO special file has  no  contents  on
       the  file  system;  the file system entry merely serves as a reference point so that pro‐
       cesses can access the pipe using a name in the file system.

       The kernel maintains exactly one pipe object for each FIFO special file that is opened by
       at  least one process.  The FIFO must be opened on both ends (reading and writing) before
       data can be passed.  Normally, opening the FIFO blocks until  the  other  end  is  opened
       also.

       A  process can open a FIFO in nonblocking mode.  In this case, opening for read only will
       succeed even if no-one has opened on the write side yet, opening for write only will fail
       with ENXIO (no such device or address) unless the other end has already been opened.

       Under  Linux,  opening  a  FIFO for read and write will succeed both in blocking and non‐
       blocking mode.  POSIX leaves this behavior undefined.  This can be used to  open  a  FIFO
       for  writing  while there are no readers available.  A process that uses both ends of the
       connection in order to communicate with itself should be very careful to avoid deadlocks.

NOTES
       When a process tries to write to a FIFO that is not opened for read on  the  other  side,
       the process is sent a SIGPIPE signal.

       FIFO  special files can be created by mkfifo(3), and are indicated by ls -l with the file
       type 'p'.

SEE ALSO
       mkfifo(1), open(2), pipe(2), sigaction(2), signal(2), socketpair(2), mkfifo(3), pipe(7)

COLOPHON
       This page is part of release 3.35 of the Linux man-pages project.  A description  of  the
       project, and information about reporting bugs, can be found at http://man7.org/linux/man-
       pages/.

Linux                                      2008-12-03                                    FIFO(7)

주의할 사항은 위에서, Bold로 표시해 두었다.


먼저 "read/write 양쪽이 열리기 전까지는 open이 block된다."는 말이 무슨 말일까?

아래 코드를 살펴보자.

int main()
{
	char msg[64];
	int fd;
	int rd;

	if (-1 == mkfifo("./fifo",0666))
		return 1;

	if (-1 == (fd = open("./fifo", O_RDWR))) // <--- (*A)
		return -1;
	printf("Before loop\n"); // <--- (*a)
	for (;;) {
		if (-1 == (rd = read(fd, msg, sizeof(msg))))
			return 1;
	}
	unlink("./fifo");
	return 0;
}

위의 코드는 실행시키면 바로 , (*a)가 수행된다.

그렇지만, (*A)의 open mode를 O_WRONLY 나 O_RDONLY 로 하면, pipe의 다른 한쪽 (read 혹은 write)가 열리기 전까지는 (*a)가 수행되지 않는다 (open에서 block된 상태) 는 말이다.



그리고, "read only의 경우 nonblocking mode로 open이 가능하다고 wirte는 안된다." 는 어떤식 문제를 가져 오는가?

앞서 이야기한 것처럼, 위의 코드에서, (*A)를 O_RDONLY로 하면, write pipe가 열리기 전까지는 (*a)가 수행되지 않는다.(block된 상태).

그렇지만, "$ echo hello > ./fifo" 같은 명령을 통해 write side를 일단 열개 되면, loop로 들어가게 되는데,

문제는, read가 NONBLOCKING으로 동작한다는 것이다.

즉, 더 이상 읽을 것이 없음에도 불구하고, block이 되지 않고 busy loop를 돌게 된다.
하지만, 위의 예시처럼, O_RDWR로 열게 되면, 더 이상 읽을게 없을 경우, read 에서 block 되어, 일반적으로 기대하는 방식으로 동작하게 된다.


추가적으로, File이란, System-wide한 resource이다.

따라서, 두 개 이상의 Process가 하나의 Pipe에 대해 동시에 read를 하고 있다면, 먼저 읽는 Process가 Pipe의 내용을 읽어가게 되고, 다른  Process들은 내용을 읽지 못한다.


'Language > C&C++' 카테고리의 다른 글

'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25
[c/c++] file copy using 'sendfile' on linux.  (0) 2013.12.17
일반 file에 대한 select/poll...  (0) 2013.11.14
Using allocated memory like 2-D array.  (0) 2013.07.10

Since Linux 2.6.33, sendfile supports regular file as output file.
That is, sendfile can be used as way for fast file copy.
(Faster than normal read -> write operation because it is done inside kernel! )
Please refer follow example code ...

...


#include <stdio.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int
main(int argc, const char *argv[]) {
	int ifd, ofd, r;
	struct stat st;

	r = stat(argv[2], &st);
	r |= (ofd = open(argv[1], O_WRONLY | O_CREAT));
	r |= (ifd = open(argv[2], O_RDONLY));
	if (r < 0)
		return 1;
	if (-1 == sendfile(ofd, ifd, 0, st.st_size))
		return 1;
	fchmod(ofd, 0664);
	return 0;
}


...

selectpollpipe등에 대해서 block하면서 event(읽을 수 있게 되는 상태)를 기다리기 위해서 많이 쓴다.

man page를 보면...

       select()  and pselect() allow a program to monitor multiple file descriptors, waiting until
       one or more of the file descriptors become "ready" for some class of I/O  operation  (e.g.,
       input  possible).   A  file descriptor is considered ready if it is possible to perform the
       corresponding I/O operation (e.g., read(2)) without blocking.

그렇지만 자칫 오해하면, 일반 파일에 대해서 새로 추가된 내용이 있는지 검사하고 새로 추가된 내용을 읽어들이기 위해서 selectpoll을 사용할 수 있을 거라고 기대할 수도 있다.

그렇지만, select/poll은 man page에 언급한 바처럼, "IO가 block되지 않고 IO를 행할 수 있는 상태"를 기다린다.

file descriptorread() operation을 통해서 EOF에 도달하더라도, read() operation은 blocking없이 계속해서 수행가능하다는 점 - 비록 계속 0 byte를 읽겠지만... - 을 생각해보면, 위와 같은 생각은 적합하지 않다는 것을 알 수 있다.

주의하자!


아래는 간단한 test code이다. 1초마다 계속해서 read() 가 정상 수행됨을 알 수 있다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>

int
main(int argc, const char *argv[]) {
	char buf[4096];
	fd_set rfds;
	struct timeval tv;
	int fd, rb, retval;

	if (-1 == (fd = open("tstf", O_RDONLY)))
		perror("open tstf");

	/* Wait up to five seconds. */
	tv.tv_sec = 60;
	tv.tv_usec = 0;

	while (1) {
		/* Watch stdin (fd 0) to see when it has input. */
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);

		retval = select(fd + 1, &rfds, NULL, NULL, &tv);
		/* Don't rely on the value of tv now! */

		if (retval == -1)
			perror("select()");
		else if (retval) {
			printf("Data is available now.\n");
			memset(buf, 0, sizeof(buf));
			if (-1 == (rb = read(fd, buf, sizeof(buf) - 1)))
				fprintf(stderr, "%s", strerror(errno));
			printf("%s", buf);
			if (rb < (sizeof(buf) - 1))
				printf("\n---------------------------\nReaches to EOF\n");

			/* FD_ISSET(0, &rfds) will be true. */
		} else
			printf("No data within 1 minutes.\n");
		sleep(1);
	}
	exit(EXIT_SUCCESS);
}

DONE.

+ Recent posts