linux C语言函数API--系统编程函数

四:系统编程函数

1. fork() :创建进程函数

【原型】	  pid_t fork(void); //void:函数执行时不需要传递任何的参数
  【头文件】 	#include <unistd.h>
  【功能】		创建一个新的进程
【参数】		无
【返回值】		pid_t(进程PID号的数据类型)
   成功:
   	父进程: 返回子进程的PID号
   	子进程: 0
   失败:
  	 父进程: -1
  	 不会创建出子进程

2. vfork() :创建进程函数

【原型】	  pid_t fork(void); //void:函数执行时不需要传递任何的参数
  【头文件】 	#include <unistd.h>
  【功能】		创建一个新的进程
【参数】		无
【返回值】		pid_t(进程PID号的数据类型)
   成功:
   	父进程: 返回子进程的PID号
   	子进程: 0
   失败:
  	 父进程: -1
  	 不会创建出子进程
    ======================fork()vfork()的差异对比========================================
        fork()vfock() 都是创建一个进程,那他们有什么区别呢?总结有以下三点区别: 
    1.  fork():子进程拷贝父进程的数据段,代码段 
        vfork():子进程与父进程共享数据段 
    2.  fork() 父子进程的执行次序不确定 
         vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec
         或exit 之后父进程才可能被调度运行。 
    3.  vfork()保证子进程先运行,在她调用exec 或exit 之后父进程才可能被调度运行。如果在
              调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。 
    =======================================================================================

代码演示:
————————————————————————————————————————————————————————

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

int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!\n");
    }
    //父进程
    if(x > 0 )
    {
        printf("2.I am parent!\n");
    }
    return 0;
}


函数注意点:
	1.fork函数之后在两个不同的进程之中返回的值不一样,父进程返回子进程的PID,子进程返回0.
	2.fork之后两个进程里面的数据不会因为互相干扰,即使是全局变量也不会被干扰,毫不相干。
————————————————————————————————————————————————————————


3. getpid()/getppid()查看自身与父进程的PID号

1getpid() : 查看自身的PID
  (2getppid():查看父进程的PID 
【头文件】
     	#include <sys/types.h>
     	#include <unistd.h>
	【原型】
     	pid_t getpid(void);  
    pid_t getppid(void); 
	【功能】 返回PID号
    注意:这两个函数不会执行失败,只会成功


代码演示:
————————————————————————————————————————————————————————
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!---> My ID --> %d,My parent ID --> %d\n",getpid(),getppid()); 
        //获得自身ID号,和父进程ID,如果这里父进程已经退出的话,其ID号将不是下面父进程的ID,是领养其子进程进程的ID号。
    }
    //父进程
    if(x > 0 )
    {
        printf("2.I am parent!---->My children PID --> %d\n",x);    //x为子进程的PID
    }
    return 0;
}

————————————————————————————————————————————————————————

4. wait() 回收子进程的资源

【原型】	pid_t wait(int *status); ---> 一直阻塞到子进程退出
【头文件】	
		#include <sys/types.h>
  		#include <sys/wait.h>
	【功能】 回收子进程的资源
	【参数】		status:监听的子进程退出状态的指针变量
    				 如果status为NULL,代表wait函数不关注子进程的退出状态。
	【返回值】
成功: 监听的子进程退出,返回该子进程的PID号
  		失败: -1


代码演示:
————————————————————————————————————————————————————————
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!\n"); 
        exit(9);
    }
    //父进程
    if(x > 0 )
    {
        sleep(1);
        int status;
        if(wait(&status)!= -1)
            printf("My children ID:%d\n",status);
        printf("2.I am parent!\n");
    }
    return 0;
}



函数注意点:
wait里面的参数:status不仅仅是存放退出值的,还有其它信息,
	退出值仅仅存放在status这个内存里面最后八位上,status是一个整形指针,共32位,其他24位是存放其他信息的,所以
	exit返回的值被status打印出来的东西不一定是其退出值,还包括其他信息。
	那么这个其他信息是些什么呢?包括是否是正常退出,异常退出,是否被信号杀死,是哪个信号呀之类的,其他24位就是存放它的。
	那么这么多状态信息,24位可能不够存之类的,那么可以通过宏定义来。
	比如:要获得最低的一个字节:就是退出值,那么需要一个宏定义
		WEXITSTATUS(status)就是获取退出值的。
————————————————————————————————————————————————————————



5.waitpid() 指定等待进程函数

【原型】	 	waitpid: pid_t waitpid(pid_t pid, int *status, int options);
【头文件】
  #include <sys/types.h>
  #include <sys/wait.h>
【功能】 指定某个等待某个进程。
【参数】
(1)pid:
pid < -1   :	等待一个进程组的指定的子进程(以绝对值来计算)
pid == -1 :   等待任何的子进程
pid == 0	:   等待一个进程组中任何的子进程
pid  > 0   : 	指定等待对应PID号的子进程

    (2) options: 
options == 0:   选项无效  ---> 阻塞(等价于wait)
options == WNOHANG 	  监听子进程的退出信息,非阻塞等待
options == WUNTRACED   监听子进程的暂停信号,阻塞等待
options == WCONTINUED  监听子进程的继续信号,阻塞等待

	status: 子进程的退出状态

代码演示:
————————————————————————————————————————————————————————
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!\n"); 
        exit(9);
    }
    //父进程
    if(x > 0 )
    {
        sleep(1);
        int status,options;
        waitpid(x,&status,WNOHANG);
        printf("等待pid为 %d ,状态为:%d 的非阻塞等待\n",x,status);
        printf("2.I am parent!\n");
    }
    return 0;
}

函数注意点:               
           					 等价于
   wait(&status)   <---------------->  waitpid(-1, &status, 0);

————————————————————————————————————————————————————————


6. exit(),_exit() 进程的退出

  【函数手册】	man 3 exit(),man 2 _exit()
    【exit头文件】	#include <stdlib.h>
	【_exit头文件】#include <unistd.h>
  	【exit函数原型】 void exit(int status);   ---> 在退出之前会刷新缓冲区内容
【_exit函数原型】void _exit(int status);   ---> 直接退出,不刷新缓冲区
【参数】   		status:  0  ---> 正常退出
            					 非0  --> 不正常退出
	【返回值】		无

代码演示:
————————————————————————————————————————————————————————
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!\n"); 
        printf("wo shi er zi ");        
        //exit(0);  //会打印上面这行
        _exit(0);   //不会打印上面这行
    }
    //父进程
    if(x > 0 )
    {
        sleep(1);
        wait(NULL);
        printf("2.I am parent!\n");
    }
    return 0;
}

函数注意点:
exit函数是会清理IO缓冲,也就是说printf()里面要打印的东西如果不用\n那么这个函数调用后也不会在屏幕上显示,
	因为它清理了IO缓冲。但是_exit函数是不会去清理IO缓冲区的,如果用_exit函数,那么就不会打印了。

————————————————————————————————————————————————————————


7.exec函数族

 1. 作用
把你给任务覆盖到本进程中,执行了exec函数后,后面的代码都会被屏蔽掉。

    ./test                   ./test_sh
   父进程去执行某些程序  ,子进程去执行另外程序
【头文件】	#include <unistd.h>
            extern char **environ;
【函数原型】
       int execl(const char *path, const char *arg, ...);
       int execlp(const char *file, const char *arg, ...);
       int execle(const char *path, const char *arg, ..., char * const envp[]);
       int execv(const char *path, char *const argv[]);
       int execvp(const char *file, char *const argv[]);
       int execvpe(const char *file, char *const argv[],char *const envp[]);
【函数参数】
   path/file : 要执行的程序的路径  ls ---> /bin/ls
   arg:依次写入参数,以NULL作为结束标志  "./jpeg_show","xxx.jpg",NULL  
   argv:把全部的参数都写入argv数组中  char *A[] = {"ls","-l","NULL"}
   envp:指定环境变量的路径



代码演示:

————————————————————————————————————————————————————————

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
    printf("下面创建一个进程\n");
    pid_t x = fork();       //创建了一个新进程。
    
    //子进程
    if(x == 0)
    {
        printf("1.I am children!\n"); 
        //1.execl
        //execl("./test1","./test1","abcd",NULL);
        
        //2.execv
        //char *arg[] = {"./test1","abcd",NULL};
        //execv("./test1",arg);
        
        //3.execl
        //execl("/bin/ls","ls","-l",NULL);  //执行的文件需要用绝对路径
        
        //4.execlp
        //execlp("ls","ls","-l",NULL);      //以环境变量的方式查找执行文件
    }
    //父进程
    if(x > 0 )
    {
        sleep(1);
        wait(NULL);
        printf("2.I am parent!\n");
    }
    return 0;
}

————————————————————————————————————————————————————————

函数注意点:
	1. p是代表环境变量。函数名字母p意味着环境变量,就是说用环境变量来查找ls这个程序。
2.如果用execl来呈现相同的效果的话,则需要用绝对路径。
3.这些函数总是配合fork函数一起使用的。


8.pipe() 创建无名管道

	【原型】int pipe(int pipefd[2]);
【头文件】	#include <unistd.h>
	【参数】 	pipefd[0] : 读端    pipefd[1] : 写端
	【返回值】	成功:0	 失败:-1

代码演示:
————————————————————————————————————————————————————————

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
    int fd[2];
    //1.创建无名管道
    int ret = pipe(fd); //fd[0]为读端,fd[1]为写端
    
    //2.判断是否已经创建成功,失败则返回
    if(ret == -1)
    {
        printf("create pipe error!\n");
    }
    //3.创建一个子进程
    pid_t x = fork();
    
    //4.父进程负责读取管道数据
    if( x > 0)
    {
        close(fd[1]);       //读取数据前需关闭写端。
        char buf[50] = {0}; //初始化数组
        read(fd[0],buf,sizeof(buf));    //向读端读取大小为buf的数据出来
        printf("from child process buf :%s \n",buf);
        wait(NULL);
    }
    if(x == 0)
    {
        close(fd[0]);
        write(fd[1],"what!",5);
        exit(0);
    }
    return 0;
}


————————————————————————————————————————————————————————

函数注意点:

	1.pipe创建管道时候不能再fork之后,也就是说管道的创建必须要在其子进程创建之前。
	2.无名管道是一对一的关系,只能在亲缘进程之间进行通话。而且是少量 数据传输。
	3.pipe里面的读端和写端是要经过内核kernel 进行转接数据的。
	4.无名管道不保证写入数据的原子性。



8. mkfifo() :创建有名管道

【函数手册】 man 3 mkfifo
【原型】int mkfifo(const char *pathname, mode_t mode);
	【头文件】 #include <sys/types.h>
  			   #include <sys/stat.h>
	
	【参数】 	pathname:有名管道的文件名字
			    mode:管道文件的权限  如:0777
    【返回值】  成功: 0   管道文件存在于文件系统中
   			    失败: -1

代码演示:jack进程给 Rose进程发送信息

jack代码
————————————————————————————————————————————————————————

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

int main()
{
    //1.判断有名管道是否存在,条件为真,即不存在则创建。
    if(access("/mnt/myfifo",F_OK))
    {
        int ret = mkfifo("/mnt/myfifo",0777);
        if(ret != 0 )
        {
            printf("create myfifo file error!\n");
            return 0;
        }
    }
    //2.打开有名管道
    int fd = open("/mnt/myfifo",O_RDWR);
    if(fd == -1)
    {
        printf("open this file error!\n");
        return 0;
    }
    char buf[50];
    
    //3.写入数据
    while(1)
    {
        bzero(buf,50);      //刷新缓冲区
        printf("My say:");
        fgets(buf,50,stdin);
        write(fd,buf,strlen(buf));
        if(strncmp(buf,"quit",4) == 0)
        {
            close(fd);
            break;
        }
    }
    return 0;
}
————————————————————————————————————————————————————————

Rose代码

————————————————————————————————————————————————————————

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


int main()
{
    //1.判断有名管道是否存在,如果不存在,即为真的话,则创建
    if(access("/mnt/myfifo",F_OK))
    {
        int ret = mkfifo("/mnt/myfifo",0777);
        if(ret != 0)
        {
            printf("create myfifo error!\n");
            return 0;
        }
    }
    
    //2.打开有名管道进行读取数据
    int fd = open("/mnt/myfifo",O_RDWR);
    if(fd == -1)
    {
        printf("open this file error!\n");
        return 0;
    }
    char buf[50] = {0};
    
    //3.准备读取数据
    while(1)
    {
        bzero(buf,50);      //清空缓存区
        read(fd,buf,sizeof(buf));
        printf("jack say:%s",buf);
        if(strncmp(buf,"quit",4) == 0)
        {
            close(fd);
            break;
        }
    }

    return 0;
}

————————————————————————————————————————————————————————

函数注意点:
1. access() 判断文件是否存在函数
  【函数手册】	man 3 access
【头文件】		#include <unistd.h>
  【函数原型】	 int access(const char *path, int amode);
  【参数】	path: 需要检查的文件的路径
  			amode:检查的选项
					R_OK:可读 
               		W_OK:可写 
                		X_OK:可执行
                		F_OK:是否存在

   【返回值】:	成功:0
  				失败:-1






9. kill()函数发送信号

 【函数手册】 man 2 kill
【原型】  int kill(pid_t pid, int sig);
【头文件】 #include <sys/types.h>
		   #include <signal.h>
 【参数】 pid:给哪个进程号发送信号
			 sig:发送什么信号
 【返回值】   成功:0   失败:-1


代码演示:

————————————————————————————————————————————————————————

 #include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
void fun()
{
    int a = 5,b=10;
    int c = a + b;
    printf("a+b = %d\n",c);
}
int main()
{
    //1.创建子进程
    
    pid_t x = fork();
    
    //父进程
    if(x > 0 )
    {
        sleep(3);       //可能父进程会先退出,所以需要先睡眠三秒
        kill(x,SIGUSR1);    //发送一个信号给PID为x的子进程。
        wait(NULL);         //等待子进程的退出
    }
    //子进程
    if(x == 0)
    {
        signal(SIGUSR1,fun);    //如果将来有一个信号为SIGUSR1的信号发过来,就执行函数名为fun的函数。
        pause();                //将进程挂起。
        exit(0);                //退出
    }
    return 0;
}


————————————————————————————————————————————————————————

10. signal() 函数捕捉信号

【函数手册】man 2 signal
【头文件】	#include <signal.h>
  【函数原型】 sighandler_t signal(int signum, sighandler_t handler);
  【函数参数】
   		 signum:捕捉的信号名
   		 handler:响应信号的函数
       			 SIG_IGN: 收到捕捉的信号时,忽略该信号
       			 SIG_DFL: 缺省(默认动作)
        			
 



代码演示:看9


11. pause()把本进程挂起,直到收到一个信号为止
 【函数手册】 man 2 pause
 【头文件】 #include <unistd.h>
 【函数原型】 int pause(void);

代码演示:看9



前言:信号集中包含很多信号,用户可以对信号集进行属性设置,也就是同时对多个信号进行设置。

12. sigemptyset()清空信号集合中的信号

 【头文件】 #include <signal.h>  
 【原型】	int sigemptyset(sigset_t *set); 
【功能】 清空信号集合中的信号
【返回值】    成功:0   失败:-1


13. sigfillset()把linux全部信号都加入到set信号集中

【原型】  int sigfillset(sigset_t *set);
【功能】  把linux全部信号都加入到set信号集中
【返回值】    成功:0   失败:-1


14. sigaddset() 添加信号

【原型】 int sigaddset(sigset_t *set, int signum);
【功能】 把signum这个信号添加到set信号集中
【返回值】    成功:0   失败:-1

15. sigismember() 判断信号的存在

【原型】 int sigismember(const sigset_t *set, int signum);
【功能】 判断signum信号是否在set信号集中
【返回值】 在信号集中:1  不在信号集中:0  函数执行失败:-1
【参数】  set: 信号集变量 --->  sigset_t(信号集的数据类型)
       	     	signum: 信号


16. sigprocmask() 对信号设置阻塞值

  【函数原型】int sigprocmask(int how , const sigset_t *restrict set,
 sigset_t *restrict oset);
【函数功能】 对信号集设置阻塞或者解除阻塞
	  【参数】	how:
        				SIG_BLOCK  :设置set信号集为阻塞属性
					SIG_SETMASK:新的信号集替换旧的信号集的阻塞信号
					SIG_UNBLOCK:解除set信号集的阻塞属性

      			set:新的信号集
      			oset:旧的信号集 ,一般为NULL


12-16:代码演示:

————————————————————————————————————————————————————————

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

void fun()
{
    printf("OK\n");
}
int main()  
{
    //1.创建一个子进程
    pid_t x = fork();
    if(x > 0)
    {
        int ret,i;
        
        //2.捕捉SIGINT信号
        signal(SIGINT,fun);
        //3.定义信号集
        sigset_t set;
        
        //4.清空信号集
        sigemptyset(&set);
        
        //5.把SIGINT 添加到信号集中
        sigaddset(&set,SIGINT);
        
        //6.确保SIGINT在信号集合中
        ret = sigismember(&set,SIGINT);
        if(ret == 1)
        {
            printf("SIGINT is in set!\n");
        }
        
        //7.设置信号集属性为阻塞
        sigprocmask(SIG_BLOCK,&set,NULL);
        
        for(i = 10;i>0;i--)
        {
            printf("%d s\n",i);
            sleep(1);
        }
        
        //8.解除阻塞
        sigprocmask(SIG_UNBLOCK,&set,NULL);
        //解除阻塞之后就会去执行fun函数。
        wait(NULL);
    }
    if(x == 0)
    {
        //sleep(1);
        kill(getppid(),SIGINT); //给父进程发送一个信号
        printf("I send signal to you!\n");
        exit(0);
    }
        
    return 0;
}


————————————————————————————————————————————————————————

函数注意点:

结论1:  如果发送过来的信号被设置为阻塞响应,那么这个信号不会被丢弃,直到该信号被解除阻塞为止就响应。
结论2:  进程的挂起队列中,相同的信号会被丢弃。
结论3:进程在响应信号时,信号会相互嵌套。
结论4:挂起队列不会被子进程继承,但是信号阻塞属性会被子进程继承。



17. sigdelset() 删除信号

【原型】  int sigdelset(sigset_t *set, int signum);
	  【功能】 把signum从信号集中删除
【返回值】    成功:0   失败:-1

18. ftok() 为IPC对象申请key值

【函数手册】  man 3 ftok
	【原型】  key_t ftok(const char *pathname, int proj_id);
	【头文件】 	#include <sys/types.h>
#include <sys/ipc.h>
	【功能】 为IPC对象申请key值
	【参数】  	 pathname: linux中合法的路径
			 	 proj_id: 一个整型数 0 - 256
	【返回值】   成功: 如果两个参数都一样,那么申请的key值就会一样
   				 失败: -1
	注意: 两个进程的所申请的key值一样才能够正常的通信。
代码演示:

————————————————————————————————————————————————————————

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main()
{
    key_t key1,key2,key3;
    key1 = ftok(".",20); //337051757
    key2 = ftok(".",20); //337051757
    key3 = ftok(".",21); //353828973
    printf("key1 = %d\n",key1);
    printf("key2 = %d\n",key2);
    printf("key3 = %d\n",key3);
    return 0;
}

函数注意点:
	1.函数的两个参数若是一样的话,则生成的key值是一样的。
	2. linux系统中,IPC对象分为以下几种: 消息队列、共享内存、信号量,使用这几种通信方式,都要向系统中
申请资源(key值)  ---> 用于区别不同的IPC对象。

————————————————————————————————————————————————————————

19. msgget() 为消息队列申请ID号

【函数手册】 	man 2 msgget
	【头文件】	#include <sys/types.h>
    					#include <sys/ipc.h>
    					#include <sys/msg.h>
	【原型】		int msgget(key_t key, int msgflg);
	【功能】	   为消息队列申请ID号  
	【参数】		key:  消息队列的key值
				msgflg:  消息队列的标志位
					IPC_CREAT | 0777 :  不存在则创建 并将权限设置为0777
					IPC_EXCL            存在就报错
 	【返回值】   成功: ID号
   				 失败: -1

20. msgrcv() 接收消息队列的数据

【函数手册】	 man 2 msgrcv
	【头文件】	#include <sys/types.h>
      				#include <sys/ipc.h>
  					#include <sys/msg.h>
	【原型】		ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
 long msgtyp,int msgflg);
	【功能】		接收消息队列的数据
	【参数】		msqid : 消息队列的ID号
					msgp : 数据缓冲区
					msgsz : 想要读取正文的大小
					msgtyp : 消息的类型
					msgflg : 标志位,普通属性为0
	【返回值】      成功:成功读取到的字节数     失败:-1

21. msgsnd() 发送消息到消息队列上


 	【头文件】	#include <sys/types.h>
   					#include <sys/ipc.h>
    					#include <sys/msg.h>
	【原型】		int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
	【参数】		msqid : 消息队列的ID号
					msgp : 整个消息的数据缓冲区		//结构体
					msgsz : 写入的正文的大小
					msgflg : 标志位,普通属性为0
	【功能】		发送消息到消息队列上
	【返回值】		成功: 0    失败: -1   

22. msgctl() 设置消息队列属性

【函数手册】		 man 2 msgctl
【头文件】		#include <sys/types.h>
   					#include <sys/ipc.h>
  					#include <sys/msg.h>
	【原型】		int msgctl(int msqid, int cmd, struct msqid_ds *buf);

	【功能】		设置消息队列属性
	【参数】		msqid:  消息队列的ID号
				cmd:  控制的命令字
					(1) IPC_STAT :获取该MSG的信息,存储在结构体msqid_ds中
(2) IPC_SET  :设置该MSG的信息,存储在结构体msqid_ds中
(3) IPC_RMID :立即删除该MSG,并且唤醒所有阻塞在该MSG上的进程,同时忽略第3个参数
(4) IPC_INFO :获得关于将当前系统中MSG的限制值信息
(5) MSG_INFO :获得关于当前系统中MSG的相关资源消耗信息
(6) MSG_STAT :同IPC_STAT,但msgid为该消息队列在内核中记录所有消息队列的信息的数组
                						下标,因此通过迭代所有的下标可以获得系统中所有消息队列的相关信息
				buf:  相关信息结构体缓冲区,不需要填该参数时--> NULL
  【返回值】 成功: (1) IPC_STAT,IPC_SET,IPC_RMID : 0
                   (2) IPC_INFO,MSG_INFO :内核中记录所有消息队列信息的数组的下标最大值
                   (3) MSG_STAT :返回消息队列的ID
            失败: -1
                    
    【备注】    
	    (1) IPC_STAT获得的属性信息被存放在以下结构体中:
        struct msqid_ds
        {
            struct ipc_perm msg_perm;   /**权限相关信息*/
            time_t msg_stime;           /**最后一次发送消息的时间*/
            time_t msg_rtime;           /**最后一次接收消息的时间*/
            time_t msg_ctime;           /**最后一次状态变更的时间*/
            unsigned long _msg_cbytes   /**当前消息队列中的数据尺寸*/
            msgqnum_t msg_qnum;         /**当前消息队列中的消息个数*/
            msglen_t msg_qbytes;        /**消息队列的最大数据尺寸*/
            pid_t msg_lspid;            /**最后一个发送消息的进程PID*/
            pid_t msg_lrpid;            /**最后一个接收消息的进程PID*/
        };
        
        其中,权限相关的信息用如下结构体来表示:
        struct ipc_perm
        {
            key_t _key;                 /**当前消息队列的键值key*/
            uid_t uid;                  /**当前消息队列所有者的有效UID*/
            gid_t gid;                  /**当前消息队列所有者的有效GID*/
            uid_t cuid;                 /**当前消息队列创建者的有效UID*/
            gid_t cgid;                 /**当前消息队列创建者的有效GID*/
            unsigned short mode;        /**消息队列的读写权限*/
            unsigned short _seq;        /**序列号*/
        };
        
    (2) 当使用IPC_INFO时,需要定义一个如下结构体来获取系统关于消息队列的限制值信息,并将这个
        结构体指针强制类型转化为第3个参数的类型
        struct msginfo
        {
            int msgpool;            /**系统消息总尺寸(千字节为单位)最大值*/
            int msgmap;             /**系统消息个数最大值*/
            int msgmax;             /**系统单个消息尺寸最大值*/
            int msgmnb;             /**写入消息队列字节数最大值*/
            int msgmni;             /**系统消息队列个数最大值*/
            int msgssz;             /**消息段尺寸*/
            int msgtql;             /**系统中所有消息队列中的消息总数最大值*/
            unsigned short int msgseg;  /**分配给消息队列的数据段的最大值*/
        };

    (3) 当使用选项MSG_INFO时,与IPC_INFO一样也是获得了一个msginfo结构体信息
        但有如下几点不同:
        <1>成员msgpool记录的是系统当前存在的MSG的个数总和
        <2>成员msgmap记录的是系统当前所有MSG中的消息个数总和。
        <3>成员msgtql记录的是系统当前所有MSG中所有消息的所有字节数总和。
     
            



18-22代码演示:
——————————————————————————————————————————————————————
发送端Jack代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>

//0.设置消息队列的数据包

struct msg_buf
{
    long mtype; //数据类型
    char mtext[50]; //数据正文
};
int main()
{
    key_t key;
    int msgid,ret;
    struct msg_buf gec;
    
    //1.为消息队列申请key值,
    key = ftok(".",10);
    printf("key = %x\n",key);
    
    //2.申请消息队列的ID号
    msgid = msgget(key,IPC_CREAT | 0777);
    printf("msgid = %d\n",msgid);
    
    //3.从消息队列中写入数据
    while(1)
    {
        bzero(gec.mtext,50);
        gec.mtype = 3;
        //strcpy(gec.mtext,"hello,1737!");
        fgets(gec.mtext,50,stdin);
        //4.发送消息到消息队列上
        ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
        //结构体数据缓冲区gec发送到消息队列ID号为msgid上,大小为strlen(gec.mtext),设置为普通文本0
        
        if(ret == -1)
        {
            printf("msgsnd error!\n");
            exit(1);
        }
    }
    //5.删除消息队列
    msgctl(msgid,IPC_RMID,NULL);
    //删除ID号为msgid的消息队列。
}

接收端代码Rose:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>

//设计消息队列的数据包
struct msg_buf
{
    long mtype;  //数据类型
    char mtext[50]; //数据的正文
};

int main()
{
    key_t key;
    int msgid,ret;
    struct msg_buf gec;
    
    //1.为消息队列申请key值
    key = ftok(".",10);
    printf("key = %x\n",key);
    
    //2.申请消息队列的ID号
    msgid = msgget(key,IPC_CREAT | 0777);
    printf("msgid = %d\n",msgid);
    
    //3.从消息队列中读取数据
    printf("buf:%s\n",gec.mtext);
    while(1)
    {
        bzero(gec.mtext,50);
        ret = msgrcv(msgid,&gec,sizeof(gec.mtext),3,0);
        //从ID 号为msgid队列中接收结构体缓冲区的数据,大小为sizeof(gec.mtext),消息的类型为3
        if(ret == -1)
        {
            printf("msgrcv error!\n");
            exit(1);
        }
        printf("Jact --> Rose:%s\n",gec.mtext);
    }
    
    //4.删除消息队列
    
    msgctl(msgid,IPC_RMID,NULL);
    
    return 0;
}
——————————————————————————————————————————————————————


23. shmget() 获取共享内存的ID号

【函数手册】		man 2 shmget
	【头文件】		#include <sys/ipc.h>
   					#include <sys/shm.h>
	【函数原型】		int shmget(key_t key, size_t size , 
int shmflg);
	【函数功能】		获取共享内存的ID号
	【函数参数】		key :  key值
					size :  共享内存尺寸,4096的整数倍    页的大小:4096
					shmflg :
						        IPC_CREAT  :如果key对应的共享内存不存在,则创建
        							IPC_EXCL   :如果该key对应的共享内存已存在,则报错
      							SHM_HUGETLB:使用"大页面"来分配共享内存
      							SHM_NORESERVE: 不在交换分区中为这块共享内存保留空间
       							mode        :共享内存的访问权限    (八进制,0644)

     【返回值】		成功: ID号		失败: -1
	 【备注】    	如果key指定为IPC_PRIVATE   :则会自动产生一个随机未用的新键值


24. shmat() 映射共享内存的地址

【函数手册】		 man 2 shmat
	【头文件】 		#include <sys/types.h>
#include <sys/shm.h>
	【函数原型】		void *shmat(int shmid, const void *shmaddr, int shmflg);
	【函数功能】		映射共享内存的地址
	【函数参数】		shmid: 共享内存的ID号
					shmaddr: 
        						NULL:系统自动分配未使用过的内存空间作为共享内存
        						不为NULL:手动分配内存地址
					shmflg: 标志位,普通属性填0

  	【返回值】		 成功:指向共享内存首地址的指针
     失败:(void *)-1


25. shmdt() 解除映射


【函数手册】		man 2 shmdt
【函数原型】		int shmdt(const void *shmaddr);
【函数参数】		shmaddr:需要解除映射的内存的指针
【返回值】	   成功:0	失败:-1

26. shmctl()设置共享内存的属性

【函数手册】		 man 2 shmctl
	【头文件】		#include <sys/ipc.h>
#include <sys/shm.h>
	【函数原型】		int shmctl(int shmid, int cmd  , struct shmid_ds *buf);

	【函数参数】		(1) shmid : 共享内存的ID号
					(2) cmd :
	                    		 IPC_STAT    获取属性信息,放到buf里面
                   			 IPC_SET     设置属性信息为buf指向的内容
                   			 IPC_RMID    将共享内存标记为 "即将被删除"状态
                   			 IPC_INFO    获得关于共享内存的系统限制值信息
                   			 SHM_INFO    获得系统为共享内存消耗的资源信息
                  			 SHM_STAT    同IPC_STAT,但shmid为该SHM在内核中记录所有SHM信息的数组的下标
                                因此通过迭代所有的下标可以获得系统中所有SHM的相关消息
                    			 SHM_LOCK    禁止系统将该SHM交换至swap分区
                    			 SHM_UNLOCK  允许系统将该SHM交换至swap分区
   					(3) buf 属性信息结构体指针
    
     【返回值】 成功: (1)IPC_INFO,SHM_INFO:内核中记录所有SHM信息的数组的下标最大值
                       (2)SHM_STAT:下标值为shmid的SHM的ID
               失败 -1

    
   	 【备注】    
                (1) IPC_STAT获得的属性信息被存放在以下结构体中:
                    struct shmid_ds
                    {
                        struct ipc_perm shm_perm;   /**权限相关信息*/
                        size_t shm_segsz;           /**共享内存尺寸(字节)*/
                        time_t shm_atime;           /**最后一次映射时间*/
                        time_t shm_dtime;           /**最后一个解除映射时间*/
                        time_t shm_ctime;           /**最后一次状态修改时间*/
                        pid_t   shm_cpid;           /**创建者PID*/
                        pid_t   shm_lpid;           /**最后一次映射或解除映射者PID*/
                        shmatt_t shm_nattch;        /**映射该SHM的进程个数*/
                    };
                其权限信息结构体如下:
                        struct ipc_perm
                        {
                            key_t _key;             /**该SHM的键值key */
                            uid_t uid;              /**所有者的有效UID*/
                            gid_t gid;              /**所有者的有效GID*/
                            uid_t cuid;             /**创建者的有效UID*/
                            gid_t cgid;             /**创建者的有效GID*/
                            unsigned short mode;    /**读写权限+SHM_DEST+SHM_LOCKED 标记*/
                            unsigned short _seq;    /**序列号*/
                        };
                
                (2) 当使用IPC_RMID后,上述结构体 struct ipc_perm 中的成员mode 
                    将可以检测SHM_DEST 但SHM并不会被真正删除,要等到shm_nattch等于0时才会被真正删除。
                    IPC_RMID 只是为了删除做准备,而不是立即删除。
                        
                (3)当使用IPC_INFO时,需要定义一个如下结构体来获取系统关于共享内存的限制值信息,并且
                    将这个结构体指针强制类型转化为第3个参数的类型
                    struct shminfo
                    {
                        unsigned long shmmax;       /**一块SHM的尺寸最大值*/
                        unsigned long shmmin;       /**一块SHM的尺寸最小值(永远为1*/
                        unsigned long shmmni;       /**系统中SHM对象个数最大值*/
                        unsigned long shmseg;       /**一个进程能映射的SHM个数最大值*/
                        unsigned long shmall;       /**系统中SHM使用的内存页数最大值*/
                    };
                    
                (4) 使用选项SHM_INFO时,必须保证宏_GNU_SOURCE有效,获得的相关信息被存放在如下结构体中:
                    struct shm_info
                    {
                        int used_ids;               /**当前存在的SHM个数*/
                        unsigned long shm_tot;      /**所有SHM占用的内存页总数*/
                        unsigned long shm_rss;      /**当前正在使用的SHM内存页个数*/
                        unsigned long shm_swp;      /**被置入交换分区的SHM个数*/
                        unsigned long swap_attempts; /**已废弃*/
                        unsigned long swap_successes;/**已废弃*/
                    };
                    
                (5) 注意:选项SHM_LOCK不是锁定读/写权限,而是锁定SHM能否与swap分区发生交换,一个SHM被交换至
                          swap分区后,如果被设置了SHM_LOCK,那么任何访问这个SHM的进程都将会遇到页错误。
                          进程可以通过IPC_STAT后得到的mode未检测SHM_LOCKED信息。
                
                

    

23-26代码演示:

#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    //1.为共享内存申请key值
    key_t key = ftok(".",10);
    
    //2.根据key值为共享内存申请ID号。
    int shmid = shmget(key,4096,IPC_CREAT| 0777);
    
    //3.映射共享内存的地址
    char *p = (char *)shmat(shmid,NULL,0);
    
    //4.写入数据到共享内存里面
    while(1)
    {
        fgets(p,4096,stdin);        //将数据写入到p中
        if(strncmp(p,"quit",4)==0)
            break;
        
    }
    return 0;
}

——————————————————————————————————————————————————————


#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <

int main()
{
    //1.为共享内存申请key值
    key_t key = ftok(".",10);
    
    //2.根据key值为共享内存申请ID 号
    int shmid = shmget(key,4096,IPC_CREAT | 0777);
    
    //3.映射共享内存的地址
    char *p = (char *)shmat(shmid,NULL,0);
    
    //4.不断地从共享内存中读取数据
    while(1)
    {
        bzero(p);
        printf("from shm :%s",p);
        usleep(500000);
        if(strncmp(p,"quit",4) == 0)
        break;
    }
    
    //5.解除映射
    shmdt(p);
    
    //6.删除系统中共享内存的IPC对象。
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

———————————————————————————————————————————————————————————

27.semget()获取信号量的ID

【函数手册】		man 2 semget
	【头文件】		#include <sys/types.h>
   					#include <sys/ipc.h>
  					#include <sys/sem.h>
	【函数原型】		int semget(key_t key, int nsems , int semflg ,IPC_CREAT|0777 ,IPC_EXCL);

	【函数功能】		获取信号量的ID
	【函数参数】		key: key值
					nsems: 信号量元素的个数
					semflg: 标志位
							IPC_CREAT :如果key对应的信号量不存在,则创建
							IPC_EXCL  :如果该key对应的信号量已存在,报错
							mode :信号量的访问权限(八进制)
	【返回值】	   成功: 信号量的ID		 失败: -1

28.semop() P/V操作

	【函数手册】		man 2 semop
 	【头文件】		#include <sys/types.h>
   					#include <sys/ipc.h>
   					#include <sys/sem.h>

 	【函数原型】		int semop(  int semid, struct sembuf *sops, unsigned nsops);

	【函数参数】		struct sembuf
    【参数】    semid 信号量ID
                sops  信号量操作结构体数组
                nsops 结构体数组元素个数
    【返回值】 成功 0 失败 -1
    
    【备注】    使用以上函数接口需要注意以下几点:
                (1) 信号量操作结构体的定义如下:
                    struct sembuf
                    {
                        unsigned short sem_num;     /**信号量元素序号(数组下标)*/
                        short sem_op;               /**操作参数*/
                        short sem_flg;              /**操作选项*/
                    };
                (2) sem_op根据其数值,信号量操作分成3种情况:
                    (1) sem_op > 0  :V操作( 信号量元素的值将被加上sem_op的值);
                    (2) sem_op = 0  : 等零操作(如果信号量元素的值为0,则成功返回);
                    (3) sem_op < 0  : P操作,信号量元素将被减去sem_op的绝对值



29. semctl() 设置信号量的属性

【函数手册】		man 2 semctl()
	【头文件】		#include <sys/types.h>
#include <sys/ipc.h>
  					#include <sys/sem.h>
	【函数原型】		int semctl(int semid, int semnum, int cmd,IPC_RMID, SETVAL , ...);

  	    【参数】    (1) semid 信号量ID
                (2) semnum 信号量元素序号(数组下标)
                (3) cmd   
                        <1> IPC_STAT    获取属性信息
                        <2> IPC_SET 设置属性信息
                        <3> IPC_RMID    立即删除该信号量,参数semnum将被忽略
                        <4> IPC_INFO    获得关于信号量的系统限制值信息
                        <5> SEM_INFO    获得系统为共享内存消耗的资源信息
                        <6> SEM_STAT    同IPC_STAT,但shmid为该SEM在内核中记录所有SEM信息的数组
                                        的下标,因此通过迭代所有的下标可以获得系统中所有SEM的相信息。
                        <7> GETALL      返回所有信号量元素的值,参数semnum将被忽略。
                        <8> GETNCNT     返回正阻塞在对该信号量元素P操作的进程总数
                        <9> GETPID      返回最后一个对该信号量元素等零操作的进程总数
                        <10> GETVAL     返回该信号量元素的值
                        <11> GETZCNT    返回正阻塞在对该信号量元素等零操作的进程总数
                        <12> SETALL     设置所有信号量元素的值,参数semnum将被忽略
                        <13> SETVAL     设置该信号量元素的值
    【返回值】 成功 (1) GETNCNT :semncnt
                     (2) GETPID  :sempid
                     (3) GETVAL  :semval
                     (4) GETZCNT :semzcnt
                     (5) IPC_INFO   内核中记录所有SEM信息的数组的下标最大值
                     (6) SEM_INFO 同IPC_INFO
                     (7) SEM_STAT :内核中记录所有SEM信息的数组,下标为semid的信号量的ID
                     (8) 其他 : 0
                     
                失败 :-1
                
    【备注】    
                (1) 这是一个变参函数,根据cmd的不同,可能需要第4个参数,4个参数是一个如下所示的联合体:
                    用户必须自己定义:
                    union semun
                    {
                        int     val;        /**当cmd 为 SETVAL时使用*/
                        struct semid_ds *buf; /**当cmd 为 IPC_STAT或IPC_SET时使用*/
                        unsigned short *array;  /**当cmd为GETALL或SETALL时使用*/
                        struct seminfo  *_buf;  /**当cmd为IPC_INFO时使用*/
                    };
                
                (2) 使用 IPC_STAT 和IPC_SET需要用到以下属性结构体
                    struct semid_ds
                    {
                        struct ipc_perm sem_perm;       /**权限相关信息*/
                        time_t sem_otime;               /**最后一次semop()的时间*/
                        time_t sem_ctime;               /**最后一次状态改变时间*/
                        unsigned short sem_nsems;       /**信号量元素个数*/
                    };
                    
                    <1>权限结构体如下:
                        struct ipc_perm
                        {
                            key_t   _key;       /**该信号量的键值*/
                            uid_t   uid;        /**所有者有效UID*/
                            gid_t   gid;        /**所有者有效GID*/
                            uid_t   cuid;       /**创建者有效UID*/
                            gid_t   cgid;       /**创建者有效GID*/
                            unsigned short mode;/**读写权限*/
                            unsigned short _seq;/**序列号*/
                        };
                (3) 使用IPC_INFO时,需要提供以下结构体
                    struct seminfo
                    {
                        int semmap;             /**当前系统信号量总数*/
                        int semmni;             /**系统信号量个数最大值*/
                        int semmns;             /**系统所有信号量元素总数最大值*/
                        int semmnu;             /**信号量操作撤销结构体个数最大值*/
                        int semmsl;             /**单个信号量中的信号量元素个数最大值*/
                        int semopm;             /**调用semop()时操作的信号量元素个数最大值*/
                        int semume;             /**单个进程对信号量执行连续撤销操作次数的最大值*/
                        int semusz;             /**撤销操作的结构体的尺寸*/
                        int semvmx;             /**信号量元素的值的最大值*/
                        int semaem;             /**撤销操作记录个数最大值*/
                    };
                
                (4) 使用SEM_INFO 时,与IPC_INFO一样都是得到一个seminfo结构体,但其中几个成员含义发生了变化:
                    <1> semusz此时代表系统当前存在的信号量的个数
                    <2> semaem此时代表系统当前存在的信号量中信号量元素的总数
                    
                          
                            





27-29函数代码实例:


(1)发送方:

/*==========
    头文件
  =========*/
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <sys/sem.h>

/*========
    正文
 ========*/
 int main()
 {
    //1.P/V操作资源(semop函数里的结构体)
    struct sembuf space;    //空间资源信号量操作结构体
    struct sembuf data;     //数据资源信号量操作结构体

    space.sem_num = 0;      //信号量元素序号(数组下标):此处无空间
    space.sem_op = -1;      //操作参数          P操作
    space.sem_flg = 0;      //操作选项

    data.sem_num = 1;       //有数据
    data.sem_op = 1;        //V操作
    data.sem_flg = 0;       //操作选项

    //2.为共享内存与信号量申请key值
    key_t key1 = ftok(".",10);      //共享内存key值
    key_t key2 = ftok(".",15);      //信号量key值

    //3.根据key值为共享内存与信号量申请ID号
    int shmid = shmget(key1,4096,IPC_CREAT|0777);
    int semid = semget(key2,2,IPC_CREAT|0777);

    //4.映射共享内存的地址
    char *p = (char *)shmat(shmid,NULL,0);

    //5.初始化信号量的值,空间下标为0,数据下标为1
    semctl(semid,0,SETVAL,1);   //代表有空间
    semctl(semid,1,SETVAL,0);   //代表无数据
    
    //6.写入数据到共享内存中
    while(1)
    {

    //6.在写入数据前,必须要申请空间才能写入
    semop(semid,&space,1);  //P操作,申请资源,1是代表结构体数组元素个数

    //7.写入数据
    fgets(p,4096,stdin);

    //8.写入数据后,必须让数据进行V操作
    semop(semid,&data,1);   //V操作 数据增加1(资源增加1)

    if(strncmp(p,"quit",4) == 0)
        break;
    }
    return 0;
}

———————————————————————————————————————————————————————————

(2)接收方:

/*==========
    头文件
  =========*/
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <sys/sem.h>

/*========
    正文
 ========*/
 int main()
 {
    //1.P/V操作资源(semop函数里的结构体)
    struct sembuf space;    //空间资源信号量操作结构体
    struct sembuf data;     //数据资源信号量操作结构体

    space.sem_num = 0;      //信号量元素序号(数组下标):此处无空间
    space.sem_op = 1;       //操作参数          V操作
    space.sem_flg = 0;      //操作选项

    data.sem_num = 1;       //有数据
    data.sem_op = -1;       //P操作
    data.sem_flg = 0;       //操作选项

    //2.为共享内存与信号量申请key值
    key_t key1 = ftok(".",10);      //共享内存key值
    key_t key2 = ftok(".",15);      //信号量key值

    //3.根据key值为共享内存与信号量申请ID号
    int shmid = shmget(key1,4096,IPC_CREAT|0777);
    int semid = semget(key2,2,IPC_CREAT|0777);

    //4.映射共享内存的地址
    char *p = (char *)shmat(shmid,NULL,0);

    //5.初始化信号量的值,空间下标为0,数据下标为1
    semctl(semid,0,SETVAL,0);   //代表无空间
    semctl(semid,1,SETVAL,1);   //代表有数据
    
    //6.不断地从共享内存中读取数据
    while(1)
    {
        //7.要想读取数据,必须进行p操作申请数据
        semop(semid,&data,1);
        printf("from shm: %s",p);
        
        //8.数据出来后,共享内存中就有空间
        //此时信号量元素的值:0(此处的0是上面semctl设置的) 会加上sem_op的值
        semop(semid,&space,1);  //释放资源V操作
        
        if(strncmp(p,"quit",4) == 0)
            break;
    }
    //9.解除映射
    shmdt(p);
    
    //10.删除系统中共享内存的IPC对象
    shmctl(shmid,IPC_RMID,NULL);
    semctl(semid,0,IPC_RMID,NULL);  
    
    return 0;
}

———————————————————————————————————————————————————————————

30. pthread_create() 创建线程

【函数手册】		man 3 pthread_create
【头文件】		#include <pthread.h>
	【函数原型】		int pthread_create(pthread_t *thread , const pthread_attr_t *attr ,
 void *(*start_routine) (void *), void *arg );
	【函数功能】		创建一条线程
	【函数参数】		thread :线程的ID号
					attr :线程的属性,如果为NULL,则线程为普通线程
					void *(*start_routine) (void *) :线程的执行例程函数
					arg :主线程给子线程传递的参数
	【返回值】		成功: 0    并且线程创建成功
    					失败: 错误码  并且线程创建失败


代码演示:

——————————————————————————————————————————————————
#include <stdio.h>
#include <pthread.h>    //线程头文件
#include <unistd.h>     //sleep头文件

void *routine()
{
    int i;
    for(i=50;i<127;i++)
    {
        sleep(1);
        printf("I'am a child thread===> %c\n",i);
    }
}
int main(void)
{
    //1.定义线程tid
    pthread_t tid;
    
    //2.创建线程
    printf("A thread has been created!\n");
    //线程tid号,线程属性,例程函数,函数参数
    pthread_create(&tid,NULL,routine,NULL);
    int i;
    for(i=50;i<127;i++)
    {
        sleep(1);
        printf("\t\tI'am the parent thread===> %c\n",i);
    }
    return 0;
}

——————————————————————————————————————————————————


31. pthread_join() 等待子线程的退出,回收子线程的资源

【函数手册】		 man 3 pthread_join
	【头文件】		#include <pthread.h>

	【函数原型】		int pthread_join(pthread_t thread, void **retval);
					等待thread号的线程退出
	【函数参数】		thread:线程的TID号
*retval:
               			 如果不为NULL,保存子线程的退出状态值的指针
                			 如果为NULL,不关注子线程的退出状态

	【返回值】		  成功:0	失败:错误码



32. pthread_exit() 线程退出函数

【函数手册】		man 3 pthread_exit
	【头文件】		#include <pthread.h>
	【函数原型】		void pthread_exit(void *retval);
					结束一个线程,返回一个值作为线程退出状态
	【函数参数】		retval:退出值数据的地址(不能是局部变量地址)
	【返回值】		无

——————————————————————————————————————————————————

31-32:代码演示:

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

void *routine(void *arg)
{
    sleep(1);
    char *msg = "abcd";
    pthread_exit((void *)msg);      //子线程的退出。返回值:msg
    
}
int main(void)
{
    //1.创建一条线程
    pthread_t tid;  //线程ID
    //tid 线程ID,NULL:线程属性 routine:例程函数,NULL:例程函数参数。
    pthread_create(&tid,NULL,routine,NULL); 
    
    //2.接收线程的返回值
    void *ret;
    pthread_join(tid,&ret); //等待接收TID为tid的线程的返回值,放到ret地址里面。
    
    //3.打印线程的返回值
    printf("ret :%s\n",(char *)ret);
    
    //4.主线程退出
    pthread_exit(NULL);   //主线程退出,返回值:NULL
}

33.pthread_attr_init()初始化线程属性变量


 	【函数手册】		man 3 pthread_attr_init
	【头文件】		#include <pthread.h>
	【函数原型】		int pthread_attr_init(pthread_attr_t *attr);
      				初始化一个属性变量
【函数参数】    pthread_attr_t: 属性变量的数据类型
	【返回值】		成功:0
					失败:非0的错误码


    【备注】    调用pthread_attr_init之后,pthread_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值。
                如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。
                如果pthread_attr_init实现时为属性对象分配了动态内存空间
                pthread_attr_destroy还会用无效的值初始化属性对象
                因此如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用
                将会导致其返回错误。
线程属性结构如下:

                typedef struct

                {
                       int                   detachstate;   /**线程的分离状态*/
                       int                   schedpolicy;   /** 线程调度策略*/
                       structsched_param     schedparam;    /**线程的调度参数*/
                       int                   inheritsched;  /**线程的继承性*/
                       int                   scope;         /**线程的作用域*/
                       size_t                guardsize;     /**线程栈末尾的警戒缓冲区大小 */
                       int                   stackaddr_set; /**线程的栈设置*/
                       void*                 stackaddr;     /**线程栈的位置*/
                       size_t                stacksize;     /**线程栈的大小 */
                }pthread_attr_t;

34.pthread_attr_setdetachstate()设置线程的分离属性

【函数手册】		 man 3 pthread_attr_setdetachstate
	【头文件】		#include <pthread.h>
	【函数原型】		int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
   					把detachstate属性添加到attr变量中
    【函数参数】	attr: 线程属性变量
					detachstate:分离属性的参数
 								 PTHREAD_CREATE_DETACHED   //分离属性   
        							//不需要主线程调用pthread_join函数来回收自身资源
              					PTHREAD_CREATE_JOINABLE  //接合属性
      						  //需要主线程调用pthread_join函数来回收自身资源
	【返回值】	成功:0		失败:非0的错误码

34. pthread_attr_setscope() 设置线程 __scope属性

【函数功能】    设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。
                POSIX的标准中定义了两个值: PTHREAD_SCOPE_SYSTEM 和 PTHREAD_SCOPE_PROCESS ,前者表示与系
                统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。
                目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
【头文件 】     <pthread.h>
【函数原型】   int pthread_attr_setscope (pthread_attr_t* attr, int scope);
【函数参数】   (1) attr: 线程属性。
               (2) scope:PTHREAD_SCOPE_SYSTEM : 表示与系统中所有线程一起竞争CPU时间,
                         PTHREAD_SCOPE_PROCESS: 表示仅与同进程中的线程竞争CPU
【返回值】     成功 0, 失败 -1

35. pthread_attr_getdetachstate()获取线程的分离属性

【函数原型】  int pthread_attr_getdetachstate(pthread_attr_t *attr,int *detachstate);
【头文件】     #include <pthread.h>
【功能】    获取线程的分离属性
【参数】    attr 线程属性变量
            detachstate:
                    PTHREAD_CREATE_DETACHED 分离
                    PTHREAD_CREATE_JOINABLE 接合
【返回值】 成功 0、 失败 errno

    备注:线程默认的状态是接合的。



33-34:代码演示:

——————————————————————————————————————————————————


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>      //错误码头文件
#include <pthread.h>
#include <unistd.h>

void *routine(void *arg)
{
    sleep(1);
    char *msg = "abcd";
    pthread_exit((void *)msg);      //只是退出一个线程。返回值为void *类型。
    
}
int main(void)
{
    //1.初始化线程属性变量
    pthread_attr_t attr;        //线程属性变量
    pthread_attr_init(&attr);       //初始化线程属性变量。
    
    //2.设置相关线程属性:PTHREAD_CREATE_DETACHED:分离属性。
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//将线程的属性设置为分离状态
    
    //3.创建一条分离属性的线程
    pthread_t tid;  //线程ID
    //tid 线程ID,NULL:线程属性 routine:例程函数,NULL:例程函数参数。
    pthread_create(&tid,&attr,routine,NULL);
    
    //4.接收分离属性之后的子线程的退出值。
    void *ret;
    if((errno = pthread_join(tid,&ret)) == 0)//如果返回成功(errno错误码为0),则打印退出的值
    {
        printf("ret :%s\n",(char *)ret);
    }
    else
    {
        perror("pthread_join() faild");     //线程接合失败。
    }
    
    pthread_exit(NULL); //主线程退出
    //return 0;
}
——————————————————————————————————————————————————

36. pthread_attr_setinheritsched()设置线程是否继承创建者的调度策略

【原型】 int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);
【头文件】 #include <pthread.h>
【功能】    设置线程是否继承创建者的调度策略
【参数】    attr:线程属性变量
            inheritsched 
                    PTHREAD_INHERIT_SCHED  :继承创建者的调度策略
                    PTHREAD_EXPLICIT_SCHED :使用属性变量中的调度策略
【返回值】 成功  0
            失败  errno

            

37. pthread_attr_getinheritsched()获取线程是否继承创建者的调度策略

【原型】    int pthread_attr_getinheritsched(pthread_attr_t *attr,int *inheritsched);
【头文件】 #include <pthread.h>
【功能】    获取线程是否继承创建者的调度策略
【参数】    attr:线程属性变量
            inheritsched 
                    PTHREAD_INHERIT_SCHED  :继承创建者的调度策略
                    PTHREAD_EXPLICIT_SCHED :使用属性变量中的调度策略
【返回值】 成功  0
            失败  errno   

38. pthread_attr_setschedpolicy()设置线程的调度策略

【原型】    int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
【头文件】 #include <pthread.h>
【功能】    设置线程的调度策略
【参数】    attr :线程属性变量
            policy:
                SCHED_FIFO :以先进先出的排队方式调度
                SCHED_RR   :以轮转的方式调度
                SCHED——OTHER :非实时调度的普通线程
返回值:    成功:0  失败:errno

39. pthread_attr_getschedpolicy()获取线程的调度策略

【原型】    int pthread_attr_getschedpolicy(pthread_attr_t *attr,int *policy);
【头文件】 #include <pthread.h>
【功能】    获取线程的调度策略
【参数】    attr :线程属性变量
            policy:
                SCHED_FIFO :以先进先出的排队方式调度
                SCHED_RR   :以轮转的方式调度
                SCHED——OTHER :非实时调度的普通线程
返回值:    成功:0  失败:errno

40. pthread_attr_setschedparam()设置线程静态优先级

【原型】    int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
【头文件】 #include<pthread.h>
【功能】    设置线程静态优先级
【参数】    attr :线程属性变量
            param :静态优先级 :0-99
【返回值】 成功:0,失败:errno

【备注】    0 为默认的非实时普通进程
            1~99 为实时进程,数值越大,优先级越高。
            

41. nice()获取、设置线程动态优先级

【原型】    int nice(int inc);
【头文件】 #include <unistd.h>
【功能】    获取、设置线程动态优先级
【参数】    inc :动态优先级:-20~19
【返回值】 成功:新的动态优先级
            失败 -1
【备注】    (1)动态优先级数值越大,优先级越低
            (2)如果编译器gcc的版本低于2.24(不含),该函数成功返回0

42. pthread_attr_setstacksize()设置线程栈大小

【原型】    int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
【头文件】 #include <pthread.h>
【功能】    设置线程栈大小
【参数】    attr :线程属性变量
            stacksize :线程栈的大小
【返回值】 成功 0 失败:errno

43. pthread_attr_getstacksize()获取线程栈大小

【原型】    int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);
【头文件】 #include <pthread.h>
【功能】    获取线程栈大小
【参数】    attr :线程属性变量
            stacksize :线程栈的大小
【返回值】 成功 0 失败:errno

44. pthread_attr_setguardsize()设置警戒区大小

【原型】    int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize);
【头文件】 #include <pthread.h>
【功能】    设置警戒区大小
【参数】    attr :线程数形变量
            guardsize :警戒区的大小
【返回值】 成功 0 失败:errno


45. pthread_attr_getguardsize()获取警戒区大小

【原型】    int pthread_attr_getguardsize(pthread_attr_t *attr,size_t *guardsize);
【头文件】 #include <pthread.h>
【功能】    获取警戒区大小
【参数】    attr :线程数形变量
            guardsize :警戒区的大小
【返回值】 成功 0 失败:errno

46. pthread_join()接合指定线程

【原型】    int pthread_join(pthread_t thread,void **retval);
【头文件】 #include<pthread.h>
【功能】    接合指定线程
【参数】    thread :线程ID号
            retval :储存线程退出值的内存指针
【返回值】 成功 0,失败:errno

47. pthread_tryjoin_np()接合指定线程

【原型】    int pthread_tryjoin_np(pthread_t thread,void **retval);
【头文件】 #include<pthread.h>
【功能】    接合指定线程
【参数】    thread :线程ID号
            retval :储存线程退出值的内存指针
【返回值】 成功 0,失败:errno

【备注】    pthread_join()是指定的线程如果还在运行,那么它将会阻塞等待
            pthread_tryjoin_np()指定的线程如果还在运行,那么它将会立即出错返回

48. pthread_attr_destroy() :销毁属性变量

【函数手册】		man 3 pthread_attr_destroy
	【函数原型】		int pthread_attr_destroy(pthread_attr_t *attr);
	【函数参数】  	 attr:已初始化的属性变量
  	【返回值】		成功:0		失败:非0的错误码


49.pthread_detach()先创建一条普通属性的线程,让线程自身分离出去

【函数手册】		man 3 pthread_detach
	【头文件】		#include <pthread.h>
	【函数原型】		int pthread_detach(pthread_t thread);
					函数可以使得线程被分离出去,线程结束不用被主线程回收,直接返还资源给系统
	【函数参数】		thread:线程的TID号
	【返回值】		成功:0		失败:错误码


50.pthread_self() 获取自身的TID号

	【函数手册】		 man 3 pthread_self
	【头文件】		#include <pthread.h>
	【函数原型】		pthread_t pthread_self(void);
	【返回值】		成功: 自身的TID号
					失败: 不存在的! f  

——————————————————————————————————————————————————


36-37代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>      //错误码头文件
#include <pthread.h>
#include <unistd.h>

void *routine(void *arg)
{
    //1.将自身线程分离
    pthread_detach(pthread_self());     //获得自己的tid将自己分离
    sleep(1);
    char *msg = "abcd";
    pthread_exit((void *)msg);      //只是退出一个线程。返回值为void *类型。
    
}
int main(void)
{
    //1.创建一条默认属性的线程
    pthread_t tid;  //线程ID
    //tid 线程ID,NULL:线程属性 routine:例程函数,NULL:例程函数参数。
    pthread_create(&tid,NULL,routine,NULL); //未分离
    
    //4.接收子线程的退出值。
    void *ret;
    if((errno = pthread_join(tid,&ret)) == 0)//如果返回成功(errno错误码为0),则打印退出的值
    {
        printf("ret :%s\n",(char *)ret);
    }
    else
    {
        perror("pthread_join() faild");     //线程接合失败。
    }
    
    pthread_exit(NULL); //主线程退出
    //return 0;
}

——————————————————————————————————————————————————

注意:

有两种情况:
	1.接合成功:子线程的分离在主线程调用pthread_join()接合函数之后。
	2.接合失败:子线程的分离在主线程调用pthread_join()接合函数之前。

这恰恰说明:线程是并发执行的,没有先后之分。


51. pthread_cancel() 线程的取消

 当线程收到取消请求时,线程会马上消失(注意:不是马上退出)

【函数手册】		man 3 pthread_cancel
	【函数头文件】	#include <pthread.h>
  	【函数原型】		int pthread_cancel(pthread_t thread);
					函数可以给线程号为thread的线程发送一个取消请求

	【函数参数】		thread: 需要接收取消请求的TID号
	【返回值】		成功: 0		失败: 非0的错误码


——————————————————————————————————————————————————

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
void *routine(void *arg)
{
    //1.设置线程的取消状态

    #ifdef ENABLE
        //可以被取消
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    #elif DISABLE
        //不能被取消
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    #endif
    
    //2.设置被取消的动作
    #ifdef DEFERRED
        //遇到取消点线程退出
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
    #elif ASYNCHRONOUS
        //一收到取消信号马上退出,不管线程正在执行什么
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    #endif
    
    unsigned long long i,j;
    long double f1,f2;
    for(i=0;i<10000;i++)
    {
        for(j=0;j<100000;j++)
        {
            f1 = f2;
        }
    }
    while(1)
    {
        fprintf(stderr,"%c",'X');
    }
    
    pthread_exit(NULL);
}
int main(void)
{
    //1.创建线程
    pthread_t tid;
    pthread_create(&tid,NULL,routine,NULL);
    
    //2.取消TID为tid的线程
    pthread_cancel(tid);
    
    int ret = pthread_join(tid,NULL);
    if(ret == 0)
        printf("join thread successfully!\n");
    else
        printf("join thread failed!\n");
    
    pthread_exit(NULL);
}


52. pthread_setcancelstate()设置线程响应取消的使能状态

【函数手册】		man 3 pthread_setcancelstate //如果是新线程,默认设置状态为使能
 	【头文件】		#include <pthread.h>
	【函数原型】		  int pthread_setcancelstate(int state, int *oldstate);
						给一条正在运行的线程设置state状态取消请求
	【函数参数】		state: 使能状态
							PTHREAD_CANCEL_ENABLE  //设置为使能响应取消请求
							PTHREAD_CANCEL_DISABLE //设置为不使能响应取消请求

					oldstate:原始类型变量存放的地址,一般NULL

	【返回值】		成功: 0		失败: 非0的错误码


53. pthread_setcanceltype()设置线程的取消类型

【函数手册】		man 3 pthread_setcanceltype
【函数原型】		int pthread_setcanceltype(int type, int *oldtype);
【函数参数】		type: 响应取消的类型
							PTHREAD_CANCEL_DEFERRED : 
延迟取消,必须遇到线程取消点函数才响应取消请求,遇到取消点线程就会消失
							PTHREAD_CANCEL_ASYNCHRONOUS : 立即取消
					oldtype:旧的类型的状态
   
【返回值】		成功: 0		失败: 非0的错误码


——————————————————————————————————————————————————

39-40代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
void *routine(void *arg)
{
    #ifdef ENABLE
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    #elif DISABLE
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    #endif
    
    #ifdef DEFERRED
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
    #elif ASYNCHRONOUS
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    #endif
    
    unsigned long long i,j;
    long double f1,f2;
    for(i=0;i<10000;i++)
    {
        for(j=0;j<100000;j++)
        {
            f1 = f2;
        }
    }
    while(1)
    {
        fprintf(stderr,"%c",'X');
    }
    
    pthread_exit(NULL);
}
int main(void)
{
    //1.创建线程
    pthread_t tid;
    pthread_create(&tid,NULL,routine,NULL);
    
    //2.取消TID为tid的线程
    pthread_cancel(tid);
    
    int ret = pthread_join(tid,NULL);
    if(ret == 0)
        printf("join thread successfully!\n");
    else
        printf("join thread failed!\n");
    
    pthread_exit(NULL);
}

——————————————————————————————————————————————————

注意:

1.当发送取消线程的函数给某个线程的时候,这个线程并不会马上消失,如果它在执行某些关键代码时候是要执行完毕才能够判断是否能够取消的,如果遇到取消点才能够被取消,线程才会退出。比如遇到一些取消点的函数。




54. pthread_cleanup_push()响应取消请求的例程函数

【原型】 void pthread_cleanup_push(void(*routine)(void *),void *arg);
【头文件】 #include <pthread.h>
【功能】    压栈线程的取消处理例程
【参数】    routine :线程的取消处理例程
            arg     : 线程取消处理例程的参数
            execute : 0 :弹栈线程的取消处理例程,但不执行该例程
                      非 0 :弹栈线程的取消处理例程,并执行该例程。
【返回值】 不返回


55. pthread_cleanup_pop()弹栈线程的取消处理例程

【原型】 void pthread_cleanup_pop(int execute);
【头文件】 #include <pthread.h>
【功能】    弹栈线程的取消处理例程
【参数】     execute : 0 :弹栈线程的取消处理例程,但不执行该例程
                      非 0 :弹栈线程的取消处理例程,并执行该例程。
【返回值】 不返回

【备注】    (1)使用pthread_cleanup_push()可以为线程的取消请求压入多个处理例程,
                 这些例程会以栈的形式保留起来,在线程被取消之后,它们以弹栈的形式后进先出地依次被执行
            (2)这两个函数必须配套使用,而且必须出现在同一层代码块中。


——————————————————————————————————————————————————


代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void *count(void *arg)
{
    int i = 1;
    while(1)
    {
        sleep(1);
        printf("sec :%d\n",i++);
    }
}

//解锁函数
void handler(void *arg)
{
    printf("[%u] is cancelled. \n",(unsigned)pthread_self());
    pthread_mutex_t *pm = (pthread_mutex_t *)arg;
    
    pthread_mutex_unlock(pm);
}
void *routine(void *arg)
{
    #ifdef CLEANUP
    //如果有一天有cancel信号取消此线程,则执行以下函数handler进行解锁
    pthread_cleanup_push(handler,(void *)&m);
    #endif
    
    //上锁
    pthread_mutex_lock(&m);
    printf("[%u] lock the mutex!\n",(unsigned)pthread_self());
    sleep(2);
    printf("[%u]: job finished!\n",(unsigned)pthread_self());

    //解锁
    pthread_mutex_unlock(&m);
    printf("[%u] unlock the mutex!\n",(unsigned)pthread_self());

    #ifdef CLEANUP
    //不执行解锁函数,直接删除
    pthread_cleanup_pop(0);
    #endif
 
    pthread_exit(NULL);
}
int main(int argc,char **argv)
{
    //1.创建3条线程
    pthread_t t,t1,t2;
    pthread_create(&t,NULL,count,NULL);
    
    pthread_create(&t1,NULL,routine,NULL);
    pthread_create(&t2,NULL,routine,NULL);
    
    //2.打印线程的TID号
    printf("[%u] ==>t1\n",(unsigned)t1);
    printf("[%u] ==>t2\n",(unsigned)t2);
    printf("[%u] ==>main\n",(unsigned)pthread_self());
    
    sleep(1);
    
    //3.取消t1,t2线程。
    pthread_cancel(t1);
    pthread_cancel(t2);
    
    sleep(2);
    //主线程上锁
    pthread_mutex_lock(&m);
    printf("[%u] locked the mutex!\n",
        (unsigned)pthread_self());
    //主线程解锁
    pthread_mutex_unlock(&m);
    
    exit(0);
}



56. sem_open() 创建或者打开有名信号量

【函数手册】		man 3 sem_open

		【头文件】		#include <fcntl.h> 
#include <sys/stat.h> 
  						#include <semaphore.h>
		【函数原型】		sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
		【函数功能】		打开有名信号量
		【函数参数】		name:有名信号量的名字,是自定义的,"/名字"  ---> 创建有名信号量
						oflag:
								O_CREAT:如果信号量不存在,则创建信号量
								O_EXCL: 存在则报错
						mode:八进制权限 0777 
						value:信号量的初始值

		【返回值】		sem_t是有名信号量的数据类型	成功:信号量的地址		失败:NULL

57. sem_close()关闭有名信号量

【函数手册】	 man 3 sem_close
	  【头文件】		#include <semaphore.h>
	  【函数原型】	int sem_close(sem_t *sem);
	  【函数参数】	sem:有名信号量的地址
   	  【返回值】		   成功:0	失败:-1

58. sem_unlink() 删除信号量

【函数手册】		man 3 sem_unlink
   	【头文件】		#include <semaphore.h>
	【函数原型】		int sem_unlink(const char *name);
	【函数参数】		name:有名信号量的名字
	【参数】			成功: 0    失败: -1

——————————————————————————————————————————————————

42-44代码演示:

(1)Jack
#include <semaphore.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>          
#include <sys/stat.h> 

int main()
{
    //1.申请key值
    key_t key = ftok(".",10);

    //2.申请ID号
    int shmid = shmget(key,4096,IPC_CREAT|0777);
    
    //3.映射共享内存
    char *p = (char *)shmat(shmid,NULL,0);

    //4.创建有名信号量
    sem_t* sem;
    sem = sem_open("/testaaa",O_CREAT,0777,0);
    
    //5.写入数据到共享内存中
    while(1)
    {
        //6.先键盘输入数据
        fgets(p,4096,stdin);
        
        //7.信号量的V操作
        sem_post(sem);
        
        //8.如果收到了quit,就退出
        if(strncmp(p,"quit",4) == 0)
        {
            break;
        }
    }
    
    //9.关闭信号量
    sem_close(sem);
    
    //10.删除信号量
    sem_unlink("/testaaa");
    
    return 0;
}
——————————————————————————————————————————————————

(2)Rose
#include <semaphore.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>          
#include <sys/stat.h> 

int main()
{
    //1.申请key值
    key_t key = ftok(".",10);

    //2.申请ID号
    int shmid = shmget(key,4096,IPC_CREAT|0777);
    
    //3.映射共享内存
    char *p = (char *)shmat(shmid,NULL,0);

    //4.创建有名信号量
    sem_t* sem;
    sem = sem_open("/testaaa",O_CREAT,0777,0);
    
    //5.从共享内存中读取数据
    while(1)
    {
        //6.信号量的P操作
        sem_wait(sem);
        
        //7.读取共享内存中数据
        printf("%s",p);
        
        //8.如果收到了quit,就退出
        if(strncmp(p,"quit",4) == 0)
        {
            break;
        }
    }
    
    //9.关闭信号量
    sem_close(sem);
    
    //10.删除信号量
    sem_unlink("/testaaa");
    
    //11.解除映射
    shmdt(p);

    return 0;
}

59. sem_wait()实现信号量的P操作

 P操作: (资源数减1--->  sem_wait   ---> man 3 sem_wait

    【头文件】	 #include <semaphore.h>
	【函数原型】		int sem_wait(sem_t *sem);
【函数功能】		只有sem的值大于0时,才能进行P操作
          			 如果sem的值=0,一直等待直到能够P操作为止

	【函数参数】		sem:有名信号量的地址
 	【返回值】		成功:0		失败:-1  信号量的值没有改变

60.sem_post()实现信号量的V操作

 V操作:  (资源数加1----> sem_post()  ---> man 3 sem_post

    【头文件】		#include <semaphore.h>
【函数原型】  int sem_post(sem_t *sem);
【函数功能】		如果信号量的值大于0,那么就会一直等到别的进程
线程调用sem_wait把信号量的值变成0为止
	【函数参数】		sem:有名信号量的地址

	【返回值】      成功:0		失败:-1  信号量的值没有改变



45-46 函数演示:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

sem_t space,data;
void *routine(void *arg)
{
    char *buf = (char *)arg;
    while(1)
    {
        //P操作,申请数据资源
        sem_wait(&data);
        printf("bytes:%d\n",strlen(buf));
        //V操作,释放空间资源
        sem_post(&space);
    }
}

int main(void)
{
    //初始化信号量
    sem_init(&space,0,1);   //0是线程间共享。
    sem_init(&data,0,0);    //数据为0
    char buf[32];
    pthread_t tid;
    pthread_create(&tid,NULL,routine,(void *)buf);
    
    while(1)
    {
        //P操作:申请空间资源
        sem_wait(&space);
        bzero(buf,32);
        fgets(buf,32,stdin);
        //V操作:增加资源
        sem_post(&data);
    }
    return 0;
}


61. sem_init() 初始化无名信号量

【函数手册】	 man 3 sem_init

  	【头文件】		#include <semaphore.h>
	【函数原型】		int sem_init(sem_t *sem , int pshared , unsigned int value);
	【函数参数】		sem : 无名信号量变量的地址
					pshared : 0  --> 作用于线程之间    非0  ---> 作用于进程之间
					value : 信号量的初始值
	【返回值】  		成功: 0		失败: -1


62. sem_destroy() 销毁无名信号量

sem_destroy()    ----  man 3 sem_destroy
	【头文件】		#include <semaphore.h>
	【函数原型】		  int sem_destroy(sem_t *sem);
	【函数参数】		sem:已被初始化的无名信号量变量地址
	【返回值】		成功: 0		失败: -1



47-48 代码演示:

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

sem_t sem;

void *routine(void *arg)
{
    //我想吃面包,首先要把资源拿下来  资源数 1-->0
    sem_wait(&sem);

    int i;
    char buf[50] = {"helloworld"};

    for(i=0;buf[i] != '\0';i++)
    {
        fprintf(stderr,"%c",buf[i]);
        usleep(500000);
    }
    
    //我吃饱了,把剩下的面包(资源)还出去   资源 0->1
    sem_post(&sem);

    pthread_exit(NULL);
}

int main()
{
    //初始化无名信号量
    sem_init(&sem,0,1);
    
    int i;
    pthread_t tid[3];
    
    for(i=0;i<3;i++)
    {
        pthread_create(&tid[i],NULL,routine,NULL);
    }
    
    for(i=0;i<3;i++)
    {
        pthread_join(tid[i],NULL);
    }
    
    //销毁无名信号量
    sem_destroy(&sem);
    
    return 0;
}

63. sigqueue()给进程发送信号同时携带数据

 【原型】    int sigqueue(pid_t pid,int sig,const union siguval value);
                struct sigaction 
                            {
                                void (*sa_handler)(int);    
                                void (*sa_sigaction)(int,siginfo_t *,void *);
                                sigset_t sa_mask;
                                int sa_flags;
                                void (*sa_restorer)(void);
                            };
    【头文件】 #include <signal.h>
    【功能】    给某进程发送一个指定的信号,同时携带一些数据
    【参数】    (1) pid : 目标进程PID
                (2) sig : 要发送的信号
                (3) value :携带的额外数据
    【返回值】 成功 0 失败 -1
    【注意】    目标进程能获取由sigqueue()发送过去的额外数据value的前提是:
                必须设置SA_SIGINFO标识。
                

64. sigaction() 捕捉指定信号,获取信号携带的额外数据

【原型】    int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
    【头文件】 #include <signal.h>
    【功能】    捕捉一个指定的信号,且可以通过扩展响应函数来获取信号携带的额外数据
    【参数】    (1) signum要捕捉的信号
                (2) act : 
                        <1> sa_handler :标准信号响应函数指针
                        <2> sa_sigaction :扩展信号响应函数指针
                        <3> sa_mask :临时信号阻塞掩码
                        <4> sa_flags: SA_NOCLDSTOP :子进程暂停时不提醒
                                      SA_NOCLDWAIT :使子进程死亡时跳出僵尸态
                                      SA_NODEFER   :不屏蔽来自本信号响应函数内部的信号
                                      SA_ONSTACK   :信号响应函数在替补栈中分配内存
                                      SA_RESETHAND :响应函数执行一遍之后重置该信号响应策略
                                      SA_RESTART   :自动重启被该信号中断的某些系统调用
                                      SA_SIGINFO   :使用扩展信号响应函数而不是标准响应函数
                        <5> sa_restorer: 废弃的接口
                (3) oldact 原有的信号处理函数
    【返回值】 成功 0 失败 -1
    
    

65. pthread_cond_init()初始化条件变量

  【原型】    int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
    【功能】    初始化条件变量
    【头文件】 #include <semaphore.h>
    【参数】    (1) cond 条件变量
                (2) attr 条件变量的属性,一般始终为 0
    【返回值】 成功 0 失败 -1
    

66. pthread_cond_destroy()销毁条件变量

【原型】    int pthread_cond_destroy(pthread_cond_t *cond);
    【功能】    销毁条件变量
    【头文件】 #include <semaphore.h>
    【参数】    (1) cond 条件变量
    【返回值】 成功 0 失败 -1
    

67. pthread_cond_timedwait() 条件变量等待获取配套互斥锁

【原型】    int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                                           pthread_mutex_t *restrict mutex,
                                           const struct timespec *restrict abstime);
    【功能】    进入条件变量等待队列同时获取配套的互斥锁,并且提供超时时间限制
    【头文件】 #include<semaphore.h>
    【参数】    (1) cond 条件变量   
                (2) mutex 互斥锁
                (3) abstime 超时时间限制
    【返回值】 成功 0 失败 -1
    

68. pthread_cond_wait()条件变量等待获取配套互斥锁

【原型】    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
    【功能】    进入条件变量等待队列同时对获取配套的互斥锁
    【头文件】 #include<semaphore.h>
    【参数】    (1) cond 条件变量   
                (2) mutex 互斥锁
    【返回值】 成功 0 失败 -1
    【备注】
首先对互斥锁进行解锁(条件判断不属于函数的功能),解锁之后自身睡眠等待条件达成
注意这时候函数并未返回,因为还缺少一步,待条件完成后重新加锁。
pthread_cond_wait提供的重要功能是保证这两个操作一定是原子操作不可分割。

69. pthread_cond_broadcast()唤醒条件变量等待线程


    【原型】    int pthread_cond_broadcast(pthread_cond_t *cond);
    【功能】    唤醒全部等待队列中的线程
    【头文件】 #incude <semaphore.h>
    【参数】    cond 条件变量
    【返回值】 成功 0 失败 -1
    

70. pthread_cond_signal()唤醒条件变量等待线程

  【原型】    int pthread_cond_signal(pthread_cond_t *cond);
    【功能】    唤醒一个等待中的线程
    【头文件】 #incude <semaphore.h>
    【参数】    cond 条件变量
    【返回值】 成功 0 失败 -1
    

71. init_pool() 初始化线程池

    
    【原型】    bool init_pool(thread_pool *pool,unsigned int threads_number);
    【功能】    创建一个新的线程,包含threads_number个活跃线程
    【头文件】 #include<thread_pool.h>
    【参数】    (1) pool 线程池指针
                (2) thread_number :初始化活跃线程个数(大于或等于1)
    【返回值】 成功 true 失败 false
    【注意】    线程池最少线程个数为 1
    

72. add_task()投送任务

 【原型】    bool add_task(thread_pool *pool,void *(*do_task)(void *arg),void *arg);
    【功能】    往线程池投送任务
    【头文件】 #include<thread_pool.h>
    【参数】    (1) pool:线程池指针
                (2) do_task :投送至线程池的执行例程
                (3) arg :执行例程do_task的参数,若该执行例程不需要参数可设置为NULL
    【返回值】 成功 true 失败 false
    【备注】    任务队列中最大任务个数为MAX_WAITING_TASKS
    

73. add_thread()增加活跃线程

【原型】    int add_thread(thread_pool *pool,unsigned int additional_threads);
    【功能】    增加线程池中活跃线程的个数
    【头文件】 #include <thread_pool.h>
    【参数】    (1) pool 需要增加线程的线程池指针
                (2) additional_threads :新增线程个数
    【返回值】  >0 : 实际新增线程个数
                -1 : 失败

74. remove_thread()删除活跃线程

 【原型】    int remove_thread(thread_pool *pool,unsigned int removing_threads);
    【功能】    删除线程池中活跃线程的个数
    【头文件】 #include <thread_pool.h>
    【参数】    (1) pool :需要删除线程的线程的个数
                (2) removing_threads :要删除的线程的个数,该参数设置为0时直接返回当前线程池
                    线程总数,对线程池不造成任何其他影响
    【返回值】 > 0 当前线程池剩余线程个数
                -1 失败
    【备注】    (1)线程池至少会存在1条活跃线程
                (2) 如果被删除的线程正在执行任务,则将等待其完成任务之后删除

77. destroy_pool()销毁线程池

  【原型】    bool destroy_pool(thread_pool *pool);
    【功能】    阻塞等待所有任务完成,然后立即销毁整个线程池,释放所有资源和内存
    【头文件】 #include <thread_pool.h>
    【参数】    (1) pool :将要销毁的线程池
    【返回值】 成功: true 失败 false
    

78. pthread_mutex_init() 初始化互斥锁

 【原型】    int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                const pthread_mutexattr_t *restrict attr);
    【功能】    初始化一个互斥锁
    【头文件】 #include <pthread.h>
    【参数】    (1)mutex:互斥锁地址。类型是 pthread_mutex_t 。
                (2)attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
    【返回值】 成功:0,成功申请的锁默认是打开的
                失败:非 0 错误码
                
    【备注】    可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁
                比如:
                pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
                (1)这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化
                (2)不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
    

79. pthread_mutex_lock() 阻塞等待上锁

  【原型】    int pthread_mutex_lock(pthread_mutex_t *mutex);
    【功能】    对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。
    【头文件】   #include <pthread.h>
    【参数】    mutex 互斥锁地址
    【返回值】 成功上锁:返回 0
                失败:返回非0 错误码

80. pthread_mutex_trylock()非阻塞等待上锁

【原型】    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    【功能】    对互斥锁上锁,若互斥锁已经上锁,不阻塞,直接返回错误
    【头文件】  #include <pthread.h>
    【参数】    mutex:互斥锁地址。
    【返回值】 成功上锁: 返回0
                失败 :EBUSY

81. pthread_mutex_unlock()解锁

【原型】     int pthread_mutex_unlock(pthread_mutex_t *mutex);
    【功能】    对指定的互斥锁解锁。
    【头文件】 #include <pthread.h>
    【参数】    mutex:互斥锁地址。
    【返回值】 成功:0
                失败:非 0 错误码
                

82. pthread_mutex_destroy()销毁互斥锁

 【原型】    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    【功能】    销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
    【头文件】 #include <pthread.h>
    【参数】    mutex:互斥锁地址。
    【返回值】 成功:0 
                失败:非 0 错误码

       
发布了9 篇原创文章 · 获赞 10 · 访问量 281

猜你喜欢

转载自blog.csdn.net/my___sunshine/article/details/103999597