linux信号实现同步问题

ReMeppo 2012-01-11 12:25:02
见Unix高级环境编程 10章 习题10.6:
实现父子进程的同步, 进程创建个文件写入整数0, 然后fork, 接着父子进程交替增加文件中计数器值, 每次计数器增1, 打印是哪个进程进行了增1操作
我按照书里的那五个同步函数来实现, 但是有问题, 自己觉得的原因写在代码注释里, 不知道是不是我的代码问题还是实现有问题... 求指教!!!

#include "apue.h"	  //一些头文件都放在里面
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FILE_PATH "/tmp/10-6Excise.file" //临时文件

static volatile sig_atomic_t sigflag; //进程间的信号标志
static sigset_t newmask, oldmask, zeromask; //屏蔽字

void TELL_WAIT();
void TELL_PARENT(pid_t pid);
void TELL_CHILD(pid_t pid);
void WAIT_PARENT();
void wait_CHILD();
static void sig_usr(int); //自定义信号处理函数

int main()
{
pid_t pid;
int fd;
char buf[128] = {0};

//写0
if((fd = open(FILE_PATH, O_CREAT | O_TRUNC | O_WRONLY)) < 0)
err_sys("open failed");
write(fd, "0", 1);
close(fd);

TELL_WAIT(); //屏蔽自定义的两个信号USR1 USR2

if((pid = fork()) < 0)
{
err_sys("fork error"); //err.c 文件里的自定义错误处理函数
return -1;
}
else if(pid == 0)
{
while(1)
{
WAIT_PARENT();

//子进程增1
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
err_sys("open failed");
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
close(fd);

printf("child proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
TELL_PARENT(getppid());
}
}

TELL_CHILD(pid); //先让子进程增1
while(1)
{
WAIT_CHILD();

//父进程增1
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
err_sys("open failed");
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
printf("parent proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
close(fd);

TELL_CHILD(pid);
/*
失败可能的原因: 这里发送信号到重新循环
WAIT_CHILD()之间有时间段,还没到再次循环WAIT_CHILD开始,子进程就处理完
了,而且发送完了TELL_PARENT信号,所以信号丢失了, 猜想是这样,所以在WAIT
后处理前加了个sleep, 还是无效,不知道为什么?
*/
}
}

void TELL_WAIT()
{
if(signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if(signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");

sigemptyset(&newmask);
sigemptyset(&zeromask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}

void TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR1);
}

void TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR2);
}

void WAIT_PARENT()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_BLOCK error");
}

void WAIT_CHILD()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_BLOCK error");
}

static void sig_usr(int signo)
{
sigflag = 1;
}


运行的结果:
...全文
131 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
ReMeppo 2012-01-12
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 qq120848369 的回复:]

你如果这里写了恢复屏蔽字, 那么就要这样写才合理:

C/C++ code
sigprocmask// 这里要锁信号
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < ……
[/Quote]
谢谢了.... 不过注释掉if冒似还是一样的结果. 是不是TELL与WAIT得弄成个原子操作才行啊. 从TELL运行到WAIT间如果另一进程已经处理完并发送了信号 这不就捕捉不到了么... 导致卡在那了么...
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
你如果这里写了恢复屏蔽字, 那么就要这样写才合理:

    sigprocmask// 这里要锁信号
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) //这里解信号
err_sys("SIG_BLOCK error");


但是, 你父进程先做了信号屏蔽, 代码简练一点的话就是不需要解信号, 始终锁着信号, 只在sigsuspend那里原子解阻塞就行了, 不需要sigprocmask解掉, 你只要把WAIT里的sigprocmask去掉就OK了。
如果不注释掉,面临的竞争条件是sigprocmask解信号之后, 代码执行到while(sigflag == 0)做判定, 发现sigflag的确==0,然后进入while循环, 此时信号被处理, sigflag = 1, 于是阻塞挂起. 父进程认为已经完成了通知, 子进程对于通知全然不知而阻塞, 这就是问题.
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
void WAIT_PARENT()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_BLOCK error");
}

这种地方就不应该恢复屏蔽字啊, 一恢复就不再阻塞信号了, 就会漏掉信号了.

注释掉两个WAIT里的if语句再跑跑看。
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
汗,看错了。。。我再看看
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
TELL_CHILD(pid); //先让子进程增1
while(1)
{
WAIT_CHILD();

//父进程增1
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
err_sys("open failed");
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
printf("parent proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
close(fd);

TELL_CHILD(pid); //TELL_PARENT

ReMeppo 2012-01-12
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 qq120848369 的回复:]

没问题。
[/Quote]
真悲剧 结贴了 谢谢了哈!
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
没问题。
ReMeppo 2012-01-12
  • 打赏
  • 举报
回复
注释了哟, 你能不能再帮我拷贝过去 运行看看???
#include "apue.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FILE_PATH "/tmp/10-6Excise.file"
static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;

void TELL_WAIT();
void TELL_PARENT(pid_t pid);
void TELL_CHILD(pid_t pid);
void WAIT_PARENT();
void WAIT_CHILD();
static void sig_usr(int);

int main()
{
pid_t pid;
int fd;
char buf[128] = {0};

if((fd = open(FILE_PATH, O_CREAT | O_TRUNC | O_WRONLY)) < 0)
err_sys("open failed");

write(fd, "0", 1);
close(fd);

TELL_WAIT();

if((pid = fork()) < 0)
{
err_sys("fork error");
return -1;
}
else if(pid == 0)
{
while(1)
{
printf("waite parent\n");
WAIT_PARENT();
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
err_sys("open failed");
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
close(fd);
printf("child proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
TELL_PARENT(getppid());
printf("tell parent\n");
}
}

TELL_CHILD(pid);
while(1)
{
printf("wait child\n");
WAIT_CHILD();
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
err_sys("open failed");
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
printf("parent proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
close(fd);
TELL_CHILD(pid);
printf("tell child\n");
}
}

void TELL_WAIT()
{
if(signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if(signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");

sigemptyset(&newmask);
sigemptyset(&zeromask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}

void TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR1);
}

void TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR2);
}

void WAIT_PARENT()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

//if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
// err_sys("SIG_BLOCK error");
}

void WAIT_CHILD()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

//if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
// err_sys("SIG_BLOCK error");
}

static void sig_usr(int signo)
{
sigflag = 1;
}
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
可以送一个信号, 没区别啊.

你再研究研究是否注释对了..
ReMeppo 2012-01-12
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 qq120848369 的回复:]

你如果这里写了恢复屏蔽字, 那么就要这样写才合理:

C/C++ code
sigprocmask// 这里要锁信号
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < ……
[/Quote] 理解你的意思了, 确实不应该解阻塞, 如果解了 在下一个while和sigsuspend间可能会响应这两个信号 导致sigsuspend接受不到 从而把进程挂起了...
但是我的注释掉了怎么还是失败了勒....

还有 为什么要注册两个信号 USR1 USR2啊 他们两个都发送一个信号 也可以吧....
ReMeppo 2012-01-12
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 qq120848369 的回复:]

C/C++ code
owenliang@linux-7lsl:~/csdn/src> gcc -o main main.c
owenliang@linux-7lsl:~/csdn/src> ./main
child proc 7796 write the buf = 1, num = 1
parent proc 7795 write the buf = 2, num = 2
child pr……
[/Quote] 啊 那我的怎么运行就卡那了.... 难道系统不同造成的....
root@ywj-virtual-machine:/codeTest# gcc 10-6Excise.c err.c -o 10-6Excise
root@ywj-virtual-machine:/codeTest# ./10-6Excise
wait child
waite parent
child proc 31995 write the buf = 1, num = 1
tell parent
waite parent
parent proc 31994 write the buf = 2, num = 2
tell child
wait child

qq120848369 2012-01-12
  • 打赏
  • 举报
回复
owenliang@linux-7lsl:~/csdn/src> gcc -o main main.c
owenliang@linux-7lsl:~/csdn/src> ./main
child proc 7796 write the buf = 1, num = 1
parent proc 7795 write the buf = 2, num = 2
child proc 7796 write the buf = 3, num = 3
parent proc 7795 write the buf = 4, num = 4
child proc 7796 write the buf = 5, num = 5
parent proc 7795 write the buf = 6, num = 6
^C
owenliang@linux-7lsl:~/csdn/src> cat main.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_PATH "/tmp/10-6Excise.file" //临时文件

static volatile sig_atomic_t sigflag; //进程间的信号标志
static sigset_t newmask, oldmask, zeromask; //屏蔽字

void TELL_WAIT();
void TELL_PARENT(pid_t pid);
void TELL_CHILD(pid_t pid);
void WAIT_PARENT();
void WAIT_CHILD();
static void sig_usr(int); //自定义信号处理函数

int main()
{
pid_t pid;
int fd;
char buf[128] = {0};

//写0
if((fd = open(FILE_PATH, O_CREAT | O_TRUNC | O_WRONLY)) < 0)
{}
write(fd, "0", 1);
close(fd);

TELL_WAIT(); //屏蔽自定义的两个信号USR1 USR2

if((pid = fork()) < 0)
{
return -1;
}
else if(pid == 0)
{
while(1)
{
WAIT_PARENT();

//子进程增1
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
;
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
close(fd);

printf("child proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
TELL_PARENT(getppid());
}
}

TELL_CHILD(pid); //先让子进程增1
while(1)
{
WAIT_CHILD();

//父进程增1
sleep(1);
memset(buf, 0, sizeof(buf));
if((fd = open(FILE_PATH, O_RDWR)) < 0)
;
read(fd, buf, sizeof(buf));
int i = atoi(buf) + 1;
sprintf(buf, "%d", i);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
printf("parent proc %d write the buf = %s, num = %d\n", getpid(), buf, i);
close(fd);

TELL_CHILD(pid);
/*
失败可能的原因: 这里发送信号到重新循环
WAIT_CHILD()之间有时间段,还没到再次循环WAIT_CHILD开始,子进程就处理完
了,而且发送完了TELL_PARENT信号,所以信号丢失了, 猜想是这样,所以在WAIT
后处理前加了个sleep, 还是无效,不知道为什么?
*/
}
}

void TELL_WAIT()
{
if(signal(SIGUSR1, sig_usr) == SIG_ERR)
;
if(signal(SIGUSR2, sig_usr) == SIG_ERR)
;
sigemptyset(&newmask);
sigemptyset(&zeromask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);


if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
;
}

void TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR1);
}

void TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR2);
}

void WAIT_PARENT()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;
/*
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_BLOCK error");
*/
}

void WAIT_CHILD()
{
while(sigflag == 0)
sigsuspend(&zeromask);

sigflag = 0;
/*
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_BLOCK error");
*/
}

static void sig_usr(int signo)
{
sigflag = 1;
}


我的想法没有问题, 你没理解估计有压力.
qq120848369 2012-01-12
  • 打赏
  • 举报
回复
我给你试试得了.
ReMeppo 2012-01-11
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 dongjiawei316 的回复:]

你用两个标志,分别控制试试?
[/Quote]还是一样的结果...
dongjiawei316 2012-01-11
  • 打赏
  • 举报
回复
你用两个标志,分别控制试试?
ReMeppo 2012-01-11
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 dongjiawei316 的回复:]

parent 不是比child加1了么?
[/Quote] 就卡在那不动了... 后面就没加了... 就出了这两行输出
dongjiawei316 2012-01-11
  • 打赏
  • 举报
回复
父子进程都用同一个信号标志sigflag,这样会存在竞争吧
dongjiawei316 2012-01-11
  • 打赏
  • 举报
回复
parent 不是比child加1了么?
第一章 走进linux 1.1 GNU与Linux的成长 1.2 Linux的开发模式和运作机制 1.3走进Linux内核 1.4 分析Linux内核的意义 1.5 Linux内核结构 1.6 Linux内核源代码 1.7 Linux内核源代码分析工具 第二章 Linux运行的硬件基础 2.1 i386的寄存器 2.2 内存地址 2.3 段机制和描述符 2.4 分页机制 2.5 Linux中的分页机制 2.6 Linux中的汇编语言 第三章中断机制 3.1 中断基本知识 3.2中断描述符表的初始化 3.3异常处理 3.4 中断处理 3.5中断的后半部分处理机制 第四章 进程描述 4.1 进程和程序(Process and Program) 4.2 Linux中的进程概述 4.3 task_struct结构描述 4.4 task_struct结构在内存中的存放 4.5 进程组织的方式 4.6 内核线程 4.7 进程的权能 4.8 内核同步 第五章进程调度 5.1 Linux时间系统 5.2 时钟中断 5.3 Linux的调度程序-Schedule( ) 5.4 进程切换 第六章 Linux内存管理 6.1 Linux的内存管理概述 6.2 Linux内存管理的初始化 6.3 内存的分配和回收 6.4 地址映射机制 6.5 请页机制 6.6 交换机制 6.7 缓存和刷新机制 6.8 进程的创建和执行 第七章 进程间通信 7.1 管道 7.2 信号(signal) 7.3 System V 的IPC机制 第八章 虚拟文件系统 8.1 概述 8.2 VFS中的数据结构 8.3 高速缓存 8.4 文件系统的注册、安装与拆卸 8.5 限额机制 8.6 具体文件系统举例 8.7 文件系统的系统调用 8 .8 Linux2.4文件系统的移植问题 第九章 Ext2文件系统 9.1 基本概念 9.2 Ext2的磁盘布局和数据结构 9.3 文件的访问权限和安全 9.4 链接文件 9.5 分配策略 第十章 模块机制 10.1 概述 10.2 实现机制 10.3 模块的装入和卸载 10.4 内核版本 10.5 编写内核模块 第十一章 设备驱动程序 11.1 概述 11.2 设备驱动基础 11.3 块设备驱动程序 11.4 字符设备驱动程序 第十二章 网络 12.1 概述 12.2 网络协议 12.3 套接字(socket) 12.4 套接字缓冲区(sk_buff) 12.5 网络设备接口 第十三章 启动系统 13.1 初始化流程 13.2 初始化的任务 13.3 Linux 的Boot Loarder 13.4 进入操作系统 13.5 main.c中的初始化 13.6 建立init进程 附录: 1 Linux 2.4内核API 2.1 驱动程序的基本函数 2.2 双向循环链表的操作 2.3 基本C库函数 2.4 Linux内存管理中Slab缓冲区 2.5 Linux中的VFS 2.6 Linux的连网 2.7 网络设备支持 2.8 模块支持 2.9 硬件接口 2.10 块设备 2.11 USB 设备
[14本经典Android开发教程] 8 Linux内核阅读心得体会 读核感悟 2 读核感悟 Linux内核启动 内核的生成 2 读核感悟 Linux内核启动 从hello world说起 3 读核感悟 Linux内核启动 BIOS 5 读核感悟 Linux内核启动 setup辅助程序 6 读核感悟 Linux内核启动 内核解压缩 8 读核感悟 Linux内核启动 开启页面映射 9 读核感悟 Linux内核启动 链接脚本 11 读核感悟 伪装现场 系统调用参数 13 读核感悟 伪装现场 fork 系统调用 15 读核感悟 伪装现场 内核线程: 17 读核感悟 伪装现场 信号通信 19 读核感悟 kbuild系统 内核模块的编译 22 读核感悟 kbuild系统 编译到内核和编译成模块的区别 24 读核感悟 kbuild系统 make bzImage的过程 26 读核感悟 kbuild系统 make menuconfig 31 读核感悟 文件系统 用C来实现面向对象 32 读核感悟 设计模式 用C来实现虚函数表和多态 32 读核感悟 设计模式 用C来实现继承和模板 33 读核感悟 设计模式 文件系统和设备的继承和接口 34 读核感悟 设计模式 文件系统与抽象工厂 36 读核感悟 阅读源代码技巧 查找定义 37 读核感悟 阅读源代码技巧 变量命名规则 42 读核感悟 内存管理 内核中的页表映射总结 43 读核感悟 健壮的代码 exception table 内核中的刑事档案 44 读核感悟 定时器 巧妙的定时器算法 45 读核感悟 内存管理 page fault处理流程 45 读核感悟 文件读写 select实现原理 47 读核感悟 文件读写 poll的实现原理 49 1 功能介绍: 49 2 关键的结构体: 49 3 poll的实现 49 4 性能分析: 50 读核感悟 文件读写 epoll的实现原理 50 1 功能介绍 50 2 关键结构体: 51 3 epoll create的实现 53 4 epoll ctl的实现 53 5 epoll wait的实现 54 6 性能分析 54 读核感悟 同步问题 同步问题概述 55 1 同步问题的产生背景 55 2 内核态与用户态的区别 55 读核感悟 同步问题 内核态自旋锁的实现 56 1自旋锁的总述 56 2非抢占式的自旋锁 56 3 锁的释放 57 4 与用户态的自旋锁的比较 57 5 总结 58 读核感悟 内存管理 free命令详解 58 读核感悟 文件读写 2 6 9内核中的AIO 59 1 AIO概述 59 2 内核态AIO的使用 61 读核感悟 文件读写 内核态AIO相关结构体 61 1 内核态AIO操作相关信息 61 2 AIO上下文: 63 3 AIO ring 63 4 异步I O事件的返回信息 64 读核感悟 文件读写 内核态AIO创建和提交操作 65 1 AIO上下文的创建 io setup 65 2 AIO请求的提交:io submit实现机制 66 读核感悟 文件操作 AIO操作的执行 66 1 在提交时执行AIO 66 2 在工作队列中执行AIO 66 3 负责AIO执行的核心函数aio run iocb 67 4 AIO操作的完成 67 读核感悟 文件读写 内核态是否支持非direct I O方式的AIO 67 已上传7本: [14本经典Android开发教程] 1 Android开发从入门到精通 http: download csdn net detail cleopard 8355245 [14本经典Android开发教程] 2 Android开发手册 API函数详解 http: download csdn net detail cleopard 8374487 [14本经典Android开发教程] 3 Android SDK 中文开发文档 http: download csdn net detail cleopard 8380429 [14本经典Android开发教程] 4 Android应用程序开发36技 http: download csdn net detail cleopard 8380495 [14本经典Android开发教程] 5 linux Android基础知识总结 http: download csdn net detail cleopard 8380529 [14本经典Android开发教程] 6 Android驱动开发入门及手机案例开发分析教程 http: download csdn net detail cleopard 8388019 [14本经典Android开发教程] 7 Android编程入门教程 http: download csdn net detail cleopard 8388043 剩余8本稍后上传 @或直接从这里寻找@ http: download csdn net user cleopard album @更多@ http: cleopard download csdn net 福利 http: xuemeilaile com 17份软件测试文档 http: download csdn net album detail 1425 13份WPF经典开发教程 http: download csdn net album detail 1115 C#资料合辑二[C#桌面编程入门篇] http: download csdn net album detail 957 C#资料合辑一[C#入门篇] http: download csdn net album detail 669 [Csharp高级编程 第6版 ] 共8压缩卷 http: download csdn net album detail 667 10个[精品资源]Java学习资料合辑[一] http: download csdn net album detail 663 10个C#Socket编程代码示例 http: download csdn net album detail 631 6份GDI+程序设计资源整合[全零分] http: download csdn net album detail 625 2014年移动游戏行业数据分析 http: download csdn net detail cleopard 8340331 一文读懂2014年全球互联网广告新生态 http: download csdn net detail cleopard 8340303">[14本经典Android开发教程] 8 Linux内核阅读心得体会 读核感悟 2 读核感悟 Linux内核启动 内核的生成 2 读核 [更多]

23,121

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧