\"\"

1.0   无名管道   (无名管道存在于内存,打开一个入口,打开一个出口

无名管道概念:

管道是Linux支持的最初Unix IPC形式之一,具有以下特点

管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

只能用于具有亲缘关系的进程(父子或者兄弟进程之间)

数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

–pipe可以用于I/O操作,但pipe只存在于内存中,在磁盘文件系统中并没有相应文件名,所以叫无名管道

管道最常用的应用就是在Shell用来实现|操作符的管道功能

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#if 0

#include <unistd.h>

创建无名管道,保存管道的描述符到 pipefd 数组中。
pipefd[0] 用于从管道读取数据;
pipefd[1] 用于各管道写入数据。
成功返回 0,失败返回 -1
int pipe(int pipefd[2]);

管道的大小的信息可参考手册第 7 部分:man 7 pipe
默认是 65536 字节,可以通过 fcntl(2) 函数查询及设置:
fcntl(2)  F_GETPIPE_SZ 查询,F_SETPIPE_SZ 设置


#endif

int main(void)
{
	int pipefd[2];
	char rbuf[32] = \"\", wbuf[32] = \"Hello world\";
	
	// 创建无名管道
	if (-1 == pipe(pipefd))
	{
		perror(\"创建无名管道失败\");
		return -1;
	}

	// 写
	write(pipefd[1], wbuf, strlen(wbuf));

	// 读
	read(pipefd[0], rbuf, sizeof rbuf);
	printf(\"从管道中读取到数据:%s\\n\", rbuf);

	// 关闭管道
	close(pipefd[0]);
	close(pipefd[1]);
	
	return 0;
}

2.0   有名管道

         有名管道,在磁盘中存在文件,但不会保存内容。

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

#if 0

// 通过命令行创建和删除命名管道
// 创建命令: mknod 管道名 p
// 或 mkfifo 管道名
// 删除命令: rm 管道名

// 也可以通过函数接口创建和关闭管道
// 创建函数为 mkfifo,删除函数为 un 

#include <sys/types.h>
#include <sys/stat.h>

创建名为 pathname 的命名管道,模式为 (mode & ~umask)
成功返回 0,失败返回 -1
int mkfifo(const char *pathname, mode_t mode);


#endif

int main(int argc, char **argv)
{
	char *fifoname;
	int fd;
	char buf[] = \"Hello world\";

	if (argc != 2)
	{
		printf(\"用法:%s <管道名>\\n\", argv[0]);
		return -1;
	}

	fifoname = argv[1];
	// 判断文件是否已存在,如果存在则打开,否则创建它
	if (0 != access(fifoname, F_OK))
	{
		if (-1 == mkfifo(fifoname, 0644))
		{
			perror(\"创建管道失败\");
			return -1;
		}
	}

	// 打开管道
	// 只有读写同时打开,open 才会返回,否则阻塞
	fd = open(fifoname, O_RDONLY);
	if (-1 == fd)
	{
		perror(\"打开管道失败\");
		return -1;
	}

	// 从管道读取数据
	read(fd, buf, sizeof (buf));
	printf(\"从管道读取数据成功:%s\\n\", buf);
	// 关闭管道
	close(fd);

	// 删除管道
	un (fifoname);
	
	return 0;
}

3.0   kill发送信号(>0进程,==0进程组 ,==-1所有进程,<-1进程组)waitpid

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>

// kill 命令可以向进程发送信号:
// kill -信号名或序号 进程号
// 如,运行 ping 命令:
// ping baidu.com
// 在另一个终端执行 kill 命令:
// kill -9 ping命令的进程号
// 或 
// kill -KILL ping命令的进程号

#if 0

#include <sys/types.h>
#include <signal.h>

向进程 pid 发送 sig 信号
pid > 0 发送给进程号为 pid 的进程
pid = 0 发送给调用进程所在的进程组中每个进程
pid = -1 发送给除 1 号进程外,所有有权发送的每个进程
pid < -1 发送给进程组 ID 为 -pid 中的每一个进程
sig = 0 则不发送任何信号,但是会做错误检查,
这可以用于检查进程 ID 或 进程组 ID 是否存在 
成功返回 0,失败返回 -1
int kill(pid_t pid, int sig);


#endif 

int main(int argc, char **argv)
{
	pid_t pid;
	int signum;

	if (argc != 3)
	{
		printf(\"用法:%s <进程号> <信号值>\\n\", argv[0]);
		return -1;
	}

	pid = atoi(argv[1]);
	signum = atoi(argv[2]);

	if (-1 == kill(pid, signum))
	{
		perror(\"发送信号失败\");
		return -1;
	}

	return 0;
}

4.0   raise向自己发信号

#include <stdio.h>
#include <signal.h>

#if 0

#include <signal.h>

向调用者发送信号 sig
等价于:kill(getpid(), sig);
如果信号处理函数执行了,则当从处理函数返回后,才从 raise 返回
成功返回 0,失败返回 非0
int raise(int sig);


#endif


int main(void)
{
	//if (raise(SIGTERM))    // 自杀
	//if (raise(SIGSTOP))    // 自闭
	if (raise(SIGCHLD))    // 不会引起进程终止的信号
	{
		printf(\"给自己发送信号失败\\n\");
	}
	else
	{
		printf(\"给自己发送信号成功\\n\");
	}

	return 0;
}

5.0  abort向自己发送SIGABRT信号

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

#if 0

#include <stdlib.h>

引起调用进程异常终止
首先解除阻塞 SIGABRT 信号,然后向调用进程发送 SIGABRT 信号,
除非信号被捕捉处理,并且不返回,否则引起进程异常终止
void abort(void);


#endif

void abort_handler(int signum)
{
    printf(\"收到信号 %d\\n\", signum);
}

int main(void)
{
    // 注册 SIGABRT 信号的处理函数
    signal(SIGABRT, abort_handler);

    // 休眠一会
    sleep(1);
    
    // 向自己发送 SIGABRT 信号
    // 当从 SIGABRT 信号处理函数 abort_handler 中返回后,
    // 当前进程依然异常终止,因此以下 printf 函数不会被调用
    abort();

    printf(\"结束\\n\");
    
    return 0;
}

6.0   pause挂起进程

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#if 0

#include <unistd.h>

挂起调用进程,等待一个信号
当进程休眠直到收到一个信号,并从信号处理函数返回时,才返回 -1
如果收到信号引起进程结束,则不会返回
int pause(void);


#endif

// 命令行执行:ps aux | grep a.out
// 查看 a.out 程序的进程号
// 再执行 kill 命令:kill -INT 进程号
// 观察结果

// SIGINT 信号处理函数
void sigint_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
}

int main(void)
{
	// 注册 SIGINT 信号
	signal(SIGINT, sigint_handler);
	
	// 挂起当前进程
	if (-1 == pause())
	{
		perror(\"收到信号\");
	}

	return 0;
}

7.0   signal捕捉信号

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

#if 0

#include <signal.h>

未使用 typedef 情况下,signal 函数的原型:

void ( *signal(int signum, void (*handler)(int)) ) (int);


使用 typedef 简化了 signal 函数原型:

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

注册 signum 信号处理函数 handler。
即当捕捉到 signum 信号时,调用 handler 函数进行处理。
handler 是一个信号处理函数,其原型为:void handler(int);
函数指针类型为:void (*)(int);
		
handler 可以是:
1. SIG_IGN    忽略信号 
2. SIG_DFL    对信号执行默认动作
3. 自定义函数 处理这个信号

当 signum 信号传递给了进程,发生以下事情:
1. 如果 handler 是 SIG_IGN,则忽略信号
2. 如果 handler 是 SIG_DFL,则执行默认动作
3. 如果 handler 是自定义函数,则首先要么复位为 SIG_DFL,要么信号被阻塞,
之后自定义函数被调用。如果调用处理函数引起信号被阻塞,那么直到处理函数
返回才会解除阻塞。

注意:SIGKILL 和 SIGSTOP 这两个信号不能被捕捉和忽略 

成功返回之前的信号处理函数指针,失败返回 SIG_ERR

#endif

// 键盘输入 CTRL + C 就是向进程发送 SIGINT 信号

void sigint_handler(int signum)
{
	// 对信号如何处理,由此函数决定

	// ... 处理代码开始 ...

	printf(\"收到信号 %d\\n\", signum);
	
	// ... 处理代码结束 ...
	
	// 可以在信号处理函数中直接退出进程
	//exit(0);
}

void sigstop_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
}

int main(void)
{
	int i;

	// 注册 SIGINT 信号,并忽略它
	//signal(SIGINT, SIG_IGN);
	// 注册 SIGINT 信号,执行默认动作,即进程终止
	//signal(SIGINT, SIG_DFL);
	signal(SIGINT, sigint_handler);
	// 不能忽略 SIGKILL 和 SIGSTOP 信号
	// 当收到 SIGKILL 和 SIGSTOP 信号时,不能忽略
	//signal(SIGKILL, SIG_IGN);
	// 不能捕捉 SIGKILL 和 SIGSTOP 信号
	// 当收到 SIGKILL 和 SIGSTOP 信号时,不能执行注册的处理函数
	//signal(SIGSTOP, sigstop_handler);

	i = 0;
	while (i < 15)
	{
		sleep(1);
		printf(\"i = %d\\n\", i++);
	}

	return 0;
}

8.0   sigqueue发送信号

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

#if 0

#include <signal.h>

将信号 sig 及数据 value 在进程 pid 中排队
int sigqueue(pid_t pid, int sig, const union sigval value);

sig 为 0 时可以用于检查 pid 进程是否存在 

如果接收信号的进程在 sigaction(2) 函数中使用了 SA_SIGINFO 标志,
则它可以通过 siginfo_t 结构体的 si_value 域接收 sigqueue 函数的
第 3 个参数表示的数据。

成功返回 0,失败返回 -1

数据是一个共用体
union sigval {
	int   sival_int;   // 数据可以是一个整数
	void *sival_ptr;   // 数据也可以是一个指针
};

#endif

int main(int argc, char **argv)
{
	union sigval value;
	pid_t pid;

	if (argc != 2)
	{
		printf(\"用法:%s <进程号>\\n\", argv[0]);
		return -1;
	}
	pid = atoi(argv[1]);
//	printf(\"pid = %d\\n\", pid);
	
	value.sival_int = 123;
	// 发送 SIGINT 信号给 pid 进程
	if (-1 == sigqueue(pid, SIGINT, value))
	{
		perror(\"发送信号失败\");
	}
	else
	{
		printf(\"发送信号成功\\n\");
	}

	return 0;
}

9.0   sigaction捕捉信号

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#if 0

#include <signal.h>

检测信号 signum,并修改其原行为 oldact 为新行为 act
成功返回 0,失败返回 -1
int sigaction(int signum, const struct sigaction *act,
			struct sigaction *oldact);

signum 可以是除 SIGKILL 和 SIGSTOP 之外的所有有效信号。
如果 act 非空,则为信号 signum 安装 act,如果 oldact 
非空,则返回之前的 act 到 oldact。

行为结构体:
struct sigaction {
	void     (*sa_handler)(int);
	void     (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
	void     (*sa_restorer)(void);    // 没有任何系统实现 sa_restorer
};

sa_handler 与 sa_sigaction 是二选一的。
sa_handler 与 signal 的第二个参数相同,是信号处理函数,
此时 sigaction 等价于 signal 函数。
sa_flags 如果为 SA_SIGINFO,则使用 sa_sigaction 函数代替 
sa_handler 对信号 signum 进行处理。
sa_mask 指定了在信号函数被调用时,要阻塞的信号的掩码。
在信号处理函数执行过程中,触发信号处理函数的信号会被阻塞。

sa_sigaction 函数的第 2 个参数:
siginfo_t {
	int      si_signo;     /* Signal number */
	int      si_errno;     /* An errno value */
	int      si_code;      /* Signal code */
	int      si_trapno;    /* Trap number that caused
							  hardware-generated signal
							  (unused on most architectures) */
	pid_t    si_pid;       /* Sending process ID */
	uid_t    si_uid;       /* Real user ID of sending process */
	int      si_status;    /* Exit value or signal */
	clock_t  si_utime;     /* User time consumed */
	clock_t  si_stime;     /* System time consumed */
	sigval_t si_value;     /* Signal value */
	int      si_int;       /* POSIX.1b signal */
	void    *si_ptr;       /* POSIX.1b signal */
	int      si_overrun;   /* Timer overrun count;
							  POSIX.1b timers */
	int      si_timerid;   /* Timer ID; POSIX.1b timers */
	void    *si_addr;      /* Memory location which caused fault */
	long     si_band;      /* Band event (was int in
							  glibc 2.3.2 and earlier) */
	int      si_fd;        /* File de or */
	short    si_addr_lsb;  /* Least significant bit of address
							  (since Linux 2.6.32) */
	void    *si_call_addr; /* Address of system call instruction
							  (since Linux 3.5) */
	int      si_syscall;   /* Number of attempted system call
							  (since Linux 3.5) */
	unsigned int si_arch;  /* Architecture of attempted system call
							  (since Linux 3.5) */
};

通过 sigqueue 的第 3 个参数 union sigval value 传递的参数,
可以通过 siginfo_t 结构体的 si_value 获取 

#endif

// SIGINT 信号处理函数
void sigint_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
}

// SIGINT 信号处理函数
void sa_sigint_handler(int signum, siginfo_t *info, void *arg)
{
	printf(\"收到信号 %d, 数据 %d\\n\", signum, info->si_value.sival_int);
}

int main(void)
{
	int i;
	struct sigaction act;

#if 0
	act.sa_handler = sigint_handler;
	//sigemptyset(&act.sa_mask);   // 清空掩码
	sigfillset(&act.sa_mask);    // 填充掩码
    // sa_flags 为 SA_SIGINFO 则选择 sa_sigaction 函数
	// 否则选择 sa_handler 函数
	act.sa_flags = 0;          // 选择 sa_handler 函数    
#else
	act.sa_sigaction = sa_sigint_handler;
	sigfillset(&act.sa_mask);    // 填充掩码
    // sa_flags 为 SA_SIGINFO 则选择 sa_sigaction 函数
	// 否则选择 sa_handler 函数
	act.sa_flags = SA_SIGINFO;   // 选择 sa_sigaction 函数
#endif
	// 注册 SIGINT 信号
	// 第 3 个参数用于得到之前的 act,如果为 NULL 则得不到
	sigaction(SIGINT, &act, NULL);
	
	i = 0;
	while (i < 15)
	{
		sleep(1);
		printf(\"i = %d\\n\", i++);
	}

	return 0;
}

10  alarm定时器

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#if 0

#include <unistd.h>

在 seconds 秒时向调用进程发送 SIGALRM 信号
如果 seconds 为 0 则取消所有的未处理的 alarm
有任何事件时之前用 alarm 设置的闹钟都被取消
返回之前闹钟剩余的秒数,如果之前没有设置闹钟则返回 0 
unsigned int alarm(unsigned int seconds);


#endif

void sigalrm_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
	// 再次设置闹钟
	alarm(2);
}

void sigint_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
	printf(\"剩余 %d 秒\\n\", alarm(2));
}

int main(void)
{
	// 注册 SIGALRM 信号
	signal(SIGALRM, sigalrm_handler);
	// 注册 SIGINT 信号,键盘输入 CTRL + C 则调用 sigint_handler 函数
	signal(SIGINT, sigint_handler);
	
	// 设置闹钟
	if (0 == alarm(2))
	{
		printf(\"第一次设置闹钟\\n\");
	}

	getchar();

	printf(\"结束\\n\");

	return 0;
}

11  setitimer定时器

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

#if 0

#include <sys/time.h>

int getitimer(int which, struct itimerval *curr_value);
为 which 类时间设置可重复的定时器 new_value,
返回之前的定时器 old_value
成功返回 0,失败返回 -1
int setitimer(int which, const struct itimerval *new_value,
			struct itimerval *old_value);

which 表示不同类的时间:

ITIMER_REAL 
真实时间,包括休眠阻塞所花的时间,到期发送 SIGALRM 信号

ITIMER_VIRTUAL 
用户空间进程执行所花时间,到期发送 SIGVTALRM 信号

ITIMER_PROF 
包括用户进程执行时间和内核为进程所花时间,到期发送 SIGPROF 信号

struct itimerval {
	struct timeval it_interval; /* Interval for periodic timer */
	struct timeval it_value;    /* Time until next expiration */
};

it_interval 周期性定时期间隔时间(一旦设置就固定了)
it_value    下次到期剩余时间(随着时间的消逝,逐渐递减)
it_value 如果设置为 0 则禁用定时器

struct timeval {
	time_t      tv_sec;         /* seconds */
	suseconds_t tv_usec;        /* microseconds */
};

#endif

void sigalrm_handler(int signum)
{
	printf(\"收到信号 %d\\n\", signum);
}

int main(void)
{
	struct itimerval itv = {{2, 50000}, {1, 50000}};
	int i = 0;

	// 注册 SIGALRM 信号
	signal(SIGALRM, sigalrm_handler);

	// 设置周期性定时器
	if (-1 == setitimer(ITIMER_REAL, &itv, NULL))
	{
		perror(\"设置定时器失败\");
		return -1;
	}
	
	//while (++i);
	getchar();

	printf(\"结束\\n\");

	return 0;
}

12  sigprocmask阻塞

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

/*
应用,先设置阻塞  ,在终端发送信号sigalrm信号 ,  阻塞后会立刻执行sigalrm信号 


#include <signal.h>

int sigprocmask(int how,const sigset_t *set,sigset_t *oset);

参数说明:

1)how:输入参数,设置信号阻塞掩码的方式。
		可以包括3种方式对信号的掩码进行设置,
		分别是阻塞信号的SIG_BLOCK、
		解除阻塞的SIG_UNBLOCK和
		设置阻塞掩码的SIG_SETMASK。

2)set:输入参数,阻塞信号集。
		当参数how为SIG_BLOCK时,该参数表明要阻塞的信号集;
		当how参数为SIG_UNBLOCK时,该参数表明要解除阻塞的信号集;
		当how参数为SIG_SETMASK时,该参数表明要阻塞的信号集。

3)oset:输出参数,原阻塞信号集。
*/

void alarm1(int signum)
{
	printf(\"this is \\n\");
}

void main()
{
	//信号掩码结构变量,用于指定新的信号掩码
    sigset_t mask;
    //信号掩码结构变量,用于保存原来的信号处理掩码
    sigset_t omask;
	
    signal(SIGALRM,alarm1);

	sigfillset(&mask);	
	
	//阻塞SIGINT信号
    sigprocmask(SIG_BLOCK,&mask,&omask);
 
	printf(\"sleep\\n\");
    //休眠10秒
    sleep(20);
    //解除SIGINT信号的阻塞
    sigprocmask(SIG_SETMASK,&omask,NULL);	
	
	printf(\"unsleep\\n\");
	
}

13  sigsuspend阻塞

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

/*
sigsuspend的原型为:

#include <signal.h>

int sigsuspend(const sigset_t *set);

参数说明:

             1)set:输入参数,执行sigsuspend过程中需要被阻塞的信号集。

返回值:

            sigsuspend的返回值永远是-1,错误码errno为EINTR

sigsuspend  pause			
sigsuspend函数是pause函数的增强版。当sigsuspend函数的参数信号集为空信号集时,sigsuspend函数是和pause函数是一样的,
可以接受任何信号的中断。但是,sigsuspend函数可以屏蔽信号,接受指定的信号中断。			
			
*/

void deal()
{
   printf(\"INT \\n\");
}

/*
sigprocmask 并不会对 sigsuspend产生影向,sigsuspend 函数会保存sigprocmask设的阻塞,在执行
sigsuspend后再恢复阻塞 

在此,我们设定了sigprocmask  阻塞 int  信号, 接着运行sigsuspend 后停止,
我们 在终端执行ctrl +c  ,可以看到sigprocmask并不能阻塞sigsuspend
*/
void main()
{
    sigset_t  sigs,sigmask;
    int  i;
    signal(SIGINT,deal);    //设置一个INT信号
	
    sigemptyset(&sigs);     //sigsuspend的信号集设置空,表示会sigsuspend会处
                             //理任何信号,如果想在处理排队的信号时屏蔽一些 
                             //信号,可以把相应的信号加到信号集中

    sigemptyset(&sigmask);
	
    sigaddset(&sigs,SIGINT);
	
    sigprocmask(SIG_BLOCK,&sigs,0); //屏蔽sigint信号,使其不能再业务处理过程
                                    //中干扰进程。
    for(i=0 ; i<5 ; i++)
    {
       printf(\" entry \\n\");    //模拟业务处理

       sigsuspend(&sigmask);       	//处理正在排队的信号,处理信号完毕后,
									//sigsuspend函数才返回,并执行下个业务处理
       printf(\"out\\n\");   	

    }    
}

14  sigsetjmp跳转

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
 /*
 #include <setjmp.h>

int sigsetjmp (struct __jmp_buf_tag env[1], int savemask);

参数说明:

        1)env[1]:输出参数,该参数实际上是一个结构体的指针。该结构体中包含了长跳转指针,是否保存信号掩码及保存的信号掩码值等信息。对于应用人员来说,该结构是透明的。

        2)savemask:是否保存信号掩码。如果该参数非零,则在调用sigsetjmp后,当前进程的信号掩码将被保存;在调用siglongjmp时,将恢复由sigsetjmp保存的信号掩码。

void siglongjmp(sigjmp_buf env,int val);

参数说明:
					
		1)env:输入参数,等效于env[1]。
		
        2)val:当由siglongjmp调用sigsetjmp时,该参数将会被隐含传给sigsetjmp作为返回值。如果val等于0,那么sigsetjmp函数将忽略该参数而返回其他非零值。

返回值:

1)sigsetjmp函数:若返回0,表明sigsetjmp不是由siglongjmp调用的;若返回非零值,则是由siglongjmp调用而返回。
 
 */
 
 
//全局变量,用于保存跳转点及其现场
static sigjmp_buf jmpbuff;
 
//SIGINT信号处理函数
void CbSigInt(int signo)
{
    //输出信号的值
    printf(\"\\nreceive signal %d\\n\",signo);
    //长跳转到jmpbuff(即sigsetjmp函数入口处),并平衡堆栈
    siglongjmp(jmpbuff,88);
}
 
void main()
{
    int res;
    //安装SIGINT信号
    signal(SIGINT,CbSigInt);
 
    //设置跳转点
    res=sigsetjmp(jmpbuff,1);
    //第一次调用sigsetjmp时将返回0
 
    if(res==0)
        printf(\"First call sigsetjmp!\\n\");
    //从信号处理函数中跳转过来时,sigsetjmp将返回非零值
    else
    {
        //输出提示信息后退出进程
        printf(\"res=%d\\n\",res);
        printf(\"sigsetjmp is called by siglongjmp!\\n\");
        return;
    }
    //暂停执行等待信号
    pause();
}
/*
运行结果如下:
First call sigsetjmp!
^C   //ctrl +  c 
receive signal 2
res=88
sigsetjmp is called by siglongjmp!

*/

 

收藏 打印