gcc 基础用法

用法

gcc 文件名 选项

文件可以是多个, 选项也可以是多个。
通常linux下,我们建议使用vim来编写代码:vim 基础教程

hello.c

#include <stdio.h>
#define N 10
int main()
{
    printf("hello world, the number is %dn", N);
}

简单示例

预处理+编译+链接 一步到位

gcc hello.c

如果没有报错, 那么会默认生成一个 a.out 的可执行文件

# 执行这个可执行文件
./a.out
hello world

编译选项 -c

可以通过 gcc 去编译这个文件

gcc hello.c -c

如果没有出现报错, 那么默认清情况下会生成一个 hello.o
接下来我们可以去链接这个对象文件

链接选项 -o

gcc hello.o -o hello

-o 之后跟随的 hello 则指明了链接生成的可执行文件的名称

./hello
hello world

运行程序看到输出

预处理选项 -E

gcc hello.c -E

此时屏幕上会输出预处理之后的文件,N 已经被替换成了10
不过这个命令通常是导出文件来查看

gcc hello.c -E > log.txt

常用选项

-v 打印较多信息,显示编译器调用的程序。
-### 与 -v 类似,但选项被引号括住,并且不执行命令。
-o 预处理+编译+汇编+链接 –> 到可执行文件
-c 预处理+编译+汇编 –> 到对象文件
-S 预处理+编译 –> 到汇编代码
-E 预处理 –> 显示预处理后的文件
-C 告诉预处理器不要丢弃注释。配合’-E’使用。
-w 禁止显示所有警告信息。
-Wall 会提示更多的警告选项,建议编译时加此选项。
-O0 禁止编译器进行优化。默认为此项。
-O1 尝试优化编译时间和可执行文件大小。
-O2 会尝试几乎全部的优化功能,但不会进行“空间换时间”的优化方法。
-O3 在 -O2 的基础上再打开一些优化选项
-Os 优化生成文件大小。会打开 -O2 除了会增加文件大小的全部选项。

linux c 守护进程

守护进程,也即后台程序。不依赖于终端存在,即终端关闭进程依旧在后台默默的运行,我们可以通过ps命令查看计算机上的进程,其实守护进程还是很多的。

ps -ef

输出

UID        PID  PPID  C STIME TTY          TIME CMD
...       ....  ....  . .. .. ...      ..........................
root      3898  3876  0 01:23 ?        00:00:03 hald-addon-storage: polling /dev
root      3919     1  0 01:23 ?        00:00:00 /usr/bin/hidd --server
root      3949     1  0 01:23 ?        00:00:00 automount
root      3971     1  0 01:23 ?        00:00:00 ./hpiod
root      3976     1  0 01:23 ?        00:00:00 python ./hpssd.py
root      4017     1  0 01:23 ?        00:00:00 /usr/sbin/sshd
root      4044     1  0 01:23 ?        00:00:00 xinetd -stayalive -pidfile /var/
root      4067     1  0 01:23 ?        00:00:00 sendmail: accepting connections
smmsp     4075     1  0 01:23 ?        00:00:00 sendmail: Queue runner@01:00:00
root      4090     1  0 01:23 ?        00:00:00 gpm -m /dev/input/mice -t exps2
root      4104     1  0 01:23 ?        00:00:00 crond
xfs       4142     1  0 01:23 ?        00:00:00 xfs -droppriv -daemon
root      4169     1  0 01:23 ?        00:00:00 /usr/sbin/atd
avahi     4200     1  0 01:23 ?        00:00:00 avahi-daemon: running [localhost
avahi     4201  4200  0 01:23 ?        00:00:00 avahi-daemon: chroot helper
root      4232     1  0 01:23 ?        00:00:00 /usr/sbin/smartd -q never
root      4236     1  0 01:23 ?        00:00:00 login -- root
root      4237     1  0 01:23 tty2     00:00:00 /sbin/mingetty tty2
root      4241     1  0 01:23 tty3     00:00:00 /sbin/mingetty tty3
root      4242     1  0 01:23 tty4     00:00:00 /sbin/mingetty tty4
root      4243     1  0 01:23 tty5     00:00:00 /sbin/mingetty tty5
root      4261     1  0 01:23 tty6     00:00:00 /sbin/mingetty tty6
root      4292     1  0 01:23 ?        00:00:00 /usr/bin/python -tt /usr/sbin/yu
root      4294     1  0 01:23 ?        00:00:00 /usr/libexec/gam_server
root      4388  4236  0 01:26 tty1     00:00:00 -bash
root      4474  4017  0 01:27 ?        00:00:01 sshd: root@pts/0
root      4509  4474  0 01:28 pts/0    00:00:00 -bash
root      4899  4017  0 01:37 ?        00:00:00 sshd: root@pts/1
root      4914  4899  0 01:37 pts/1    00:00:00 -bash

那么 TTY 终端为 ? 问号程序就是守护进程, 而其他的, 比如 tty2 这是一个终端的名称, 如果使用这个终端登录的用户退出, 那么所有这个终端下的程序都会结束掉.
而守护进程不依赖于终端, 所以即使终端退出(比如远程连接的putty)只要电脑还开着, 那么它就默默的在后台运行.

setsid() 函数

setsid() 调用成功后,当前进程会脱离原有的会话(session, 终端与会话是对应的), 成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
简单点可以理解, 刚登陆的时候有个session产生, 这个session与终端对应. 如果我把这个终端关掉(例如关掉连接中的 putty ) 那么与之相关的session还有进程组都会被干掉。
这个还是还比较好理解的,如果我用putty登陆,上面开了很多程序, 这个时候我把putty关掉, 那么系统也会把我刚刚登陆的这个putty(终端)上开的程序神马的统统关掉.
那么, 我不希望系统关掉我的程序, 我想让我的程序在后台一直运行, 那么就只有让程序脱离于putty对应的session和进程组(setsid就可以做到)

那么, 需要注意的是:进程组组长不能创建守护进程. 其实就是进程组组长不能调用 setsid , 因为他本身就是组长了.

下面看下实现的实例:

#include <stdio.h>
#include <stdlib.h>	// exit()
#include <unistd.h>	// fork() setsid() chdir() close()
#include <sys/types.h>	// fork() umask()
#include <sys/stat.h>	// umask()


// 与终端无关,在后台默默运行的守护进程
void mydaemon(int ischdir,int isclose)
{
	// 调用setsid() 的不能是进程组组长,当前程序有可能是进程组组长
	pid_t pid = fork();

	// 非子进程则退出
	if(pid != 0)
		exit(-1);

	// 父进程退出,留下子进程

	// 创建一个新的会话期,从而脱离原有的会话期
	// 进程同时与控制终端脱离
	setsid();

	// 此时子进程成为新会话期的领导和新的进程组长
	// 但这样的身份可能会通过fcntl去获到终端
	pid = fork();

	// 非子进程则退出
	if(pid != 0)
		exit(-1);

	// 此时留下来的是孙子进程,再也不能获取终端

	// 通常来讲, 守护进程应该工作在一个系统永远不会删除的目录下
	if(ischdir == 0)
	{
		chdir("/");
	}

	// 关闭输入输出和错误流 (通过日志来查看状态)
	if(isclose == 0)
	{
		close(0);
		close(1);
		close(2);
	}

	//去掩码位
	umask((mode_t)0);//sys/stat.h
}

int main(int argc, char *argv[])
{
	mydaemon(1,1);

	while(1)
		;

	exit(EXIT_SUCCESS);
}
gcc mydaemon.c -o mydaemon
./mydaemon

运行之后应该是什么效果都没有的, 这个时候通过ps命令来查看

ps -ef

输出:

其他省略n项...
root      4388  4236  0 01:26 tty1     00:00:00 -bash
root      4474  4017  0 01:27 ?        00:00:01 sshd: root@pts/0
root      4509  4474  0 01:28 pts/0    00:00:00 -bash
root      4899  4017  0 01:37 ?        00:00:00 sshd: root@pts/1
root      4914  4899  0 01:37 pts/1    00:00:00 -bash
root      9231     1 97 04:40 ?        00:01:19 ./mydaemon
root      9278  4509  0 04:41 pts/0    00:00:00 ps -ef

发现我们的程序已经运行了起来, 并且终端处是 ? 问号, 即成为了不依赖于任何终端而存在后台程序.

那么, 虽然有点小残忍, 但是这里还是要顺便提一下怎么杀死这样的进程:

#向进程id为9231的进程发送 SIGKILL 信号
kill -9 9231

详细见: Linux 查看进程和删除进程

linux c 线程同步 互斥量 实例

互斥量 线程同步

使用互斥量是实现多线程程序中的同步访问的另一种手段
程序员给某个对象加上一把“锁”,每次只允许一个线程去访问它
如果想对代码关键部分的访问进行控制,你必须在进入这段代码之前锁定一把互斥量,在完成操作后再打开它

互斥量对象用 pthread_mutex_t 类型表示,相关函数都在 头文件里面声明

int pthread_mutex_init(pthread_mutext *mutex,const pthread_mutexttr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

那么, 函数没什么好介绍的, 看名字猜都能猜的出来.
博主也不废话, 这里直接来个典型一点的例子:

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
	int res, i;
	pthread_t thread_arr[5];
	void *thread_result;

	// 初始化互斥量
	res = pthread_mutex_init(&work_mutex,NULL);

	// 创建5个线程
	for( i=0; i<5; ++i)
	{
		res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
	}

	// 线程创建完毕设置flag为0
	syn_flag = 0;

	// 避免主进程先退出
	while(1)
		;

	exit(0);
}

void* thread_func(void *arg)
{
	// 若5个线程未创建完,则阻塞
	while(syn_flag)
		;

	printf("线程%d 已开启n", (int)(arg)+1);

	// 上锁
	pthread_mutex_lock(&work_mutex);

	printf("线程%d 上锁n", (int)(arg)+1);

	// 修改全局变量 g_mum
	printf("线程%d: 线程id:%u g_num:%dn", (int)arg+1,
										   (unsigned int)pthread_self(),
										   ++g_num
										);

	// 休眠 -----> 看其他线程能否访问
	printf("线程%d 休眠2sn", (int)(arg)+1);
	sleep(2);
	printf("线程%d 醒来n", (int)(arg)+1);

	// 解锁
	pthread_mutex_unlock(&work_mutex);
	printf("线程%d 解锁n", (int)(arg)+1);

	// 线程退出
	pthread_exit(0);
}

输出

线程1 已开启
线程1 上锁
线程1: 线程id:3086453648 g_num:1
线程1 休眠2s
线程2 已开启
线程3 已开启
线程4 已开启
线程5 已开启
线程1 醒来
线程1 解锁
线程2 上锁
线程2: 线程id:3075963792 g_num:2
线程2 休眠2s
线程2 醒来
线程2 解锁
线程3 上锁
线程3: 线程id:3065473936 g_num:3
线程3 休眠2s
线程3 醒来
线程3 解锁
线程4 上锁
线程4: 线程id:3054984080 g_num:4
线程4 休眠2s
线程4 醒来
线程4 解锁
线程5 上锁
线程5: 线程id:3044494224 g_num:5
线程5 休眠2s
线程5 醒来
线程5 解锁

很明显的看到互斥量将线程上锁,没有解锁时候,特意让线程休眠,发现其他线程则依旧兢兢业业的继续等待,直到解锁才能继续访问g_num
互斥锁还真是个不错的东西。不过事情并没有大家想象中的那么好。

那么接下来我们再来看下, 博主研究的互斥量的其他例子。

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
	int res, i;
	pthread_t thread_arr[5];
	void *thread_result
	res = pthread_mutex_init(&work_mutex,NULL);

	for( i=0; i<5; ++i)
	{
		res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
	}

	syn_flag = 0;

	while(1)
		;

	exit(0);
}

// 线程调用函数
void* thread_func(void *arg)
{
	while(syn_flag)
		;
	// 如果全局变量g_num未到13则继续累加
	while(g_num < 13)
	{
		printf("线程%d: 线程id:%u g_num:%dn", (int)arg,
											   (unsigned int)pthread_self(),
											   ++g_num
											);
	}
	pthread_exit(0);
}

输出

线程0: 线程id:3086224272 g_num:1
线程0: 线程id:3086224272 g_num:2
线程0: 线程id:3086224272 g_num:3
线程0: 线程id:3086224272 g_num:4
线程0: 线程id:3086224272 g_num:5
线程0: 线程id:3086224272 g_num:6
线程0: 线程id:3086224272 g_num:7
线程0: 线程id:3086224272 g_num:8
线程0: 线程id:3086224272 g_num:9
线程0: 线程id:3086224272 g_num:10
线程0: 线程id:3086224272 g_num:11
线程0: 线程id:3086224272 g_num:12
线程0: 线程id:3086224272 g_num:13

准备做的事情是累加一个全局变量, 类比到项目中可能是一个统计在线人数的功能也可能是统计其他的东西,当然例子跟项目的区别还是很大, 这里主要是想让大家看到问题。

首先, 本例还没有使用互斥量, 那么出现第一个问题就是, 事情被第一个线程做完了。 其他线程连资源都没竞争到(也许是加到13确实需要时间比较少)
但是这与我们的初衷相差甚远——使用多线程处理。那么这个我们让第一个线程休眠一会, 让其他线程都能访问的到全局变量.

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
	int res, i;
	pthread_t thread_arr[5];
	void *thread_result;

	res = pthread_mutex_init(&work_mutex,NULL);

	for( i=0; i<5; ++i)
	{
		res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
	}

	syn_flag = 0;

	while(1)
		;

	exit(0);
}

void* thread_func(void *arg)
{
	while(syn_flag)
		;
	while(g_num < 13)
	{
		sleep(1);
		printf("线程%d: 线程id:%u g_num:%dn", (int)arg,
											   (unsigned int)pthread_self(),
											   ++g_num
											);
	}
	pthread_exit(0);
}

输出

线程0: 线程id:3086125968 g_num:1
线程1: 线程id:3075636112 g_num:2
线程2: 线程id:3065146256 g_num:3
线程3: 线程id:3054656400 g_num:4
线程4: 线程id:3044166544 g_num:5
线程0: 线程id:3086125968 g_num:6
线程1: 线程id:3075636112 g_num:7
线程2: 线程id:3065146256 g_num:8
线程3: 线程id:3054656400 g_num:9
线程4: 线程id:3044166544 g_num:10
线程0: 线程id:3086125968 g_num:11
线程1: 线程id:3075636112 g_num:12
线程2: 线程id:3065146256 g_num:13
线程3: 线程id:3054656400 g_num:14
线程4: 线程id:3044166544 g_num:15
线程0: 线程id:3086125968 g_num:16
线程1: 线程id:3075636112 g_num:17

好吧~ 结果如你所见, 原本我们只准备加到13的,结果最后加到了17。 这又是为什么?
调动函数中, 我们让进程刚进来就有休眠一会, 这可以让其他线程有机会竞争到CPU资源, 然后执行。
但是问题来了, 到12之前的时候都是好好的, 到了12的输出的时候, 意味着线程1 已经休眠完了。
在线程1 进入循环, 并且休眠的过程中, 其他又进来了四个线程(线程2、3、4、0)
接着线程1 休眠结束 累加到12, 线程2 什么的这时候还在睡觉, 进来的四个线程都还在睡着, 所以 12 还是小于 13 。 于是 线程1 又无赖的加了进来, 所以在 g_num < 13 这个条件满足之前, 5个线程都进入到了循环中休眠, 并且伺机累加这个全局变量。
随后的事情我们都看到了。 线程们一个个醒来, 一口气在原有的 12 基础上连续累加了到了 17 (5个线程赖在里面)。

问题描述完,那么我们尝试使用互斥量来解决这个问题:

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
int res, i;
pthread_t thread_arr[5];
void *thread_result;

res = pthread_mutex_init(&work_mutex,NULL);

for( i=0; i<5; ++i)
{
res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
}

syn_flag = 0;

while(1)
;

exit(0);
}

void* thread_func(void *arg)
{
while(syn_flag)
;
while(g_num < 13)
{
// 给其他线程访问的机会
sleep(1);
// 上锁
int res = pthread_mutex_lock(&work_mutex);
if(res == 0){ // 如果成功
printf("线程%d: 线程id:%u g_num:%dn", ((int)arg,
(unsigned int)pthread_self(),
++g_num
);
// 解锁
pthread_mutex_unlock(&work_mutex);
}
}
pthread_exit(0);
}

输出

线程0: 线程id:3085958032 g_num:1
线程1: 线程id:3075468176 g_num:2
线程2: 线程id:3064978320 g_num:3
线程3: 线程id:3054488464 g_num:4
线程4: 线程id:3043998608 g_num:5
线程0: 线程id:3085958032 g_num:6
线程1: 线程id:3075468176 g_num:7
线程2: 线程id:3064978320 g_num:8
线程3: 线程id:3054488464 g_num:9
线程4: 线程id:3043998608 g_num:10
线程0: 线程id:3085958032 g_num:11
线程1: 线程id:3075468176 g_num:12
线程2: 线程id:3064978320 g_num:13
线程3: 线程id:3054488464 g_num:14
线程4: 线程id:3043998608 g_num:15
线程0: 线程id:3085958032 g_num:16
线程1: 线程id:3075468176 g_num:17

输出是否让你有些惊讶? 结果依旧累加到了17。 原因其实跟刚刚一样。
不要以为上锁了, 就一定能hold住数据。 到了线程1累加到12的时候, 跟刚刚一样,已经有4个线程赖着进来等着解锁了
回头线程1累加完了12解锁, 自己也跟着在大家休眠的时候赖进来了。
所以要注意上锁的位置。

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
int res, i;
pthread_t thread_arr[5];
void *thread_result;

res = pthread_mutex_init(&work_mutex,NULL);

for( i=0; i<5; ++i)
{
res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
}

syn_flag = 0;

while(1)
;

exit(0);
}

void* thread_func(void *arg)
{
int res;
while(syn_flag)
;
while((res = pthread_mutex_lock(&work_mutex)) == 0 && g_num < 13)
{
sleep(1);
printf("线程%d: 线程id:%u g_num:%dn", (int)arg,
(unsigned int)pthread_self(),
++g_num
);
pthread_mutex_unlock(&work_mutex);
}
pthread_exit(0);
}

输出

线程0: 线程id:3086199696 g_num:1
线程0: 线程id:3086199696 g_num:2
线程0: 线程id:3086199696 g_num:3
线程0: 线程id:3086199696 g_num:4
线程0: 线程id:3086199696 g_num:5
线程0: 线程id:3086199696 g_num:6
线程0: 线程id:3086199696 g_num:7
线程0: 线程id:3086199696 g_num:8
线程0: 线程id:3086199696 g_num:9
线程0: 线程id:3086199696 g_num:10
线程0: 线程id:3086199696 g_num:11
线程0: 线程id:3086199696 g_num:12
线程0: 线程id:3086199696 g_num:13

这下终于控制住了全局变量, 可是问题又回到了开始的地方, 不能让线程1吃独食, 不然要其他线程干吗呢?
其实这里的问题是因为休眠的位置问题。休眠的位置在锁中间, 这个时候休眠的话, 其他线程还在干等着解锁, 休眠根本没把资源让出去。 于是一解锁 线程0 又贱贱的自己锁上了, 其他线程继续干等。 于是就变成了这个情况。

所以我们只要修改一下休眠的位置即可:

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
int res, i;
pthread_t thread_arr[5];
void *thread_result;

res = pthread_mutex_init(&work_mutex,NULL);

for( i=0; i<5; ++i)
{
res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
}

syn_flag = 0;

while(1)
;

exit(0);
}

void* thread_func(void *arg)
{
int res;
while(syn_flag)
;
while((res = pthread_mutex_lock(&work_mutex)) == 0 && g_num < 13)
{
printf("线程%d: 线程id:%u g_num:%dn", (int)arg,
(unsigned int)pthread_self(),
++g_num
);
pthread_mutex_unlock(&work_mutex);
// 解锁后休眠
sleep(1);
}
pthread_exit(0);
}

线程0: 线程id:3086486416 g_num:1
线程1: 线程id:3075996560 g_num:2
线程2: 线程id:3065506704 g_num:3
线程3: 线程id:3055016848 g_num:4
线程4: 线程id:3044526992 g_num:5
线程0: 线程id:3086486416 g_num:6
线程1: 线程id:3075996560 g_num:7
线程2: 线程id:3065506704 g_num:8
线程3: 线程id:3055016848 g_num:9
线程4: 线程id:3044526992 g_num:10
线程0: 线程id:3086486416 g_num:11
线程1: 线程id:3075996560 g_num:12
线程2: 线程id:3065506704 g_num:13

好了, 这下终于安心了, 问题已解决。
那么实际上我们使用互斥量的时候可能不是本例中的样子, 但是希望大家能在本例推导的过程中有所收获。
如果有碰到这方面的问题, 也可以找博主讨论( 这个很欢迎的 🙂 )

gcc 警告:传递实参 1 丢弃了指针目标类型的限定

将一个 const char * 赋个一个 char * 的时候会报这个错例如:

#include <stdio.h>

void prtstr(char *str)
{
        printf(str);
}

int main()
{
        const char *hi = "hello wolrd";
        prtstr(hi);
}

信息

gcc test.c
test.c: In function ‘main’:
test.c:11: 警告:传递实参 1(属于 ‘prtstr’)丢弃了指针目标类型的限定

linux c 线程基础

线程创建

头文件

函数原型:
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);

参数一
传入一个 pthread_t 类型的指针 thread,函数成功返回之后线程的id会保存在这个变量中.
参数二
用来设置线程属性
参数三
是线程运行调用的函数
参数四
是运行函数的参数

返回值
如果成功, the pthread_create() 函数返回0; 否则, 会返回错误的number.

简述:
在一个程序里的多个执行路线就叫做线程(thread).更准确的定义是:线程是“一个进程内部的控制序列”, 所以线程属于进程, 一个进程可以有多个线程, 一旦某个线程结束进程那么所有线程都会终止(因为是一个进程).
注意:链接这些线程函数库时要使用编译器命令的“-lpthread”选项

与进程的差别

  • 进程是资源管理的最小单元,线程是程序执行的最小单元
  • 进程管理的资源 虚拟内存、I/O句柄、信号处理器并借助内存管理单元(MMU)硬件,防止自身遭受其他进程破坏。
  • 线程基本上不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但它可与同一个进程中的其他线程共享进程所拥的资源
#include <stdio.h>;	// for printf
#include <pthread.h>;	// for pthread_*

void * thread_call()
{
	pid_t process_id;		// 进程id
	pthread_t thread_id;		// 线程id
	int i=0;

	process_id = getpid();		// 获取进程id
	thread_id = pthread_self();	// 获取线程id

	while(i<6)
	{
		printf(&quot;进程id %u 线程id %u (0x%x) 当前第 %d 次调用n&quot;, (unsigned int)process_id, (unsigned int)thread_id, (unsigned int)thread_id, i++);
		sleep(1);
	}
	return (void *)0;
}

int main()
{
	pthread_t thread_one;
	pthread_t thread_two;

	// 创建线程一, 设置线程调用函数
	if (pthread_create(&amp;thread_one, NULL, thread_call, NULL) != 0)
	{
		printf(&quot;线程创建失败!n&quot;);
	}

	// 错开线程二的时间,休眠一秒
	sleep(1);

	// 创建线程二, 设置线程调用函数
	if (pthread_create(&amp;thread_one, NULL, thread_call, NULL) != 0)
	{
		printf(&quot;线程创建失败!n&quot;);
	}

	// 防止进程退出
	while(1)
		;

	return 0;
}
# 链接线程的库编译
gcc pth_create.c -o pth_create -lpthread
./pth_create

输出:

进程id 9629 线程id -1209021552 (0xb7efcb90) 当前第 0 次调用
进程id 9629 线程id -1219511408 (0xb74fbb90) 当前第 0 次调用
进程id 9629 线程id -1209021552 (0xb7efcb90) 当前第 1 次调用
进程id 9629 线程id -1219511408 (0xb74fbb90) 当前第 1 次调用
进程id 9629 线程id -1209021552 (0xb7efcb90) 当前第 2 次调用
进程id 9629 线程id -1219511408 (0xb74fbb90) 当前第 2 次调用
进程id 9629 线程id -1209021552 (0xb7efcb90) 当前第 3 次调用

参数传递

#include <stdio.h>;     // for printf
#include <pthread.h>;   // for pthread_*

struct time {
    int month;
    int day;
};

void * thread_call(void *arg)
{
    struct time *local = (struct time *)arg;
    printf(&quot;主线程传来的时间: %d月%d日n&quot;, local->;month, local->;day);    
    return (void *)0;
}

int main()
{
    pthread_t thread_one;
    pthread_t thread_two;

    struct time t = { 4, 18 };
    
    if (pthread_create(&amp;thread_one, NULL, thread_call, (void *)&amp;t) != 0)
    {
        printf(&quot;线程创建失败!n&quot;);
    }

    // 防止进程退出
    while(1)
        ;

    return 0;
}
gcc pth_arg.c -o pth_arg-lpthread
./pth_arg

输出:
主线程传来的时间: 4月18日

pthread_exit函数

功能:结束线程
原型
void pthread_exit (void *retbal);
参数
void *retbal:执行线程的函数指针
说明:线程在结束时必须调用pthread_exit函数,这与一个进程在结束时要调用exit函数是同样的道理

pthread_join函数

功能:在线程结束后把它们归并到一起
原型
int pthread_join(pthread_t th, void**thread_return);
参数
th: 指定了将要等待的线程标识符
thread_return: 它指向另外一个指针,而后者指向线程的返回值
返回值:成功时返回“0”,失败时返回一个错误代码
说明:pthread_join相当于进程用来等待子进程的wait函数

自杀
线程指令序列执行完毕
pthread_exit
他杀
pthread_cancel

线程同步

两个(或多个)线程同时执行时,经常需要访问到公共资源或代码的关键部分,这时就涉及到了线程的同步问题,我们可以通过下面两种方法来更好地控制线程的执行情况和更好地访问代码的关键部分

用互斥量进行同步

使用互斥量是实现多线程程序中的同步访问的另一种手段
程序员给某个对象加上一把“锁”,每次只允许一个线程去访问它
如果想对代码关键部分的访问进行控制,你必须在进入这段代码之前锁定一把互斥量,在完成操作后再打开它

互斥量对象用 pthread_mutex_t 类型表示,相关函数都在头文件里面声明

int pthread_mutex_init(pthread_mutext *mutex,const pthread_mutexttr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

示例:

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

void * thread_func(void *arg);

pthread_mutex_t work_mutex;

int g_num = 0;
int syn_flag = 1;

int main() {
	int res, i;
	pthread_t thread_arr[5];
	void *thread_result;

	res = pthread_mutex_init(&work_mutex,NULL);

	for( i=0; i<5; ++i)
	{
		res = pthread_create(&thread_arr[i],NULL,thread_func, (void *)i);
	}

	syn_flag = 0;

	while(1)
		;

	exit(0);
}

void* thread_func(void *arg)
{
	while(syn_flag)
		;

	printf("进入线程%dn", (arg));
	pthread_mutex_lock(&work_mutex);
	printf("进入线程%d 上锁n", (arg));
	printf("线程%d: 线程id:%u g_num:%dn", (int)arg,
										   (unsigned int)pthread_self(),
										   ++g_num
										);
	printf("进入线程%d 休眠3sn", (arg));
	sleep(2);
	printf("进入线程%d 醒来n", (arg));
	pthread_mutex_unlock(&work_mutex);
	printf("进入线程%d 解锁n", (arg));

	pthread_exit(0);
}

输出:

进入线程0
进入线程0 上锁
线程0: 线程id:3086105488 g_num:1
进入线程0 休眠3s
进入线程1
进入线程2
进入线程3
进入线程4
进入线程0 醒来
进入线程0 解锁
进入线程1 上锁
线程1: 线程id:3075615632 g_num:2
进入线程1 休眠3s
进入线程1 醒来
进入线程1 解锁
进入线程2 上锁
线程2: 线程id:3065125776 g_num:3
进入线程2 休眠3s
进入线程2 醒来
进入线程2 解锁
进入线程3 上锁
线程3: 线程id:3054635920 g_num:4
进入线程3 休眠3s
进入线程3 醒来
进入线程3 解锁
进入线程4 上锁
线程4: 线程id:3044146064 g_num:5
进入线程4 休眠3s
进入线程4 醒来
进入线程4 解锁

更多的互斥量示例: linux c 线程同步 互斥量 实例

继续整理中…


gdb 基本用法

list 列出代码
next 执行下一步(不会进入函数)
step 执行下一步(进入函数)
run 运行程序
continue 跳至下一个断点
break 设置断点: break 62 在62行设置断点; break memmove 在memmove函数入口处设断点
print 打印变量的值
finish 跳出函数
quit 退出程序
watch 观察变量

gcc memmove.c -o memmove -g
gdb memmove
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /mnt/hgfs/trunk/lellansin/1. 复制函数、剪切函数  ( 6个 )/memmove...done.
(gdb) list	#列出代码查看
53                              *sc1++ = *sc2++;
54                      }
55              }
56              return s1;
57      }
58
59      int main()
60      {
61              char text[20];
62              printf("%sn", memmove(text, "hello world", 5));
(gdb) l		#列出查看的简写
63              return 0;
64      }
(gdb) break 62	#断点设在62行
Breakpoint 1 at 0x80484b4: file memmove.c, line 62.
(gdb) continue	#继续执行程序
The program is not being run.	#提示程序尚未运行
(gdb) run	#运行程序
Starting program: /mnt/hgfs/trunk/lellansin/string.h/memmove
#在断点处停止
Breakpoint 1, main () at memmove.c:62
62              printf("%sn", memmove(text, "hello world", 5));
(gdb) step	#逐步进入函数
memmove (s1=0xbfffe8d0, s2=0x80485c0, n=5) at memmove.c:39
39              sc1 = s1;
(gdb) l		#列出代码
34      void *memmove(void *s1, const void *s2, size_t n)
35      {
36              char *sc1;
37              const char *sc2;
38
39              sc1 = s1;
40              sc2 = s2;
41
42              // 如果 sc1 的地址比 sc2 要低,并且两者相处不足 n 字节
43              if (sc2 < sc1 && sc1 < sc2 + n)
(gdb) next	#执行下一步
40              sc2 = s2;
(gdb) n		#执行下一步的简写
43              if (sc2 < sc1 && sc1 < sc2 + n)
(gdb) print sc2	#打印变量 sc2 的值
$1 = 0x80485c0 "hello world"
(gdb) p n	#打印变量 n 的值的简写
$2 = 5
(gdb) l		#列出代码
38
39              sc1 = s1;
40              sc2 = s2;
41
42              // 如果 sc1 的地址比 sc2 要低,并且两者相处不足 n 字节
43              if (sc2 < sc1 && sc1 < sc2 + n)
44              {
45                      for(sc1 += n, sc2 += n; 0 < n; --n) // 逆向复制
46                      {
47                              *--sc1 = *--sc2;
(gdb) finish	#跳出函数
Run till exit from #0  memmove (s1=0xbfffe8c0, s2=0x80485c0, n=5)
    at memmove.c:43
0x080484cf in main () at memmove.c:62
62              printf("%sn", memmove(text, "hello world", 5));
Value returned is $3 = (void *) 0xbfffe8c0
(gdb) quit	#退出程序
A debugging session is active.

        Inferior 2 [process 7394] will be killed.

Quit anyway? (y or n) y		#确认退出

watch 的使用

gcc test.c -o test -g
gdb test
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /mnt/hgfs/trunk/lellansin/string.h/test...done.
(gdb) l		#列出代码
1       #include <stdio.h>
2
3       int main()
4       {
5               int i, t = 0;
6               for(i = 0; i < 28; i+= 3)
7               {
8                       t = i;
9               }
10      }
(gdb) watch t	#观察变量 t 
No symbol "t" in current context.	#提示符号不识别
(gdb) b 5	#断点设置在第5行
Breakpoint 1 at 0x8048365: file test.c, line 5.
(gdb) r		#运行程序
Starting program: /mnt/hgfs/trunk/lellansin/string.h/test
#程序到断点处暂停
Breakpoint 1, main () at test.c:5
5               int i, t = 0;
(gdb) watch t	#此时 t 已申明,可以观察
Hardware watchpoint 2: t
(gdb) n		#下一步
6               for(i = 0; i < 28; i+= 3)
(gdb) c		#跳至下一个断点
Continuing.
Hardware watchpoint 2: t	# 变量 t 一旦改变,程序就会中断

Old value = 10065408
New value = 0
main () at test.c:6
6               for(i = 0; i < 28; i+= 3)
(gdb) c		#跳至下一个断点
Continuing.
Hardware watchpoint 2: t	# 变量 t 改变

Old value = 0
New value = 3
main () at test.c:6
6               for(i = 0; i < 28; i+= 3)
(gdb) c		#跳至下一个断点
Continuing.
Hardware watchpoint 2: t	# 变量 t 改变

Old value = 3
New value = 6
main () at test.c:6
6               for(i = 0; i < 28; i+= 3)
(gdb) c		#跳至下一个断点
Continuing.
Hardware watchpoint 2: t	# 变量 t 改变

Old value = 6
New value = 9
main () at test.c:6
6               for(i = 0; i < 28; i+= 3)
(gdb) q
A debugging session is active.

        Inferior 1 [process 7811] will be killed.

Quit anyway? (y or n) y

linux c 信号

# 查看信号列表
kill -l

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6
59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
#include <stdio.h>
#include <signal.h>

void handle(int sig)
{
	printf("Receive siganl=%dn", sig);
}

int main()
{
	int i;
	// 获取当前进程ID
	pid_t pid = getpid();

	// 安装从1到64的信号
	for(i=1;i<64;i++)
	{
		signal(i,handle);
	}

	for(i=1;i<64;i++)
	{
		if( i==9 || i==19 || i==32 || i==33 )
			continue;
		// 向当前进程发送信号
		kill(pid,i);
	}

	return 0;
}

sigaction